diff options
Diffstat (limited to 'src')
74 files changed, 4539 insertions, 1631 deletions
diff --git a/src/core/xbblockread.cpp b/src/core/xbblockread.cpp index 09076b1..7e2c5fc 100755 --- a/src/core/xbblockread.cpp +++ b/src/core/xbblockread.cpp @@ -50,10 +50,14 @@ xbBlockRead::~xbBlockRead(){ #ifdef XB_DEBUG_SUPPORT void xbBlockRead::DumpReadBlockInternals(){ + + xbUInt32 ulRecCnt; + dbf->GetRecordCnt( ulRecCnt ); + std::cout << "------- DumpBlockInternals ---------" << std::endl; std::cout << "Dflt Blk Size = [" << dbf->GetXbasePtr()->GetDefaultBlockReadSize() << "]" << std::endl; - std::cout << "Dbf Record Count = [" << dbf->GetRecordCount() << "]" << std::endl; + std::cout << "Dbf Record Count = [" << ulRecCnt << "]" << std::endl; std::cout << "Dbf Record Len = [" << dbf->GetRecordLen() << "]" << std::endl; std::cout << "ulBlkSize = [" << ulBlkSize << "]" << std::endl; std::cout << "ulMaxRecs = [" << ulMaxRecs << "]" << std::endl; @@ -136,7 +140,12 @@ xbInt16 xbBlockRead::GetBlockForRecNo( xbUInt32 ulRecNo ){ xbInt16 iErrorStop = 0; try{ - xbUInt32 ulDbfRecCnt = dbf->GetRecordCount(); + xbUInt32 ulDbfRecCnt; + + if(( iRc = dbf->GetRecordCnt( ulDbfRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } // calc to determine block number for the requested record, 0 based offset xbUInt32 ulBlockNo = (xbUInt32)(ulRecNo / ulMaxRecs); @@ -183,7 +192,6 @@ xbInt16 xbBlockRead::GetBlockForRecNo( xbUInt32 ulRecNo ){ return iRc; } - /************************************************************************/ //! @brief Get the current block size. /*! diff --git a/src/core/xbdate.cpp b/src/core/xbdate.cpp index ba0fdf1..54834ac 100755 --- a/src/core/xbdate.cpp +++ b/src/core/xbdate.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -25,22 +25,36 @@ int xbDate::iAggregatedDaysInMonths[2][13]; //! @brief Constructor. xbDate::xbDate() { - Sysdate(); + sDate8.Set( "" ); SetDateTables(); } /*************************************************************************/ //! @brief Constructor. /*! + \param iInitOpt - Constructor to use to initialize date static variables + Called by the main xbXbase::xbXBase constructor +*/ + +xbDate::xbDate( xbUInt16 ) { + + SetDateTables(); + Sysdate(); +} +/*************************************************************************/ +//! @brief Constructor. +/*! \param sDate8In - Input date. */ xbDate::xbDate( const xbString & sDate8In ) { + if( DateIsValid( sDate8In )) sDate8.Set( sDate8In ); else - Sysdate(); - SetDateTables(); + sDate8.Set( "" ); + + // SetDateTables(); } /*************************************************************************/ @@ -53,8 +67,9 @@ xbDate::xbDate( const char * sDate8In ) { if( DateIsValid( sDate8In )) sDate8.Set( sDate8In ); else - Sysdate(); - SetDateTables(); + sDate8.Set( "" ); + + // SetDateTables(); } /*************************************************************************/ @@ -63,7 +78,7 @@ xbDate::xbDate( const char * sDate8In ) { \param lJulDate - Input julian date. */ xbDate::xbDate( xbInt32 lJulDate ) { - SetDateTables(); + // SetDateTables(); JulToDate8( lJulDate ); } @@ -81,68 +96,76 @@ void xbDate::operator=( const xbDate & dt ){ /*************************************************************************/ //! @brief operator += /*! - This routine adds lDays to the date + This routine adds lDays to the date if the date is not null. \param lDays - Number of days to add to the date. */ void xbDate::operator+=( xbInt32 lDays ){ - JulToDate8( JulianDays() + lDays ); + if( !IsNull() ) + JulToDate8( JulianDays() + lDays ); } /*************************************************************************/ //! @brief operator -= /*! - This routine subtracts lDays from the date. + This routine subtracts lDays from the date if the date is not null. \param lDays - Number of days to subtract from the date. */ void xbDate::operator-=( xbInt32 lDays ){ - JulToDate8( JulianDays() - lDays ); + if( !IsNull() ) + JulToDate8( JulianDays() - lDays ); } /*************************************************************************/ //! @brief operator ++ /*! - This routine adds one day to the date field. + This routine adds one day to the date field if the date is not null. */ void xbDate::operator++(xbInt32){ - *this+=1; + if( !IsNull() ) + *this+=1; } /*************************************************************************/ //! @brief operator -- /*! - This routine subtracts one day from the date field. + This routine subtracts one day from the date field if the date is not null. */ void xbDate::operator--(xbInt32){ - *this-=1; - return; + if( !IsNull()) + *this-=1; } /*************************************************************************/ //! @brief operator - /*! This routine subtracts one date from another date returning the difference. - \param dt - Date to subtract. - \returns Number of days + \param dt - Date to subtract + \returns Number of days difference or zero if one of the dates is null. */ xbInt32 xbDate::operator-( const xbDate &dt ) const{ - return JulianDays() - dt.JulianDays(); + if( !IsNull() && !dt.IsNull() ) + return JulianDays() - dt.JulianDays(); + else + return 0; } /*************************************************************************/ //! @brief operator + /*! - This routine adds additional days to a date field. + This routine adds additional days to a valid date field. \param lCount - Number of days to add. \returns New date in CCYYMMDD format. */ const char *xbDate::operator+( xbInt32 lCount ){ - JulToDate8( JulianDays() + lCount ); + if( !IsNull() ) + JulToDate8( JulianDays() + lCount ); return sDate8.Str(); } /*************************************************************************/ //! @brief operator - /*! - This routine subtracts days from a date field. + This routine subtracts days from a valid date field. \param lCount - Number of days to subtract. \returns New date in CCYYMMDD format. */ const char *xbDate::operator-( xbInt32 lCount ){ - JulToDate8( JulianDays() - lCount ); + if( !IsNull() ) + JulToDate8( JulianDays() - lCount ); return sDate8; } /*************************************************************************/ @@ -232,13 +255,14 @@ xbBool xbDate::operator>=( const xbDate &dt ) const { /*! This routine calculates a century for a given year. It uses an 80/20 rolling date window to calculate the century. - + \param iCalcYear - Two digit year to calculate a century for. \returns Century calculated for the two digit year. */ xbInt16 xbDate::CalcRollingCenturyForYear( xbInt16 iCalcYear ) const { xbDate d; + d.Sysdate(); xbInt16 iThisYear = d.YearOf(); xbInt16 iThisCentury = d.CenturyOf(); iThisYear -= (iThisCentury * 100); @@ -254,14 +278,18 @@ xbInt16 xbDate::CalcRollingCenturyForYear( xbInt16 iCalcYear ) const { /*************************************************************************/ //! @brief Get century for date. /*! - \returns This routine returns the century from the date. + \returns the century from the valid date.\ or 0 for a null date. */ xbInt16 xbDate::CenturyOf() const { - char Century[3]; - Century[0] = sDate8[1]; - Century[1] = sDate8[2]; - Century[2] = 0x00; - return( atoi( Century )); + if( !IsNull() ){ + char Century[3]; + Century[0] = sDate8[1]; + Century[1] = sDate8[2]; + Century[2] = 0x00; + return( atoi( Century )); + } else { + return 0; + } } /*************************************************************************/ //! @brief Get the day of the week. @@ -271,22 +299,24 @@ xbInt16 xbDate::CenturyOf() const { */ xbInt16 xbDate::CharDayOf( xbString &sOutCharDay ) { - struct tm tblock; - char buf[25]; - - tblock.tm_year = YearOf() - 1900; - tblock.tm_mon = MonthOf() - 1; - tblock.tm_mday = DayOf( XB_FMT_MONTH ); - tblock.tm_hour = 0; - tblock.tm_min = 0; - tblock.tm_sec = 1; - tblock.tm_isdst = -1; - if( mktime( &tblock ) == -1 ){ - sOutCharDay = "" ; - return XB_INVALID_DATE; - } else { - strftime( buf, 25, "%A", &tblock ); - sOutCharDay = buf; + if( !IsNull()){ + struct tm tblock; + char buf[25]; + + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharDay = "" ; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%A", &tblock ); + sOutCharDay = buf; + } } return XB_NO_ERROR;; } @@ -297,21 +327,24 @@ xbInt16 xbDate::CharDayOf( xbString &sOutCharDay ) { \returns XB_INVALID_DATE<br>XB_NO_ERROR */ xbInt16 xbDate::CharMonthOf( xbString &sOutCharMonth ) { - struct tm tblock; - char buf[25]; - tblock.tm_year = YearOf() - 1900; - tblock.tm_mon = MonthOf() - 1; - tblock.tm_mday = DayOf( XB_FMT_MONTH ); - tblock.tm_hour = 0; - tblock.tm_min = 0; - tblock.tm_sec = 1; - tblock.tm_isdst = -1; - if( mktime( &tblock ) == -1 ){ - sOutCharMonth = ""; - return XB_INVALID_DATE; - } else { - strftime( buf, 25, "%B", &tblock ); - sOutCharMonth = buf; + + if( !IsNull()){ + struct tm tblock; + char buf[25]; + tblock.tm_year = YearOf() - 1900; + tblock.tm_mon = MonthOf() - 1; + tblock.tm_mday = DayOf( XB_FMT_MONTH ); + tblock.tm_hour = 0; + tblock.tm_min = 0; + tblock.tm_sec = 1; + tblock.tm_isdst = -1; + if( mktime( &tblock ) == -1 ){ + sOutCharMonth = ""; + return XB_INVALID_DATE; + } else { + strftime( buf, 25, "%B", &tblock ); + sOutCharMonth = buf; + } } return XB_NO_ERROR; } @@ -387,45 +420,48 @@ xbBool xbDate::DateIsValid( const xbString &sDateIn ) const { xbInt16 xbDate::DayOf( xbInt16 iFormat ) const { - xbInt16 iOutDay = 0; - char sDay[3]; - xbInt16 iDay, iMonth, iYear, iDay2; - - // check for valid format switch - if( iFormat!=XB_FMT_WEEK && iFormat!=XB_FMT_MONTH && iFormat!=XB_FMT_YEAR ) - return XB_INVALID_OPTION; - - if( iFormat == XB_FMT_WEEK ){ - //DayOf( XB_FMT_MONTH, iDay ); - iDay = DayOf( XB_FMT_MONTH ); - iMonth = MonthOf(); - iYear = YearOf(); - - // The following formula uses Zeller's Congruence to determine the day of the week - if( iMonth > 2 ) // init to February - iMonth -= 2; - else { - iMonth += 10; - iYear--; - } - iDay2 = ((13 * iMonth - 1) / 5) + iDay + ( iYear % 100 ) + - (( iYear % 100 ) / 4) + ((iYear /100 ) / 4 ) - 2 * - ( iYear / 100 ) + 77 ; + if( !IsNull()){ + xbInt16 iOutDay = 0; + char sDay[3]; + xbInt16 iDay, iMonth, iYear, iDay2; + + // check for valid format switch + if( iFormat!=XB_FMT_WEEK && iFormat!=XB_FMT_MONTH && iFormat!=XB_FMT_YEAR ) + return XB_INVALID_OPTION; + + if( iFormat == XB_FMT_WEEK ){ + //DayOf( XB_FMT_MONTH, iDay ); + iDay = DayOf( XB_FMT_MONTH ); + iMonth = MonthOf(); + iYear = YearOf(); + + // The following formula uses Zeller's Congruence to determine the day of the week + if( iMonth > 2 ) // init to February + iMonth -= 2; + else { + iMonth += 10; + iYear--; + } + iDay2 = ((13 * iMonth - 1) / 5) + iDay + ( iYear % 100 ) + + (( iYear % 100 ) / 4) + ((iYear /100 ) / 4 ) - 2 * + ( iYear / 100 ) + 77 ; - iOutDay = iDay2 - 7 * ( iDay2 / 7 ); - iOutDay == 6 ? iOutDay = 0 : iOutDay++; - } - else if( iFormat == XB_FMT_MONTH ){ - sDay[0] = sDate8[7]; - sDay[1] = sDate8[8]; - sDay[2] = 0x00; - iOutDay = atoi( sDay ); + iOutDay = iDay2 - 7 * ( iDay2 / 7 ); + iOutDay == 6 ? iOutDay = 0 : iOutDay++; + } + else if( iFormat == XB_FMT_MONTH ){ + sDay[0] = sDate8[7]; + sDay[1] = sDate8[8]; + sDay[2] = 0x00; + iOutDay = atoi( sDay ); + } else { + iOutDay = iAggregatedDaysInMonths[IsLeapYear()][MonthOf()-1] + DayOf( XB_FMT_MONTH ); + } + return iOutDay; } else { - iOutDay = iAggregatedDaysInMonths[IsLeapYear()][MonthOf()-1] + DayOf( XB_FMT_MONTH ); + return 0; } - return iOutDay; } - /*************************************************************************/ #ifdef XB_DEBUG_SUPPORT //! @brief Dump date information to stdout. @@ -466,7 +502,6 @@ void xbDate::DumpDateTables(){ */ xbInt16 xbDate::CTOD( const xbString &sCtodInDate ){ - if( sCtodInDate[1] != ' ' && ( sCtodInDate[3] == '\\' || sCtodInDate[3] == '/') ){ char yy[3]; yy[0] = sCtodInDate[7]; @@ -518,6 +553,9 @@ xbInt16 xbDate::FormatDate( const xbString &sFmtIn, xbString &sOutFmtDate ){ xbString sWrkFmt; sOutFmtDate = ""; + if( IsNull()) + return XB_NO_ERROR; + /* use format for this specific string if available, else use default format */ if( strlen( sFmtIn ) > 0 ) sWrkFmt = sFmtIn; @@ -600,6 +638,8 @@ const char * xbDate::Str() const{ \returns xbTrue - Is leapyear.<br> xbFalse - Not a leap year. */ xbBool xbDate::IsLeapYear() const { + if( IsNull() ) + return xbFalse; xbInt16 iYear = YearOf(); if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ) return xbTrue; @@ -619,15 +659,30 @@ xbBool xbDate::IsLeapYear( xbInt16 iYear ) const { return xbFalse; } /*************************************************************************/ +//! @brief Determine if date is null date +/*! + \returns xbTrue - If null date.<br> xbFalse - Not a null date. +*/ +xbBool xbDate::IsNull() const { + if( sDate8.Len() < 8 ) + return xbTrue; + else + return xbFalse; +} +/*************************************************************************/ //! @brief Calculate julian days for a given date. /*! \returns The number of days since 01/01/0001 + JUL_OFFSET. */ xbInt32 xbDate::JulianDays() const{ - xbInt32 ly = YearOf() - 1; - xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L; - lDays += DayOf( XB_FMT_YEAR ); - return lDays + JUL_OFFSET; + if( !IsNull()){ + xbInt32 ly = YearOf() - 1; + xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L; + lDays += DayOf( XB_FMT_YEAR ); + return lDays + JUL_OFFSET; + } else { + return 0; + } } /*************************************************************************/ //! @brief Convert the number of julian days to gregorian date. @@ -637,24 +692,23 @@ xbInt32 xbDate::JulianDays() const{ */ xbInt16 xbDate::JulToDate8( xbInt32 lJulDays ) { - lJulDays -= JUL_OFFSET; - // calculate the year - xbInt16 iYear = (xbInt16)(lJulDays / 365.24 ); - lJulDays -= (iYear * 365L) + (iYear / 4L) - (iYear / 100L) + (iYear / 400L); - iYear++; - while( lJulDays <= 0 ){ - iYear--; - lJulDays += (365L + IsLeapYear( iYear )); - } - // this for loop calculates the month by comparing the number of days remaining to one of the tables - xbInt16 iIsLeap = IsLeapYear(iYear); - xbInt16 iMonth = 1; - while( ((xbInt16) lJulDays > iAggregatedDaysInMonths[iIsLeap][iMonth]) && (iMonth < 12) ) - iMonth++; - - lJulDays -= iAggregatedDaysInMonths[iIsLeap][iMonth-1]; - sDate8.Sprintf( "%04d%02d%02ld", iYear, iMonth, lJulDays ); - return XB_NO_ERROR; + lJulDays -= JUL_OFFSET; + // calculate the year + xbInt16 iYear = (xbInt16)(lJulDays / 365.24 ); + lJulDays -= (iYear * 365L) + (iYear / 4L) - (iYear / 100L) + (iYear / 400L); + iYear++; + while( lJulDays <= 0 ){ + iYear--; + lJulDays += (365L + IsLeapYear( iYear )); + } + // this for loop calculates the month by comparing the number of days remaining to one of the tables + xbInt16 iIsLeap = IsLeapYear(iYear); + xbInt16 iMonth = 1; + while( ((xbInt16) lJulDays > iAggregatedDaysInMonths[iIsLeap][iMonth]) && (iMonth < 12) ) + iMonth++; + lJulDays -= iAggregatedDaysInMonths[iIsLeap][iMonth-1]; + sDate8.Sprintf( "%04d%02d%02ld", iYear, iMonth, lJulDays ); + return XB_NO_ERROR; } /*************************************************************************/ //! @brief Set the date to the last day of month for a given date. @@ -663,7 +717,8 @@ xbInt16 xbDate::JulToDate8( xbInt32 lJulDays ) \returns XB_NO_ERROR */ xbInt16 xbDate::LastDayOfMonth(){ - sDate8.Sprintf( "%4.4d%2.2d%2.2d", YearOf(), MonthOf(), iDaysInMonths[IsLeapYear()][MonthOf()]); + if( !IsNull()) + sDate8.Sprintf( "%4.4d%2.2d%2.2d", YearOf(), MonthOf(), iDaysInMonths[IsLeapYear()][MonthOf()]); return XB_NO_ERROR; }; /*************************************************************************/ @@ -672,27 +727,33 @@ xbInt16 xbDate::LastDayOfMonth(){ \returns The month of the date. */ xbInt16 xbDate::MonthOf() const { - xbInt16 iOutMonth; - char month[3]; - month[0] = sDate8[5]; - month[1] = sDate8[6]; - month[2] = 0x00; - iOutMonth = atoi( month ); - return iOutMonth; + if( !IsNull()){ + xbInt16 iOutMonth; + char month[3]; + month[0] = sDate8[5]; + month[1] = sDate8[6]; + month[2] = 0x00; + iOutMonth = atoi( month ); + return iOutMonth; + } else { + return 0; + } } /*************************************************************************/ //! @brief Set the date. /*! \param sDateIn - Input date. - \returns XB_NO_ERROR<br>XB_INVALID_DATE + \returns XB_NO_ERROR */ xbInt16 xbDate::Set( const xbString & sDateIn ){ + if( DateIsValid( sDateIn )){ sDate8 = sDateIn; - return XB_NO_ERROR; + } else { + sDate8 = ""; // set to null date if invalid date } - return XB_INVALID_DATE; + return XB_NO_ERROR; } /*************************************************************************/ //! @brief This routine sets up static data tables on startup. @@ -790,13 +851,17 @@ xbInt16 xbDate::Sysdate(){ \returns The year of the date. */ xbInt16 xbDate::YearOf() const { - char year[5]; - year[0] = sDate8[1]; - year[1] = sDate8[2]; - year[2] = sDate8[3]; - year[3] = sDate8[4]; - year[4] = 0x00; - xbInt16 iOutYear = atoi( year ); - return iOutYear; + if( !IsNull()){ + char year[5]; + year[0] = sDate8[1]; + year[1] = sDate8[2]; + year[2] = sDate8[3]; + year[3] = sDate8[4]; + year[4] = 0x00; + xbInt16 iOutYear = atoi( year ); + return iOutYear; + } else { + return 0; + } }; } /* namespace */
\ No newline at end of file diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp index 4033ef9..8904a6d 100755 --- a/src/core/xbdbf.cpp +++ b/src/core/xbdbf.cpp @@ -159,12 +159,14 @@ xbInt16 xbDbf::Abort(){ //! @brief Add an index to the internal list of indices for this table. /*! The index list is used during any table update process to update any open - index file. Index files can contain one or more tags. + index file. Index files can contain one or more tags. Temporary tags + are not included here because they are created after a table is open + and will be deleted when the table is closed. \param ixIn Pointer to index object for a given index file. - \param sFmt NDX or MDX. + \param sFmt NDX, MDX or TDX. \returns <a href="xbretcod_8h.html">Return Codes</a> - + */ xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){ @@ -295,6 +297,7 @@ xbInt16 xbDbf::AppendRecord(){ // calculate the latest header information xbDate d; + d.Sysdate(); cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); @@ -411,7 +414,7 @@ xbInt16 xbDbf::AppendRecord(){ \param sIxType Currently only NDX. Future versions will support additional non prod index types. \param sIxName The index name. \param iOpt 0 - Add index to .INF if not already there<br> - 1 - Remove index from .INF if there + 1 - Remove index from .INF if exists \returns <a href="xbretcod_8h.html">Return Codes</a> @@ -534,7 +537,7 @@ xbInt16 xbDbf::BlankRecord() This method is used to check an index tag's intgerity. \param iTagOpt 0 - Check current tag<br> - 1 - Check all tag<br> + 1 - Check all tags<br> \param iOutputOpt Output message destination<br> 0 = stdout<br> @@ -585,21 +588,41 @@ xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){ \param iTagOpt 0 - Reindex current tag<br> 1 - Reindex all tags<br> + 2 - Reindex for tag identified by vpTag + \param iErrorOpt 0 - Don't delete tag on reindex failure<br> + 1 - Delete tag on reindex failure + \param vpTag if option 2 used, pointer to tag to reindex \returns <a href="xbretcod_8h.html">Return Codes</a> */ -xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ +xbInt16 xbDbf::Reindex( xbInt16 iTagOpt, xbInt16 iErrorOpt, xbIx **ppIx, void **vppTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; void *vp; + xbString sType; + xbString sTagName; + + if( iTagOpt < 0 || iTagOpt > 2 || (iTagOpt == 2 && (!ppIx || !vppTag ))) + return XB_INVALID_OPTION; + + + #ifdef XB_BLOCKREAD_SUPPORT + xbBool bOriginalBlockReadSts = GetBlockReadStatus(); + #endif + try{ + + #ifdef XB_BLOCKREAD_SUPPORT + if( !bOriginalBlockReadSts ) + EnableBlockReadProcessing(); + #endif + if( iTagOpt == 0 ){ if( pCurIx ){ - iRc = pCurIx->Reindex( &vpCurIxTag ); - if( iRc != XB_NO_ERROR ){ + if(( iRc = pCurIx->Reindex( &vpCurIxTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } @@ -608,7 +631,7 @@ xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ return XB_INVALID_TAG; } - } else { + } else if( iTagOpt == 1 ) { xbLinkListNode<xbTag *> *llN = GetTagList(); xbTag *pTag; @@ -622,6 +645,19 @@ xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ } llN = llN->GetNextNode(); } + } else if( iTagOpt == 2 ){ + + // xbIx *pIx; + // pIx = *ppIx; + xbIx *pIx = *ppIx; + // void *vpTag; + // vpTag = *vppTag; + void *vpTag = *vppTag; + + if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } } } catch (xbInt16 iRc ){ @@ -630,6 +666,12 @@ xbInt16 xbDbf::Reindex( xbInt16 iTagOpt ){ xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } + + #ifdef XB_BLOCKREAD_SUPPORT + if( !bOriginalBlockReadSts ) + DisableBlockReadProcessing(); + #endif + return iRc; } @@ -939,10 +981,10 @@ xbInt16 xbDbf::CopyDbfStructure( xbDbf * dNewTable, const xbString &sNewTableNam #ifdef XB_INDEX_SUPPORT //! @brief Create a new tag (index) for this dbf file (table). /*! - This routine creates a new tag (index) on a dbf file. The library currently supports NDX and MDX + This routine creates a new tag (index) on a dbf file. The library currently supports NDX, MDX ans TDX. indices. If you don't have a specific need for an NDX file, use MDX. - \param sIxType "MDX" or "NDX". + \param sIxType "MDX", "NDX" or "NTX". \param sName Index or tag name. \param sKey Index key expression, \param sFilter Filter expression. Not applicable for NDX indices. @@ -1002,6 +1044,8 @@ xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const } *pIxOut = ixNdx; + + // Set the current tag if one not already set if( sCurIxType == "" ){ sCurIxType = "NDX"; @@ -1031,6 +1075,7 @@ xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const ixMdx = (xbIxMdx *) ixList->ix; bMdxFound = xbTrue; } + ixList = ixList->next; } if( !bMdxFound ) @@ -1061,7 +1106,53 @@ xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const pCurIx = ixMdx; vpCurIxTag = ixMdx->GetTag(0); } + #endif + + #ifdef XB_TDX_SUPPORT + } else if( sIxType == "TDX" ){ + + if( GetVersion() == 3 ){ // TDX indexes were version 4 and higher + iErrorStop = 140; + iRc = XB_INVALID_INDEX; + throw iRc; + } + xbIxTdx *ixTdx; + xbString s; + // look through the index list and see if there is an mdx pointer we can grab + xbBool bTdxFound = xbFalse; + xbIxList *ixList = GetIxList(); + while( ixList && !bTdxFound ){ + s = ixList->sFmt->Str(); + if( s == "TDX" ){ + ixTdx = (xbIxTdx *) ixList->ix; + bTdxFound = xbTrue; + } + ixList = ixList->next; + } + if( !bTdxFound ) + ixTdx = new xbIxTdx( this ); + + if(( iRc = ixTdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if( !bTdxFound ){ + if(( iRc = AddIndex( ixTdx, "TDX" )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + *pIxOut = ixTdx; + + // set the current tag if one not already set + if( sCurIxType == "" ){ + + sCurIxType = "TDX"; + pCurIx = ixTdx; + vpCurIxTag = ixTdx->GetTag(0); + } + #endif } else { @@ -1192,17 +1283,23 @@ xbInt16 xbDbf::DeleteAllIndexFiles(){ // close any open index files, delete it, remove from the ix list while( ixList ){ + + // next two lines for debugging + ixList->ix->GetFileNamePart( sIxName ); ixList->ix->Close(); - ixList->ix->xbRemove(); + if(( iRc = ixList->ix->xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } #ifdef XB_INF_SUPPORT // if XB_INF_SUPPORT is enabled, all open non prod indices should be in here - if( *ixList->sFmt != "MDX" ){ // production indices not stored in .INF dataset + if( *ixList->sFmt != "MDX" && *ixList->sFmt != "TDX" ){ // production and temp indices not stored in .INF dataset if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){ - iErrorStop = 110; + iErrorStop = 120; throw iRc; } if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){ - iErrorStop = 120; + iErrorStop = 130; throw iRc; } } @@ -1391,7 +1488,7 @@ xbInt16 xbDbf::DeleteTable(){ /*! This routine deletes an index tag - \param sIxType Either "NDX" or "MDX".<br> + \param sIxType Either "NDX", "MDX" or "TDX".<br> \param sName Tag name to delete.<br> \returns <a href="xbretcod_8h.html">Return Codes</a> */ @@ -1452,7 +1549,7 @@ xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ throw iRc; } - if( ixl->ix == pCurIx ) + if( !ixList || ixl->ix == pCurIx ) SetCurTag( "", NULL, NULL ); } @@ -1500,7 +1597,64 @@ xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ iErrorStop = 170; throw iRc; } - if( ixl->ix == pCurIx ) + if( !ixList || ixl->ix == pCurIx ) + SetCurTag( "", NULL, NULL ); + } + } + ixlPrev = ixl; + ixl = ixlNext; + } + + if( !bDone ) + return XB_INVALID_TAG; + #endif + + #ifdef XB_TDX_SUPPORT + } else if( sIxType == "TDX" ){ + xbIxList *ixl = ixList; + xbIxList *ixlNext; + xbIxList *ixlPrev = NULL; + xbBool bDone = xbFalse; + xbIxTdx *pTdx; + xbMdxTag *pMdxTag; + xbInt16 iTagCnt = 0; + + while( ixl && !bDone ){ + ixlNext = ixl->next; + pTdx = (xbIxTdx *) ixl->ix; + iTagCnt = pTdx->GetTagCount(); + for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){ + pMdxTag = (xbMdxTag *) pTdx->GetTag( i ); + if( pTdx->GetTagName( pMdxTag ) == sName ){ + bDone = xbTrue; + iRc = pTdx->DeleteTag( pMdxTag ); + if( iRc > 0 ){ + // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header + cIndexFlag = 0x00; + if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + if( ixlPrev == NULL ){ + // std::cout << "setting ixList to null or should be\n"; + ixList = ixlNext; + } else { + ixlPrev = ixlNext; + } + } else if( iRc < 0 ){ + iErrorStop = 170; + throw iRc; + } + if( ixList ) + std::cout << "ixlist not null\n"; + else + std::cout << "ixlist null\n"; + + if( !ixList || ixl->ix == pCurIx ) SetCurTag( "", NULL, NULL ); } } @@ -1513,6 +1667,8 @@ xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ #endif + + } else { iErrorStop = 180; iRc = XB_INVALID_OPTION; @@ -1562,15 +1718,19 @@ xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){ \returns <a href="xbretcod_8h.html">Return Codes</a> */ -xbInt16 xbDbf::DumpHeader( xbInt16 iOption ) const { +xbInt16 xbDbf::DumpHeader( xbInt16 iOption ){ int i; int iMemoCtr = 0; if( iOption < 1 || iOption > 4 ) return XB_INVALID_OPTION; - if( iDbfStatus == XB_CLOSED ) - return XB_NOT_OPEN; + xbInt16 iRc = ReadHeader( xbTrue, 0 ); + if( iRc != XB_NO_ERROR ) + return iRc; + +// if( iDbfStatus == XB_CLOSED ) +// return XB_NOT_OPEN; std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl; @@ -1859,7 +2019,7 @@ xbUInt32 xbDbf::GetAppendLocked() const { This routine returns the table setting if set, otherwise returns the system level setting. - + \returns Not 0 - Auto commit on for this table.<br> 0 - Auto commit off for this table. */ @@ -1920,8 +2080,10 @@ xbIx *xbDbf::GetCurIx() const { /************************************************************************/ //! @brief Get pointer to current tag for the current index. /*! - An index file can have one or more tags. An NDX index has one tag. - An MDX file can have up to 47 tags. + An index file can have one or more tags + NDX index files have one tag per file. + MDX index files can can have up to 47 tags per file. + TDX index files can can have up to 47 tags per file. \returns Pointer to current tag. */ @@ -1932,7 +2094,8 @@ void *xbDbf::GetCurTag() const { //! @brief Get the current index type. /*! \returns NDX for single tag index.<br> - MDX for production multi tag index. + MDX for production multi tag index.<br> + TDX for temporary tag index. */ const xbString &xbDbf::GetCurIxType() const { return sCurIxType; @@ -2097,12 +2260,16 @@ xbBool xbDbf::GetBof() { /************************************************************************/ //! @brief Return true if dbf file empty or positioned to the last record /*! - \returns Returns true if dbf file is empty or positioned on the last record. + \returns Returns true if error, dbf file is empty or positioned on the last record. */ xbBool xbDbf::GetEof() { - xbUInt32 ulRecCnt = GetRecordCount(); + + // xbUInt32 ulRecCnt = GetRecordCount(); - if( ulRecCnt == 0 || GetCurRecNo() == ulRecCnt ) + xbUInt32 ulRecCnt; + xbInt16 iRc = GetRecordCnt( ulRecCnt ); + + if( iRc != XB_NO_ERROR || ulRecCnt == 0 || GetCurRecNo() == ulRecCnt ) return xbTrue; else return xbFalse; @@ -2542,6 +2709,7 @@ char * xbDbf::GetRecordBuf( xbInt16 iOpt ) const { /*! \returns Record count or <a href="xbretcod_8h.html">Return Codes</a> */ +/* xbUInt32 xbDbf::GetRecordCount(){ xbUInt32 ulCnt; @@ -2551,7 +2719,7 @@ xbUInt32 xbDbf::GetRecordCount(){ else return ulCnt; } - +*/ /************************************************************************/ //! @brief Get the current number of records in the dbf data file. /*! @@ -2626,8 +2794,8 @@ xbBool xbDbf::GetTableLocked() const { This routine returns a list of tags for the file.<br> The library is structured to support one or more files of the same or differing - index types (NDX/MDX), with each file supporting one or more index tags.<br> - + index types (NDX/MDX/TDX), with each file supporting one or more index tags.<br> + \returns Tag list for the file/table. */ xbLinkListNode<xbTag *> *xbDbf::GetTagList() const { @@ -2944,6 +3112,19 @@ xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction ) } #endif + #ifdef XB_TDX_SUPPORT + if( *ixLI->sFmt == "TDX" ){ + if( !ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 ); + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbTrue ); + } + } + #endif + } else if( iLockFunction == XB_UNLOCK ){ #ifdef XB_NDX_SUPPORT @@ -2970,6 +3151,18 @@ xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction ) } #endif + #ifdef XB_TDX_SUPPORT + if( *ixLI->sFmt == "MDX" ){ + if( ixLI->ix->GetLocked()){ + if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + ixLI->ix->SetLocked( xbFalse ); + } + } + #endif + } ixLI = ixLI->next; } @@ -3377,7 +3570,7 @@ xbInt16 xbDbf::OpenIndex( const xbString &sIxType, const xbString &sFileName ){ xbInt16 xbDbf::Pack() { xbUInt32 ulDeletedRecCnt; - return Pack( ulDeletedRecCnt ); + return Pack( ulDeletedRecCnt ); } @@ -3484,6 +3677,7 @@ xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt ) // update header record count xbDate d; + d.Sysdate(); cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); @@ -3520,7 +3714,6 @@ xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt ) } } #endif // XB_MEMO_SUPPORT - } catch (xbInt16 iRc ){ if( iRc != XB_LOCK_FAILED ){ @@ -3665,6 +3858,7 @@ xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo) // update latest header date if changed xbDate d; + d.Sysdate(); if( (cUpdateYY != (char)(d.YearOf() - 1900)) || (cUpdateMM != (char) d.MonthOf()) || (cUpdateDD != (char)d.DayOf( XB_FMT_MONTH))){ cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); @@ -3716,9 +3910,9 @@ xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo) LockHeader( XB_UNLOCK ); LockAppend( XB_UNLOCK ); LockRecord( XB_UNLOCK, ulRecNo ); - #ifdef XB_INDEX_SUPPORT + #ifdef XB_INDEX_SUPPORT LockIndices( XB_UNLOCK ); - #endif // XB_INDEX_SUPPORT + #endif // XB_INDEX_SUPPORT } #endif // XB_LOCKING_SUPPORT @@ -3972,7 +4166,7 @@ xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){ //! @brief Set the current tag for the dbf file. /*! - \param sIxType - One of "NDX" or MDX", + \param sIxType - One of "NDX", MDX or TDX", \param pIx - Pointer to index object. \param vpTag - Pointer to tag object. \returns <a href="xbretcod_8h.html">Return Codes</a> @@ -4206,6 +4400,7 @@ xbInt16 xbDbf::Zap(){ #endif xbDate d; + d.Sysdate(); cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); diff --git a/src/core/xbdbf3.cpp b/src/core/xbdbf3.cpp index d57be3c..926cc0d 100755 --- a/src/core/xbdbf3.cpp +++ b/src/core/xbdbf3.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -180,6 +180,7 @@ xbInt16 xbDbf3::CreateTable( const xbString & sTableName, const xbString & sAlia ulCurRec = 0L; uiHeaderLen = 33 + iNoOfFields * 32; xbDate d; + d.Sysdate(); cUpdateYY = (char) (d.YearOf() - 1900); cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); diff --git a/src/core/xbdbf4.cpp b/src/core/xbdbf4.cpp index 59ba39a..a1f770e 100755 --- a/src/core/xbdbf4.cpp +++ b/src/core/xbdbf4.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -180,6 +180,7 @@ xbInt16 xbDbf4::CreateTable( const xbString &sTableName, const xbString &sAlias, ulCurRec = 0L; uiHeaderLen = 33 + iNoOfFields * 32; xbDate d; + d.Sysdate(); cUpdateYY = (char) (d.YearOf() - 1900); cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); @@ -510,7 +511,7 @@ xbInt16 xbDbf4::Open( const xbString & sTableName, const xbString & sAlias, /************************************************************************/ //! @brief Rename table. /*! - This routine renames a give table, associated memo and inf files + This routine renames a table, associated memo, mdx and inf files \param sNewName - New file name. \returns <a href="xbretcod_8h.html">Return Codes</a> */ diff --git a/src/core/xbexp.cpp b/src/core/xbexp.cpp index deea53d..b2d4db9 100755 --- a/src/core/xbexp.cpp +++ b/src/core/xbexp.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2017,2021,2022 Gary A Kunkel +Copyright (c) 1997,2003,2014,2017,2021,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. @@ -1097,12 +1097,12 @@ xbBool xbExp::IsOperator( const xbString & sExpression ){ } /*************************************************************************/ -//! Is Token seperator -/*! This method determines if the next token is a seperator. +//! Is Token separator +/*! This method determines if the next token is a separator. \param sExpression - String expression to be evaluated. - \returns xbTrue - Is a token seperator.<br> - xbFalse - Is not a token seperator. + \returns xbTrue - Is a token separator.<br> + xbFalse - Is not a token separator. */ char xbExp::IsTokenSeparator( char c ){ if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' || c == '#' || @@ -1205,12 +1205,13 @@ xbInt16 xbExp::ParseExpression( xbDbf *dbf, const xbString &sExpression ){ */ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ - xbExpNode *n; + xbExpNode *n = NULL; xbExpNode *nLastNode = NULL; // pointer to the last node processed xbExpToken t; xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbString s; + xbBool bNewNode = xbFalse; try { @@ -1249,6 +1250,7 @@ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ case XB_EXP_CONSTANT: n = new xbExpNode( t.sToken, t.cNodeType ); + bNewNode = xbTrue; if(( iRc = ParseExpressionConstant( t, n )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; @@ -1257,6 +1259,7 @@ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ case XB_EXP_FUNCTION: n = new xbExpNode( t.cNodeType ); + bNewNode = xbTrue; if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; @@ -1265,6 +1268,7 @@ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ case XB_EXP_FIELD: n = new xbExpNode( t.cNodeType ); + bNewNode = xbTrue; if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; @@ -1275,6 +1279,7 @@ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ case XB_EXP_PRE_OPERATOR: case XB_EXP_POST_OPERATOR: n = new xbExpNode( t.sToken, t.cNodeType ); + bNewNode = xbTrue; if(( iRc = ParseExpressionOperator( t, n, iWeight )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; @@ -1429,6 +1434,8 @@ xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){ } } catch (xbInt16 iRc ){ + if( bNewNode && n ) + delete n; xbString sMsg; sMsg.Sprintf( "xbexp::ParseExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); @@ -1933,7 +1940,9 @@ xbInt16 xbExp::ProcessExpression( xbInt16 iRecBufSw ){ throw iRc; } if( sWork1 == " " ){ - nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in ndx to this if spaces on dbf record + // std::cout << "xbExp::ProcessExpression() line 1938 sWork is spaces\n"; + //nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in both ndx and mdx index files to this if spaces on dbf record + nWork->SetResult( (xbDouble) XB_NULL_DATE ); } else { dtWork1.Set( sWork1 ); nWork->SetResult( (xbDouble) dtWork1.JulianDays() ); @@ -2423,7 +2432,6 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ xbString sMsg; try{ - n->GetNodeText( sOperator ); nChild1 = n->GetChild( 0 ); if( !n->IsUnaryOperator()) @@ -2532,10 +2540,19 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ if( nChild1->GetReturnType() == XB_EXP_CHAR ) n->SetResult((xbBool)(nChild1->GetStringResult() > nChild2->GetStringResult())); - else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult())); - else { + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 > d2)); + // n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult())); + + } else { iErrorStop = 410; iRc = XB_PARSE_ERROR; throw iRc; @@ -2546,10 +2563,18 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ if( nChild1->GetReturnType() == XB_EXP_CHAR ) n->SetResult((xbBool)(nChild1->GetStringResult() >= nChild2->GetStringResult())); - else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult())); - else { + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 >= d2)); + //n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult())); + + } else { iErrorStop = 420; iRc = XB_PARSE_ERROR; throw iRc; @@ -2558,13 +2583,23 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ else if( sOperator == "<" ){ - if( nChild1->GetReturnType() == XB_EXP_CHAR ) + if( nChild1->GetReturnType() == XB_EXP_CHAR ){ n->SetResult((xbBool)( nChild1->GetStringResult() < nChild2->GetStringResult())); - else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){ n->SetResult((xbBool)( nChild1->GetNumericResult() < nChild2->GetNumericResult())); - else { + } else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + + n->SetResult((xbBool)( d1 < d2)); + + // std::cout << "xbexp() line 2567 [" << nChild1->GetNumericResult() << "][" << nChild2->GetNumericResult() << "]\n"; + + } else { iErrorStop = 430; iRc = XB_PARSE_ERROR; throw iRc; @@ -2576,10 +2611,18 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ if( nChild1->GetReturnType() == XB_EXP_CHAR ) n->SetResult((xbBool)( nChild1->GetStringResult() <= nChild2->GetStringResult())); - else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult())); - else { + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 <= d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult())); + + } else { iErrorStop = 440; iRc = XB_PARSE_ERROR; throw iRc; @@ -2591,10 +2634,18 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ if( nChild1->GetReturnType() == XB_EXP_CHAR ) n->SetResult((xbBool)( nChild1->GetStringResult() != nChild2->GetStringResult())); - else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ) n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult())); - else { + else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 != d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult())); + + } else { iErrorStop = 450; iRc = XB_PARSE_ERROR; throw iRc; @@ -2623,15 +2674,24 @@ xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){ sChld2.Rtrim(); n->SetResult((xbBool)( sChld1 == sChld2 )); - } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE ) + } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){ n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult())); - else { + } else if( nChild1->GetReturnType() == XB_EXP_DATE ){ + xbDouble d1 = nChild1->GetNumericResult(); + xbDouble d2 = nChild2->GetNumericResult(); + if( d1 == XB_NULL_DATE ) d1 = 0; + if( d2 == XB_NULL_DATE ) d2 = 0; + n->SetResult((xbBool)( d1 == d2)); + // n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult())); + + } else { iErrorStop = 470; iRc = XB_PARSE_ERROR; throw iRc; } + } else { iErrorStop = 500; iRc = XB_PARSE_ERROR; diff --git a/src/core/xbexpnode.cpp b/src/core/xbexpnode.cpp index 77a8c33..d11e8cc 100755 --- a/src/core/xbexpnode.cpp +++ b/src/core/xbexpnode.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2017,2022 Gary A Kunkel +Copyright (c) 1997,2003,2014,2017,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. diff --git a/src/core/xbfields.cpp b/src/core/xbfields.cpp index e683d9c..85ac145 100755 --- a/src/core/xbfields.cpp +++ b/src/core/xbfields.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -87,8 +87,8 @@ xbInt16 xbDbf::GetDateField( xbInt16 iFieldNo, xbDate &dtFieldValue ) const{ xbInt16 iRc; if(( iRc = GetField( iFieldNo, s )) != XB_NO_ERROR ) return iRc; - dtFieldValue.Set( s ); - return XB_NO_ERROR; + return dtFieldValue.Set( s ); + // return XB_NO_ERROR; } /************************************************************************/ @@ -106,8 +106,8 @@ xbInt16 xbDbf::GetDateField( const xbString &sFieldName, xbDate &dtFieldValue ) xbInt16 iRc; if(( iRc = GetField( sFieldName, s )) != XB_NO_ERROR ) return iRc; - dtFieldValue.Set( s ); - return XB_NO_ERROR; + return dtFieldValue.Set( s ); + // return XB_NO_ERROR; } /************************************************************************/ @@ -163,6 +163,7 @@ xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue, xbInt16 iRecBu return XB_NO_ERROR; } + /************************************************************************/ //! @brief Get decimal for field name. /*! @@ -605,6 +606,68 @@ xbInt16 xbDbf::GetMemoFieldLen( const xbString &sFieldName, xbUInt32 &ulMemoFiel #endif // XB_MEMO_SUPPORT /************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldNo Number of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \returns The field routines return one of:<br> + XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br> + XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA +*/ +xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull ) const { + return GetNullSts( iFieldNo, bIsNull, 0 ); +} + +/************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldName Field Name of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \returns The field routines return one of:<br> + XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br> + XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetNullSts( const xbString &sFieldName, xbBool &bIsNull ) const { + return GetNullSts( GetFieldNo( sFieldName ), bIsNull, 0 ); +} + +/************************************************************************/ +//! @brief Get field null status +/*! + \param iFieldNo Number of field to retrieve. + \param bIsNull Output field value. If field is all spaces on record buffer, returns true. + \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data. + \returns The field routines return one of:<br> + XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br> + XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA +*/ + +xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull, xbInt16 iRecBufSw ) const +{ + if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) { + return XB_INVALID_FIELD_NO; + } + bIsNull = xbTrue; + char *p; + if( iRecBufSw ) + p = (char *) SchemaPtr[iFieldNo].pAddress2; + else + p = (char *) SchemaPtr[iFieldNo].pAddress; + + xbUInt32 ulLen = SchemaPtr[iFieldNo].cFieldLen; + xbUInt32 ul = 0; + + while( ul < ulLen && bIsNull ){ + if( *p++ != ' ' ) + bIsNull = xbFalse; + else + ul++; + } + return XB_NO_ERROR; +} + +/************************************************************************/ //! @brief Get unsigned long field for field number. /*! \param iFieldNo Number of field to retrieve. @@ -625,13 +688,11 @@ xbInt16 xbDbf::GetULongField( xbInt16 iFieldNo, xbUInt32 & ulFieldValue ) const iErrorStop = 100; throw rc; } - if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){ iErrorStop = 110; rc = XB_INVALID_FIELD_TYPE; throw rc; } - xbString sTemp; if(( rc = GetField( iFieldNo, sTemp, 0 )) != XB_NO_ERROR ){ iErrorStop = 120; @@ -645,14 +706,12 @@ xbInt16 xbDbf::GetULongField( xbInt16 iFieldNo, xbUInt32 & ulFieldValue ) const } ulFieldValue = strtoul( sTemp.Str(), NULL, 10 ); } - catch (xbInt16 rc ){ xbString sMsg; sMsg.Sprintf( "xbDbf::GetULongField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( rc )); } - return XB_NO_ERROR; } diff --git a/src/core/xbfile.cpp b/src/core/xbfile.cpp index 3b798a2..6376e9a 100755 --- a/src/core/xbfile.cpp +++ b/src/core/xbfile.cpp @@ -49,119 +49,6 @@ xbFile::~xbFile(){ } /************************************************************************/ -//! @brief Create Home Folders. -/*! - Create xbase64 log and data folders in the home directory for current usre. - - \returns <a href="xbretcod_8h.html">Return Codes</a> -*/ - -xbInt16 xbFile::SetHomeFolders(){ - - xbInt16 iErrorStop = 0; - xbInt16 iRc = XB_NO_ERROR; - xbString sHomeDir; - char cPathSeperator; - xbString sDflt; - - try{ - - GetHomeDir( sHomeDir ); - //std::cout << "CreateHomeFolders() home dir = [" << sHomeDir.Str() << "]\n"; - - if( FileExists( sHomeDir ) == xbFalse ){ - iErrorStop = 100; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - - #ifdef WIN32 - cPathSeperator = '\\'; - #else - cPathSeperator = '/'; - #endif - sDflt.Sprintf( ".%c", cPathSeperator ); - // set the default folders just in case later steps fail - xbase->SetDataDirectory( sDflt ); - #ifdef XB_LOGGING_SUPPORT - xbase->SetLogDirectory( sDflt ); - #endif - - if( sHomeDir[sHomeDir.Len()] != cPathSeperator ) - sHomeDir += cPathSeperator; - - xbString sWork( sHomeDir ); - sWork += "xbase64"; - - if( FileExists( sWork ) == xbFalse ){ - #ifdef WIN32 - if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ - iErrorStop = 130; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #else - // 0777 mode is correct, the mode will be modified by the user's umask - if( mkdir( sWork.Str(), 0777 ) == -1 ){ - iErrorStop = 140; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #endif - } - - #ifdef XB_LOGGING_SUPPORT - sWork.Sprintf( "%sxbase64%clogs", sHomeDir.Str(), cPathSeperator ); - // std::cout << "logdir = " << sWork.Str() << "\n"; - - if( FileExists( sWork ) == xbFalse ){ - #ifdef WIN32 - if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ - iErrorStop = 110; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #else - if( mkdir( sWork.Str(), 0777 ) == -1 ){ - iErrorStop = 120; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #endif - } - xbase->SetLogDirectory( sWork ); - #endif // XB_LOGGING_SUPPORT - - sWork.Sprintf( "%sxbase64%cdata", sHomeDir.Str(), cPathSeperator ); - // std::cout << "datadir = " << sWork.Str() << "\n"; - if( FileExists( sWork ) == xbFalse ){ - #ifdef WIN32 - if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ - iErrorStop = 130; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #else - if( mkdir( sWork.Str(), 0777 ) == -1 ){ - iErrorStop = 140; - iRc = XB_DIRECTORY_ERROR; - throw iRc; - } - #endif - } - xbase->SetDataDirectory( sWork ); - - } - catch (xbInt16 iRc ){ - xbString sMsg; - sMsg.Sprintf( "xbFile::CreateHomeFolders() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); - xbase->WriteLogMessage( sMsg.Str() ); - xbase->WriteLogMessage( GetErrorMessage( iRc )); - } - return iRc; -} - -/************************************************************************/ //! @brief Create a unique file name. /*! Given a directory and file extension as inputs, create a unique file name. @@ -171,15 +58,16 @@ xbInt16 xbFile::SetHomeFolders(){ \param sFqnOut A fully qualifed unique file name as output \returns <a href="xbretcod_8h.html">Return Codes</a> */ +/* xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut ){ return CreateUniqueFileName( sDirIn, sExtIn, sFqnOut, 0 ); } - +*/ /************************************************************************/ //! @brief Create a unique file name. /*! Given a directory and file extension as inputs, create a unique file name. - + \param sDirIn Directory \param sExtIn File Extension \param iOption 0 - look only for one file for a given directory and extension<br> @@ -192,12 +80,16 @@ xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & xbBool bUniqueFileNameFound = xbFalse; xbFile f( xbase); - xbInt32 l = 1; + xbInt32 l = 1; xbString sMemoFileName; + xbString sDir = sDirIn; + char c = GetPathSeparator(); + if( sDirIn.Len() > 0 && sDirIn[sDirIn.Len()] != c ) + sDir += c; while( !bUniqueFileNameFound ){ - sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDirIn.Str(), l, sExtIn.Str()); + sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDir.Str(), l, sExtIn.Str()); if( iOption == 1 && sExtIn == "DBF" ){ sMemoFileName.Sprintf( "%sxbTmp%03d.DBT", sDirIn.Str(), l ); } @@ -693,7 +585,6 @@ const xbString & xbFile::GetDirectory() const { \returns <a href="xbretcod_8h.html">Return Codes</a> */ - xbInt16 xbFile::GetFileDirPart( xbString & sFileDirPartOut ) const { return GetFileDirPart( sFqFileName, sFileDirPartOut ); } @@ -1139,7 +1030,6 @@ xbInt16 xbFile::SetBlockSize( xbUInt32 ulBlockSize ){ \param sDirectory - Valid directory name */ - void xbFile::SetDirectory( const xbString & sDirectory ){ this->sDirectory = sDirectory; @@ -1242,6 +1132,136 @@ void xbFile::SetFqFileName( const xbString & sFqFileName ){ } /************************************************************************/ +//! @brief Set Home Folders. +/*! + Create xbase64 log, data and temp folders in the home directory for current usre. + + \returns <a href="xbretcod_8h.html">Return Codes</a> +*/ + +xbInt16 xbFile::SetHomeFolders(){ + + xbInt16 iErrorStop = 0; + xbInt16 iRc = XB_NO_ERROR; + xbString sHomeDir; + char cPathSeparator; + xbString sDflt; + + try{ + + GetHomeDir( sHomeDir ); + //std::cout << "CreateHomeFolders() home dir = [" << sHomeDir.Str() << "]\n"; + + if( FileExists( sHomeDir ) == xbFalse ){ + iErrorStop = 100; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + + #ifdef WIN32 + cPathSeparator = '\\'; + #else + cPathSeparator = '/'; + #endif + sDflt.Sprintf( ".%c", cPathSeparator ); + // set the default folders just in case later steps fail + xbase->SetDataDirectory( sDflt ); + #ifdef XB_LOGGING_SUPPORT + xbase->SetLogDirectory( sDflt ); + #endif + + if( sHomeDir[sHomeDir.Len()] != cPathSeparator ) + sHomeDir += cPathSeparator; + + xbString sWork( sHomeDir ); + sWork += "xbase64"; + + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 130; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + // 0777 mode is correct, the mode will be modified by the user's umask + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 140; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + + #ifdef XB_LOGGING_SUPPORT + sWork.Sprintf( "%sxbase64%clogs", sHomeDir.Str(), cPathSeparator ); + // std::cout << "logdir = " << sWork.Str() << "\n"; + + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 110; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 120; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + xbase->SetLogDirectory( sWork ); + #endif // XB_LOGGING_SUPPORT + + sWork.Sprintf( "%sxbase64%cdata", sHomeDir.Str(), cPathSeparator ); + // std::cout << "datadir = " << sWork.Str() << "\n"; + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 130; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 140; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + + sWork.Sprintf( "%sxbase64%ctemp%c", sHomeDir.Str(), cPathSeparator, cPathSeparator ); + // std::cout << "tempdir = " << sWork.Str() << "\n"; + if( FileExists( sWork ) == xbFalse ){ + #ifdef WIN32 + if( CreateDirectory( sWork.Str(), NULL ) == 0 ){ + iErrorStop = 150; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #else + if( mkdir( sWork.Str(), 0777 ) == -1 ){ + iErrorStop = 160; + iRc = XB_DIRECTORY_ERROR; + throw iRc; + } + #endif + } + xbase->SetTempDirectory( sWork ); + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbFile::CreateHomeFolders() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/************************************************************************/ //! @brief Write a block of data to file. /*! @@ -1744,6 +1764,7 @@ xbInt16 xbFile::xbRemove( const xbString & sFileNameIn ) { xbInt16 xbFile::xbRemove( const xbString & sFileNameIn, xbInt16 iOption ) { xbInt32 iRc = remove( sFileNameIn.Str()); + if( iRc != 0 ) return XB_DELETE_FAILED; @@ -1904,7 +1925,7 @@ xbInt16 xbFile::xbLock( xbInt16 iFunction, xbInt64 lOffset, size_t stLen ){ Qsplit lPos; Qsplit lLen; - lPos.Qword = lOffset; + lPos.Qword = (size_t) lOffset; lLen.Qword = stLen; do{ @@ -2037,7 +2058,9 @@ xbInt16 xbFile::DumpMemToDisk( char *p, size_t lSize ){ try{ - sDir = GetLogDirectory(); + // sDir = GetLogDirectory(); + sDir = xbase->GetLogDirectory(); + char cLastChar = sDir[sDir.Len()]; // build logfile name @@ -2127,7 +2150,8 @@ xbInt16 xbFile::DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlkSize ){ throw iRc; } - sDir = GetLogDirectory(); +// sDir = GetLogDirectory(); + sDir = xbase->GetLogDirectory(); char cLastChar = sDir[sDir.Len()]; for( xbUInt32 l = ulStartBlock; l < ulEndBlock; l++ ){ diff --git a/src/core/xbfilter.cpp b/src/core/xbfilter.cpp index 0fb643d..5c5f276 100755 --- a/src/core/xbfilter.cpp +++ b/src/core/xbfilter.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. diff --git a/src/core/xbfuncs.cpp b/src/core/xbfuncs.cpp index a038611..f127211 100755 --- a/src/core/xbfuncs.cpp +++ b/src/core/xbfuncs.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2017,2022 Gary A Kunkel +Copyright (c) 1997,2003,2014,2017,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. @@ -611,12 +611,13 @@ xbInt16 xbXBase::RTRIM( const xbString &sIn, xbString &sOut ){ /*! Expression function SPACE(). \param lCnt Input - Number of spaces. - \param sOut Output - Output String. + \param sOut Output - Output string consisting of specified number of spaces. \returns XB_NO_ERROR. */ xbInt16 xbXBase::SPACE( xbInt32 lCnt, xbString &sOut ){ sOut = ""; - sOut.PadLeft( ' ', (xbUInt32) lCnt ); + for( xbInt32 i = 0; i < lCnt; i++ ) + sOut += ' '; return XB_NO_ERROR; } diff --git a/src/core/xbixbase.cpp b/src/core/xbixbase.cpp index 8b64fd6..e2f929c 100755 --- a/src/core/xbixbase.cpp +++ b/src/core/xbixbase.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. diff --git a/src/core/xbixmdx.cpp b/src/core/xbixmdx.cpp index 135cde8..0eef64b 100755 --- a/src/core/xbixmdx.cpp +++ b/src/core/xbixmdx.cpp @@ -92,11 +92,7 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ iErrorStop = 100; throw iRc; } - xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); - - // std::cout << "xbIxMdx::AddKeys() lKeyCnt = " << lKeyCnt << " KeysPerBlock = " << npTag->iKeysPerBlock << " npBlockNo = " << npTag->npCurNode->ulBlockNo << "\n"; - if( lKeyCnt < npTag->iKeysPerBlock ){ // Section A - add key to appropriate position if space available // std::cout << "AddKey Section A begin\n"; @@ -104,12 +100,8 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ iErrorStop = 110; throw iRc; } - //std::cout << "AddKey Section A end\n"; } else { - // land here with a full leaf node - // std::cout << "Section B begin split leaf node\n"; - iHeadNodeUpdateOpt = 1; npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ); if( !npRightNode ){ @@ -122,26 +114,14 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ ulNewRightChild = npRightNode->ulBlockNo * (xbUInt32) iBlockFactor; } - // std::cout << "ulNewRightChild = " << ulNewRightChild << "\n"; - if(( iRc = xbIxMdx::SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo; - - //std::cout << "ulTempBlockNo = " << ulTempBlockNo << "\n"; - //std::cout << "key count left block " << GetKeyCount( npTag->npCurNode ) << "\n"; - //std::cout << "key count right block " << GetKeyCount( npRightNode ) << "\n"; - - - //std::cout << "Section B end\n"; - // section C - go up the tree, splitting nodes as necessary xbIxNode * npParent = npTag->npCurNode->npPrev; - - while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){ //std::cout << "Section C begin interior node is full\n"; npRightNode = FreeNodeChain( npRightNode ); @@ -165,23 +145,15 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ npParent = npParent->npPrev; } - // std::cout << "Past Section C Cur Node Block No = " << npTag->npCurNode->ulBlockNo << " root page = " << npTag->ulRootPage << "\n"; // section D - if cur node is split root, create new root - if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){ - - // std::cout << "Section D begin right node = " << npRightNode << "\n"; - if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } - if( npRightNode ) npRightNode = FreeNodeChain( npRightNode ); - // std::cout << "Section D end\n"; - } else { // std::cout << "Section E, put key in parent\n"; @@ -213,8 +185,6 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ if( ulNewRightChild > 0 ){ -// std::cout << "ulRightChild was = " << npTag->ulRightChild << " changed to " << ulNewRightChild << "\n"; - char cBuf[4]; ePutUInt32( cBuf, ulNewRightChild ); if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 252), SEEK_SET )) != XB_NO_ERROR ){ @@ -225,7 +195,6 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ iErrorStop = 220; throw iRc; } -// std::cout << "setting right child\n"; npTag->ulRightChild = ulNewRightChild; } @@ -243,7 +212,6 @@ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ } return iRc; } - /***********************************************************************/ void xbIxMdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ xbMdxTag * mdxTag = (xbMdxTag *) vpTag; @@ -282,11 +250,6 @@ xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *np } npTag->ulRootPage = npRoot->ulBlockNo; - // std::cout << "AddKeyNewRoot - RootBlock = " << npRoot->ulBlockNo << "\n"; - // std::cout << "AddKeyNewRoot - LeftBlock = " << npLeft->ulBlockNo << "\n"; - // std::cout << "AddKeyNewRoot - RightBlock = " << npRight->ulBlockNo << "\n"; - - pLastKey = (char *) malloc( (size_t) npTag->iKeyLen ); if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ iErrorStop = 110; @@ -303,17 +266,8 @@ xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *np // set the key pTrg+= 4; - // std::cout << "AddKeyNewRoot - key [" << pLastKey << "] len = [" << strlen( pLastKey) << "]\n"; memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); - - // set the right node number - //pTrg+= (npTag->iKeyLen); - // pTrg+= (npTag->iKeyItemLen)-4; - pTrg+= npTag->iKeyItemLen - 4; - - //pTrg-=4; - ePutUInt32( pTrg, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); // write out the new block @@ -326,8 +280,6 @@ xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *np // position the file xbUInt32 ulPagePos = npTag->ulTagHdrPageNo * 512; - // std::cout << "ulPagePos = " << ulPagePos << " root block no = " << npRoot->ulBlockNo << " \n"; - // save the number to a buffer char cBuf[4]; ePutUInt32( cBuf, npRoot->ulBlockNo * ((xbUInt32) iBlockFactor )); @@ -375,9 +327,6 @@ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt xbIxNode *n = NULL; try{ - - // std::cout << "xbIxMdx::AllocateIxNode()\n"; - if(( n = xbIx::AllocateIxNode( ulBufSize )) == NULL ){ iRc = XB_NO_MEMORY; iErrorStop = 100; @@ -387,10 +336,7 @@ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt p += 4; if( ulFirstFreePage > 0 && bReuseEmptyNodes ){ - // we have an empty node we can reuse - - // std::cout << "Reusing node " << ulFirstFreePage << "\n"; - + // have an empty node we can reuse n->ulBlockNo = PageToBlock( ulFirstFreePage ); if(( iRc = ReadBlock( n->ulBlockNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ iRc = 110; @@ -409,7 +355,6 @@ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt // memset cpBlockData to zeroes memset( n->cpBlockData, 0x00, GetBlockSize()); - } else { n->ulBlockNo = ulPageCnt / (xbUInt32) iBlockFactor; ulPageCnt += (xbUInt32) iBlockFactor; @@ -419,9 +364,6 @@ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt if( ulBlock2 > 0 ){ ePutUInt32( p, ulBlock2 ); } - - // std::cout << "AllocateIxNode incremented the block to " << ulPageCnt << "\n"; - // std::cout << "AllocateIxNode new block number = " << n->ulBlockNo << "\n"; } catch( xbInt16 iRc ){ xbString sMsg; @@ -431,9 +373,7 @@ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt if( n ) n = FreeNodeChain( n ); } - return n; - } /***********************************************************************/ //! @brief Calculate B-tree pointers. @@ -496,8 +436,6 @@ void xbIxMdx::CalcBtreePointers(){ xbString s; xbMdxTag *mpTag = mdxTagTbl; for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ -// s.Sprintf( "tag = [%d] parent = [%d] left = [%d] right = [%d]\n", i, iaParent[i], iaLeftChild[i], iaRightChild[i]); -// std::cout << s; mpTag->cLeftChild = (char ) iaLeftChild[i]; mpTag->cRightChild = (char ) iaRightChild[i]; mpTag->cParent = (char ) iaParent[i]; @@ -597,9 +535,7 @@ xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ xbBool bLocked = xbFalse; #endif - try{ -// xbase->WriteLogMessage( "xbIxMdx::CheckTagIntegrity()", iOpt ); #ifdef XB_LOCKING_SUPPORT if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ @@ -612,7 +548,6 @@ xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ #endif memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen ); - // memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyItemLen ); cKeyType = GetKeyType( vpTag ); pPrevKeyBuf = (char *) calloc( 1, (size_t) npTag->iKeyLen ); @@ -716,10 +651,8 @@ xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ xbase->WriteLogMessage( sMsg, iOpt ); } - if( pPrevKeyBuf ) free( pPrevKeyBuf ); - } catch (xbInt16 iRc ){ xbString sMsg; @@ -734,7 +667,6 @@ xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ xbase->WriteLogMessage( sMsg, iOpt ); } } - #ifdef XB_LOCKING_SUPPORT if( bLocked ){ dbf->LockTable( XB_UNLOCK ); @@ -795,7 +727,6 @@ xbInt16 xbIxMdx::Close(){ return iRc; } - /***********************************************************************/ //! @brief Create key. /*! @@ -830,9 +761,6 @@ xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ xbMdxTag *npTag = (xbMdxTag *) vpTag; npTag->iKeySts = 0; - // char *p0 = dbf->GetRecordBuf(0); - // char *p1 = dbf->GetRecordBuf(1); - // do tag filter logic if( npTag->cHasFilter ){ if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ @@ -843,8 +771,6 @@ xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ iErrorStop = 110; throw iRc; } - // std::cout << "cp1 f0 = " << bFilter0 << "\n"; - // printf( "del byte 0 [%x] 1 [%x]\n", *p0, *p1 ); } else { bFilter0 = xbTrue; } @@ -922,10 +848,6 @@ xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ npTag->iKeySts = XB_ADD_KEY; } } - -// std::cout << "xbIxMdx::CreateKey key sts = " << npTag->iKeySts << " iOpt = " << iOpt << " type = " << npTag->exp->GetReturnType() << " name = " << npTag->cTagName; -// std::cout << " f0 = " << bFilter0 << " f1 = " << bFilter1 << "\n"; - } catch (xbInt16 iRc ){ xbString sMsg; @@ -933,16 +855,15 @@ xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ 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. @@ -960,11 +881,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const xbInt16 iErrorStop = 0; xbMdxTag *tte = NULL; - - // 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 )){ @@ -1016,8 +932,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const throw iRc; } - // tte->filter->DumpTree( 1 ); - if((tte->filter->GetReturnType()) != 'L' ){ iRc = XB_INVALID_TAG; iErrorStop = 160; @@ -1042,7 +956,7 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const } xbDate d; - + d.Sysdate(); if( iTagUseCnt == 0 ){ // first tag, new mdx file // create the file name @@ -1132,11 +1046,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const 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 ); @@ -1158,7 +1067,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const throw iRc; } - // Begin Tag Header tte->ulRootPage = ulPageCnt; tte->ulTagSize = (xbUInt32) iBlockFactor; @@ -1185,15 +1093,8 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const 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; @@ -1280,7 +1181,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const iTagUseCnt++; cNextTag++; - if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 270; throw iRc; @@ -1350,9 +1250,6 @@ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const tteWork = tteWork->next; } free( pBuf ); - -// xbIx::SetCurTag( ( void *) tte ); - } catch (xbInt16 iRc ){ @@ -1447,13 +1344,6 @@ xbInt16 xbIxMdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo throw iRc; } - // do any empty node processing here -// if( bReuseEmptyNodes ){ -// if( bLeaf && lKeyCnt == 1 ){ -// std::cout << "Empty node ready for reuse\n"; -// } -// } - } catch (xbInt16 iRc ){ xbString sMsg; @@ -1486,8 +1376,6 @@ xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ npTag->npNodeChain = NULL; xbIxNode * npSaveCurNode = npTag->npCurNode; - // std::cout << "xbIxMdx::DeleteKey()\n"; - try{ xbString sMsg; @@ -1502,7 +1390,6 @@ xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ // as prev key, ascend tree looking for an interior node needing // updated key value - xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ iErrorStop = 110; @@ -1591,7 +1478,7 @@ xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ } /***********************************************************************/ -//! @brief Dump a given block for a tag +//! @brief Delete a given tag /*! \param vpTag Input tag ptr for tag to be deleted<br> \returns <a href="xbretcod_8h.html">Return Codes</a><br> @@ -2100,8 +1987,6 @@ xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt ) if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) return iRc; -// std::cout << "xbIxMdx::DumpHeader options - " << iDestOpt << " fmtopt = " << iFmtOpt << "\n"; - char c, tfv, cDisplayMask = 1; cDisplayMask = cDisplayMask << 7; if( iFmtOpt != 2 && iFmtOpt != 4 ){ @@ -2116,9 +2001,6 @@ xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt ) } xbase->WriteLogMessage( s, iOpt ); - -// std::cout << std::endl; -// std::cout << s.Sprintf( "Create Date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); xbase->WriteLogMessage( s, iOpt ); @@ -2724,9 +2606,6 @@ xbInt16 xbIxMdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw = 0 ){ throw iRc; } } - // else { - // throw iRc; - // } } catch (xbInt16 iRc ){ xbString sMsg; @@ -2766,23 +2645,16 @@ xbString &xbIxMdx::GetKeyFilter( const void * vpTag ) const{ xbMdxTag * mpTag = (xbMdxTag *) vpTag; - // std::cout << "GetKeyFilter() "; if( mpTag->sFiltExp ) return *mpTag->sFiltExp; -// std::cout << " not null\n"; else return sNullString; - // std::cout << " null\n"; - -// next line causes seg faults -// return *mpTag->sFiltExp; } /**************************************************************************************************/ xbInt16 xbIxMdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { xbInt16 iRc = XB_NO_ERROR; - xbInt16 iErrorStop = 0; try{ @@ -3339,15 +3211,9 @@ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iO try{ -// std::cout << "xbIxMdx::HarvestEmptyNode() page= " << BlockToPage( npNode->ulBlockNo ); -// std::cout << " block = " << npNode->ulBlockNo << "\n"; - if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){ bRootPage = xbTrue; - -// std::cout << "special root page processing *****************\n"; } - memset( npNode->cpBlockData, 0x00, GetBlockSize()); char *pTrg = npNode->cpBlockData; @@ -3356,7 +3222,6 @@ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iO ePutUInt32( pTrg, ulFirstFreePage ); } - if( bRootPage ){ if( mpTag->cHasKeys ){ @@ -3374,7 +3239,6 @@ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iO // might need to update left sibling and right sibling here. // Fields don't seem to be updated consistently by other xbase tools, // for now, not updating - } } else { @@ -3393,7 +3257,6 @@ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iO iErrorStop = 130; throw iRc; } - } if( iOpt == 1 ){ @@ -3418,8 +3281,6 @@ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iO return iRc; } - - /***********************************************************************/ //! @brief Harvest Tag Nodes. /*! @@ -3523,9 +3384,7 @@ xbInt16 xbIxMdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xb xbInt16 iCopyLen; xbInt16 iNewKeyPos = 8; - try{ - xbInt32 lKeyCnt = GetKeyCount( npNode ); iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); char *pSrc = npNode->cpBlockData; @@ -3642,12 +3501,6 @@ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, ch try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); - -// std::cout << "InsertNodeL Keycount = " << lKeyCnt << "\n"; -// next line is correct, this aligns with db7 -// "4" is the four byte record number stored to the left of the key -// xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; - iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); // length of number of keys that need to be moved to the right @@ -3657,24 +3510,18 @@ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, ch iCopyLen = 0; // +8 is to include the first two 4 byte fields in the block - // xbUInt32 ulRqdBufSize = (xbUInt32) (iKeyPos + (npTag->iKeyItemLen * 2) + iCopyLen + 8); xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8; -/* - std::cout << "InsertNodeL CopyLen = " << iCopyLen << "\n"; - std::cout << "InsertNodeL iNewKeyPos = " << iNewKeyPos << "\n"; - std::cout << "InsertNodeL SlotNo = " << iSlotNo << "\n"; - std::cout << "InsertNodeL lKeyCnt = " << lKeyCnt << "\n"; - std::cout << "InsertNodeL node buf size = " << npNode->ulBufSize << "\n"; - std::cout << "InsertNodeL key item len = " << npTag->iKeyItemLen << "\n"; - std::cout << "InsertNodeL key len = " << npTag->iKeyLen << "\n"; - std::cout << "required buf size = " << ulRqdBufSize << "\n"; - std::cout << "InsertNodeL key buf strlen = " << strlen( npTag->cpKeyBuf ) << "\n"; -*/ - if( ulRqdBufSize > npNode->ulBufSize ){ + npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); + + // init the newly acquired buffer space + char *p = npNode->cpBlockData; + p += (npNode->ulBufSize - (xbUInt32) npTag->iKeyItemLen); + memset( p, 0x00, (size_t) npTag->iKeyItemLen ); + if( !npNode->cpBlockData ){ iErrorStop = 100; iRc = XB_NO_MEMORY; @@ -3688,10 +3535,6 @@ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, ch pNewKeyPos += iNewKeyPos; if( iSlotNo < lKeyCnt ) { - // pKeyPos = npNode->cpBlockData; - // pKeyPos += iKeyPos; - // pKeyPos += iNewKeyPos; - // pTrg = pKeyPos; pTrg = pNewKeyPos; pTrg += npTag->iKeyItemLen; memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); @@ -3710,14 +3553,11 @@ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, ch // update number of keys on the node ePutInt32( npNode->cpBlockData, ++lKeyCnt ); -// std::cout << "lKeyCntA = " << GetKeyCount( npNode ) << "\n"; - // determine length of node, zap everything to the right of it xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen ); xbUInt32 iClearLen = npNode->ulBufSize - iStartPos; -// std::cout << "InsertNodeL SP = " << iStartPos << " clear len = " << iClearLen << " ulBufsize = " << npNode->ulBufSize << "\n"; char *p = npNode->cpBlockData; p += iStartPos; memset( p, 0x00, iClearLen ); @@ -3727,10 +3567,6 @@ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, ch iErrorStop = 100; throw iRc; } - -// std::cout << "lKeyCntB = " << GetKeyCount( npNode ) << "\n"; - - } catch (xbInt16 iRc ){ xbString sMsg; @@ -3754,8 +3590,6 @@ inline xbBool xbIxMdx::IsLeaf( void *vpTag, xbIxNode *npNode ) const{ p+=8; p+= mTag->iKeyItemLen * lNoOfKeys; - // printf( "IsLeaf p = [%d] b1 = [%x] keylen = [%d]\n", eGetUInt32( p ), *p, mTag->iKeyItemLen ); - if( eGetUInt32( p ) == 0 ){ // std::cout << "leaf node\n"; return true; @@ -3773,8 +3607,6 @@ xbInt16 xbIxMdx::KeyExists( void * vpTag ) xbMdxTag *mpTag = (xbMdxTag *) vpTag; xbInt16 iRc = FindKey( vpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); - // iRc == 0 ? return 1 : return 0; - if( iRc == 0 ) return 1; else @@ -3940,8 +3772,6 @@ xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){ // option 1 - Load the entire tag detail // option 2 - Load the dynamic variables only - // std::cout << "LoadTagDetail() iOption = " << iOption << "\n"; - xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; size_t iReadSize; @@ -4020,10 +3850,6 @@ xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){ p+=221; tte->cKeyFmt3 = *p; -//for( int i = 0; i < 5; i++ ) -// printf( "%d [%x]\n", i, *p++ ); -// p+=2; - if( tte->cHasFilter ){ p+=282; tte->sFiltExp = new xbString(); @@ -4135,7 +3961,6 @@ xbInt16 xbIxMdx::LoadTagTable() ttel->next = tte; else mdxTagTbl = tte; - // tte->sKeyExp = new xbString(); ttel = tte; tte->next = NULL; @@ -4268,13 +4093,9 @@ xbInt16 xbIxMdx::PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt1 xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt ) { - - xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; -// std::cout << "ReadHeadBlock() option = " << iOpt << "\n"; - try{ if( !FileIsOpen()){ iRc = XB_NOT_OPEN; @@ -4367,8 +4188,6 @@ xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt ) cUpdateMM = *p++; cUpdateDD = *p; } - - } catch (xbInt16 iRc ){ xbString sMsg; @@ -4425,7 +4244,7 @@ xbInt16 xbIxMdx::Reindex( void **vpTag ){ iErrorStop = 100; throw iRc; } - bLocked = xbTrue; + bLocked = xbTrue; } #endif @@ -4476,13 +4295,6 @@ xbInt16 xbIxMdx::Reindex( void **vpTag ){ // create new file & add the tags while( p ){ - //std::cout << "Reindex() linked list extract\n"; - //std::cout << "Tag Name = [" << p->sTagName << "]\n"; - //std::cout << "Key Exp = [" << p->sKeyExp->Str() << "]\n"; - //std::cout << "Filt Exp = [" << p->sFiltExp->Str() << "]\n"; - //std::cout << "bDesc = [" << p->bDesc << "]\n"; - //std::cout << "bUnique = [" << p->bUnique << "]\n"; - if(( iRc = CreateTag( p->sTagName, p->sKeyExp->Str(), p->sFiltExp->Str(), p->bDesc, p->bUnique, xbTrue, vpTag )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; @@ -4550,6 +4362,7 @@ xbInt16 xbIxMdx::Reindex( void **vpTag ){ sMsg.Sprintf( "xbIxMdx::ReIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); + this->DeleteTag( mpTag ); } #ifdef XB_LOCKING_SUPPORT @@ -4594,39 +4407,6 @@ void xbIxMdx::SetReuseEmptyNodesSw( xbBool bEmptyNodesSw ) { } /***********************************************************************/ -//! @brief Set Tag Pointer. -/*! - Set binary tree pointer value. The MDX tags are stored with binary - tree positions. This routine sets the value in memory. - \param cPtr L - Left child.<br> - R - Right child.<br> - P - Parent. - \param iWhich - Which tag to update - \param cVal - Value to set. - \returns void -*/ - -/* -void xbIxMdx::SetTagPtr( char cPtr, xbInt16 iWhich, char cVal ){ - - xbMdxTag *mpTag = (xbMdxTag *) GetTag( iWhich ); - if( mpTag ){ - switch( cPtr ){ - case 'L': - mpTag->cLeftChild = cVal; - break; - case 'R': - mpTag->cRightChild = cVal; - break; - case 'P': - mpTag->cParent = cVal; - break; - } - } -} -*/ - -/***********************************************************************/ //! @brief Split an interior node /*! @@ -4662,7 +4442,6 @@ xbInt16 xbIxMdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, char *pSrc; char *pTrg; - try{ xbInt32 lKeyCnt = GetKeyCount( npLeft ); xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor); @@ -4816,11 +4595,8 @@ xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ xbInt16 iErrorStop = 0; try{ - xbInt64 lPos = (mpTag->ulTagHdrPageNo * 512) + 20; -// std::cout << "UpdateSerialNo offset = " << lPos << " option = " << iOption << "\n"; - if( iOption != 2 ){ if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; @@ -4845,7 +4621,6 @@ xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ throw iRc; } } - } catch (xbInt16 iRc ){ xbString sMsg; @@ -4856,7 +4631,6 @@ xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ return iRc; } - /***********************************************************************/ //! @brief UpdateTagKey /*! @@ -4881,9 +4655,6 @@ xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ try{ // save off any needed fields for updating xbUInt32 ulTagSizeSave = npTag->ulTagSize; - // std::cout << "old size = " << ulTagSizeSave << " new size = " << npTag->ulTagSize << "\n"; - //std::cout << "UpdateTagKey - tag size was updated need to do something here - test \n"; - if( cAction == 'D' || cAction == 'R' ){ // std::cout << "UpdateTagKey-delete going to DeleteKey \n"; @@ -4891,7 +4662,6 @@ xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ iErrorStop = 100; throw iRc; } - //std::cout << "UpdateTagKey-delete back from DeleteKey \n"; } if( cAction == 'A' || cAction == 'R' ){ @@ -4941,7 +4711,8 @@ xbInt16 xbIxMdx::WriteHeadBlock( xbInt16 iOption ){ xbInt16 iErrorStop = 0; try{ - xbDate d; // default is system date, today + xbDate d; + d.Sysdate(); // set to system date, today cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); diff --git a/src/core/xbixndx.cpp b/src/core/xbixndx.cpp index 9d946dd..b28dd9d 100755 --- a/src/core/xbixndx.cpp +++ b/src/core/xbixndx.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -309,8 +309,8 @@ xbInt16 xbIxNdx::CheckForDupKey( void *vpTag ) \param vpTag Tag to create key for. \param iOpt Output message destination<br> - 0 = stdout<br> - 1 = Syslog<br> + 0 = Syslog<br> + 1 = Stdout<br> 2 = Both<br> \returns <a href="xbretcod_8h.html">Return Codes</a> */ @@ -609,7 +609,8 @@ xbInt16 xbIxNdx::CreateTag( const xbString &sName, const xbString &sKey, npTag->iUnique = iUnique; npTag->ulRootBlock = 1L; - npTag->ulTotalBlocks = 2l; + //npTag->ulTotalBlocks = 2l; + npTag->ulTotalBlocks = 2L; npTag->sKeyExpression = sKey; GetFileNamePart( npTag->sTagName ); @@ -2426,62 +2427,75 @@ xbInt16 xbIxNdx::Reindex( void **vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; - //xbNdxTag * npTag; - //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; + xbNdxTag *npTag = ndxTag; try{ - xbString sFileName = GetFqFileName(); - xbString sKey; // = GetKeyExpression( vpTag ); - sKey.Set( GetKeyExpression( *vpTag )); xbInt16 iUnique = GetUnique( *vpTag ); - xbString sFilter = ""; - void *vpTag2; - if(( iRc = CreateTag( sFileName, sKey, sFilter, 0, iUnique, xbTrue, &vpTag2 )) != XB_NO_ERROR ){ + npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); + npTag->npCurNode = NULL; + npTag->ulRootBlock = 1L; + npTag->ulTotalBlocks = 2L; + + if(( iRc = xbTruncate( 1024 )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } + if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + char buf[512]; + memset( buf, 0x00, 512 ); + + if(( iRc = WriteBlock( 1, 0, buf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + xbUInt32 ulRecCnt = 0; if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ - iErrorStop = 110; + iErrorStop = 130; throw iRc; } for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){ if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){ - iErrorStop = 120; + iErrorStop = 140; throw iRc; } - if(( iRc = CreateKey( vpTag2, 1 )) != XB_NO_ERROR ){ - iErrorStop = 130; + if(( iRc = CreateKey( npTag, 1 )) != XB_NO_ERROR ){ + iErrorStop = 150; throw iRc; } if( iUnique ){ - iRc = CheckForDupKey( vpTag2 ); + // iRc = CheckForDupKey( vpTag2 ); + iRc = CheckForDupKey( npTag ); if( iRc != 0 ){ if( iRc < 0 ){ - iErrorStop = 140; + iErrorStop = 160; throw iRc; } return XB_KEY_NOT_UNIQUE; } } - if(( iRc = AddKey( vpTag2, l )) != XB_NO_ERROR ){ + if(( iRc = AddKey( npTag, l )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } - *vpTag = vpTag2; + *vpTag = npTag; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); + this->DeleteTag( NULL ); // Don't leave the index in an incomplete state } return iRc; } diff --git a/src/core/xbixtdx.cpp b/src/core/xbixtdx.cpp new file mode 100755 index 0000000..4137725 --- /dev/null +++ b/src/core/xbixtdx.cpp @@ -0,0 +1,661 @@ +/* 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.<br>xbFalse - Not unique. + \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay. + \param vpTag Output from method Pointer to vptag pointer. + \returns <a href="xbretcod_8h.html">Return Codes</a> +*/ + + +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<br> + \returns <a href="xbretcod_8h.html">Return Codes</a><br> + 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 */
\ No newline at end of file diff --git a/src/core/xblog.cpp b/src/core/xblog.cpp index 6031c9e..9443006 100755 --- a/src/core/xblog.cpp +++ b/src/core/xblog.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -25,11 +25,15 @@ namespace xb{ //! @brief Constructor. xbLog::xbLog() : xbFile( NULL ){ + + // std::cout << "xbLog::xbLog(1) Directory = [" << GetLogDirectory() << "]\n"; + // std::cout << "xbLog::xbLog(1) Name = [" << GetLogFileName() << "]\n"; + SetDirectory( GetLogDirectory()); SetFileName ( GetLogFileName()); bLoggingStatus = xbFalse; - lLogSize = 50000; + lLogSize = 100000; #ifdef XB_LOCKING_SUPPORT iShareMode = XB_MULTI_USER; @@ -49,7 +53,7 @@ xbLog::xbLog( const xbString & sLogFileName ) : xbFile( NULL ){ SetFileName( sLogFileName ); // no file path bLoggingStatus = xbFalse; - lLogSize = 50000; + lLogSize = 100000; #ifdef XB_LOCKING_SUPPORT iShareMode = XB_MULTI_USER; @@ -95,6 +99,8 @@ void xbLog::LogSetLogSize( size_t lSize ){ \returns void */ void xbLog::LogSetStatus( xbBool bStatus ){ + if( bLoggingStatus && !bStatus ) + LogClose(); bLoggingStatus = bStatus; } /******************************************************************************/ @@ -104,6 +110,19 @@ void xbLog::LogSetStatus( xbBool bStatus ){ */ xbInt16 xbLog::LogOpen(){ xbInt16 rc; + +// std::cout << "*****\nxbLog::LogOpen(1) GetLogDirectory = " << GetLogDirectory() << "\n"; +// std::cout << "xbLog::LogOpen(1) GetLogFileName = " << GetLogFileName() << "\n"; +// std::cout << "xbLog::GetFqFileName(1) = " << GetFqFileName() << "\n\n"; + + // 4.1.3 added next two lines for dynamic log file name changing + SetDirectory( GetLogDirectory()); + SetFileName ( GetLogFileName()); + +// std::cout << "*****\nxbLog::LogOpen(2) GetLogDirectory = " << GetLogDirectory() << "\n"; +// std::cout << "xbLog::LogOpen(2) GetLogFileName = " << GetLogFileName() << "\n"; +// std::cout << "xbLog::GetFqFileName(2) = " << GetFqFileName() << "\n\n"; + if(( rc = xbFopen( "a", iShareMode )) != XB_NO_ERROR ) return rc; xbFTurnOffFileBuffering(); diff --git a/src/core/xbmemo.cpp b/src/core/xbmemo.cpp index 2bde853..406a77d 100755 --- a/src/core/xbmemo.cpp +++ b/src/core/xbmemo.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. diff --git a/src/core/xbmemo3.cpp b/src/core/xbmemo3.cpp index 60c1d53..767e9d2 100755 --- a/src/core/xbmemo3.cpp +++ b/src/core/xbmemo3.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -133,15 +133,18 @@ xbInt16 xbMemoDbt3::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ xbBool bDone = xbFalse; sMemoData = ""; try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ iErrorStop = 100; throw rc; } + if( ulBlockNo == 0L ){ sMemoData = ""; return XB_NO_ERROR; } spp = NULL; + while( !bDone ){ if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){ iErrorStop = 120; @@ -256,6 +259,7 @@ xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI #endif try{ + #ifdef XB_LOCKING_SUPPORT if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ @@ -275,7 +279,8 @@ xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI // create temp file xbString sTempMemoName; - if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } @@ -297,7 +302,8 @@ xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI xbString sMemoFldData; for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){ - if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){ + + if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } @@ -310,6 +316,8 @@ xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI iErrorStop = 160; throw iRc; } + + if( cFldType == 'M' ){ // copy it to work field if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ @@ -363,11 +371,13 @@ xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI throw iRc; } } + //close and delete target if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){ iErrorStop = 250; throw iRc; } + if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){ iErrorStop = 260; throw iRc; diff --git a/src/core/xbmemo4.cpp b/src/core/xbmemo4.cpp index d02df99..9770806 100755 --- a/src/core/xbmemo4.cpp +++ b/src/core/xbmemo4.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -798,7 +798,8 @@ xbInt16 xbMemoDbt4::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUI // create temp file xbString sTempMemoName; - if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } diff --git a/src/core/xbssv.cpp b/src/core/xbssv.cpp index d3e1070..532f942 100755 --- a/src/core/xbssv.cpp +++ b/src/core/xbssv.cpp @@ -21,6 +21,7 @@ const xbErrorMessage xbErrorMessages[] = { { XB_NO_ERROR, "No Error" }, { XB_NO_MEMORY, "No Memory" }, { XB_INVALID_OPTION, "Invalid Option" }, + { XB_INVALID_PARAMETER, "Invalid Parameter" }, { XB_DUP_TABLE_OR_ALIAS, "Duplicate Alias/Table Name" }, { XB_INVALID_NODELINK, "Invalid Node Link" }, { XB_KEY_NOT_UNIQUE, "Key Not Unique" }, @@ -77,7 +78,6 @@ const xbErrorMessage xbErrorMessages[] = { }; // see also xbretcod.h - xbInt16 xbSsv::iEndianType = 0; xbString xbSsv::sDefaultDateFormat = "MM/DD/YY"; @@ -85,17 +85,12 @@ xbInt16 xbSsv::iDefaultFileVersion = 4; xbString xbSsv::sNullString = ""; xbBool xbSsv::bDefaultAutoCommit = xbTrue; - -#ifdef WIN32 -xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; -#else -xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; -#endif // +xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR; +xbString xbSsv::sTempDirectory = PROJECT_TEMP_DIR; #ifdef XB_LOGGING_SUPPORT -xbString xbSsv::sLogDirectory = PROJECT_LOG_DIR; -//xbString xbSsv::sLogDirectory = ""; -xbString xbSsv::sLogFileName = PROJECT_DFLT_LOGFILE; +xbString xbSsv::sLogDirectory = PROJECT_LOG_DIR; +xbString xbSsv::sLogFileName = PROJECT_DFLT_LOGFILE; #endif // XB_LOGGING_SUPPORT #ifdef XB_LOCKING_SUPPORT @@ -223,25 +218,51 @@ const char * xbSsv::GetErrorMessage( xbInt16 iErrorCode ) const{ //! @brief Get home directory. /*! \param sHomeDirOut - Output home directory for current user. - \returns void */ void xbSsv::GetHomeDir( xbString &sHomeDirOut ){ - #ifdef WIN32 + #ifdef HAVE_GETENV_S_F + + char sPath[MAX_PATH]; + size_t lSize; + + sHomeDirOut = ""; + memset( sPath, 0x00, MAX_PATH ); + + getenv_s( &lSize, NULL, 0, "HOMEDRIVE" ); + if( lSize > 0 ){ + getenv_s( &lSize, sPath, lSize, "HOMEDRIVE" ); + sHomeDirOut = sPath; + memset( sPath, 0x00, MAX_PATH ); + } + + getenv_s( &lSize, NULL, 0, "HOMEPATH" ); + if( lSize > 0 ){ + getenv_s( &lSize, sPath, lSize, "HOMEPATH" ); + sHomeDirOut += sPath; + } + if( sHomeDirOut == "" ) + sHomeDirOut = "C:\xbase64"; + + #elif defined(WIN32) sHomeDirOut.Sprintf( "%s%s", getenv( "HOMEDRIVE" ), getenv( "HOMEPATH" )); + #else sHomeDirOut.Sprintf( "%s", getenv( "HOME" )); sHomeDirOut.Trim(); if( sHomeDirOut == "" ) sHomeDirOut.Sprintf( "%s", getpwuid( getuid())->pw_dir ); #endif + sHomeDirOut.Trim(); } + + /*************************************************************************/ //! @brief Set the data directory. /*! - \param sDataDirectory Set the data direcroty. + \param sDataDirectory Set the data directory. */ void xbSsv::SetDataDirectory( const xbString &sDataDirectory ){ @@ -302,12 +323,55 @@ void xbSsv::SetEndianType() { } /*************************************************************************/ +//! @brief Set the temp directory. +/*! + \param sTempDirectory Set the data direcroty. +*/ + +void xbSsv::SetTempDirectory( const xbString &sTempDirectory ){ + this->sTempDirectory = sTempDirectory; + + #ifdef WIN32 + this->sTempDirectory.SwapChars( '/', '\\' ); + #else + this->sTempDirectory.SwapChars( '\\', '/' ); + #endif + +} + +/*************************************************************************/ +//! @brief Get the OS dependent path separator. +/*! + \returns Returns '\' for windows environment, otherwise returns '/'. +*/ + +char xbSsv::GetPathSeparator() const { + #ifdef WIN32 + return '\\'; + #else + return '/'; + #endif +} + +/*************************************************************************/ +//! @brief Get the current temp directory. +/*! + \returns xbString containing the current data directory + where the database files are stored. +*/ + +xbString &xbSsv::GetTempDirectory() const { + return sTempDirectory; +} + +/*************************************************************************/ #ifdef XB_LOGGING_SUPPORT //! @brief Get the default log file name. /*! \returns Returns the log file name. */ + xbString & xbSsv::GetLogFileName() const { return sLogFileName; } @@ -317,17 +381,18 @@ xbString & xbSsv::GetLogFileName() const { /*! \returns Returns the log directory. */ + + xbString & xbSsv::GetLogDirectory() const { return sLogDirectory; } - - /*************************************************************************/ //! @brief Set the default log directory name. /*! \param sLogDirectory Name of desired log directory. */ + void xbSsv::SetLogDirectory( const xbString &sLogDirectoryIn ){ this->sLogDirectory = sLogDirectoryIn; @@ -339,13 +404,29 @@ void xbSsv::SetLogDirectory( const xbString &sLogDirectoryIn ){ } +//! @brief Set the log file name. +/*! + \param sLogFileName - Log File Name. + \return void +*/ + +void xbSsv::SetLogFileName( const xbString & sLogFileName ){ + + this->sLogFileName = sLogFileName; +} + + #else xbString & xbSsv::GetLogFileName() const { return sNullString; } + xbString & xbSsv::GetLogDirectory() const { + +std::cout << "xbSsv::GetLogDirectory() returning null\n"; + return sNullString; } @@ -353,6 +434,10 @@ void xbSsv::SetLogDirectory( const xbString &sLogDirectory ){ return; } +void xbSsv::SetLogFileName( const xbString & sLogFileName ){ + return; +} + #endif /*************************************************************************/ diff --git a/src/core/xbstring.cpp b/src/core/xbstring.cpp index 701e50e..89cefb6 100755 --- a/src/core/xbstring.cpp +++ b/src/core/xbstring.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2021,2022 Gary A Kunkel +Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. @@ -175,7 +175,7 @@ xbString &xbString::operator+=( const xbString &s ) { *t++ = s.GetCharacter(i+1); data[newLen] = '\0'; - size += Len; + size == 0 ? size += (Len + 1) : size += Len; return (*this); } @@ -197,7 +197,8 @@ xbString &xbString::operator+=( const char *s ) { for( xbUInt32 i = 0; i < Len; i++ ) data[i+oldLen] = s[i]; data[newLen] = '\0'; - size += Len; + // size += Len; + size == 0 ? size+= (Len + 1) : size += Len; return (*this); } /************************************************************************/ @@ -211,7 +212,8 @@ xbString &xbString::operator+=( char c ) { data = (char *)realloc(data, oldLen+Len+1); data[oldLen] = c; data[oldLen+1] = 0; - size++; + // size++; + size == 0 ? size += 2 : size++; return (*this); } /************************************************************************/ @@ -237,7 +239,8 @@ xbString &xbString::operator-=( const xbString &s ) { data[i+oldLen] = s.GetCharacter(i+1); data[newLen] = '\0'; - size += Len; + //size += Len; + size == 0 ? size += (Len+1) : size += Len; Rtrim(); return (*this); } @@ -265,7 +268,9 @@ xbString &xbString::operator-=(const char *s) { data[i+oldLen] = s[i]; data[newLen] = '\0'; - size += Len; + //size += Len; + size == 0 ? size += (Len+1) : size += Len; + Rtrim(); return (*this); } @@ -278,7 +283,10 @@ xbString &xbString::operator-=(const char *s) { xbString &xbString::operator-=(const char c) { Rtrim(); xbUInt32 oldSize = size; - size += 1; + + // size += 1; + size == 0 ? size += 2 : size += 1; + data = (char *)realloc( data, size ); if( oldSize == 0 ) data[0] = 0; data[size-2] = c; @@ -534,7 +542,10 @@ xbString &xbString::AddBackSlash( char c ) { if( data ) free( data ); data = p; - size += lCnt; + + // size += lCnt; + size == 0 ? size += (lCnt+1) : size += lCnt; + return *this; } /************************************************************************/ @@ -571,26 +582,37 @@ xbString &xbString::Append( char c ) { /************************************************************************/ //! @brief Append data to string. /*! - \param s String data to append. + \param s String data to append. \param ulByteCount Maximum number of bytes to append. \returns Reference to this string. */ xbString &xbString::Append( const char *s, xbUInt32 ulByteCount ) { - if (s == NULL) return (*this); - xbUInt32 oldLen = this->Len(); - xbUInt32 newLen = ulByteCount + oldLen; + if ( s == NULL || !*s || ulByteCount == 0) + return (*this); - data = (char *)realloc(data, newLen+1); + xbUInt32 ulOrigLen = this->Len(); - if(oldLen == 0) - data[0] = 0; + // s might not be null byte at the end, can't use strlen + // xbUInt32 ulAddLen = strlen( s ); + xbUInt32 ulAddLen = 0; + const char *p = s; - for( xbUInt32 i = 0; i < ulByteCount; i++ ) - data[i+oldLen] = s[i]; + while( ulAddLen < ulByteCount && *p ){ + p++; + ulAddLen++; + } - data[newLen] = '\0'; - size += ulByteCount; + if( ulAddLen > ulByteCount ) + ulAddLen = ulByteCount; + + size = ulOrigLen + ulAddLen + 1; + data = (char *) realloc( data, size ); + + for( xbUInt32 i = 0; i < ulAddLen; i++ ) + data[i+ulOrigLen] = s[i]; + + data[size-1] = 0x00; return (*this); } @@ -616,7 +638,10 @@ xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos, xbUInt32 ulCo if((( ulCopyLen - 1) + ulStartPos ) > lLen ) ulCopyLen = lLen - ulStartPos + 1; data = (char *)calloc(1, ulCopyLen + 1); - size = ulCopyLen; + + //size = ulCopyLen + 1; + size == 0 ? size += (ulCopyLen+1) : size += ulCopyLen; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; data[ulCopyLen] = '\0'; @@ -643,7 +668,9 @@ xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos){ xbUInt32 ulCopyLen; ulCopyLen = ulSrcLen - ulStartPos + 1; data = (char *)calloc(1, ulCopyLen + 1); - size = ulCopyLen; + + size = ulCopyLen + 1; + for( xbUInt32 i = 0; i < ulCopyLen; i++ ) data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)]; data[ulCopyLen] = '\0'; @@ -672,7 +699,7 @@ xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos, xbUInt32 u if((( ulCopyLen - 1) + ulStartPos ) > ulSrcLen ) ulCopyLen = ulSrcLen - ulStartPos + 1; data = (char *)calloc(1, ulCopyLen + 1); - size = ulCopyLen; + size = ulCopyLen + 1; for( xbUInt32 i = 0; i < ulCopyLen; i++ ) data[i] = sStr[i + ulStartPos]; data[ulCopyLen] = '\0'; @@ -703,6 +730,7 @@ xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos){ for( xbUInt32 i = 0; i < ulCopyLen; i++ ) data[i] = sStr[i + ulStartPos]; data[ulCopyLen] = '\0'; + size++; return (*this); } /************************************************************************/ @@ -895,6 +923,21 @@ void xbString::DumpHex( const char * title ) const { /************************************************************************/ //! @brief Extract an element out of a delimited string. /*! + \param sSrc Source string. + \param cDelim Delimiter. + \param lSkipCnt Number of delimiters to skip. + \param iOpt 0 - ignore single and double quotes.<br> + 1 - ignore delimiters between single or double quotes. + \returns Reference to string extracted from element. +*/ +xbString &xbString::ExtractElement( xbString &sSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt ) +{ + return ExtractElement( sSrc.Str(), cDelim, lSkipCnt, iOpt ); +} + +/************************************************************************/ +//! @brief Extract an element out of a delimited string. +/*! \param pSrc Source string. \param cDelim Delimiter. \param lSkipCnt Number of delimiters to skip. @@ -1043,6 +1086,8 @@ xbUInt32 xbString::GetLastPos(const char* s) const{ /************************************************************************/ //! @brief Get the path separator out of the string. /*! + This method assumes the string is a valid path name. + If it is, it returns either / or \. \returns Char value containing either / or \ depending on OS. */ char xbString::GetPathSeparator() const { @@ -1147,12 +1192,15 @@ xbString &xbString::Ltrim(){ s++; size--; } + xbUInt32 i; for( i = 0; i < size; i++ ) data[i] = data[i+s]; data[i] = 0x00; data = (char *) realloc( data, size ); + return *this; + } /************************************************************************/ @@ -1162,10 +1210,6 @@ xbString &xbString::Ltrim(){ \returns Reference to this string. */ xbString &xbString::Ltrunc( xbUInt32 ulCnt ){ - // left truncate cnt bytes - - char * ndata; - char * p; if( ulCnt >= size ){ if( size > 0 ){ free( data ); @@ -1174,6 +1218,9 @@ xbString &xbString::Ltrunc( xbUInt32 ulCnt ){ } return *this; } + + char * ndata; + char * p; ndata = (char *) calloc( 1, size - ulCnt ); p = data; p += ulCnt; @@ -1203,12 +1250,27 @@ xbString &xbString::Mid( xbUInt32 ulStartPos, xbUInt32 ulTargLen ){ return( *this ); if( ulStartPos > Len() ) return( *this ); +/* + // Resize( ulTargLen + 1 ); char *pTarg = data; char *pSrc = data + ulStartPos - 1; for( xbUInt32 l = 0; l < ulTargLen; l++ ) *pTarg++ = *pSrc++; *pTarg = 0x00; - Resize( ulTargLen + 1 ); + // Resize( ulTargLen + 1 ); + */ + + char * newData = (char *) calloc( 1, ulTargLen + 1 ); + char *pTarg = newData; + char *pSrc = data + ulStartPos - 1; + for( xbUInt32 l = 0; l < ulTargLen; l++ ) + *pTarg++ = *pSrc++; + *pTarg = 0x00; + + free( data ); + data = newData; + size = ulTargLen + 1; + return *this; } @@ -1300,8 +1362,6 @@ xbUInt32 xbString::Pos(char c, xbUInt32 ulStartPos ) const { return 0; } - - /************************************************************************/ //! @brief Determine position of a given character /*! @@ -1323,7 +1383,6 @@ xbUInt32 xbString::Pos(char c) const { p++; } } - if( iFound ) return iPos + 1; else @@ -1396,10 +1455,6 @@ xbString &xbString::Remove(xbUInt32 ulStartPos, xbUInt32 ulDelSize ) { return( *this ); } - - - - /************************************************************************/ //! @brief Replace a value within a string with another value /*! @@ -1457,6 +1512,7 @@ xbString &xbString::Replace( const char *sReplace, const char *sReplaceWith, xbI free(data); data = sBuf2; + size = ulNewLen; } } return *this; @@ -1471,12 +1527,11 @@ xbString &xbString::Replace( const char *sReplace, const char *sReplaceWith, xbI //the new size includes the null termination byte xbString &xbString::Resize(xbUInt32 ulSize) { -// data = (char *) realloc((void *) data, ulSize ); +// data = (char *) realloc( data, ulSize ); // original - data = (char *)realloc(data, ulSize); - + data = (char *) realloc( data, ulSize ); if( ulSize > 0 ) data[ulSize-1] = 0; @@ -1495,6 +1550,8 @@ xbString &xbString::Rtrim(){ xbUInt32 l = Len(); if( l == 0 ) return *this; + + xbUInt32 ulOrigSize = size; l--; for(;;) { @@ -1506,6 +1563,9 @@ xbString &xbString::Rtrim(){ break; l--; } + + if( ulOrigSize != size ) + data = (char * ) realloc( data, size ); return *this; } @@ -1525,7 +1585,7 @@ xbString &xbString::Set( const char *s ) { free(data); data = NULL; } - if(s == NULL ) { + if( s == NULL || !*s ) { if( data ) free( data ); data = NULL; @@ -1546,14 +1606,17 @@ xbString &xbString::Set( const char *s ) { */ xbString &xbString::Set( const xbString &s ) { +// if( s.Str() == NULL || s.Len() == 0 ){ + if( s.Str() == NULL ){ if( data ) free( data ); data = NULL; size = 0; } else { - char *p = (char *) calloc( 1, s.Len() + 1 ); + xbUInt32 ulLen = s.Len(); + char *p = (char *) calloc( 1, ulLen + 1 ); xb_strcpy( p, s.Str()); - size = s.Len() + 1; + size = ulLen + 1; if( data ) free( data ); data = p; } @@ -1619,12 +1682,8 @@ xbString &xbString::Sprintf( const char *sFormat, ...) { xbInt32 iRc; va_list ap; - char *t; -// if( data ) -// free( data ); - #ifdef HAVE__VSNPRINTF_S_F va_start( ap, sFormat ); @@ -1646,7 +1705,7 @@ xbString &xbString::Sprintf( const char *sFormat, ...) { va_start( ap, sFormat ); // size = (xbUInt32) vsprintf_s( NULL, 0, sFormat, ap ) + 1; - size = _vscprintf( sFormat, ap ) + 1; + size = (xbUInt32) _vscprintf( sFormat, ap ) + 1; va_end( ap ); t = (char *) malloc( size ); @@ -1772,7 +1831,6 @@ xbString &xbString::Trim(){ return *this; } - /************************************************************************/ //! @brief Private function used for reallocateing memory /*! @@ -1816,11 +1874,8 @@ char * xbString::xb_realloc( char * pIn, xbUInt32 iLen ){ char * xbString::xb_strcpy( char *sTarget, const char *sSource ){ char *temp = sTarget; - while( *sSource != '\0'){ + while( *sSource != '\0') *sTarget++ = *sSource++; -// sTarget++; -// sSource++; - } *sTarget= '\0'; return temp; } @@ -1890,7 +1945,8 @@ xbString &xbString::ZapChar( char c ){ } } *t = 0x00; - Resize( size ); + + data = (char *) realloc( data, size ); return *this; } @@ -1914,7 +1970,6 @@ xbString &xbString::ZapLeadingChar( char c ){ return *this; } - /************************************************************************/ //! @brief Remove trailing character from a string. /*! @@ -1926,7 +1981,7 @@ xbString &xbString::ZapTrailingChar( char c ){ xbUInt32 l = Len(); if( l == 0 ) return *this; - + xbUInt32 ulOrigSize = size; l--; for(;;) { if( data[l] != c ) @@ -1937,6 +1992,8 @@ xbString &xbString::ZapTrailingChar( char c ){ break; l--; } + if( ulOrigSize != size ) + data = (char *) realloc( data, size ); return *this; } diff --git a/src/core/xbtag.cpp b/src/core/xbtag.cpp index a71880b..621d44b 100755 --- a/src/core/xbtag.cpp +++ b/src/core/xbtag.cpp @@ -3,7 +3,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. diff --git a/src/core/xbtblmgr.cpp b/src/core/xbtblmgr.cpp index 2fe6a8c..53b6dd9 100755 --- a/src/core/xbtblmgr.cpp +++ b/src/core/xbtblmgr.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -124,6 +124,7 @@ xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sFqTblName, const if( t && (strcmp( t->psTblAlias->Str(), sAlias.Str()) == 0 )){ iErrorStop = 120; delete i->psFqTblName; + delete i->psTblName; delete i->psTblAlias; free( i ); iRc = XB_DUP_TABLE_OR_ALIAS; @@ -194,7 +195,7 @@ xbDbf *xbTblMgr::GetDbfPtr(const xbString& sTblAlias) const { t = TblList; while( t ){ - std::cout << "s = [" << s.Str() << "] tbl name = [" << t->psTblName->Str() << "]\n"; + // std::cout << "s = [" << s.Str() << "] tbl name = [" << t->psTblName->Str() << "]\n"; if( s == t->psTblName->Str()){ std::cout << "found\n"; return t->pDbf; diff --git a/src/core/xbuda.cpp b/src/core/xbuda.cpp index 0a9c2e7..5db7aec 100755 --- a/src/core/xbuda.cpp +++ b/src/core/xbuda.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. diff --git a/src/core/xbxbase.cpp b/src/core/xbxbase.cpp index 20b7fcc..7267f98 100755 --- a/src/core/xbxbase.cpp +++ b/src/core/xbxbase.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -21,13 +21,13 @@ namespace xb{ //! @brief Class Constructor. xbXBase::xbXBase() { SetEndianType(); - #ifdef XB_LOGGING_SUPPORT - xLog = new xbLog(); - #endif xbFile f( this ); f.SetHomeFolders(); - + xbDate d( (xbUInt16) 1); // initialize xbDate static variables + #ifdef XB_LOGGING_SUPPORT + xLog = new xbLog(); + #endif } /*************************************************************************/ //! @brief Class Deconstructor. @@ -37,11 +37,10 @@ xbXBase::~xbXBase(){ delete xLog; #endif } - /*************************************************************************/ //! @brief Close all tables / files. /*! - This closes everything. + This closes everything and deletes references to the associated xbDbf objects. \returns <a href="xbretcod_8h.html">Return Codes</a> */ xbInt16 xbXBase::CloseAllTables(){ @@ -58,6 +57,7 @@ xbInt16 xbXBase::CloseAllTables(){ iErrorStop = 100; throw iRc; } + delete d; } else { iRc = XB_INVALID_OBJECT; iErrorStop = 110; @@ -74,7 +74,39 @@ xbInt16 xbXBase::CloseAllTables(){ return iRc; } +/************************************************************************/ +//! @brief Fully qualified file name from a directory, filename and extension. +/*! + Given a directory, file name and file extension as inputs, create a fully qualified file name. + \param sDirIn Directory + \param sFileIn File Name + \param sExtIn File Extension + \param sFqnOut A fully qualifed unique file name as output + \returns XB_INVALIED_PARAMETER or XB_NO_ERROR +*/ +xbInt16 xbXBase::CreateFqn( const xbString &sDirIn, const xbString &sNameIn, const xbString &sExtIn, xbString &sFqnOut ){ + + if( sNameIn == "" || sExtIn == "" ) + return XB_INVALID_PARAMETER; + + sFqnOut = sDirIn; + #ifdef WIN32 + sFqnOut.SwapChars( '/', '\\' ); + if( sFqnOut[sFqnOut.Len()] != '\\' ) + sFqnOut += '\\'; + #else + sFqnOut.SwapChars( '\\', '/' ); + if( sFqnOut[sFqnOut.Len()] != '/' ) + sFqnOut += '/'; + #endif + sFqnOut += sNameIn; + if( sExtIn != "" ){ + sFqnOut += '.'; + sFqnOut += sExtIn; + } + return XB_NO_ERROR; +} /*************************************************************************/ //! @brief Parse commmand line options for a given parm request @@ -134,18 +166,21 @@ const xbString & xbXBase::GetLogFqFileName() const { /*! \returns Returns the log file name. */ +/* const xbString & xbXBase::GetLogFileName() const { return xLog->GetFileName(); } +*/ //! @brief Get the log directory. /*! \returns Returns the log directory. */ +/* const xbString & xbXBase::GetLogDirectory() const { - return xLog->GetDirectory(); + return GetLogDirectory(); } - +*/ //! @brief Get the log directory. /*! \returns xbTrue - Logging enabled.<br>xbFalse - Logging disables. @@ -159,19 +194,23 @@ xbBool xbXBase::GetLogStatus() const { \param sLogFileName - Log File Name. \return void */ +/* void xbXBase::SetLogFileName( const xbString & sLogFileName ){ xLog->SetFileName( sLogFileName ); } - +*/ //! @brief Set the log directory. /*! \param sLogDirectory - Log File Directory. \return void */ +/* void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ xLog->SetDirectory( sLogDirectory ); } +*/ + //! @brief Set the logfile size. /*! @@ -182,6 +221,15 @@ void xbXBase::SetLogSize( size_t lSize ) { xLog->LogSetLogSize( lSize ); } +//! @brief Get the logfile size. +/*! + \return log file size +*/ +size_t xbXBase::GetLogSize() const { + return xLog->LogGetLogSize(); +} + + //! @brief Write message to logfile. /*! \param sLogMessage - Message to write. @@ -225,18 +273,22 @@ xbInt16 xbXBase::FlushLog() { const xbString & xbXBase::GetLogFqFileName() const { return sNullString; } +/* const xbString & xbXBase::GetLogFileName() const { return sNullString; } const xbString & xbXBase::GetLogDirectory() const { return sNullString; } +*/ +/* void xbXBase::SetLogFileName( const xbString & sLogFileName ){ return; } void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){ return; } +*/ xbBool xbXBase::GetLogStatus() const { return xbFalse; } @@ -258,6 +310,7 @@ xbInt16 xbXBase::FlushLog() { void xbXBase::SetLogSize( size_t lSize ) { return; } + #endif // XB_LOGGING_SUPPORT /*************************************************************************/ @@ -554,8 +607,8 @@ xbInt16 xbXBase::GetFunctionInfo( const xbString &sExpLine, char &cReturnType, x \param lMillisecs Milliseconds to sleep. */ void xbXBase::xbSleep( xbInt32 lMillisecs ){ - #ifdef WIN32 - Sleep( lMillisecs ); + #ifdef WIN32 + Sleep( (xbUInt32) lMillisecs ); #else usleep( (xbInt64) lMillisecs * 1000 ); #endif @@ -703,6 +756,7 @@ xbInt16 xbXBase::OpenHighestVersion( const xbString &sTableName, const xbString iRc = XB_FILE_NOT_FOUND; throw iRc; } + unsigned char cFileTypeByte; if(( iRc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){ iErrorStop = 120; diff --git a/src/examples/xb_ex_date.cpp b/src/examples/xb_ex_date.cpp index fc038b5..7b92dbe 100755 --- a/src/examples/xb_ex_date.cpp +++ b/src/examples/xb_ex_date.cpp @@ -24,6 +24,8 @@ using namespace xb; int main() { + xbXBase x; /* initial date static variables */ + xbString StringDate( "19601007" ); /* oct 7 1960 */ char CharDate[9] = "19611109"; /* nov 9 1961 */ diff --git a/src/examples/xb_ex_expression.cpp b/src/examples/xb_ex_expression.cpp index 840e9c6..f16c79f 100755 --- a/src/examples/xb_ex_expression.cpp +++ b/src/examples/xb_ex_expression.cpp @@ -168,7 +168,6 @@ int main(){ } PrintResult( &sExpression, &exp ); - // String expression example sExpression = "CFLD1+CFLD2+'{'+DTOS(DATE1)+'}'"; xbExp exp2( &x ); @@ -176,11 +175,13 @@ int main(){ iErrorStop = 210; throw iRc; } + // Process the parsed expression if(( iRc = exp2.ProcessExpression()) != XB_NO_ERROR ){ iErrorStop = 220; return -1; } + PrintResult( &sExpression, &exp2 ); // Date example @@ -211,8 +212,6 @@ int main(){ } PrintResult( &sExpression, &exp4 ); - - // Cleanup MyFile->DeleteTable(); delete MyFile; diff --git a/src/examples/xb_ex_log.cpp b/src/examples/xb_ex_log.cpp new file mode 100755 index 0000000..cf5320f --- /dev/null +++ b/src/examples/xb_ex_log.cpp @@ -0,0 +1,77 @@ +/* xb_ex_log.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 program demostrates how to use logging + + +#include "xbase.h" + +using namespace xb; + +int main( int argCnt, char **av ) +{ + + #ifdef XB_LOGGING_SUPPORT + + xbXBase x; + xbString sMsg; + + + std::cout << "Default Logfile Name is: [" << x.GetLogFqFileName().Str() + << "] Rollover size = [" << x.GetLogSize() + << "]" << std::endl; + + if( x.GetLogStatus() ) + std::cout << "Logging is active" << std::endl; + else + std::cout << "Logging is inactive" << std::endl; + + x.SetLogDirectory( PROJECT_LOG_DIR ); // use the library log directory + x.SetLogFileName ( "MySpecialLogFile.txt" ); // set to use a special name + x.SetLogSize ( x.GetLogSize() * 2 ); // double the log file size + + // enable the logfile and write a message for the new settings to take effect + x.EnableMsgLogging(); + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + + std::cout << "New Logfile Name is: [" << x.GetLogFqFileName().Str() + << "] Rollover size = [" << x.GetLogSize() + << "]" << std::endl; + + if( x.GetLogStatus() ) + std::cout << "Logging is active" << std::endl; + else + std::cout << "Logging is inactive" << std::endl; + + // write some messages to the logfile + for( int i = 0; i < 5; i++ ){ + sMsg.Sprintf( "Test message [%d]", i ); + x.WriteLogMessage( sMsg ); + } + + sMsg.Sprintf( "Program [%s] terminating..", av[0] ); + x.WriteLogMessage( sMsg ); + + x.FlushLog(); // not really needed, but here for demonstration purposes + + #endif // B_LOGGING_SUPPORT + + return 0; +} + + + + diff --git a/src/examples/xb_ex_ssv.cpp b/src/examples/xb_ex_ssv.cpp index a78345f..61d72a2 100755 --- a/src/examples/xb_ex_ssv.cpp +++ b/src/examples/xb_ex_ssv.cpp @@ -19,7 +19,9 @@ This program demonstrates using functionality of the xbSsv class (Shared system using namespace xb; -int main( int ac, char ** av ){ +//int main( int ac, char ** av ){ + +int main( int, char ** av ){ xbXBase x; // set up xbase for business xbString sMsg; // a message string diff --git a/src/examples/xb_ex_string.cpp b/src/examples/xb_ex_string.cpp index 30fd74e..0cd7671 100755 --- a/src/examples/xb_ex_string.cpp +++ b/src/examples/xb_ex_string.cpp @@ -116,8 +116,9 @@ int main() std::cout << s3.Str() << std::endl; - s3 = 'A' + s1; - + // The following compiles and runs, but is not valid + // s3 = 'A' + s1; + std::cout << std::endl << "== operator tests" << std::endl; if( s1 == s2 ) std::cout << s1.Str() << " == " << s2.Str() << std::endl; diff --git a/src/examples/xb_ex_v3_create_dbf.cpp b/src/examples/xb_ex_v3_create_dbf.cpp index ecfcd72..d6f7047 100755 --- a/src/examples/xb_ex_v3_create_dbf.cpp +++ b/src/examples/xb_ex_v3_create_dbf.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -47,19 +47,19 @@ int main() // Create Dbase3 NDX style indices if support compiled in -#ifdef XB_NDX_SUPPORT + #ifdef XB_NDX_SUPPORT xbIxNdx MyIndex1( MyDbfFile ); /* class for index 1 */ xbIxNdx MyIndex2( MyDbfFile ); /* class for index 2 */ xbIxNdx MyIndex3( MyDbfFile ); /* class for index 3 */ -#endif + #endif -// fixme -// Create Clipper NTX style indices if support compiled in - bring this back to life in a future release -#ifdef XB_INDEX_NTX + // fixme + // Create Clipper NTX style indices if support compiled in - bring this back to life in a future release + #ifdef XB_INDEX_NTX xbNtx MyIndex4( &MyDbfFile ); /* class for index 4 */ xbNtx MyIndex5( &MyDbfFile ); /* class for index 5 */ -#endif + #endif xbInt16 rc; @@ -68,7 +68,7 @@ int main() else { -#ifdef XB_NDX_SUPPORT + #ifdef XB_NDX_SUPPORT xbIx *pIx; void *pTag; @@ -83,6 +83,7 @@ int main() if(( rc = MyDbfFile->CreateTag ( "NDX", "MYINDEX1.NDX", "LASTNAME", "", 0, 1, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) x.DisplayError( rc ); + /* define a multi-field index "LASTNAME FIRSTNAME" */ if(( rc = MyDbfFile->CreateTag( "NDX", "MYINDEX2.NDX", "LASTNAME+FIRSTNAME", "", 0, 1, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) x.DisplayError( rc ); @@ -91,12 +92,19 @@ int main() if(( rc = MyDbfFile->CreateTag( "NDX", "MYINDEX3.NDX", "ZIPCODE", "", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ) x.DisplayError( rc ); -#endif + std::cout << "Tag Count in MYINDEX3.NDX = " << pIx->GetTagCount() << "\n"; + xbString sTagName; + sTagName = pIx->GetTagName( &pTag ); + std::cout << "Tag Name in MYINDEX3.NDX = " << sTagName.Str() << "\n"; + + + #endif } MyDbfFile->Close(); /* Close database and associated indexes */ + delete MyDbfFile; -#endif // XB_DBF3_SUPPORT + #endif // XB_DBF3_SUPPORT return 0; } diff --git a/src/examples/xb_ex_v3_upd_dbf.cpp b/src/examples/xb_ex_v3_upd_dbf.cpp index aa6ec49..eecba16 100755 --- a/src/examples/xb_ex_v3_upd_dbf.cpp +++ b/src/examples/xb_ex_v3_upd_dbf.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2021,2022 Gary A Kunkel +Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. @@ -45,7 +45,9 @@ int main() x.WriteLogMessage( "Program [xb_ex_v3_upd_dbf] initializing..." ); - xbDbf * MyTable = new xbDbf3( &x ); /* class for V3 table */ + xbDbf * MyTable = new xbDbf3( &x ); /* class for V3 table */ + + xbString sSearchKey; /* string for doing an index lookup */ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; @@ -53,29 +55,36 @@ int main() try{ if(( iRc = MyTable->Open( "MyV3Table1.DBF" )) != XB_NO_ERROR ){ - iErrorStop = 1; + iErrorStop = 100; throw iRc; } + #ifdef XB_NDX_SUPPORT // V3 NDX style indices can be opened manually (vs production MDX index files opened automatically) + + if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX1.NDX")) != XB_NO_ERROR ){ - iErrorStop = 2; + iErrorStop = 110; throw iRc; } + + +/* if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX2.NDX" )) != XB_NO_ERROR ){ - iErrorStop = 3; + iErrorStop = 120; throw iRc; } + if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX3.NDX" )) != XB_NO_ERROR ){ - iErrorStop = 4; + iErrorStop = 130; throw iRc; } +*/ + std::cout << "Current tag = [" << MyTable->GetCurTagName().Str() << "]\n"; #endif - - // get the field numbers for all the fields in the table fld_FIRSTNAME = MyTable->GetFieldNo( "FIRSTNAME" ); fld_LASTNAME = MyTable->GetFieldNo( "LASTNAME" ); @@ -84,189 +93,223 @@ int main() fld_RETIRED = MyTable->GetFieldNo( "RETIRED?" ); fld_ZIPCODE = MyTable->GetFieldNo( "ZIPCODE" ); + + + // do an index lookup for (key does not exist in this example) + sSearchKey = "abc123"; + if(( iRc = MyTable->Find( sSearchKey )) != XB_NOT_FOUND ){ + iErrorStop = 140; + throw iRc; + } + std::cout << "RC = " << iRc << "\n"; + + + #ifdef XB_MEMO_SUPPORT fld_MEMO1 = MyTable->GetFieldNo( "MEMO1" ); #endif + // Blank the record buffer if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ - iErrorStop = 7; + iErrorStop = 140; throw iRc; } + // put field examples - using field numbers if(( iRc = MyTable->PutField( fld_LASTNAME, "JONES" )) != XB_NO_ERROR ){ - iErrorStop = 8; + iErrorStop = 150; throw iRc; } + if(( iRc = MyTable->PutField( fld_FIRSTNAME, "JERRY" )) != XB_NO_ERROR ){ - iErrorStop = 9; + iErrorStop = 160; throw iRc; } + if(( iRc = MyTable->PutField( fld_AMOUNT, "12.35" )) != XB_NO_ERROR ){ - iErrorStop = 10; + iErrorStop = 170; throw iRc; } if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19880208" )) != XB_NO_ERROR ){ - iErrorStop = 10; + iErrorStop = 180; throw iRc; } if(( iRc = MyTable->PutLogicalField( fld_RETIRED, "Y" )) != XB_NO_ERROR ){ - iErrorStop = 11; + iErrorStop = 190; throw iRc; } if(( iRc = MyTable->PutLongField( fld_ZIPCODE, 12345 )) != XB_NO_ERROR ){ - iErrorStop = 12; + iErrorStop = 200; throw iRc; } #ifdef XB_MEMO_SUPPORT sMemoData = "Memo data record 1"; if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){ - iErrorStop = 13; + iErrorStop = 210; throw iRc; } #endif // Append the first record if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ - iErrorStop = 15; - throw iRc; + // here is where you would address any errors. + // in this program, we simply abort and continue + MyTable->Abort(); } // put field to the record buffer using field name (slightly less efficient than using field numbers) - // Blank the record buffer if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ - iErrorStop = 20; + iErrorStop = 220; throw iRc; } if(( iRc = MyTable->PutField( "LASTNAME", "EINSTIEN" )) != XB_NO_ERROR ){ - iErrorStop = 21; + iErrorStop = 230; throw iRc; } if(( iRc = MyTable->PutField( "FIRSTNAME", "ALBERT" )) != XB_NO_ERROR ){ - iErrorStop = 22; + iErrorStop = 240; throw iRc; } if(( iRc = MyTable->PutField( "AMOUNT", "987.55" )) != XB_NO_ERROR ){ - iErrorStop = 23; + iErrorStop = 250; throw iRc; } if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19890209" )) != XB_NO_ERROR ){ - iErrorStop = 24; + iErrorStop = 260; throw iRc; } if(( iRc = MyTable->PutLogicalField( "RETIRED?", "N" )) != XB_NO_ERROR ){ - iErrorStop = 25; + iErrorStop = 270; throw iRc; } if(( iRc = MyTable->PutLongField( "ZIPCODE", 44256 )) != XB_NO_ERROR ){ - iErrorStop = 26; + iErrorStop = 280; throw iRc; } #ifdef XB_MEMO_SUPPORT sMemoData = "Memo data record 2"; if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){ - iErrorStop = 27; + iErrorStop = 290; throw iRc; } #endif // Append the second record if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ - iErrorStop = 30; - throw iRc; + // here is where you would address any errors. + // in this program, we simply abort and continue + MyTable->Abort(); } + + if(( iRc = MyTable->GetRecord( 1 )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } // get a field with a field number - xbString FirstName; - if(( iRc = MyTable->GetField( fld_FIRSTNAME, FirstName )) < 0 ){ - iErrorStop = 40; + xbString sFirstName; + if(( iRc = MyTable->GetField( fld_FIRSTNAME, sFirstName )) < 0 ){ + iErrorStop = 310; throw iRc; } - std::cout << "First Name is [" << FirstName.Str() << "]" << std::endl; + std::cout << "First Name is [" << sFirstName.Str() << "]" << std::endl; - xbString LastName; - if(( iRc = MyTable->GetField( "LASTNAME", LastName )) < 0 ){ - iErrorStop = 41; + xbString sLastName; + if(( iRc = MyTable->GetField( "LASTNAME", sLastName )) < 0 ){ + iErrorStop = 320; throw iRc; } - std::cout << "Last Name is [" << LastName.Str() << "]" << std::endl; + std::cout << "Last Name is [" << sLastName.Str() << "]" << std::endl; xbInt16 iNoOfDecimals; if(( iRc = MyTable->GetFieldDecimal( "AMOUNT", iNoOfDecimals )) != XB_NO_ERROR ){ - iErrorStop = 42; + iErrorStop = 330; throw iRc; } std::cout << "There are " << iNoOfDecimals << " decimals in the AMOUNT field" << std::endl; xbString FieldName; if(( iRc = MyTable->GetFieldName( 4, FieldName )) != XB_NO_ERROR ){ - iErrorStop = 43; + iErrorStop = 340; throw iRc; } std::cout << "Field #4 name is " << FieldName.Str() << std::endl; xbString sRetired; if(( iRc = MyTable->GetLogicalField( "RETIRED?", sRetired )) < 0 ){ - iErrorStop = 45; + iErrorStop = 350; throw iRc; } std::cout << "Switch value = [" << sRetired.Str() << "]" << std::endl; xbInt32 lZip; if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){ - iErrorStop = 46; + iErrorStop = 360; throw iRc; } std::cout << "Long value = [" << lZip << "]" << std::endl; // Initialize the record buffer in preparation for another record if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){ - iErrorStop = 48; + iErrorStop = 370; throw iRc; } // Append another record (it will be blank) if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){ - iErrorStop = 49; - throw iRc; + // here is where you would address any errors. + // in this program, we simply abort and continue + MyTable->Abort(); }; - // mark current record for deletion + // mark record 1 for deletion + if(( iRc = MyTable->GetRecord( 1 )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + if(( iRc = MyTable->DeleteRecord()) != XB_NO_ERROR ){ - iErrorStop = 50; + iErrorStop = 380; throw iRc; }; // save current record if(( iRc = MyTable->PutRecord()) != XB_NO_ERROR ){ - iErrorStop = 51; + iErrorStop = 390; throw iRc; } // pack the table with no options if(( iRc = MyTable->Pack()) != XB_NO_ERROR ){ - iErrorStop = 52; + iErrorStop = 400; throw iRc; } + if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){ + // here is where you would address any errors. + // in this program, we simply abort and continue + MyTable->Abort(); + } + /* Close database and associated indexes */ if(( iRc = MyTable->Close()) != XB_NO_ERROR ){ - iErrorStop = 53; + iErrorStop = 410; throw iRc; } @@ -276,7 +319,8 @@ int main() std::cout << x.GetErrorMessage( rc ) << std::endl; } -#endif // XB_DBF3_SUPPORT + delete MyTable; + #endif // XB_DBF3_SUPPORT return 0; } diff --git a/src/examples/xb_ex_v4_create_dbf.cpp b/src/examples/xb_ex_v4_create_dbf.cpp index 6169213..d6bfb2f 100755 --- a/src/examples/xb_ex_v4_create_dbf.cpp +++ b/src/examples/xb_ex_v4_create_dbf.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -60,12 +60,12 @@ int main() xbInt16 iRc; xbDbf * MyDbfFile; - + #ifdef XB_MDX_SUPPORT xbIx *pIx; void *pTag; #endif // XB_MDX_SUPPORT - + MyDbfFile = new xbDbf4( &x ); if(( iRc = MyDbfFile->CreateTable( "Address.DBF", "Address", MyAddressBookRecord, XB_OVERLAY, XB_MULTI_USER )) != XB_NO_ERROR ) @@ -93,7 +93,8 @@ int main() } MyDbfFile->Close(); /* Close database and associated indexes */ + delete MyDbfFile; -#endif // XB_DBF4_SUPPORT + #endif // XB_DBF4_SUPPORT return 0; } diff --git a/src/examples/xb_ex_v4_upd_dbf.cpp b/src/examples/xb_ex_v4_upd_dbf.cpp index 75d0641..d128eba 100755 --- a/src/examples/xb_ex_v4_upd_dbf.cpp +++ b/src/examples/xb_ex_v4_upd_dbf.cpp @@ -209,7 +209,7 @@ int main() } std::cout << "Switch value = [" << sFriend.Str() << "]" << std::endl; - xbInt32 lZip; + xbInt32 lZip = 0; if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){ iErrorStop = 350; throw iRc; @@ -259,9 +259,15 @@ int main() } // example code to loop through the table - for( xbUInt32 ul = 1; ul <= MyTable->GetRecordCount(); ul++ ){ + xbUInt32 ulRecCnt; + if(( iRc = MyTable->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 430; + throw iRc; + } + + for( xbUInt32 ul = 1; ul <= ulRecCnt; ul++ ){ if(( iRc = MyTable->GetRecord( ul )) != XB_NO_ERROR ){ - iErrorStop = 430; + iErrorStop = 440; throw iRc; } // do something with the record here @@ -271,9 +277,11 @@ int main() /* Close database and associated indexes */ if(( iRc = MyTable->Close()) != XB_NO_ERROR ){ - iErrorStop = 440; + iErrorStop = 450; throw iRc; } + delete MyTable; + } catch( xbInt16 iRc ){ diff --git a/src/include/xbconfig.h.in b/src/include/xbconfig.h.in index f0d475a..f084038 100755 --- a/src/include/xbconfig.h.in +++ b/src/include/xbconfig.h.in @@ -11,6 +11,7 @@ #define PROJECT_RUNTIME_DIR "@PROJECT_RUNTIME_DIR@" #define PROJECT_DATA_DIR "@PROJECT_DATA_DIR@" #define PROJECT_LOG_DIR "@PROJECT_LOG_DIR@" +#define PROJECT_TEMP_DIR "@PROJECT_TEMP_DIR@" #define PROJECT_DFLT_LOGFILE "@CMAKE_SYSTEM_NAME@_@XB_PLATFORM@.xbLog.txt" #define EXTRA_LIBS "@EXTRA_LIBS@" #define CMAKE_RUNTIME_OUTPUT_DIRECTORY "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@" @@ -69,11 +70,12 @@ #cmakedefine HAVE__FILENO_F #cmakedefine HAVE_FOPEN_S_F #cmakedefine HAVE__FSOPEN_F - #cmakedefine HAVE_FORK_F #cmakedefine HAVE__FSEEKI64_F #cmakedefine HAVE_FSEEKO_F #cmakedefine HAVE_FTRUNCATE_F + +#cmakedefine HAVE_GETENV_S_F #cmakedefine HAVE__LOCALTIME64_S_F #cmakedefine HAVE_LOCKFILE_F #cmakedefine HAVE_LOCKING_F @@ -84,6 +86,7 @@ #cmakedefine HAVE__VSNPRINTF_S_F #cmakedefine HAVE_VSPRINTF_S_F + #cmakedefine XB_PLATFORM_32 #cmakedefine XB_PLATFORM_64 #cmakedefine XB_DEBUG_SUPPORT @@ -98,6 +101,7 @@ #cmakedefine XB_INDEX_SUPPORT #cmakedefine XB_NDX_SUPPORT #cmakedefine XB_MDX_SUPPORT +#cmakedefine XB_TDX_SUPPORT #cmakedefine XB_SQL_SUPPORT #cmakedefine XB_INF_SUPPORT #cmakedefine XB_FILTER_SUPPORT diff --git a/src/include/xbcrix.cpp b/src/include/xbcrix.cpp new file mode 100755 index 0000000..b89baaa --- /dev/null +++ b/src/include/xbcrix.cpp @@ -0,0 +1,292 @@ +/* xbcrix.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022 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 + +*/ + +#include "xbase.h" + +#ifdef XB_SQL_SUPPORT + +namespace xb{ + + +/***********************************************************************/ +#ifdef XB_INDEX_SUPPORT +xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ + + // std::cout << "CREATE INDEX " << sCmdLine << std::endl; + + // expected format to create an Dbase 3, NDX index: + // CREATE INDEX ixname.ndx ON tablename.dbf ( EXPRESSION ) [ASSOCIATE] + + // expected format to create an Dbase 4, tag on an MDX index: + // CREATE [UNIQUE] INDEX tagname ON tablename.dbf ( EXPRESSION ) [DESC] [FILTER .NOT. DELETED()] + + // The ASSOCIATE parameter is specific to Xbase64 library, it is used to associate + // a non production (NDX) index file to a dbf file so it will be automatically + // opened with the dbf file whenever the dbf file is opened by the xbase64 routines. + + // The [ASSOCIATE] parameter is not used with MDX production indices + + // This method first looks for ".NDX" in the file name to determine if an NDX + // index should be created. + // if .NDX is not in the filename, it looks in the uda for "IXTYPE" for either + // NDX or MDX to detmermine the index type to create + // if IXTYPE not found, create an MDX tag + + // The optional DESC parameter defines an entire index key as descending. This is + // different than other SQL implementations where specific fields can be descending. + + // The optional FILTER parameter is specific to the XBASE64 library, is it used to + // assign a filter to a tag in an MDX style index. Everything to the right of + // the keyword FILTER is considered part of the filter. + + // The original DBASE indices used to '+' to create an index on more than one field + // ie: FIELD1+FIELD2+FIELD3 + // SQL uses commas: ie: FIELD1, FIELD2, FIELD3 + // The Xbase library supports either '+' or ',' when creating mutli field indices. + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + xbString sTableName; + xbString sIxName; + xbString sIxType; + xbUInt32 ulPos; + xbString sCmd = sCmdLine; + xbString sNode; + xbBool bUnique = xbFalse; + xbDbf * dbf = NULL; + xbBool bTableLocked = xbFalse; + + try{ + + // drop off the first node + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + sNode.ExtractElement( sCmd, ' ', 1, 0 ); + sNode.ToUpperCase(); + + if( sNode == "UNIQUE" ){ + //std::cout << "unique ix\n"; + bUnique = xbTrue; + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + } + + // go past the index keyword + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // pull the index name off the cmd line + sIxName.ExtractElement( sCmd, ' ', 1, 0 ); + + #ifdef XB_NDX_SUPPORT + xbString sTemp = sIxName; + sTemp.ToUpperCase(); + ulPos = sTemp.Pos( ".NDX" ); + if(ulPos == (sTemp.Len() - 3) ) + sIxType = "NDX"; + #endif // XB_NDX_SUPPORT + + if( sIxType == "" ){ + if(( iRc = uda.GetTokenForKey( "IXTYPE", sIxType )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + } + + #ifdef XB_NDX_SUPPORT + if( sIxType == "NDX" ){ + xbFile f( xbase ); + f.SetFileName( sIxName ); + if( f.FileExists()){ + + iErrorStop = 110; + iRc = XB_FILE_EXISTS; + throw iRc; + } + } + #endif // XB_NDX_SUPPORT + + // skip past index name + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // skip past "ON" + ulPos = sCmd.Pos( ' ' ); + sCmd.Ltrunc( ulPos ); + sCmd.Ltrim(); + + // get the table name + ulPos = sCmd.Pos( '(' ); + sTableName.ExtractElement( sCmd, '(', 1, 0 ); + sTableName.Trim(); + + xbFile fDbf( xbase ); + fDbf.SetFileName( sTableName ); + + // if not open, attempt to open it + dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); + + if( !dbf ){ + if(( iRc = xbase->OpenHighestVersion( sTableName, "", &dbf )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + + if( dbf == NULL ){ + iErrorStop = 130; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + sCmd.Ltrunc( ulPos ); + + //ulPos = sCmd.GetLastPos( ')' ); + xbString sKeyExpression; + xbBool bDone = xbFalse; + xbUInt32 lPos = 1; + xbInt16 iParenCtr = 0; + + while( !bDone && lPos < sCmd.Len()){ + if( sCmd[lPos] == '(' ){ + iParenCtr++; + sKeyExpression.Append( sCmd[lPos] ); + } else if( sCmd[lPos] == ')' ){ + if( iParenCtr > 0 ){ + iParenCtr--; + sKeyExpression.Append( sCmd[lPos] ); + } else { + bDone = xbTrue; + } + } else if( sCmd[lPos] == ',' && iParenCtr == 0 ){ + sKeyExpression.Append( '+' ); + } else if( sCmd[lPos] != ' ' ){ + sKeyExpression.Append( sCmd[lPos] ); + } + lPos++; + } + + // std::cout << "Key Expression =[" << sKeyExpression << "]\n"; + sCmd.Ltrunc( lPos ); + sCmd.Trim(); + + xbBool bDesc = xbFalse; + // std::cout << "sCmd - looking for DESC [" << sCmd << "]\n"; + if( sCmd.Len() > 4 ){ + sNode = sCmd; + sNode.ToUpperCase(); + ulPos = sNode.Pos( "DESC" ); + if( ulPos > 0 ){ + bDesc = xbTrue; + sCmd.Ltrunc( 4 ); + sCmd.Trim(); + std::cout << "Descending\n"; + } + } + + // std::cout << "sCmd - looking for FILTER stuff [" << sCmd << "]\n"; + xbString sFilter; + if( sCmd.Len() > 6 ){ + sNode = sCmd; + sNode.ToUpperCase(); + ulPos = sNode.Pos( "FILTER" ); + if( ulPos > 0 ){ + sFilter = sCmd; + sFilter.Ltrunc( ulPos + 6 ); + sFilter.Trim(); + } + } + // std::cout << "sCmd - FILTER = [" << sFilter << "]\n"; + + #ifdef XB_LOCKING_SUPPORT + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } else { + bTableLocked = xbTrue; + } + #endif // XB_LOCKING_SUPPORT + + xbIx *pIx; + void *vpTag; + + if(( iRc = dbf->CreateTag( sIxType, sIxName, sKeyExpression, sFilter, bDesc, bUnique, xbFalse, &pIx, &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + + #ifdef XB_NDX_SUPPORT + if( sIxType == "NDX"){ + sCmd.Ltrunc( ulPos ); + sCmd.Trim(); + if( sCmd.Len() > 0 ){ + sCmd.ToUpperCase(); + if( sCmd.Pos( "ASSOCIATE" )){ + if(( iRc = dbf->AssociateIndex( "NDX", sIxName, 0 )) != XB_NO_ERROR ){ + iErrorStop = 160; + throw iRc; + } + } + } + } + #endif // XB_NDX_SUPPORT + + if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ + +// if(( iRc = dbf->Reindex( 2, &vpTag )) != XB_NO_ERROR ){ + iErrorStop = 170; + throw iRc; + } + + + //std::cout << "Tag count = " << pIx->GetTagCount() << "\n"; + // s = pIx->GetTagName( &vpTag ); + // std::cout << "tagname = [" << s.Str() << "]\n"; + + + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked ){ + if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc; + } else { + bTableLocked = xbFalse; + } + } + #endif // XB_LOCKING_SUPPORT + + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbSql::SqlCreateIndex() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked && dbf ) + dbf->LockTable( XB_UNLOCK ); + #endif // XB_LOCKING_SUPPORT + + return iRc; +} +#endif // XB_INDEX_SUPPORT +/***********************************************************************/ +} /* namespace */ +#endif /* XB_SQL_SUPPORT */ + diff --git a/src/include/xbdate.h b/src/include/xbdate.h index 8dff463..e914d65 100755 --- a/src/include/xbdate.h +++ b/src/include/xbdate.h @@ -66,6 +66,7 @@ class XBDLLEXPORT xbDate : public xbSsv { xbDate( const char * Date8 ); xbDate( const xbString &Date8 ); xbDate( xbInt32 lJulDate ); + xbDate( xbUInt16 iInit ); // Constructor used to set the static variables, also defaults to sysdate ~xbDate(); void operator=( const xbDate &d ); @@ -96,6 +97,7 @@ class XBDLLEXPORT xbDate : public xbSsv { const char *Str() const; xbBool IsLeapYear( xbInt16 iYear ) const; xbBool IsLeapYear() const; + xbBool IsNull() const; xbInt32 JulianDays() const; xbInt16 JulToDate8( xbInt32 lJulDate ); xbInt16 LastDayOfMonth(); @@ -111,7 +113,8 @@ class XBDLLEXPORT xbDate : public xbSsv { private: void SetDateTables(); - xbString sDate8; /* CCYYMMDD date format ie; 20140718 */ + xbString sDate8; // CCYYMMDD date format ie; 20140718 + // Null date is identified by sDate.Len() < 8 static int iAggregatedDaysInMonths[2][13]; static int iDaysInMonths[2][13]; diff --git a/src/include/xbdbf.h b/src/include/xbdbf.h index 16799f2..fe24e72 100755 --- a/src/include/xbdbf.h +++ b/src/include/xbdbf.h @@ -164,7 +164,7 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbInt16 DeleteAll ( xbInt16 iOption ); virtual xbInt16 DeleteAllRecords (); virtual xbInt16 DeleteRecord (); - virtual xbInt16 DumpHeader ( xbInt16 iOption ) const; + virtual xbInt16 DumpHeader ( xbInt16 iOption ); virtual xbInt16 DumpRecord ( xbUInt32 ulRecNo, xbInt16 iOutputDest = 0, xbInt16 iOutputFmt = 0 ); virtual xbInt16 GetAutoCommit () const; virtual xbInt16 GetAutoCommit ( xbInt16 iOption ) const; @@ -186,7 +186,7 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbInt16 GetPrevRecord ( xbInt16 iOption ); virtual xbInt16 GetRecord ( xbUInt32 ulRecNo ); - virtual xbUInt32 GetRecordCount (); + // virtual xbUInt32 GetRecordCount (); virtual xbInt16 GetRecordCnt ( xbUInt32 & ulRecCnt ); virtual char * GetRecordBuf ( xbInt16 iOpt = 0 ) const; @@ -207,7 +207,7 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbInt16 PutRecord (); // Put record to current location virtual xbInt16 PutRecord ( xbUInt32 ulRecNo ); - virtual xbInt16 ReadHeader ( xbInt16 iFilePositionOption, xbInt16 iReadOption ); +// virtual xbInt16 ReadHeader ( xbInt16 iFilePositionOption, xbInt16 iReadOption ); virtual xbInt16 RecordDeleted ( xbInt16 iOpt = 0 ) const; virtual xbInt16 Rename ( const xbString sNewName ) = 0; @@ -251,7 +251,7 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbInt16 GetLongField( xbInt16 iFieldNo, xbInt32 &lFieldValue ) const; virtual xbInt16 GetLongField( const xbString &sFieldName, xbInt32 &lFieldValue ) const; virtual xbInt16 PutLongField( xbInt16 iFieldNo, xbInt32 lFieldValue ); - virtual xbInt16 PutLongField( const xbString &sFieldNo, xbInt32 lFieldValue ); + virtual xbInt16 PutLongField( const xbString &sFieldName, xbInt32 lFieldValue ); virtual xbInt16 GetULongField( xbInt16 iFieldNo, xbUInt32 &lFieldValue ) const; virtual xbInt16 GetULongField( const xbString &sFieldName, xbUInt32 &lFieldValue ) const; @@ -274,6 +274,10 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbInt16 PutDateField( xbInt16 iFieldNo, const xbDate &dt ); virtual xbInt16 PutDateField( const xbString &sFieldName, const xbDate &dt ); + virtual xbInt16 GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull ) const; + virtual xbInt16 GetNullSts( const xbString &sFieldName, xbBool &bIsNull ) const; + virtual xbInt16 GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull, xbInt16 iRecBufSw ) const; + #ifdef XB_MEMO_SUPPORT @@ -282,7 +286,7 @@ class XBDLLEXPORT xbDbf : public xbFile { virtual xbUInt32 GetCreateMemoBlockSize() const; virtual xbInt16 GetMemoField ( xbInt16 iFldNo, xbString &sMemoData ); virtual xbInt16 GetMemoField ( const xbString & sFldName, xbString &sMemoData ); - virtual xbInt16 GetMemoFieldLen ( xbInt16 iFldNo, xbUInt32 &ullMemoFieldLen ); + virtual xbInt16 GetMemoFieldLen ( xbInt16 iFldNo, xbUInt32 &ulMemoFieldLen ); virtual xbInt16 GetMemoFieldLen ( const xbString & sFldName, xbUInt32 &ulMemoFieldLen ); virtual xbBool MemoFieldExists ( xbInt16 iFieldNo ) const; virtual xbBool MemoFieldExists ( const xbString &sFieldName ) const; @@ -347,7 +351,7 @@ class XBDLLEXPORT xbDbf : public xbFile { xbLinkListNode<xbTag *> *GetTagList () const; virtual xbInt16 OpenIndex( const xbString &sIxType, const xbString &sIndexName ); - virtual xbInt16 Reindex( xbInt16 iTagOpt ); + virtual xbInt16 Reindex( xbInt16 iTagOpt = 0, xbInt16 iErrorOpt = 1, xbIx **pIx = NULL, void **vpTag = NULL ); virtual xbInt16 SetCurTag( const xbString &sTagName ); virtual void SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ); @@ -373,14 +377,20 @@ class XBDLLEXPORT xbDbf : public xbFile { #ifdef XB_INDEX_SUPPORT friend class xbIx; friend class xbIxMdx; + friend class xbIxTdx; xbInt16 AddIndex( xbIx *ix, const xbString &sFmt ); void ClearTagList(); xbInt16 RemoveIndex( xbIx * ix ); void UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ); + + virtual xbInt16 UpdateTagList (); #endif // XB_INDEX_SUPPORT + virtual xbInt16 ReadHeader ( xbInt16 iFilePositionOption, xbInt16 iReadOption ); + + #ifdef XB_INF_SUPPORT virtual xbInt16 GetInfFileName( xbString &sNdxIdxFileName ); #endif // XB_INF_SUPPORT diff --git a/src/include/xbexp.h b/src/include/xbexp.h index 96413ac..4792b0e 100755 --- a/src/include/xbexp.h +++ b/src/include/xbexp.h @@ -22,6 +22,7 @@ Email Contact: // #pragma interface // #endif +#define XB_NULL_DATE 21474835648 #ifdef XB_FUNCTION_SUPPORT diff --git a/src/include/xbexpnode.h b/src/include/xbexpnode.h index f50d9eb..51efa9b 100755 --- a/src/include/xbexpnode.h +++ b/src/include/xbexpnode.h @@ -103,7 +103,7 @@ class XBDLLEXPORT xbExpNode { xbUInt32 ulResultLen; // for string results, accumulated length of character operations // includes the sum of all nodes under this + this // date = 8, numeric = 4, logical = 1 - xbInt16 iWeight; // used for buildign the tree of nodes, assigned to operators + xbInt16 iWeight; // used for building the tree of nodes, assigned to operators // the higher the number, the lower it goes on the tree }; diff --git a/src/include/xbfile.h b/src/include/xbfile.h index af04e98..e346a75 100755 --- a/src/include/xbfile.h +++ b/src/include/xbfile.h @@ -56,108 +56,109 @@ This class could be used if you want to write a platform independent program tha class XBDLLEXPORT xbFile : public xbSsv { public: - // xbFile(); xbFile( xbXBase * x ); - ~xbFile(); - xbInt16 SetHomeFolders(); - - xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut ); - xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut, xbInt16 iOption ); - const xbString& GetDirectory() const; const xbString& GetFileName() const; const xbString& GetFqFileName() const; - void SetDirectory ( const xbString &sDirectory); - void SetFileName ( const xbString &sFileName ); - void SetFqFileName( const xbString &sFqName ); - xbUInt32 GetBlockSize () const; - xbInt16 SetBlockSize ( xbUInt32 ulBlockSize ); + xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut, xbInt16 iOption = 0 ); + + xbInt16 DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const; + xbInt16 DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const; + + xbDouble eGetDouble ( const char *p ) const; + xbInt32 eGetInt32 ( const char *p ) const; + xbUInt32 eGetUInt32 ( const char *p ) const; + xbInt16 eGetInt16 ( const char *p ) const; + xbUInt16 eGetUInt16 ( const char *p ) const; + void ePutDouble ( char *p, xbDouble d ); + void ePutInt32 ( char *p, xbInt32 l ); + void ePutUInt32 ( char *p, xbUInt32 ul ); + void ePutInt16 ( char *p, xbInt16 s ); + void ePutUInt16 ( char *p, xbUInt16 s ); + + xbBool FileExists () const; + xbBool FileExists ( xbInt16 iOption ) const; + xbBool FileExists ( const xbString &sFileName ) const; + xbBool FileExists ( const xbString &sFileName, xbInt16 iOption ) const; + xbBool FileIsOpen () const; - xbInt16 GetOpenMode () const; - xbInt16 GetShareMode () const; + xbUInt32 GetBlockSize () const; xbInt16 GetFileDirPart ( xbString &sFileDirPartOut ) const; xbInt16 GetFileDirPart ( const xbString &sCompleteFileNameIn, xbString &sFileDirPartOut ) const; xbInt16 GetFileExtPart ( xbString &sFileExtPartOut ) const; xbInt16 GetFileExtPart ( const xbString &sCompleteFileNameIn, xbString &sFileExtPartOut ) const; + xbInt16 GetFileMtime ( time_t &mtime ); xbInt16 GetFileNamePart( xbString &sFileNamePartOut ) const; xbInt16 GetFileNamePart( const xbString &sCompleteFileNameIn, xbString &sFileNamePartOut ) const; + xbInt16 GetFileSize ( xbUInt64 &ullFileSize ); xbInt16 GetFileType ( xbString &sFileType ) const; + + xbInt16 GetOpenMode () const; + xbInt16 GetShareMode () const; + + xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion ); xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte ); xbInt16 GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ); - xbInt16 DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const; - xbInt16 DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const; - xbBool FileExists () const; - xbBool FileExists ( xbInt16 iOption ) const; - xbBool FileExists ( const xbString &sFileName ) const; - xbBool FileExists ( const xbString &sFileName, xbInt16 iOption ) const; + xbInt16 NameSuffixMissing( const xbString &sFileName, xbInt16 iOption ) const; - xbBool FileIsOpen () const; + xbInt16 ReadBlock ( xbUInt32 ulBlockNo, size_t readSize, void *buf ); + xbInt16 ReadBlock ( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t readSize, void *buf ); - xbInt16 ReadBlock ( xbUInt32 ulBlockNo, size_t readSize, void *buf ); - xbInt16 ReadBlock ( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t readSize, void *buf ); - xbInt16 WriteBlock( xbUInt32 ulBlockNo, size_t writeSize, void *buf ); - - xbInt16 GetFileSize( xbUInt64 &ullFileSize ); - xbInt16 GetFileMtime( time_t &mtime ); - - xbDouble eGetDouble( const char *p ) const; - xbInt32 eGetInt32 ( const char *p ) const; - xbUInt32 eGetUInt32( const char *p ) const; - xbInt16 eGetInt16 ( const char *p ) const; - xbUInt16 eGetUInt16( const char *p ) const; - void ePutDouble( char *p, xbDouble d ); - void ePutInt32 ( char *p, xbInt32 l ); - void ePutUInt32( char *p, xbUInt32 ul ); - void ePutInt16 ( char *p, xbInt16 s ); - void ePutUInt16( char *p, xbUInt16 s ); - - xbInt16 xbFclose (); - xbInt16 xbFeof (); - xbInt16 xbFflush (); - xbInt16 xbFgetc ( xbInt32 &c ); - xbInt16 xbFgetc ( char &c ); + xbInt16 SetBlockSize ( xbUInt32 ulBlockSize ); + void SetDirectory ( const xbString &sDirectory); + void SetFileName ( const xbString &sFileName ); + void SetFqFileName ( const xbString &sFqName ); + xbInt16 SetHomeFolders(); - #ifdef XB_LOCKING_SUPPORT - xbInt16 xbLock ( xbInt16 iFunction, xbInt64 llOffset, size_t stLen ); - xbInt16 GetLockRetryCount() const; - void SetLockRetryCount( xbInt16 iLockRetries ); - #endif + xbInt16 WriteBlock ( xbUInt32 ulBlockNo, size_t writeSize, void *buf ); - xbInt16 xbFopen ( xbInt16 iOpenMode ); - xbInt16 xbFopen ( const xbString &sOpenMode, xbInt16 iShareMode ); - xbInt16 xbFopen ( xbInt16 iOpenMode, xbInt16 iShareMode ); - xbInt16 xbFopen ( const xbString &sMode, const xbString &sFileName, xbInt16 iShareMode ); + xbInt16 xbFclose (); + xbInt16 xbFeof (); + xbInt16 xbFflush (); + xbInt16 xbFgetc ( xbInt32 &c ); + xbInt16 xbFgetc ( char &c ); + xbInt16 xbFgets ( size_t lSize, xbString &sLine ); - xbInt16 xbFputc ( xbInt32 c ); - xbInt16 xbFputc ( xbInt32 c, xbInt32 iNoTimes ); - xbInt16 xbFputs ( const xbString &s ); - xbInt16 xbFread ( void *ptr, size_t size, size_t nmemb ); - xbInt16 xbFgets ( size_t lSize, xbString &sLine ); - size_t xbFtell (); - xbInt16 xbFseek ( xbInt64 llOffset, xbInt32 whence ); + xbInt16 xbFopen ( xbInt16 iOpenMode ); + xbInt16 xbFopen ( const xbString &sOpenMode, xbInt16 iShareMode ); + xbInt16 xbFopen ( xbInt16 iOpenMode, xbInt16 iShareMode ); + xbInt16 xbFopen ( const xbString &sMode, const xbString &sFileName, xbInt16 iShareMode ); + xbInt16 xbFputc ( xbInt32 c ); + xbInt16 xbFputc ( xbInt32 c, xbInt32 iNoTimes ); + xbInt16 xbFputs ( const xbString &s ); + xbInt16 xbFread ( void *ptr, size_t size, size_t nmemb ); + xbInt16 xbFseek ( xbInt64 llOffset, xbInt32 whence ); + size_t xbFtell (); void xbFTurnOffFileBuffering(); - xbInt16 xbFwrite ( const void *ptr, size_t lSize, size_t lNmemb ); + + xbInt16 xbReadUntil ( const char cDelim, xbString &sOut ); + xbInt16 xbRemove ( const xbString &sFileName, xbInt16 iOption ); + xbInt16 xbRemove ( const xbString &sFileName ); + xbInt16 xbRemove (); - xbInt16 xbReadUntil ( const char cDelim, xbString &sOut ); - xbInt16 xbRemove ( const xbString &sFileName, xbInt16 iOption ); - xbInt16 xbRemove ( const xbString &sFileName ); - xbInt16 xbRemove (); + xbInt16 xbRename ( const xbString &sOldName, const xbString &sNewName ); + void xbRewind (); - xbInt16 xbRename ( const xbString &sOldName, const xbString &sNewName ); - void xbRewind (); + xbInt16 xbFwrite ( const void *ptr, size_t lSize, size_t lNmemb ); - xbInt16 xbTruncate ( xbInt64 llSize ); - xbInt16 NameSuffixMissing( const xbString &sFileName, xbInt16 iOption ) const; + xbInt16 xbTruncate ( xbInt64 llSize ); + + + #ifdef XB_LOCKING_SUPPORT + xbInt16 xbLock ( xbInt16 iFunction, xbInt64 llOffset, size_t stLen ); + xbInt16 GetLockRetryCount() const; + void SetLockRetryCount( xbInt16 iLockRetries ); + #endif #ifdef XB_DEBUG_SUPPORT xbInt16 DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlockSize ); diff --git a/src/include/xbindex.h b/src/include/xbindex.h index b42f76e..959562c 100755 --- a/src/include/xbindex.h +++ b/src/include/xbindex.h @@ -167,7 +167,6 @@ class XBDLLEXPORT xbIx : public xbFile { virtual xbBool GetSortOrder( void *vpTag ) const = 0; virtual xbInt16 Open( const xbString &sFileName ); - virtual xbInt16 Reindex( void **vpTag ) = 0; virtual xbInt16 SetCurTag( xbInt16 iTagNo ) = 0; virtual xbInt16 SetCurTag( xbString &sTagName ) = 0; virtual void SetCurTag( void * vpCurTag ); @@ -176,6 +175,10 @@ class XBDLLEXPORT xbIx : public xbFile { virtual void TestStub( char *s, void *vpTag ) {}; + + virtual xbInt16 Reindex( void **vpTag ) = 0; + + #ifdef XB_DEBUG_SUPPORT virtual xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ) { return XB_NO_ERROR; } virtual xbInt16 DumpHeader( xbInt16 iDestOpt = 0, xbInt16 iFmtOpt = 0 ) = 0; @@ -199,17 +202,13 @@ class XBDLLEXPORT xbIx : public xbFile { virtual xbInt16 CreateKeys( xbInt16 iOpt ); virtual xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ) = 0; virtual xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) = 0; -// virtual xbInt16 DeleteKeys( xbUInt32 ulRecNo ); virtual xbInt16 DeleteKeys(); virtual xbInt16 DeleteKey( void *vpTag ) = 0; - virtual xbInt16 DeleteTag( void *vpTag ) = 0; - virtual xbInt16 FindKeyForCurRec( void *vpTag ) = 0; virtual xbIxNode *FreeNodeChain( xbIxNode *np ); virtual xbInt16 GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf = 0 ); -// virtual xbBool GetIndexUpdated() const = 0; virtual xbInt32 GetKeyCount( xbIxNode *npNode ) const; virtual char *GetKeyData( xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const; virtual xbInt16 GetKeySts( void *vpTag ) const = 0; @@ -217,9 +216,9 @@ class XBDLLEXPORT xbIx : public xbFile { virtual xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ) = 0; virtual xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; virtual xbInt16 KeyExists( void * ) = 0; -// virtual xbInt16 KeyUpdated( void *vpTag ) const = 0; virtual void NodeFree( xbIxNode * ixNode ); virtual xbInt16 ReadHeadBlock( xbInt16 iOpt = 0 ) = 0; + // virtual xbInt16 Reindex( void **vpTag ) = 0; virtual void SetDbf( xbDbf *dbf ); virtual xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ) = 0; virtual xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; @@ -267,7 +266,6 @@ struct XBDLLEXPORT xbNdxTag { char *cpKeyBuf; // key buffer, for searches and adds char *cpKeyBuf2; // key buffer, for deletes xbString sTagName; // tag name - is the file name without the extension -// xbInt16 iKeyUpdated; // key updated? set in method KeyUpdated, checked in AddKey and DeleteKey routines xbBool bFoundSts; // key found? used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey @@ -307,7 +305,6 @@ class XBDLLEXPORT xbIxNdx : public xbIx { ~xbIxNdx(); xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); -// xbInt16 DeleteTag( void *vpTag ); xbInt16 FindKey( void *vpTag, const void *vpKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); xbInt16 GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ); @@ -326,12 +323,9 @@ class XBDLLEXPORT xbIxNdx : public xbIx { xbInt16 GetTagCount() const; xbBool GetUnique( void *vpTag = NULL ) const; xbBool GetSortOrder( void *vpTag ) const; - xbInt16 Reindex( void **vpTag ); xbInt16 SetCurTag( xbInt16 iTagNo ); xbInt16 SetCurTag( xbString &sTagName ); - - #ifdef XB_DEBUG_SUPPORT xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ); xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmt = 0 ); @@ -340,6 +334,9 @@ class XBDLLEXPORT xbIxNdx : public xbIx { xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const; #endif + xbInt16 Reindex( void **vpTag ); + + protected: friend class xbDbf; xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); @@ -349,20 +346,16 @@ class XBDLLEXPORT xbIxNdx : public xbIx { xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); xbInt16 DeleteKey( void *vpTag ); - xbInt16 DeleteTag( void *vpTag ); - xbInt16 FindKeyForCurRec( void *vpTag ); -// xbBool GetIndexUpdated() const; xbInt16 GetKeyTypeN( const void *vpTag ) const; xbInt16 GetKeySts( void *vpTag ) const; xbInt16 GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw = 1 ); xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ); xbInt16 KeyExists( void *vpTag = NULL ); -// xbBool KeyFiltered( void *vpTag ) const; -// xbInt16 KeyUpdated( void *vpTag ) const; xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk NDX file +// xbInt16 Reindex( void **vpTag ); xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); @@ -377,7 +370,6 @@ class XBDLLEXPORT xbIxNdx : public xbIx { xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; xbInt16 KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddKeyRecNo ); xbInt16 KeySetPosDel( xbNdxTag *npTag ); - // void SetCurNode( void *vpTag, xbIxNode *np ); xbNdxTag *ndxTag; }; @@ -409,7 +401,6 @@ struct XBDLLEXPORT xbMdxTag { // one unused byte fits here char cTag11; // dbase sets to 0x1B - xbInt16 iKeyLen; xbInt16 iKeysPerBlock; xbInt16 iSecKeyType; @@ -418,10 +409,8 @@ struct XBDLLEXPORT xbMdxTag { char cSerialNo; // Increments +1 for each tag update char cUnique; xbString *sKeyExp; // Key expression - char cHasFilter; // 0x00 or 0x01 char cHasKeys; // 0x00 or 0x01 - xbUInt32 ulLeftChild; // dbase 7 sets this to the root page on tag creation xbUInt32 ulRightChild; // dbase 7 sets this to the root page on tag creation @@ -441,12 +430,6 @@ struct XBDLLEXPORT xbMdxTag { char *cpKeyBuf; // key buffer char *cpKeyBuf2; // key buffer -// xbBool iKeyUpdated; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines - // 0 - no update - // 1 - Add - // 2 - Update - // 3 - Delete -// xbBool bKeyFiltered; // key filtered? True if included key, False if excluded key. Set in method CreateKey, checked in AddKey and DeleteKey routines xbString *sTagName; // string tag name @@ -469,59 +452,67 @@ class XBDLLEXPORT xbIxMdx : public xbIx { public: xbIxMdx( xbDbf * d ); ~xbIxMdx(); - xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); - xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); -// xbInt16 DeleteTag( void *vpTag ); - xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); - xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); - xbInt16 GetFirstKey( void *vpTag, xbInt16 lRetrieveSw ); - xbString &GetKeyExpression( const void *vpTag ) const; - xbString &GetKeyFilter( const void *vpTag ) const; - char GetKeyType( const void *vpTag ) const; - xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ); - xbInt16 GetNextKey( void *vpTag, xbInt16 lRetrieveSw ); - xbInt16 GetPrevKey( void *vpTag, xbInt16 lRetrieveSw ); - xbBool GetReuseEmptyNodesSw() const; - xbBool GetSortOrder( void *vpTag ) const; - void *GetTag( xbInt16 iTagNo ) const; - void *GetTag( xbString &sTagName ) const; - xbInt16 GetTagCount() const; + virtual xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); + virtual xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); + virtual xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); + virtual xbInt16 GetFirstKey( void *vpTag, xbInt16 lRetrieveSw ); + virtual xbString &GetKeyExpression( const void *vpTag ) const; + virtual xbString &GetKeyFilter( const void *vpTag ) const; + virtual char GetKeyType( const void *vpTag ) const; + virtual xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ); + virtual xbInt16 GetNextKey( void *vpTag, xbInt16 lRetrieveSw ); + virtual xbInt16 GetPrevKey( void *vpTag, xbInt16 lRetrieveSw ); + virtual xbBool GetReuseEmptyNodesSw() const; + virtual xbBool GetSortOrder( void *vpTag ) const; + virtual void *GetTag( xbInt16 iTagNo ) const; + virtual void *GetTag( xbString &sTagName ) const; + virtual xbInt16 GetTagCount() const; - xbString &GetTagName( void *vpTag ) const; - const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; - void GetTagName( void *vpTag, xbString &sTagName ); + virtual xbString &GetTagName( void *vpTag ) const; + virtual const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; + virtual void GetTagName( void *vpTag, xbString &sTagName ); + + virtual xbInt16 GetUnique( void *vpTag ) const; + virtual xbInt16 SetCurTag( xbInt16 iTagNo ); + virtual xbInt16 SetCurTag( xbString &sTagName ); + void SetReuseEmptyNodesSw( xbBool bReuse ); + + void TestStub( char *s, void *vpTag ); - xbInt16 GetUnique( void *vpTag ) const; - xbInt16 Reindex( void **vpTag ); - xbInt16 SetCurTag( xbInt16 iTagNo ); - xbInt16 SetCurTag( xbString &sTagName ); - void SetReuseEmptyNodesSw( xbBool bReuse ); - void TestStub( char *s, void *vpTag ); + virtual xbInt16 Reindex( void **vpTag ); + protected: friend class xbDbf; xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); xbIxNode *AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ); + void CalcBtreePointers(); + char CalcTagKeyFmt( xbExp &exp ); + xbInt16 CheckForDupKey( void *vpTag ); - xbInt16 Close(); + virtual xbInt16 Close(); xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); xbInt16 DeleteKey( void *vpTag ); - xbInt16 DeleteTag( void *vpTag ); + virtual xbInt16 DeleteTag( void *vpTag ); xbInt16 FindKeyForCurRec( void *vpTag ); xbInt16 GetKeySts( void *vpTag ) const; xbInt16 GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 lRetrieveSw ); void *GetTagTblPtr() const; + + xbInt16 HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse ); void Init( xbInt16 iOpt = 0 ); xbInt16 InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); xbInt16 InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); xbInt16 KeyExists( void * ); xbInt16 LoadTagTable(); xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk file + //virtual xbInt16 Reindex( void **vpTag ); xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); @@ -535,34 +526,10 @@ class XBDLLEXPORT xbIxMdx : public xbIx { void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const; #endif - private: - xbInt16 AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); - void AppendNodeChain( void *vpTag, xbIxNode *npNode ); - xbUInt32 BlockToPage( xbUInt32 ulBlockNo ); - void CalcBtreePointers(); - char CalcTagKeyFmt( xbExp &exp ); - char CalcTagFwdThread1(); - char CalcTagFwdThread2(); - xbMdxTag *ClearTagTable(); - xbInt16 DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag * mpTag ); - xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; - xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; - xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); - xbInt16 HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot = xbFalse ); - xbInt16 HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse ); - xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; - xbInt16 KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddKeyRecNo ); - xbInt16 KeySetPosDel( xbMdxTag *mpTag ); - xbInt16 LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ); - xbUInt32 PageToBlock( xbUInt32 ulPageNo ); - xbInt16 TagSerialNo( xbInt16 iOption, xbMdxTag *mpTag ); - xbInt16 UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ); - #ifdef XB_DEBUG_SUPPORT - xbInt16 PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ); - #endif + xbMdxTag *mdxTagTbl; - // MDX File Header Fields +// MDX File Header Fields char cVersion; char cCreateYY; char cCreateMM; @@ -588,18 +555,59 @@ class XBDLLEXPORT xbIxMdx : public xbIx { // end of MDX Header fields - - xbMdxTag *mdxTagTbl; - xbBool bReuseEmptyNodes; // Reuese empty MDX nodes when all keys deleted? // DBase 7.x and MS ODBC drivers do not reuse empty nodes, leaves them stranded in the file // Codebase 6.x reuses empty nodes. // Setting this to True will reuse empty nodes in the same manner Codebase 6.x reuses them. + + private: + xbInt16 AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); + void AppendNodeChain( void *vpTag, xbIxNode *npNode ); + xbUInt32 BlockToPage( xbUInt32 ulBlockNo ); + xbMdxTag *ClearTagTable(); + xbInt16 DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag * mpTag ); + xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; + xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; + xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); + xbInt16 HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot = xbFalse ); + xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; + xbInt16 KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddKeyRecNo ); + xbInt16 KeySetPosDel( xbMdxTag *mpTag ); + xbInt16 LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ); + xbUInt32 PageToBlock( xbUInt32 ulPageNo ); + xbInt16 TagSerialNo( xbInt16 iOption, xbMdxTag *mpTag ); + xbInt16 UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ); + + #ifdef XB_DEBUG_SUPPORT + xbInt16 PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ); + #endif + + + }; + #endif /* XB_MDX_SUPPORT */ + + +#ifdef XB_TDX_SUPPORT + +class XBDLLEXPORT xbIxTdx : public xbIxMdx { + public: + xbIxTdx( xbDbf * d ); + ~xbIxTdx(); + + xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); + + protected: + friend class xbDbf; + xbInt16 Close(); + xbInt16 DeleteTag( void *vpTag ); + + private: }; -#endif /* XB_MDX_SUPPORT */ - -} /* namespace xb */ -#endif /* XB_INDEX_SUPPORT */ -#endif /* __XB_INDEX_H__ */ +#endif /* XB_TDX_SUPPORT */ + + + } /* namespace xb */ + #endif /* XB_INDEX_SUPPORT */ +#endif /* __XB_INDEX_H__ */ diff --git a/src/include/xbindex.h.nope b/src/include/xbindex.h.nope new file mode 100755 index 0000000..b42f76e --- /dev/null +++ b/src/include/xbindex.h.nope @@ -0,0 +1,605 @@ +/* xbindex.h + +XBase64 Software Library + +Copyright (c) 1997,2003,2014, 2018, 2022 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 + +*/ + + +#ifndef __XB_INDEX_H__ +#define __XB_INDEX_H__ + +#ifdef XB_INDEX_SUPPORT + + +#define XB_ADD_KEY 1 +#define XB_UPD_KEY 2 +#define XB_DEL_KEY 3 + +namespace xb{ + + + +///@cond DOXYOFF +// structure for index nodes, each node contains information regarding one block +struct XBDLLEXPORT xbIxNode { + xbIxNode *npPrev; // pointer to previous node in chain + xbIxNode *npNext; // pointer to next node in chain + xbUInt32 iCurKeyNo; // current key number in the node, 0 offset + xbUInt32 ulBlockNo; // this block number + xbUInt32 ulBufSize; // size of cpBlockData + char *cpBlockData; // pointer to memory version of block data stored in file +}; +///@endcond DOXYOFF + + +//! @brief Base class for handling dbf indices. +/*! + +The xbIx class is used as a base class for accessing index files. +Each index file can have one or more tags. + +Calls to the index routines to perform index updates are handled automatically by the dbf class. +The application program does not need to be concerned with index updates. + +If there is a production MDX index, it is opened automatically when the dbf file is opened. +If there is an ndx file, that has been associated with the dbf file with the metadata routines, +it will be opened automatically when the dbf file is opened. +If there are non prod ndx indices that are not associated with the dbf file, the application +program will need to open as appropriate. +The meta data association logic is specific to the Xbase64 library and is not applicable to +other available tools that handle ndx indices. +All index files are automatically closed when the dbf file is closed. + + +<br> +The class is designed to support the addition of additional indices with a minimal amount of effort +needed to integrate into the library. +If you are looking at adding an new index type to the library, create a derived class using xbIx as a +base class and modify methods needed to support the new index file version. +The xbDbf class (and derived classes) perform the needed calls to the index routines for updates.<br> +See the following for examples on how to start on this:<br> +xbIxNdx is a derived class and supports a single tag.<br> +xbIxMdx is a derived class and supports multiple tags.<br> + + + +<br> +How data fields are stored in index files: +<table> +<tr><th>Field Type<th>Stored in DBF as<th>Stored in NDX as<th>Stored in MDX as</tr> +<tr><td>C<td>char<td>char<td>char +<tr><td>F<td>text numbers<td>xbDouble<td>xbBcd +<tr><td>N<td>text numbers<td>xbDouble<td>xbBcd +<tr><td>D<td>text CCYYMMDD<td>xbDouble - julian<td>xbDouble - julian +</table> + + +<br> +Pages Vs Blocks +<br> +For purposes of the Xbase index classes, a page is considered to be 512 bytes of data +and a block is made up of one or more 512 byte pages. +<br>Default block sixe of NDX indices is one 512 byte page. +<br>Default block size of MDX indices is two 512 byte pages or 1024 bytes. + +<br>The WriteBlock and GetBlock functions calculate the physical position in the +file based on a combination of Block Number and Block Size. Block size is set at +time of index file creation, default is 1024 or two pages. + +<br>Page numbers are stored in the physical file, but block reads and writes +are performed. + +<br> +Duplicate Keys +<br> +With the original DBase unique indexing option, if a table has multiple records with the +same key value, DBase would allow multiple records in the table, but only the first +record would be found in the index. +<br> +XBase64 can be configured to support the original DBase duplicate key implementation, +or can be configured to halt with a DUPLICATE_KEY error on the insertion of a record +with a duplicate key. +<br> +<table> +<tr><th>Option<th>Description</tr> +<tr><td>XB_HALT_ON_DUPKEY</td><td>Return error XB_KEY_NOT_UNIQUE when attempting to append record with duplicate key</td></tr> +<tr><td>XB_EMULATE_DBASE</td><td>Emulate DBase, allow duplicate records with the same key, only the first record is indexed</td></tr> +</table> +*/ + + +class XBDLLEXPORT xbIx : public xbFile { + public: + xbIx( xbDbf * d ); + virtual ~xbIx(); + + virtual xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ) = 0; + virtual xbInt16 Close(); + virtual xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ) = 0; + virtual xbInt16 FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const char * cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); + virtual xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ) = 0; + virtual void *GetCurTag() const; + virtual xbDbf *GetDbf() const; + + virtual xbString &GetKeyExpression( const void *vpTag ) const = 0; + virtual xbString &GetKeyFilter( const void *vpTag ) const = 0; + virtual char GetKeyType( const void *vpTag ) const = 0; + virtual xbBool GetLocked() const; + + virtual xbInt16 GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetFirstKey( void *vpTag ); + virtual xbInt16 GetFirstKey(); + + virtual xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ) = 0; + virtual xbInt16 GetLastKey( void *vpTag ); + virtual xbInt16 GetLastKey(); + + virtual xbInt16 GetNextKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetNextKey( void *vpTag ); + virtual xbInt16 GetNextKey(); + + virtual xbInt16 GetPrevKey( void *vpTag, xbInt16 iRetrieveSw ) = 0; + virtual xbInt16 GetPrevKey( void *vpTag ); + virtual xbInt16 GetPrevKey(); + + virtual void *GetTag( xbInt16 iTagNo ) const = 0; + virtual void *GetTag( xbString &sTagName ) const = 0; + virtual xbInt16 GetTagCount() const = 0; + + virtual xbString &GetTagName( void *vpTag ) const = 0; + virtual const char * GetTagName( void *vpTag, xbInt16 iOpt ) const = 0; + virtual void GetTagName( void *vpTag, xbString &sTagName ) {}; + + virtual xbBool GetUnique( void *vpTag ) const = 0; + virtual xbBool GetSortOrder( void *vpTag ) const = 0; + + virtual xbInt16 Open( const xbString &sFileName ); + virtual xbInt16 Reindex( void **vpTag ) = 0; + virtual xbInt16 SetCurTag( xbInt16 iTagNo ) = 0; + virtual xbInt16 SetCurTag( xbString &sTagName ) = 0; + virtual void SetCurTag( void * vpCurTag ); + virtual void SetLocked( xbBool bLocked ); + + virtual void TestStub( char *s, void *vpTag ) {}; + + + #ifdef XB_DEBUG_SUPPORT + virtual xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ) { return XB_NO_ERROR; } + virtual xbInt16 DumpHeader( xbInt16 iDestOpt = 0, xbInt16 iFmtOpt = 0 ) = 0; + virtual xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) = 0; + virtual void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const = 0; + virtual xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const; + virtual xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ) = 0; + + #endif + + protected: + friend class xbDbf; + + virtual xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ) = 0; + virtual xbInt16 AddKeys( xbUInt32 ulRecNo ); + virtual xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 ); + virtual xbInt16 BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyLen, const void *vpKey, xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending = xbFalse ) const; + virtual xbInt16 CheckForDupKeys(); + virtual xbInt16 CheckForDupKey( void *vpTag ) = 0; + virtual xbInt16 CompareKey( char cKeyType, const void *v1, const void *v2, size_t lKeyLen ) const; + virtual xbInt16 CreateKeys( xbInt16 iOpt ); + virtual xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ) = 0; + virtual xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) = 0; +// virtual xbInt16 DeleteKeys( xbUInt32 ulRecNo ); + virtual xbInt16 DeleteKeys(); + virtual xbInt16 DeleteKey( void *vpTag ) = 0; + + virtual xbInt16 DeleteTag( void *vpTag ) = 0; + + + virtual xbInt16 FindKeyForCurRec( void *vpTag ) = 0; + virtual xbIxNode *FreeNodeChain( xbIxNode *np ); + virtual xbInt16 GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf = 0 ); +// virtual xbBool GetIndexUpdated() const = 0; + virtual xbInt32 GetKeyCount( xbIxNode *npNode ) const; + virtual char *GetKeyData( xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const; + virtual xbInt16 GetKeySts( void *vpTag ) const = 0; + virtual xbInt16 GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 lRetrieveSw ) = 0; + virtual xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ) = 0; + virtual xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; + virtual xbInt16 KeyExists( void * ) = 0; +// virtual xbInt16 KeyUpdated( void *vpTag ) const = 0; + virtual void NodeFree( xbIxNode * ixNode ); + virtual xbInt16 ReadHeadBlock( xbInt16 iOpt = 0 ) = 0; + virtual void SetDbf( xbDbf *dbf ); + virtual xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ) = 0; + virtual xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0; + virtual xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ) = 0; + virtual xbInt16 WriteHeadBlock( xbInt16 iOption ) = 0; + + xbDbf *dbf; + char *cNodeBuf; // pointer to memory for processing in a block of index data + void *vpCurTag; // pointer to active tag. Single index files have only one tag + + private: + virtual void AppendNodeChain( void *vpTag, xbIxNode *npNode ) = 0; + virtual xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const = 0; + virtual xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const = 0; + // virtual void SetCurNode( void *vpTag, xbIxNode *npNode ) = 0; + + xbBool bLocked; // index file locked? +}; + +#ifdef XB_NDX_SUPPORT + +#define XB_NDX_BLOCK_SIZE 512 + + +///@cond DOXYOFF +struct XBDLLEXPORT xbNdxTag { + + // NDX File Header Fields + xbUInt32 ulRootBlock; // header node is 0 + xbUInt32 ulTotalBlocks; // includes header node + char cKeyType; // C = Char, F = Numeric, D = Date + xbInt16 iKeyLen; // length of key data + xbInt16 iKeysPerBlock; // max number keys per block <=100 + xbInt16 iKeyType; // 00 = Char, 01 = Numeric + xbInt16 iKeyItemLen; // KeyLen + 8 bytes + char cSerNo; // rolling incrementing serial number +1 on each index update + xbInt16 iUnique; // True if unique + xbString sKeyExpression; // index expression + // end of NDX Header field + + xbExp *exp; // pointer to expression for expression keys + time_t tNodeChainTs; // node chain time stamp + xbIxNode *npNodeChain; + xbIxNode *npCurNode; + char *cpKeyBuf; // key buffer, for searches and adds + char *cpKeyBuf2; // key buffer, for deletes + xbString sTagName; // tag name - is the file name without the extension +// xbInt16 iKeyUpdated; // key updated? set in method KeyUpdated, checked in AddKey and DeleteKey routines + xbBool bFoundSts; // key found? used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey + + + xbInt16 iKeySts; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // old key filtered new key filtered iKeySts + // Y Y XB_UPD_KEY 2 - update key if changed (delete and add) + // Y N XB_DEL_KEY 3 - delete key + // N Y XB_ADD_KEY 1 - add key + // N N 0 - no update + +}; +///@endcond DOXYOFF + +//! @brief Class for handling NDX single tag indices. +/*! + +The xbIxNdx class is derived from the xbIx base class and is specific to handling NDX single tag index files. +Each NDX index file can have only one tag, but the methods are set up to take an argument for a specific tag. +This was done in order to provide a consistant interface across index types. + +Calls to the ndx index routines to perform index updates are handled automatically be the dbf class after +the ndx file has been opened. + +Xbase64 provides a mechanism to automatically open ndx files when a dbf file is opened. +If the ndx file has been associated with the dbf file with the metadata routines, +it will be opened automatically when the dbf file is opened. +If there are non prod ndx indices that are not associated with the dbf file, the application +program will need to open as appropriate. +The meta data association logic is specific to the Xbase64 library and is not applicable to +other available tools that handle ndx indices. + +*/ + +class XBDLLEXPORT xbIxNdx : public xbIx { + public: + xbIxNdx( xbDbf * d ); + ~xbIxNdx(); + xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); + xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); +// xbInt16 DeleteTag( void *vpTag ); + xbInt16 FindKey( void *vpTag, const void *vpKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + xbInt16 GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ); + + xbInt16 GetLastKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 GetNextKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 GetPrevKey( void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt32 GetKeyLen ( const void *vpTag ) const; + char GetKeyType ( const void *vpTag ) const; + xbString &GetKeyExpression( const void *vpTag ) const; + xbString &GetKeyFilter( const void *vpTag ) const; + void *GetTag( xbInt16 iTagNo ) const; + void *GetTag( xbString &sTagName ) const; + xbString &GetTagName( void *vpTag ) const; + const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; + + xbInt16 GetTagCount() const; + xbBool GetUnique( void *vpTag = NULL ) const; + xbBool GetSortOrder( void *vpTag ) const; + xbInt16 Reindex( void **vpTag ); + xbInt16 SetCurTag( xbInt16 iTagNo ); + xbInt16 SetCurTag( xbString &sTagName ); + + + + #ifdef XB_DEBUG_SUPPORT + xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ); + xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmt = 0 ); + xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ); + void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const; + xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const; + #endif + + protected: + friend class xbDbf; + xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); + xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 ); + xbInt16 CheckForDupKey( void *vpTag ); + xbIxNode *CreateIxNode( xbUInt32 ulBufSize ); + xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); + xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); + xbInt16 DeleteKey( void *vpTag ); + + xbInt16 DeleteTag( void *vpTag ); + + xbInt16 FindKeyForCurRec( void *vpTag ); +// xbBool GetIndexUpdated() const; + xbInt16 GetKeyTypeN( const void *vpTag ) const; + xbInt16 GetKeySts( void *vpTag ) const; + xbInt16 GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw = 1 ); + xbInt16 InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 KeyExists( void *vpTag = NULL ); +// xbBool KeyFiltered( void *vpTag ) const; +// xbInt16 KeyUpdated( void *vpTag ) const; + xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk NDX file + xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); + xbInt16 WriteHeadBlock( xbInt16 iOption ); + + private: + xbInt16 AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); + void AppendNodeChain( void *vpTag, xbIxNode *npNode ); + xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; + xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; + xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); + xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; + xbInt16 KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddKeyRecNo ); + xbInt16 KeySetPosDel( xbNdxTag *npTag ); + // void SetCurNode( void *vpTag, xbIxNode *np ); + xbNdxTag *ndxTag; +}; + +#endif /* XB_NDX_SUPPORT */ + + +#ifdef XB_MDX_SUPPORT +//#define XB_MDX_BLOCK_SIZE 1024 + +struct XBDLLEXPORT xbMdxTag { + + // next 7 fields comprise the tag table entry + xbUInt32 ulTagHdrPageNo; // 512 byte page number, NOT block number + char cTagName[11]; + char cKeyFmt; // always 0x10 w/ DBase V7 + char cLeftChild; // cFwdTagThread + char cRightChild; // cFwdTagThread2 + char cParent; // cBwdTagThread + char c2; + char cKeyType; // C,D,N + + xbUInt32 ulRootPage; // 512 byte page number, NOT block number + xbUInt32 ulTagSize; // Number of 512 byte pages allocated to the tag. Tag size of two is a single 1024 block + + char cKeyFmt2; // 0x10 - base + // 0x08 - descending + // 0x40 - unique + char cKeyType2; + // one unused byte fits here + + char cTag11; // dbase sets to 0x1B + + xbInt16 iKeyLen; + xbInt16 iKeysPerBlock; + xbInt16 iSecKeyType; + xbInt16 iKeyItemLen; // iKeyLen + 4 + + char cSerialNo; // Increments +1 for each tag update + char cUnique; + xbString *sKeyExp; // Key expression + + char cHasFilter; // 0x00 or 0x01 + char cHasKeys; // 0x00 or 0x01 + + xbUInt32 ulLeftChild; // dbase 7 sets this to the root page on tag creation + xbUInt32 ulRightChild; // dbase 7 sets this to the root page on tag creation + + char cTagYY; + char cTagMM; + char cTagDD; + + char cKeyFmt3; // dbase 7 sets this 0x01 if discreet field or 0x00 if calculated or combination field key expression on tag creation + + xbString *sFiltExp; // Filter expression + + time_t tNodeChainTs; + xbIxNode *npNodeChain; + xbIxNode *npCurNode; + xbExp *exp; // pointer to expression for expression based tags + xbExp *filter; // pointer to expression for index filter + + char *cpKeyBuf; // key buffer + char *cpKeyBuf2; // key buffer +// xbBool iKeyUpdated; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // 0 - no update + // 1 - Add + // 2 - Update + // 3 - Delete +// xbBool bKeyFiltered; // key filtered? True if included key, False if excluded key. Set in method CreateKey, checked in AddKey and DeleteKey routines + xbString *sTagName; // string tag name + + + xbMdxTag *next; + xbBool bFoundSts; // key found? used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey + + + xbInt16 iKeySts; // key updated? set in method CreateKey, checked in AddKey and DeleteKey routines + // old key filtered new key filtered iKeySts + // Y Y XB_UPD_KEY 2 - update key if changed (delete and add) + // Y N XB_DEL_KEY 3 - delete key + // N Y XB_ADD_KEY 1 - add key + // N N 0 - no update + + +}; + + +class XBDLLEXPORT xbIxMdx : public xbIx { + public: + xbIxMdx( xbDbf * d ); + ~xbIxMdx(); + xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt ); + xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ); +// xbInt16 DeleteTag( void *vpTag ); + xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ); + xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ); + xbInt16 GetFirstKey( void *vpTag, xbInt16 lRetrieveSw ); + xbString &GetKeyExpression( const void *vpTag ) const; + xbString &GetKeyFilter( const void *vpTag ) const; + char GetKeyType( const void *vpTag ) const; + xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw ); + xbInt16 GetNextKey( void *vpTag, xbInt16 lRetrieveSw ); + xbInt16 GetPrevKey( void *vpTag, xbInt16 lRetrieveSw ); + xbBool GetReuseEmptyNodesSw() const; + xbBool GetSortOrder( void *vpTag ) const; + void *GetTag( xbInt16 iTagNo ) const; + void *GetTag( xbString &sTagName ) const; + xbInt16 GetTagCount() const; + + xbString &GetTagName( void *vpTag ) const; + const char * GetTagName( void *vpTag, xbInt16 iOpt ) const; + void GetTagName( void *vpTag, xbString &sTagName ); + + xbInt16 GetUnique( void *vpTag ) const; + xbInt16 Reindex( void **vpTag ); + xbInt16 SetCurTag( xbInt16 iTagNo ); + xbInt16 SetCurTag( xbString &sTagName ); + void SetReuseEmptyNodesSw( xbBool bReuse ); + + void TestStub( char *s, void *vpTag ); + + protected: + friend class xbDbf; + + xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo ); + xbIxNode *AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ); + xbInt16 CheckForDupKey( void *vpTag ); + xbInt16 Close(); + xbInt16 CreateKey( void * vpTag, xbInt16 iOpt ); + xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ); + xbInt16 DeleteKey( void *vpTag ); + + xbInt16 DeleteTag( void *vpTag ); + + xbInt16 FindKeyForCurRec( void *vpTag ); + xbInt16 GetKeySts( void *vpTag ) const; + xbInt16 GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 lRetrieveSw ); + void *GetTagTblPtr() const; + void Init( xbInt16 iOpt = 0 ); + xbInt16 InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 KeyExists( void * ); + xbInt16 LoadTagTable(); + xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk file + xbInt16 SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ); + xbInt16 SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ); + xbInt16 UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ); + xbInt16 WriteHeadBlock( xbInt16 iOption ); + + #ifdef XB_DEBUG_SUPPORT + xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL ); + xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ); + xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmtOpt = 0 ); + xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ); + void DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const; + #endif + + private: + xbInt16 AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ); + void AppendNodeChain( void *vpTag, xbIxNode *npNode ); + xbUInt32 BlockToPage( xbUInt32 ulBlockNo ); + void CalcBtreePointers(); + char CalcTagKeyFmt( xbExp &exp ); + char CalcTagFwdThread1(); + char CalcTagFwdThread2(); + xbMdxTag *ClearTagTable(); + xbInt16 DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag * mpTag ); + xbInt16 GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const; + xbInt16 GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const; + xbInt16 GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf ); + xbInt16 HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot = xbFalse ); + xbInt16 HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse ); + xbBool IsLeaf( void *vpTag, xbIxNode *npNode ) const; + xbInt16 KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddKeyRecNo ); + xbInt16 KeySetPosDel( xbMdxTag *mpTag ); + xbInt16 LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ); + xbUInt32 PageToBlock( xbUInt32 ulPageNo ); + xbInt16 TagSerialNo( xbInt16 iOption, xbMdxTag *mpTag ); + xbInt16 UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ); + + #ifdef XB_DEBUG_SUPPORT + xbInt16 PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ); + #endif + + // MDX File Header Fields + char cVersion; + char cCreateYY; + char cCreateMM; + char cCreateDD; + xbString sFileName; + xbInt16 iBlockFactor; // 1-32 #of 512 byte segments in a block + + // use file version + // xbInt16 iBlockSize; // Stored at the xbFile level + + char cProdIxFlag; + char cTagEntryCnt; + xbInt16 iTagLen; + xbInt16 iTagUseCnt; + char cNextTag; // byte 28 +1 + char c1B; // always 0x1B + xbUInt32 ulPageCnt; // number of 512 byte pages in the mdx file + xbUInt32 ulFirstFreePage; // page number corresponding to the next free block + xbUInt32 ulNoOfBlockAvail; // might be improperly named?? not sure how it is used + char cUpdateYY; + char cUpdateMM; + char cUpdateDD; + // end of MDX Header fields + + + + xbMdxTag *mdxTagTbl; + + xbBool bReuseEmptyNodes; // Reuese empty MDX nodes when all keys deleted? + // DBase 7.x and MS ODBC drivers do not reuse empty nodes, leaves them stranded in the file + // Codebase 6.x reuses empty nodes. + // Setting this to True will reuse empty nodes in the same manner Codebase 6.x reuses them. + +}; +#endif /* XB_MDX_SUPPORT */ + + +} /* namespace xb */ +#endif /* XB_INDEX_SUPPORT */ +#endif /* __XB_INDEX_H__ */ diff --git a/src/include/xblog.h b/src/include/xblog.h index e78f476..5c7d721 100755 --- a/src/include/xblog.h +++ b/src/include/xblog.h @@ -44,13 +44,14 @@ class XBDLLEXPORT xbLog : public xbFile { xbLog( const xbString &sLogFileName ); ~xbLog(); - xbInt16 LogClose (); - xbInt16 LogGetStatus (); - xbInt16 LogOpen (); - void LogSetStatus ( xbBool bLogStatus ); - void LogSetLogSize( size_t lSize ); // { LogSize = size; } - xbInt16 LogWrite ( const xbString &LogEntry, xbInt16 iOutputOption = 0 ); - xbInt16 LogWriteBytes( xbUInt32 lByteCnt, const char *p ); + xbInt16 LogClose (); + xbInt16 LogGetStatus (); + xbInt16 LogOpen (); + void LogSetStatus ( xbBool bLogStatus ); + void LogSetLogSize( size_t lSize ); // { LogSize = size; } + xbInt16 LogWrite ( const xbString &LogEntry, xbInt16 iOutputOption = 0 ); + xbInt16 LogWriteBytes( xbUInt32 lByteCnt, const char *p ); + size_t LogGetLogSize() const { return lLogSize; } private: xbBool bLoggingStatus; // false = logging off diff --git a/src/include/xbretcod.h b/src/include/xbretcod.h index ec1f91b..f2885b1 100755 --- a/src/include/xbretcod.h +++ b/src/include/xbretcod.h @@ -24,9 +24,10 @@ namespace xb{ #define XB_NO_ERROR 0 // general #define XB_NO_MEMORY -100 // general #define XB_INVALID_OPTION -101 // general + #define XB_INVALID_PARAMETER -102 // general #define XB_DUP_TABLE_OR_ALIAS -110 // table manager #define XB_INVALID_NODELINK -120 // linklist - #define XB_KEY_NOT_UNIQUE -121 // linklist + #define XB_KEY_NOT_UNIQUE -121 // linklist, index #define XB_MEMCPY_ERROR -122 // memcpy failure #define XB_FILE_EXISTS -200 // file diff --git a/src/include/xbssv.h b/src/include/xbssv.h index d050bcb..3b87a13 100755 --- a/src/include/xbssv.h +++ b/src/include/xbssv.h @@ -64,11 +64,19 @@ class XBDLLEXPORT xbSsv{ void DisplayError ( xbInt16 ErrorCode ) const; xbString& GetDefaultDateFormat () const; xbString& GetDataDirectory () const; - xbInt16 GetEndianType () const; + xbString& GetTempDirectory () const; + + void GetHomeDir ( xbString &sHomeDirOut ); + + xbInt16 GetEndianType () const; const char *GetErrorMessage ( xbInt16 ErrorCode ) const; + char GetPathSeparator () const; + void SetDataDirectory ( const xbString &sDataDirectory ); void SetDefaultDateFormat ( const xbString &sDefaultDateFormat ); + void SetTempDirectory ( const xbString &sTempDirectory ); + xbBool BitSet ( unsigned char c, xbInt16 iBitNo ) const; void BitDump ( unsigned char c ) const; @@ -77,11 +85,11 @@ class XBDLLEXPORT xbSsv{ xbBool GetDefaultAutoCommit () const; void SetDefaultAutoCommit ( xbBool bDefaultAutoCommit ); - void GetHomeDir ( xbString &sHomeDirOut ); - xbString& GetLogDirectory () const; xbString& GetLogFileName () const; void SetLogDirectory ( const xbString &sLogDirectory ); + void SetLogFileName ( const xbString &sLogFileName ); + xbBool GetMultiUser () const; void SetMultiUser ( xbBool bMultiUser ); @@ -127,10 +135,11 @@ class XBDLLEXPORT xbSsv{ static xbString sDefaultDateFormat; static xbString sDataDirectory; //Data file directory + static xbString sTempDirectory; //Temp file directory #ifdef XB_LOGGING_SUPPORT - static xbString sLogDirectory; //Default location to store log files - static xbString sLogFileName; //Default LogFileName + static xbString sLogDirectory; //Default location to store log files + static xbString sLogFileName; //Default LogFileName #endif static xbInt16 iDefaultFileVersion; // 3 = DBase 3 diff --git a/src/include/xbstring.h b/src/include/xbstring.h index 3e70acc..7fe1692 100755 --- a/src/include/xbstring.h +++ b/src/include/xbstring.h @@ -95,8 +95,8 @@ class XBDLLEXPORT xbString { xbString &Append(char c); xbString &Assign(const char *srcStr, xbUInt32 lStartPos, xbUInt32 lCopyLen ); xbString &Assign(const char *srcStr, xbUInt32 lStartPos ); - xbString &Assign(const xbString &s, xbUInt32 pos, xbUInt32 lCopyLen ); - xbString &Assign(const xbString &s, xbUInt32 lCopyLen ); + xbString &Assign(const xbString &s, xbUInt32 ulStartPos, xbUInt32 lCopyLen ); + xbString &Assign(const xbString &s, xbUInt32 ulStartPos ); xbString Copy() const; xbUInt32 CountChar( char c ) const; @@ -112,7 +112,9 @@ class XBDLLEXPORT xbString { void DumpHex( const char *title ) const; #endif + xbString &ExtractElement(xbString &s, char delim, xbUInt32 iCnt, xbInt16 iOpt = 0 ); xbString &ExtractElement(const char *src, char delim, xbUInt32 iCnt, xbInt16 iOpt = 0 ); + char GetCharacter( xbUInt32 lPos ) const; xbUInt32 GetLastPos(char c) const; xbUInt32 GetLastPos(const char *s) const; diff --git a/src/include/xbxbase.h b/src/include/xbxbase.h index fe1f323..702da23 100755 --- a/src/include/xbxbase.h +++ b/src/include/xbxbase.h @@ -126,12 +126,13 @@ class XBDLLEXPORT xbXBase : public xbTblMgr{ ~xbXBase(); xbInt16 CloseAllTables(); + xbInt16 CreateFqn( const xbString &sDirIn, const xbString &sNameIn, const xbString &sExtIn, xbString &sFqfnOut ); void DisableMsgLogging(); void EnableMsgLogging (); xbInt16 FlushLog(); - const xbString &GetLogDirectory () const; - const xbString &GetLogFileName () const; + //const xbString &GetLogDirectory () const; + //const xbString &GetLogFileName () const; const xbString &GetLogFqFileName() const; xbBool GetLogStatus () const; @@ -140,9 +141,13 @@ class XBDLLEXPORT xbXBase : public xbTblMgr{ xbDbf * Open( const xbString &sTableName, xbInt16 &iRc ); xbDbf * Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode, xbInt16 iShareMode, xbInt16 iVersion, xbInt16 &iRc ); - void SetLogDirectory( const xbString &sLogFileDirectory ); - void SetLogFileName ( const xbString &sLogFileName ); + + // next three methods moved to xbssv for consistency + // void SetLogDirectory( const xbString &sLogFileDirectory ); + // void SetLogFileName ( const xbString &sLogFileName ); void SetLogSize ( size_t lSize ); + size_t GetLogSize () const; + xbInt16 WriteLogMessage( const xbString &sLogMessage, xbInt16 iOutputOpt = 0 ); xbInt16 WriteLogBytes ( xbUInt32 lCnt, const char *p ); diff --git a/src/sql/xbcrix.cpp b/src/sql/xbcrix.cpp index 6a391f7..8efdc39 100755 --- a/src/sql/xbcrix.cpp +++ b/src/sql/xbcrix.cpp @@ -66,6 +66,7 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ xbString sNode; xbBool bUnique = xbFalse; xbDbf * dbf = NULL; + xbBool bTableLocked = xbFalse; try{ @@ -113,6 +114,7 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ xbFile f( xbase ); f.SetFileName( sIxName ); if( f.FileExists()){ + iErrorStop = 110; iRc = XB_FILE_EXISTS; throw iRc; @@ -140,12 +142,14 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ // if not open, attempt to open it dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); + if( !dbf ){ if(( iRc = xbase->OpenHighestVersion( sTableName, "", &dbf )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } + if( dbf == NULL ){ iErrorStop = 130; iRc = XB_FILE_NOT_FOUND; @@ -153,8 +157,6 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ } sCmd.Ltrunc( ulPos ); - // std::cout << "cp1 ulPos = " << ulPos << " sCmd = [" << sCmd << "]\n"; - //ulPos = sCmd.GetLastPos( ')' ); xbString sKeyExpression; xbBool bDone = xbFalse; @@ -181,7 +183,6 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ } // std::cout << "Key Expression =[" << sKeyExpression << "]\n"; - sCmd.Ltrunc( lPos ); sCmd.Trim(); @@ -199,7 +200,6 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ } } - // std::cout << "sCmd - looking for FILTER stuff [" << sCmd << "]\n"; xbString sFilter; if( sCmd.Len() > 6 ){ @@ -218,11 +218,11 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; + } else { + bTableLocked = xbTrue; } #endif // XB_LOCKING_SUPPORT - // std::cout << "SqlCreateIndex() - ixtype = " << sIxType << "\n"; - xbIx *pIx; void *vpTag; @@ -230,14 +230,16 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ iErrorStop = 150; throw iRc; } - // std::cout << "SqlCreateIndex() - back from tag create\n"; + #ifdef XB_NDX_SUPPORT + xbBool bAssociate = xbFalse; if( sIxType == "NDX"){ sCmd.Ltrunc( ulPos ); sCmd.Trim(); if( sCmd.Len() > 0 ){ sCmd.ToUpperCase(); if( sCmd.Pos( "ASSOCIATE" )){ + bAssociate = xbTrue; if(( iRc = dbf->AssociateIndex( "NDX", sIxName, 0 )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; @@ -245,25 +247,36 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ } } } -// if( sIxType == "NDX"){ -// if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ -// iErrorStop = 160; -// throw iRc; -// } -// } #endif // XB_NDX_SUPPORT + iRc = dbf->Reindex( 2, 1, &pIx, &vpTag ); - if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){ - iErrorStop = 170; + #ifdef XB_NDX_SUPPORT + if( iRc != XB_NO_ERROR && sIxType == "NDX" && bAssociate ){ + xbInt16 iRc2; + if(( iRc2 = dbf->AssociateIndex( "NDX", sIxName, 1 )) != XB_NO_ERROR ){ + iErrorStop = 180; + throw iRc2; + } + iErrorStop = 190; + throw iRc; + } + #endif // XB_NDX_SUPPORT + + if( iRc != XB_NO_ERROR ){ + iErrorStop = 200; throw iRc; } #ifdef XB_LOCKING_SUPPORT - if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ - iErrorStop = 180; - throw iRc; + if( bTableLocked ){ + if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } else { + bTableLocked = xbFalse; + } } #endif // XB_LOCKING_SUPPORT @@ -273,11 +286,10 @@ xbInt16 xbSql::SqlCreateIndex( const xbString &sCmdLine ){ sMsg.Sprintf( "xbSql::SqlCreateIndex() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); - } - #ifdef XB_LOCKING_SUPPORT - dbf->LockTable( XB_UNLOCK ); + if( bTableLocked && dbf ) + dbf->LockTable( XB_UNLOCK ); #endif // XB_LOCKING_SUPPORT return iRc; diff --git a/src/sql/xbcrtbl.cpp b/src/sql/xbcrtbl.cpp index dfb3403..e22b0b5 100755 --- a/src/sql/xbcrtbl.cpp +++ b/src/sql/xbcrtbl.cpp @@ -69,8 +69,8 @@ xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ sTableName.ExtractElement( s, ' ', 3, 0 ); sTableName.Trim(); - // std::cout << "Tablename = [" << sTableName << "]\n"; - +// std::cout << "Create table - Tablename = [" << sTableName.Str() << "]\n"; +// std::cout << "Cm line = [" << sCmdLine.Str() << "]\n"; // determine if it already exists xbFile f( xbase ); @@ -160,17 +160,21 @@ xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ if( iAttribCnt == 1 ){ sAttrib1 = sWork; } else { + lCmPos = sWork.Pos( ',' ); if( lCmPos <= 0 ){ iErrorStop = 140; iRc = XB_INVALID_FIELD_LEN; throw iRc; } + sAttrib1.Assign( sWork, 1, lCmPos - 1); sAttrib1.Trim(); + sWork.Ltrunc( lCmPos ); sAttrib2 = sWork; sAttrib2.Trim(); + } s.Ltrunc( lPos ); } @@ -239,13 +243,15 @@ xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ #elif defined (XB_DBF3_SUPPORT) dbf = new xbDbf3( xbase ); #endif + if(( iRc = dbf->CreateTable( sTableName, "", schema, 0, XB_MULTI_USER )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } - free( schema ); + ll.Clear(); + } catch (xbInt16 iRc ){ xbString sMsg; @@ -258,6 +264,7 @@ xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ xbase->WriteLogMessage( GetErrorMessage( iRc )); if( schema ) free( schema ); } + return iRc; } diff --git a/src/sql/xbcrtbl.save.cpp b/src/sql/xbcrtbl.save.cpp deleted file mode 100755 index 445ad56..0000000 --- a/src/sql/xbcrtbl.save.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* xbcrtbl.cpp - -XBase64 Software Library - -Copyright (c) 1997,2003,2014,2019,2022 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 - -*/ - -#include "xbase.h" - -#ifdef XB_SQL_SUPPORT - -namespace xb{ - - -/***********************************************************************/ -xbInt16 xbSql::SqlCreateTable( const xbString &sCmdLine ){ - - - // std::cout << "CREATE TABLE " << sCmdLine << std::endl; - - // expected format: - // CREATE TABLE tablename.dbf (Field1 CHAR(10), INTFLD1 INTEGER, ... ) - - // supported field types - // - // SQL TYPE XBASE Field Type - // -------- ---------------- - // SMALLINT NUMERIC(6,0) - // INTEGER NUMERIC(11,0) - // DECIMAL(x,y) NUMERIC(x+1,y) - // NUMERIC(x,y) NUMERIC(x,y) - // FLOAT(x,y) FLOAT(x,y) - // CHAR(n) CHARACTER(n) - // DATE DATE - // VARCHAR MEMO - // LOGICAL LOGICAL - - xbInt16 iRc = 0; - xbInt16 iErrorStop = 0; - xbString sTableName; - xbString sFieldName; - xbString sDataType; - xbString sAttrib1; - xbString sAttrib2; - xbString sLlEntry; - xbInt16 iType = 0; - xbInt16 iAttribCnt; // number of attributes for a given data type - xbString s; - xbUInt32 lPos = 0; - xbUInt32 lSpPos = 0; // space position - xbUInt32 lCmPos = 0; // comma position - xbUInt32 lLpPos = 0; // left paren position - xbLinkList<xbString> ll; - xbSchema *schema = NULL; - xbString sMsg; - - try{ - // retrieve table name - s.ExtractElement( sCmdLine, '(', 1, 0 ); - sTableName.ExtractElement( s, ' ', 3, 0 ); - sTableName.Trim(); - - // std::cout << "Tablename = [" << sTableName << "]\n"; - - - // determine if it already exists - xbFile f( xbase ); - f.SetFileName( sTableName ); - if( f.FileExists() ){ - iErrorStop = 100; - iRc = XB_FILE_EXISTS; - throw iRc; - } - - // build out table structure with parms from the sql string - lPos = sCmdLine.Pos( '(' ); - s = sCmdLine; - s.Ltrunc( lPos ); - s.Trim(); - - // remove the last byte, should be a ) - s.Remove( s.Len(), 1 ); - - s.Trim(); - //std::cout << "s = [" << s << "]\n"; - - - xbBool bDone = xbFalse; - xbInt16 iLoop = 0; - while( !bDone && iLoop++ < 255 ){ - sFieldName.ExtractElement( s, ' ', 1 , 0 ); - lPos = sFieldName.Len(); - sFieldName.Trim(); - if( sFieldName.Len() > 10 ) - sFieldName.Mid( 1, 10 ); // shrink to 10 buytes if too big - - //std::cout << "field name=[" << sFieldName << "]\n"; - s.Ltrunc( lPos + 1 ); - s.Ltrim(); - //std::cout << "remainder after field name removed = [" << s << "]\n"; - - // Data type is delimited with either a space, comma or left paren - lPos = 9999999; - lSpPos = s.Pos( ' ' ); - lCmPos = s.Pos( ',' ); - lLpPos = s.Pos( '(' ); - if( lSpPos != 0 ) lPos = lSpPos; - if( lCmPos != 0 && lCmPos < lPos ) lPos = lCmPos; - if( lLpPos != 0 && lLpPos < lPos ) lPos = lLpPos; - - //sMsg.Sprintf( "SpPos=[%d] CmPos=[%d] LpPos=[%d] lPos=[%d]", lSpPos, lCmPos, lLpPos, lPos ); - //std::cout << sMsg << "\n"; - - sDataType.Assign( s, 1, lPos-1 ); - - //std::cout << "DataType=[" << sDataType << "]\n"; - if( sDataType == "CHAR" ) - iAttribCnt = 1; - else if( sDataType == "DECIMAL" || sDataType == "NUMERIC" || sDataType == "FLOAT" ) - iAttribCnt = 2; - else if( sDataType == "SMALLINT" || sDataType == "INTEGER" || sDataType == "DATE" || sDataType == "VARCHAR" || sDataType == "LOGICAL" ) - iAttribCnt = 0; - else{ - iErrorStop = 110; - iRc = XB_INVALID_FIELD_TYPE; - throw iRc; - } - - sAttrib1 = ""; - sAttrib2 = "0"; - - if( iAttribCnt == 0 ){ - s.Ltrunc( sDataType.Len()); - - } else if( iAttribCnt > 0 ){ - lPos = s.Pos( '(' ); - if( lPos <= 0 ){ - iErrorStop = 110; - iRc = XB_INVALID_FIELD_LEN; - throw iRc; - } - s.Ltrunc( lPos ); - if( iAttribCnt == 1 ) - lPos = s.Pos( ')' ); - else - lPos = s.Pos( ',' ); - - sAttrib1.Assign( s, 1, lPos-1 ); - sAttrib1.Trim(); - s.Ltrunc( lPos ); - - if( iAttribCnt > 1 ){ - lPos = s.Pos( ')' ); - sAttrib2.Assign( s, 1, lPos-1 ); - s.Ltrunc( lPos ); - } - } - - s.Ltrim(); - s.ZapLeadingChar( ',' ); - s.Ltrim(); - - if( sDataType == "CHAR" ){ - iType = XB_CHAR_FLD; - } else if( sDataType == "DECIMAL" ){ - xbInt32 lVal = atol( sAttrib1.Str()) + 1; - sAttrib1.Sprintf( "%d", lVal ); - iType = XB_NUMERIC_FLD; - } else if( sDataType == "SMALLINT" ){ - sAttrib1 = "6"; - iType = XB_NUMERIC_FLD; - } else if( sDataType == "INTEGER" ){ - sAttrib1 = "11"; - iType = XB_NUMERIC_FLD; - } else if( sDataType == "NUMERIC" ){ - iType = XB_NUMERIC_FLD; - } else if( sDataType == "FLOAT" ) { - iType = XB_FLOAT_FLD; - } else if( sDataType == "DATE" ){ - iType = XB_DATE_FLD; - sAttrib1 = "8"; - } else if( sDataType == "VARCHAR" ){ - iType = XB_MEMO_FLD; - sAttrib1 = "10"; - } else if( sDataType == "LOGICAL" ){ - iType = XB_LOGICAL_FLD; - sAttrib1 = "1"; - } - sLlEntry.Sprintf( "%s,%s,%c,%s,%s", sFieldName.Str(), sDataType.Str(), iType, sAttrib1.Str(), sAttrib2.Str()); - ll.InsertAtEnd( sLlEntry ); - - if( s.Len() == 0 ) - bDone = xbTrue; - } - - schema = (xbSchema *) calloc( ll.GetNodeCnt()+1, sizeof( xbSchema )); - xbLinkListNode<xbString> * llN = ll.GetHeadNode(); - xbUInt32 ulCnt = ll.GetNodeCnt(); - - char *pTrg; - for( xbUInt32 i = 0; i < ulCnt; i++ ){ - s = llN->GetKey(); - sFieldName.ExtractElement( s, ',', 1 , 0 ); - pTrg = schema[i].cFieldName; - for( xbUInt32 j = 0; j < sFieldName.Len(); j++ ) - *pTrg++ = sFieldName[j+1]; - sDataType.ExtractElement( s, ',', 3, 0 ); - schema[i].cType = sDataType[1]; - sAttrib1.ExtractElement( s, ',', 4, 0 ); - schema[i].iFieldLen = atoi( sAttrib1.Str()); - sAttrib2.ExtractElement( s, ',', 5, 0 ); - schema[i].iNoOfDecs = atoi( sAttrib2.Str()); - llN = llN->GetNextNode(); - } - - // create the table - xbDbf * dbf = NULL; - #ifdef XB_DBF4_SUPPORT - dbf = new xbDbf4( xbase ); - #elif defined (XB_DBF3_SUPPORT) - dbf = new xbDbf3( xbase ); - #endif - if(( iRc = dbf->CreateTable( sTableName, "", schema, 0, XB_MULTI_USER )) != XB_NO_ERROR ){ - iErrorStop = 120; - throw iRc; - } - - free( schema ); - ll.Clear(); - } - catch (xbInt16 iRc ){ - xbString sMsg; - if( sFieldName.Len() > 0 ) - sMsg.Sprintf( "xbSql::SqlCreateTbl() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s] field = [%s]", iErrorStop, iRc, sTableName.Str(), sFieldName.Str() ); - else - sMsg.Sprintf( "xbSql::SqlCreateTbl() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); - - xbase->WriteLogMessage( sMsg.Str() ); - xbase->WriteLogMessage( GetErrorMessage( iRc )); - if( schema ) free( schema ); - } - return iRc; -} - -/***********************************************************************/ -} /* namespace */ -#endif /* XB_SQL_SUPPORT */ - diff --git a/src/sql/xbdelete.cpp b/src/sql/xbdelete.cpp index 82b4937..9bd4279 100755 --- a/src/sql/xbdelete.cpp +++ b/src/sql/xbdelete.cpp @@ -134,7 +134,7 @@ xbInt16 xbSql::SqlDelete( const xbString &sCmdLine ){ catch (xbInt16 iRc ){ xbString sMsg; - sMsg.Sprintf( "xbSql::SqlDropTable() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); + sMsg.Sprintf( "xbSql::SqlDelete() Exception Caught. Error Stop = [%d] rc = [%d] table = [%s]", iErrorStop, iRc, sTableName.Str() ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } diff --git a/src/sql/xbdrptbl.cpp b/src/sql/xbdrptbl.cpp index b03ee8f..ee3e325 100755 --- a/src/sql/xbdrptbl.cpp +++ b/src/sql/xbdrptbl.cpp @@ -2,7 +2,7 @@ XBase64 Software Library -Copyright (c) 1997,2003,2014,2022 Gary A Kunkel +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. @@ -86,8 +86,10 @@ xbInt16 xbSql::SqlDropTable( const xbString &sCmdLine ){ dbf = xbase->GetDbfPtr( fDbf.GetFqFileName()); if( !dbf ){ - dbf = xbase->Open( sTableName, iRc ); - if( iRc != XB_NO_ERROR ){ + + //dbf = xbase->Open( sTableName, iRc ); + iRc = xbase->OpenHighestVersion( sTableName, "", &dbf ); + if( iRc != XB_NO_ERROR ){ if( iRc == XB_FILE_NOT_FOUND && bIfExists ){ return XB_NO_ERROR; } else { @@ -106,6 +108,9 @@ xbInt16 xbSql::SqlDropTable( const xbString &sCmdLine ){ iErrorStop = 130; throw iRc; } + + delete dbf; + // dbf = NULL; } catch (xbInt16 iRc ){ diff --git a/src/tests/tstfuncs.cpp b/src/tests/tstfuncs.cpp index d9b108d..377b199 100755 --- a/src/tests/tstfuncs.cpp +++ b/src/tests/tstfuncs.cpp @@ -415,7 +415,7 @@ xbInt16 SetCmd( xbXBase &x, const xbString &sFileName, const xbString &sCmd, con #ifdef HAVE__FSOPEN_F // 0x40 is SH_DENYNO or _SH_DENYNO - if(( f = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL){ + if(( f = _fsopen( sFileName.Str(), "w", 0x40 )) == NULL){ x.xbSleep( 250 ); iTryCnt++; } diff --git a/src/tests/xb_test_date.cpp b/src/tests/xb_test_date.cpp index 7aee584..0efe102 100755 --- a/src/tests/xb_test_date.cpp +++ b/src/tests/xb_test_date.cpp @@ -69,6 +69,8 @@ int main( int argCnt, char **av ) iRc += TestMethod( po, "FormatDate( '', sOutDate ) (sys default format)", d3.FormatDate( sFmt, sOutDate ), 0 ); iRc += TestMethod( po, "FormatDate( '', sOutDate ) (sys default format)", sOutDate, "07/09/12", 8 ); + + iRc += TestMethod( po, "CenturyOf()" , d3.CenturyOf(), 20 ); iRc += TestMethod( po, "YearOf()", d3.YearOf(), 2012 ); iRc += TestMethod( po, "MonthOf()", d3.MonthOf(), 7 ); @@ -84,12 +86,11 @@ int main( int argCnt, char **av ) d1.Set( "20010102" ); iRc += TestMethod( po, "d1.Set('20010102')", d1.Str(), "20010102", 8 ); - iRc += TestMethod( po, "d1.CalcRollingCenturyForYear(10)", d1.CalcRollingCenturyForYear( 10 ), 20 ); + iRc += TestMethod( po, "d1.CalcRollingCenturyForYear(10)", d1.CalcRollingCenturyForYear( 10 ), 20 ); iRc += TestMethod( po, "d1.JulianDays()", d1.JulianDays(), 2451912 ); - - iRc += TestMethod( po, "d1.Set( '20140709' )", d1.Set( "20140709" ) , 0 ); + d1.CharDayOf( s ); iRc += TestMethod( po, "d1.CharDayOf(s)", s, "Wednesday", 9 ); @@ -135,8 +136,8 @@ int main( int argCnt, char **av ) iRc+= TestMethod( po, "d1+2", d1+2, "20130407", 8 ); iRc+= TestMethod( po, "d1-5", d1-5, "20130402", 8 ); -// d1.Dump( "d1" ); -// d2.Dump( "d2" ); + //d1.Dump( "d1" ); + //d2.Dump( "d2" ); iRc+= TestMethod( po, "d1==d2", d1==d2, 0 ); d2 = d1; @@ -159,6 +160,29 @@ int main( int argCnt, char **av ) iRc+= TestMethod( po, "d1<=d2", d1<=d2, 0 ); iRc+= TestMethod( po, "d1-d2", d1-d2, 1 ); + + // null date testing follows + iRc+= TestMethod( po, "IsNull", d1.IsNull(), xbFalse ); + xbDate dN; + iRc+= TestMethod( po, "IsNull", dN.IsNull(), xbTrue ); + + xbDate dtNull1; + xbDate dtNull2; + + iRc+= TestMethod( po, "dtNull1==dtNull2", dtNull1 == dtNull2, 1 ); + iRc+= TestMethod( po, "dtNull1!=dtNull2", dtNull1 != dtNull2, 0 ); + + iRc+= TestMethod( po, "dtNull1 < d2", dtNull1 < d2, 1 ); + iRc+= TestMethod( po, "dtNull1 > d2", dtNull1 > d2, 0 ); + iRc+= TestMethod( po, "dtNull1 <= d2", dtNull1 <= d2, 1 ); + iRc+= TestMethod( po, "dtNull1 >= d2", dtNull1 >= d2, 0 ); + + iRc+= TestMethod( po, "d2 < dtNull1", d2 < dtNull1, 0 ); + iRc+= TestMethod( po, "d2 > dtNull1", d2 > dtNull1, 1 ); + iRc+= TestMethod( po, "d2 <= dtNull1", d2 <= dtNull1, 0 ); + iRc+= TestMethod( po, "d2 >= dtNull1", d2 >= dtNull1, 1 ); + + /* xbDate d4( "20171015" ); std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n"; diff --git a/src/tests/xb_test_dbf_v3_memos.cpp b/src/tests/xb_test_dbf_v3_memos.cpp index e42edb1..7940c35 100755 --- a/src/tests/xb_test_dbf_v3_memos.cpp +++ b/src/tests/xb_test_dbf_v3_memos.cpp @@ -206,7 +206,6 @@ int main( int argCnt, char **av ) rc += TestMethod( po, "UpdateMemoField()", V3Dbf.UpdateMemoField( fldMemo2, sData ), XB_NO_ERROR ); rc += TestMethod( po, "AppendRecord()", V3Dbf.AppendRecord(), XB_NO_ERROR ); - // Flag 1, 3, 5 and 7 for deletion rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 1 ), XB_NO_ERROR ); rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); @@ -219,6 +218,7 @@ int main( int argCnt, char **av ) rc += TestMethod( po, "GetRecord()", V3Dbf.GetRecord( 7 ), XB_NO_ERROR ); rc += TestMethod( po, "DeleteRecord()", V3Dbf.DeleteRecord(), XB_NO_ERROR ); + rc += TestMethod( po, "Pack()", V3Dbf.Pack(), XB_NO_ERROR ); xbString sDir; @@ -228,8 +228,8 @@ int main( int argCnt, char **av ) sDbfName.Sprintf( "%snewV3nm.DBF", sDir.Str()); sDbtName.Sprintf( "%snewV3nm.DBT", sDir.Str()); - std::cout << "remove [" << sDbfName.Str() << "\n"; - std::cout << "remove [" << sDbtName.Str() << "\n"; + //std::cout << "remove [" << sDbfName.Str() << "]\n"; + //std::cout << "remove [" << sDbtName.Str() << "]\n"; V3Dbf.xbRemove( sDbfName ); V3Dbf.xbRemove( sDbtName ); diff --git a/src/tests/xb_test_dbf_v4_nomemos.cpp b/src/tests/xb_test_dbf_v4_nomemos.cpp index 984eb63..dd4976d 100755 --- a/src/tests/xb_test_dbf_v4_nomemos.cpp +++ b/src/tests/xb_test_dbf_v4_nomemos.cpp @@ -125,8 +125,22 @@ int main( int argCnt, char **av ) rc += TestMethod( po, "GetFieldNo()", V4Dbf.GetFieldNo("LASTNAME"), 1 ); rc += TestMethod( po, "GetFieldNo()", V4Dbf.GetFieldNo("MIDDLEINIT"), 2 ); + xbBool bIsNull; + rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("LASTNAME"), bIsNull, 0 ), XB_NO_ERROR ); + rc += TestMethod( po, "GetNullSts()", bIsNull, xbTrue ); + + + xbInt16 fldLastName = V4Dbf.GetFieldNo( "LASTNAME" ); rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldLastName, "NELSON" ), XB_NO_ERROR ); + + rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("LASTNAME"), bIsNull, 0 ), XB_NO_ERROR ); + rc += TestMethod( po, "GetNullSts()", bIsNull, xbFalse ); + + rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("FIRSTNAME"), bIsNull, 0 ), XB_NO_ERROR ); + rc += TestMethod( po, "GetNullSts()", bIsNull, xbTrue ); + + rc += TestMethod( po, "PutField()", V4Dbf.PutField( "FIRSTNAME", "WILLIE" ), XB_NO_ERROR ); rc += TestMethod( po, "PutField()", V4Dbf.PutField( "AMOUNT", "12.34" ), XB_NO_ERROR ); xbDate dt( "19500209" ); diff --git a/src/tests/xb_test_expression.cpp b/src/tests/xb_test_expression.cpp index ac1cde7..52c0b5c 100755 --- a/src/tests/xb_test_expression.cpp +++ b/src/tests/xb_test_expression.cpp @@ -384,6 +384,8 @@ int main( int argCnt, char **av ) { "CHAR2", XB_CHAR_FLD, 7, 0 }, { "DATE1", XB_DATE_FLD, 8, 0 }, { "DATE2", XB_DATE_FLD, 8, 0 }, + { "NULLDATE1", XB_DATE_FLD, 8, 0 }, + { "NULLDATE2", XB_DATE_FLD, 8, 0 }, { "NUM1", XB_NUMERIC_FLD, 9, 2 }, { "",0,0,0 } }; @@ -391,6 +393,10 @@ int main( int argCnt, char **av ) xbXBase x; xbDbf * MyFile; xbDate d; + xbDate dtTest1( "19890303" ); + xbDate dtTest2( "20120708" ); + + #ifdef XB_LOGGING_SUPPORT x.SetLogDirectory( PROJECT_LOG_DIR ); @@ -412,8 +418,6 @@ int main( int argCnt, char **av ) std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; } -// return 0; - #ifdef XB_DBF4_SUPPORT MyFile = new xbDbf4( &x ); /* version 4 dbf file */ #else @@ -421,13 +425,8 @@ int main( int argCnt, char **av ) #endif - -// return 0; - rc2 = MyFile->CreateTable( "ExpTest.DBF", "ExpTest", MyRecord, XB_OVERLAY, XB_MULTI_USER ); -// return 0; - iRc += TestMethod( po, "CreateTable()", rc2, XB_NO_ERROR ); iRc += TestMethod( po, "PutField()", MyFile->PutField( "CHAR1", "TEST" ), XB_NO_ERROR ); iRc += TestMethod( po, "PutField()", MyFile->PutField( "CHAR2", "TEST7B" ), XB_NO_ERROR ); @@ -437,8 +436,6 @@ int main( int argCnt, char **av ) iRc += TestMethod( po, "AppendRecord()", MyFile->AppendRecord(), XB_NO_ERROR ); -// return 0; - iRc += TestTokenMethod( &x, po, "EOX Test1", "", "", "", '?', XB_EXP_UNKNOWN, XB_NO_ERROR, XB_END_OF_EXPRESSION ); iRc += TestTokenMethod( &x, po, "EOX Test2 ", " ", "", "", '?', XB_EXP_UNKNOWN, XB_NO_ERROR, XB_END_OF_EXPRESSION ); @@ -634,49 +631,64 @@ int main( int argCnt, char **av ) iRc += TestMethod( &x, MyFile, po, "FuncTest41", "VAL( \"89\" )", (xbDouble) 89 ); iRc += TestMethod( &x, MyFile, po, "FuncTest42", "VAL( \"22.13 and some text\" )", (xbDouble) 22.13 ); iRc += TestMethod( &x, MyFile, po, "FuncTest43", "YEAR( STOD( \"20171017\" ))", (xbDouble) 2017 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest44", "CTOD( \"07\\08\\12\" )", dtTest2 ); + xbDate dtToday; + dtToday.Sysdate(); + iRc += TestMethod( &x, MyFile, po, "FuncTest45", "DATE()", dtToday ); + dtTest2 = "28870625"; + iRc += TestMethod( &x, MyFile, po, "FuncTest46", "DESCEND( DATE2 )", dtTest2 ); + iRc += TestMethod( &x, MyFile, po, "FuncTest47", "STOD( \"19890303\" )", dtTest1 ); + // date logic tests - xbDate dtTest1( "19890303" ); - xbDate dtTest2( "20120708" ); + iRc += TestMethod( &x, MyFile, po, "DateTest1", "ExpTest->DATE1", dtTest1 ); iRc += TestMethod( &x, MyFile, po, "DateTest2", "DATE1", dtTest1 ); + dtTest2.Set( "20120708" ); iRc += TestMethod( &x, MyFile, po, "DateTest3", "ExpTest->DATE2", dtTest2 ); iRc += TestMethod( &x, MyFile, po, "DateTest4", "DATE2", dtTest2 ); iRc += TestMethod( &x, MyFile, po, "DateTest5", "DATE2 - DATE1", (xbDouble) 8528 ); + dtTest1.Set( "20120705" ); iRc += TestMethod( &x, MyFile, po, "DateTest6", "DATE2 - 3", dtTest1 ); - iRc += TestMethod( &x, MyFile, po, "DateTest7", "DATE2 -= 3", dtTest1 ); - dtTest1.Set( "20120718" ); iRc += TestMethod( &x, MyFile, po, "DateTest8", "DATE2 + 10", dtTest1 ); iRc += TestMethod( &x, MyFile, po, "DateTest9", "DATE2 += 10", dtTest1 ); - dtTest1.Set( "20120709" ); iRc += TestMethod( &x, MyFile, po, "DateTest10", "++DATE2", dtTest1 ); dtTest1.Set( "20120707" ); iRc += TestMethod( &x, MyFile, po, "DateTest11", "--DATE2", dtTest1 ); - dtTest1.Set( "20120708" ); iRc += TestMethod( &x, MyFile, po, "DateTest12", "DATE2++", dtTest1 ); iRc += TestMethod( &x, MyFile, po, "DateTest13", "DATE2--", dtTest1 ); - iRc += TestMethod( &x, MyFile, po, "DateTest14", "{07/08/12}", dtTest1 ); iRc += TestMethod( &x, MyFile, po, "DateTest15", "{07/08/2012}", dtTest1 ); - iRc += TestMethod( &x, MyFile, po, "DateTest16", "{07/11/12} -3", dtTest1 ); iRc += TestMethod( &x, MyFile, po, "DateTest17", "{07/06/2012} + 2", dtTest1 ); - iRc += TestMethod( &x, MyFile, po, "FuncTest44", "CTOD( \"07\\08\\12\" )", dtTest1 ); - - xbDate dtToday; - iRc += TestMethod( &x, MyFile, po, "FuncTest45", "DATE()", dtToday ); - - dtTest2 = "28870625"; - iRc += TestMethod( &x, MyFile, po, "FuncTest46", "DESCEND( DATE2 )", dtTest2 ); - iRc += TestMethod( &x, MyFile, po, "FuncTest47", "STOD( \"20120708\" )", dtTest1 ); + iRc += TestMethod( &x, MyFile, po, "DateTest18", "ExpTest->NULLDATE1 = {07/06/2012}", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest19", "ExpTest->NULLDATE1 != {07/06/2012}", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest20", "ExpTest->NULLDATE1 < {07/06/2012}", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest21", "ExpTest->NULLDATE1 <= {07/06/2012}", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest22", "ExpTest->NULLDATE1 > {07/06/2012}", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest23", "ExpTest->NULLDATE1 >= {07/06/2012}", (xbBool) xbFalse ); + + iRc += TestMethod( &x, MyFile, po, "DateTest24", "{07/06/2012} = ExpTest->NULLDATE1", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest25", "{07/06/2012} != ExpTest->NULLDATE1", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest26", "{07/06/2012} < ExpTest->NULLDATE1", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest27", "{07/06/2012} <= ExpTest->NULLDATE1", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest28", "{07/06/2012} > ExpTest->NULLDATE1", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest29", "{07/06/2012} >= ExpTest->NULLDATE1", (xbBool) xbTrue ); + + iRc += TestMethod( &x, MyFile, po, "DateTest18", "ExpTest->NULLDATE1 = ExpTest->NULLDATE2", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest19", "ExpTest->NULLDATE1 != ExpTest->NULLDATE2", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest20", "ExpTest->NULLDATE1 < ExpTest->NULLDATE2", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest21", "ExpTest->NULLDATE1 <= ExpTest->NULLDATE2", (xbBool) xbTrue ); + iRc += TestMethod( &x, MyFile, po, "DateTest22", "ExpTest->NULLDATE1 > ExpTest->NULLDATE2", (xbBool) xbFalse ); + iRc += TestMethod( &x, MyFile, po, "DateTest23", "ExpTest->NULLDATE1 >= ExpTest->NULLDATE2", (xbBool) xbTrue ); // boolean logic tests iRc += TestMethod( &x, MyFile, po, "LogicTest1", "3=5", (xbBool) xbFalse ); diff --git a/src/tests/xb_test_file.cpp b/src/tests/xb_test_file.cpp index cf835c2..0db6eca 100755 --- a/src/tests/xb_test_file.cpp +++ b/src/tests/xb_test_file.cpp @@ -27,24 +27,24 @@ using namespace xb; int main( int argCnt, char **av ) { - int rc = 0; - int po = 1; /* print option */ + int iRc = 0; + int iPo = 1; /* print option */ /* 0 - QUIET */ /* 1 - NORMAL */ /* 2 - VERBOSE */ if( argCnt > 1 ) { if( av[1][0] == 'Q' ) - po = 0; + iPo = 0; else if( av[1][0] == 'V' ) - po = 2; + iPo = 2; } xbXBase x; #ifdef XB_LOGGING_SUPPORT x.SetLogDirectory( PROJECT_LOG_DIR ); x.EnableMsgLogging(); - if( po ){ + if( iPo ){ std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; } xbString sMsg; @@ -65,14 +65,14 @@ int main( int argCnt, char **av ) sWrkStr.SwapChars( '\\', '/' ); #endif - rc += TestMethod( po, "Set/GetDataDirectory()", f.GetDataDirectory(), sWrkStr, sWrkStr.Len()); + iRc += TestMethod( iPo, "Set/GetDataDirectory()", f.GetDataDirectory(), sWrkStr, sWrkStr.Len()); f.SetFileName( "TestFile.txt" ); sWrkStr = "TestFile.txt"; - rc += TestMethod( po, "Set/GetFileName()", f.GetFileName(), sWrkStr, sWrkStr.Len()); + iRc += TestMethod( iPo, "Set/GetFileName()", f.GetFileName(), sWrkStr, sWrkStr.Len()); f.GetFileType( sWrkStr ); - rc += TestMethod( po, "GetFileType()", sWrkStr, "TXT", 3 ); + iRc += TestMethod( iPo, "GetFileType()", sWrkStr, "TXT", 3 ); #ifdef WIN32 sWrkStr = "\\my\\directory\\"; @@ -81,10 +81,10 @@ int main( int argCnt, char **av ) #endif f.SetDirectory( sWrkStr ); - rc += TestMethod( po, "Set/GetDirectory()", f.GetDirectory(), sWrkStr, sWrkStr.Len()); + iRc += TestMethod( iPo, "Set/GetDirectory()", f.GetDirectory(), sWrkStr, sWrkStr.Len()); sWrkStr += "TestFile.txt"; - rc += TestMethod( po, "GetFqFileName()", f.GetFqFileName(), sWrkStr, 26 ); + iRc += TestMethod( iPo, "GetFqFileName()", f.GetFqFileName(), sWrkStr, 26 ); #ifdef WIN32 sWrkStr = "\\some\\directory\\myfile.dat"; @@ -95,115 +95,115 @@ int main( int argCnt, char **av ) #endif f.SetFqFileName( sWrkStr ); - rc += TestMethod( po, "GetDirectory()", f.GetDirectory(), sWrkStr2, 16 ); - rc += TestMethod( po, "GetFileName()", f.GetFileName(), "myfile.dat", 10 ); + iRc += TestMethod( iPo, "GetDirectory()", f.GetDirectory(), sWrkStr2, 16 ); + iRc += TestMethod( iPo, "GetFileName()", f.GetFileName(), "myfile.dat", 10 ); - rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "myfile.dbf", 1 ), 0 ); - rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "myfile", 1 ), 1 ); - rc += TestMethod( po, "NameSuffixMissing()", f.NameSuffixMissing( "MYFILE", 1 ), 2 ); + iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "myfile.dbf", 1 ), 0 ); + iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "myfile", 1 ), 1 ); + iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "MYFILE", 1 ), 2 ); f.SetDirectory( PROJECT_DATA_DIR ); f.SetFileName( "xbfile.txt" ); - rc += TestMethod( po, "xbFopen()", f.xbFopen( "w+b", XB_MULTI_USER ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "w+b", XB_MULTI_USER ), XB_NO_ERROR ); xbString sTest; sTest = "Test Data"; - rc += TestMethod( po, "xbWrite()", f.xbFwrite( sTest.Str(), 9, 1 ), XB_NO_ERROR ); - rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbWrite()", f.xbFwrite( sTest.Str(), 9, 1 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); - rc += TestMethod( po, "xbFopen()", f.xbFopen( "r+b", XB_MULTI_USER ), XB_NO_ERROR ); - rc += TestMethod( po, "xbFseek()", f.xbFseek( 0, SEEK_SET ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "r+b", XB_MULTI_USER ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFseek()", f.xbFseek( 0, SEEK_SET ), XB_NO_ERROR ); char buf[10]; for( int i = 0; i < 10; i++ ) buf[i] = 0x00; - rc += TestMethod( po, "xbFread()", f.xbFread( buf, 5, 1 ), XB_NO_ERROR ); - rc += TestMethod( po, "xbFread()", buf, "Test ", 5 ); + iRc += TestMethod( iPo, "xbFread()", f.xbFread( buf, 5, 1 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFread()", buf, "Test ", 5 ); - rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); - rc += TestMethod( po, "xbRemove()", f.xbRemove(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbRemove()", f.xbRemove(), XB_NO_ERROR ); xbInt16 iWork = 100; char cBuf[9]; char *p = cBuf; f.ePutInt16( cBuf, iWork ); - rc += TestMethod( po, "Put/GetShort()", f.eGetInt16( p ), 100 ); + iRc += TestMethod( iPo, "Put/GetShort()", f.eGetInt16( p ), 100 ); xbInt32 lWork = 10101; f.ePutInt32( p, lWork ); - rc += TestMethod( po, "Put/GetLong()", f.eGetInt32( p ), 10101 ); + iRc += TestMethod( iPo, "Put/GetLong()", f.eGetInt32( p ), 10101 ); lWork = 2147483647; f.ePutInt32( p, lWork ); - rc += TestMethod( po, "Put/GetLong()", f.eGetInt32( p ), 2147483647 ); - rc += TestMethod( po, "Put/GetLong()", (xbInt32) f.eGetUInt32( p ), 2147483647 ); + iRc += TestMethod( iPo, "Put/GetLong()", f.eGetInt32( p ), 2147483647 ); + iRc += TestMethod( iPo, "Put/GetLong()", (xbInt32) f.eGetUInt32( p ), 2147483647 ); xbDouble d = 123456.789; f.ePutDouble( p, d ); - rc += TestMethod( po, "Put/GetDouble()", f.eGetDouble( p ), 123456.789 ); + iRc += TestMethod( iPo, "Put/GetDouble()", f.eGetDouble( p ), 123456.789 ); xbString sFqnS; xbString sFqnT; xbFile f2( &x ); - rc += TestMethod( po, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnS ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnS ), XB_NO_ERROR ); - rc += TestMethod( po, "FileExists()", f2.FileExists( sFqnS ), xbFalse ); - rc += TestMethod( po, "xbFopen()", f2.xbFopen( "w+b", sFqnS, XB_SINGLE_USER ), XB_NO_ERROR ); - rc += TestMethod( po, "xbFclose()", f2.xbFclose(), XB_NO_ERROR ); - rc += TestMethod( po, "FileExists()", f2.FileExists( sFqnS ), xbTrue ); + iRc += TestMethod( iPo, "FileExists()", f2.FileExists( sFqnS ), xbFalse ); + iRc += TestMethod( iPo, "xbFopen()", f2.xbFopen( "w+b", sFqnS, XB_SINGLE_USER ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFclose()", f2.xbFclose(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "FileExists()", f2.FileExists( sFqnS ), xbTrue ); - rc += TestMethod( po, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnT ), XB_NO_ERROR ); - rc += TestMethod( po, "xbRename()", f2.xbRename( sFqnS, sFqnT ), XB_NO_ERROR ); - rc += TestMethod( po, "xbRemove()", f.xbRemove( sFqnT ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnT ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbRename()", f2.xbRename( sFqnS, sFqnT ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbRemove()", f.xbRemove( sFqnT ), XB_NO_ERROR ); xbString sFn; - rc += TestMethod( po, "GetFileNamePart()", f2.GetFileNamePart( sFqnS , sFn ), XB_NO_ERROR ); - rc += TestMethod( po, "GetFileExtPart()", f2.GetFileExtPart( sFqnS , sFn ), XB_NO_ERROR ); - rc += TestMethod( po, "GetFileExtPart()", f2.GetFileDirPart( sFqnS , sFn ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFileNamePart()", f2.GetFileNamePart( sFqnS , sFn ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFileExtPart()", f2.GetFileExtPart( sFqnS , sFn ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFileExtPart()", f2.GetFileDirPart( sFqnS , sFn ), XB_NO_ERROR ); - rc += TestMethod( po, "SetBlockSize()", f.SetBlockSize( 100 ), XB_INVALID_BLOCK_SIZE ); - rc += TestMethod( po, "SetBlockSize()", f.SetBlockSize( 512 ), XB_NO_ERROR ); - rc += TestMethod( po, "GetBlockSize()", (xbInt32) f.GetBlockSize(), 512 ); + iRc += TestMethod( iPo, "SetBlockSize()", f.SetBlockSize( 100 ), XB_INVALID_BLOCK_SIZE ); + iRc += TestMethod( iPo, "SetBlockSize()", f.SetBlockSize( 512 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetBlockSize()", (xbInt32) f.GetBlockSize(), 512 ); char BlockBuf[513]; memset( BlockBuf, 0x00, 513 ); - rc += TestMethod( po, "xbFopen()", f.xbFopen( "w+b", XB_SINGLE_USER ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "w+b", XB_SINGLE_USER ), XB_NO_ERROR ); for( int i = 0; i < 512; i++ ) BlockBuf[i] = 'A'; - rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 0L, 512, BlockBuf ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 0L, 512, BlockBuf ), XB_NO_ERROR ); for( int i = 0; i < 512; i++ ) BlockBuf[i] = 'B'; - rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 1L, 512, BlockBuf ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 1L, 512, BlockBuf ), XB_NO_ERROR ); for( int i = 0; i < 512; i++ ) BlockBuf[i] = 'C'; - rc += TestMethod( po, "WriteBlock()", f.WriteBlock( 2L, 512, BlockBuf ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 2L, 512, BlockBuf ), XB_NO_ERROR ); char BlockBuf2[513]; memset( BlockBuf2, 0x00, 513 ); - rc += TestMethod( po, "ReadBlock()", f.ReadBlock( 2L, 512, BlockBuf2 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "ReadBlock()", f.ReadBlock( 2L, 512, BlockBuf2 ), XB_NO_ERROR ); xbString s1 = BlockBuf; xbString s2 = BlockBuf2; - rc += TestMethod( po, "ReadBlock()", s1, s2, 512 ); - - rc += TestMethod( po, "xbTruncate()", f.xbTruncate( 1000 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "ReadBlock()", s1, s2, 512 ); + iRc += TestMethod( iPo, "xbTruncate()", f.xbTruncate( 1000 ), XB_NO_ERROR ); xbUInt64 ullFsize; - rc += TestMethod( po, "GetFileSize()", f.GetFileSize( ullFsize ), XB_NO_ERROR ); - rc += TestMethod( po, "xbGetFileSize()", (xbInt32) ullFsize, 1000 ); - rc += TestMethod( po, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "GetFileSize()", f.GetFileSize( ullFsize ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "xbGetFileSize()", (xbInt32) ullFsize, 1000 ); + iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR ); + - if( po > 0 || rc < 0 ) - fprintf( stdout, "Total Errors = %d\n", rc * -1 ); + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); #ifdef XB_LOGGING_SUPPORT - sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); x.WriteLogMessage( sMsg ); #endif - return rc; + return iRc; } diff --git a/src/tests/xb_test_funcs.cpp b/src/tests/xb_test_funcs.cpp index c7bf0d3..7eb8b16 100755 --- a/src/tests/xb_test_funcs.cpp +++ b/src/tests/xb_test_funcs.cpp @@ -125,6 +125,8 @@ int main( int argCnt, char **av ) iRc += TestMethod( po, "CTOD( \"01\\03\\87\", dtResult )", x.CTOD( "01\\03\\87", dtResult ), XB_NO_ERROR ); iRc += TestMethod( po, "CTOD( \"01\\03\\87\", dtResult )", dtResult.Str(), "19870103", 8 ); iRc += TestMethod( po, "DATE( dtResult )", x.DATE( dtResult ), XB_NO_ERROR ); + + d.Sysdate(); iRc += TestMethod( po, "DATE( dtResult )", dtResult.Str(), d.Str(), 8 ); iRc += TestMethod( po, "DAY(\"19870103\", dResult )", x.DAY( "19870103", dResult ), XB_NO_ERROR ); iRc += TestMethod( po, "DAY(\"19870103\", dResult )", dResult, (xbDouble) 3 ); @@ -229,6 +231,7 @@ int main( int argCnt, char **av ) iRc += TestMethod( po, "x.RTRIM( \"zzz \", sResult )", sResult, "zzz", 3 ); iRc += TestMethod( po, "x.TRIM( \"aaa \", sResult )", x.TRIM( "aaa ", sResult ), XB_NO_ERROR ); iRc += TestMethod( po, "x.TRIM( \"aaa \", sResult )", sResult, "aaa", 3 ); + iRc += TestMethod( po, "x.SPACE( 3, sResult )", x.SPACE( 3, sResult ), XB_NO_ERROR ); iRc += TestMethod( po, "x.SPACE( 3, sResult )", sResult, " ", 3 ); iRc += TestMethod( po, "x.SQRT( 9, dResult )", x.SQRT( 9, dResult ), XB_NO_ERROR ); diff --git a/src/tests/xb_test_lock.cpp b/src/tests/xb_test_lock.cpp index 39332ad..2df71a8 100755 --- a/src/tests/xb_test_lock.cpp +++ b/src/tests/xb_test_lock.cpp @@ -34,7 +34,8 @@ int main( int argCnt, char **av ) /* 2 - VERBOSE */ xbInt16 iErrorStop = 0; - xbString sLockFile = "locktest.txt"; + xbString sLockFile; + xbString sLockFile2; xbString sLockCmd; xbString sResult; @@ -69,23 +70,34 @@ int main( int argCnt, char **av ) x.WriteLogMessage( sMsg ); #endif + x.SetDataDirectory( PROJECT_DATA_DIR ); InitTime(); #ifdef XB_DBF4_SUPPORT - MyFile = new xbDbf4( &x ); /* version 4 dbf file */ + MyFile = new xbDbf4( &x ); // version 4 dbf file #else - MyFile = new xbDbf3( &x ); /* version 3 dbf file */ + MyFile = new xbDbf3( &x ); // version 3 dbf file #endif iRc2 = MyFile->CreateTable( "LockTest.DBF", "LockTest", MyRecord, XB_OVERLAY, XB_MULTI_USER ); iRc += TestMethod( po, "CreateTable()", iRc2, XB_NO_ERROR ); + + #ifdef XB_MDX_SUPPORT + xbIx *ixPtr; + void *pTag; + iRc2 = MyFile->CreateTag( "MDX", "LockTag", "LOCKTEST", "", 0, 0, XB_OVERLAY, &ixPtr, &pTag ); + iRc += TestMethod( po, "CreateTag()", iRc2, XB_NO_ERROR ); + #endif // XB_MDX_SUPPORT + + iRc += TestMethod( po, "PutField()", MyFile->PutField( "LOCKTEST", "TEST" ), XB_NO_ERROR ); iRc += TestMethod( po, "AppendRecord()", MyFile->AppendRecord(), XB_NO_ERROR ); - iRc += TestMethod( po, "Close()", MyFile->Close(), XB_NO_ERROR ); - MyFile->Close(); + iRc += TestMethod( po, "Close()", MyFile->Close(), XB_NO_ERROR ); + + sLockFile.Sprintf( "%slocktest.txt", PROJECT_DATA_DIR ); + // std::cout << "xb_test_lock - lockfile = [ " << sLockFile.Str() << "]\n"; - //unlink( sLockFile ); - remove( sLockFile ); + remove( sLockFile.Str() ); #if defined (HAVE_FORK_F) pid_t pid; @@ -104,14 +116,15 @@ int main( int argCnt, char **av ) xbString sLastLockCmd; #ifdef XB_DBF4_SUPPORT - MyFileChld = new xbDbf4( &x ); /* version 4 dbf file */ + MyFileChld = new xbDbf4( &x ); // version 4 dbf file #else - MyFileChld = new xbDbf3( &x ); /* version 3 dbf file */ + MyFileChld = new xbDbf3( &x ); // version 3 dbf file #endif x.xbSleep( 250 ); while( !bDone ){ + iRc2 = GetCmd( x, sLockFile, sLockCmd, 'C', po ); if( sLockCmd == sLastLockCmd ) @@ -139,7 +152,6 @@ int main( int argCnt, char **av ) x.xbSleep( 250 ); } else { - if( sLockCmd == "START" ){ // begin the process iRcChld = MyFileChld->Open( "LockTest.DBF" ); @@ -199,8 +211,31 @@ int main( int argCnt, char **av ) #else sLockCmd = "OK"; #endif + + } else if( sLockCmd == "IL" ){ + // index lock + #ifdef XB_MDX_SUPPORT + if(( iRcChld = MyFileChld->LockIndices( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif // XB_MDX_SUPPORT + + } else if( sLockCmd == "IU" ){ + // index unlock + #ifdef XB_MDX_SUPPORT + if(( iRcChld = MyFileChld->LockIndices( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif // XB_MDX_SUPPORT } + #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Child task [%s] Result [%s] RC = [%d]", av[0], sLockCmd.Str(), sResult.Str(), iRcChld ); x.WriteLogMessage( sMsg ); @@ -212,7 +247,6 @@ int main( int argCnt, char **av ) delete MyFileChld; } } - } else { iRc = iRc2; bDone = xbTrue; @@ -226,6 +260,8 @@ int main( int argCnt, char **av ) delete MyFile; delete MyFileChld; + remove( sLockFile ); + if( po > 0 ) std::cout << "Exiting child\n"; @@ -241,9 +277,7 @@ int main( int argCnt, char **av ) xbInt16 iLoopCtr = 0; try{ - // start - #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing START command", av[0] ); x.WriteLogMessage( sMsg ); @@ -266,7 +300,7 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 10; + iErrorStop = 100; iRc2 = -1; throw iRc2; } @@ -294,19 +328,19 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 20; + iErrorStop = 110; iRc2 = -1; throw iRc2; } // attempt to lock table, should fail if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){ - iErrorStop = 30; + iErrorStop = 120; throw iRc2; } if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){ - iErrorStop = 40; + iErrorStop = 130; throw iRc2; } @@ -336,24 +370,24 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 50; + iErrorStop = 140; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){ - iErrorStop = 60; + iErrorStop = 150; throw iRc2; } if( po > 0 ) std::cout << "[PASS] LockTable Test 2\n"; if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ - iErrorStop = 70; + iErrorStop = 160; throw iRc2; } - /* record lock */ + // record lock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing RL command", av[0] ); x.WriteLogMessage( sMsg ); @@ -376,19 +410,19 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 80; + iErrorStop = 170; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){ - iErrorStop = 90; + iErrorStop = 180; throw iRc2; } if( po > 0 ) std::cout << "[PASS] LockRecord Test 1\n"; - /* record unlock */ + // record unlock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing RU command", av[0] ); x.WriteLogMessage( sMsg ); @@ -411,29 +445,29 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 100; + iErrorStop = 190; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){ - iErrorStop = 110; + iErrorStop = 200; throw iRc2; } std::cout << "[PASS] LockRecord Test 2\n"; if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){ - iErrorStop = 120; + iErrorStop = 210; throw iRc2; } - /* memo lock */ + // memo lock #ifdef XB_MEMO_SUPPORT #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing ML command", av[0] ); x.WriteLogMessage( sMsg ); - #endif + #endif // XB_LOGGING_SUPPORT sLockCmd = "ML"; SetCmd( x, sLockFile, sLockCmd, 'P', po ); @@ -449,26 +483,26 @@ int main( int argCnt, char **av ) #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); x.WriteLogMessage( sMsg ); - #endif + #endif // XB_LOGGING_SUPPORT if( sResult != "OK" ){ - iErrorStop = 130; + iErrorStop = 220; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){ - iErrorStop = 140; + iErrorStop = 230; throw iRc2; } if( po > 0 ) std::cout << "[PASS] LockMemo Test 1\n"; - /* memo unlock */ + // memo unlock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing MU command", av[0] ); x.WriteLogMessage( sMsg ); - #endif + #endif // XB_LOGGING_SUPPORT sLockCmd = "MU"; SetCmd( x, sLockFile, sLockCmd, 'P', po ); @@ -484,30 +518,110 @@ int main( int argCnt, char **av ) #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); x.WriteLogMessage( sMsg ); - #endif + #endif // XB_LOGGING_SUPPORT if( sResult != "OK" ){ - iErrorStop = 150; + iErrorStop = 240; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){ - iErrorStop = 160; + iErrorStop = 250; throw iRc2; } std::cout << "[PASS] LockMemo Test 2\n"; if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ - iErrorStop = 170; + iErrorStop = 260; throw iRc2; } - #endif + #endif // XB_MEMO_SUPPORT + + // index lock + #ifdef XB_MDX_SUPPORT + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing IL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif // XB_LOGGING_SUPPORT + + sLockCmd = "IL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif // XB_LOGGING_SUPPORT + + if( sResult != "OK" ){ + iErrorStop = 270; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockIndices( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 280; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockIndex Test 1\n"; + + // memo unlock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing IU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif // XB_LOGGING_SUPPORT + + sLockCmd = "IU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 30 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif // XB_LOGGING_SUPPORT + + if( sResult != "OK" ){ + iErrorStop = 290; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc2; + } + + std::cout << "[PASS] LockIndex Test 2\n"; + if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 310; + throw iRc2; + } + #endif // XB_MDX_SUPPORT + + // exit sLockCmd = "EXIT"; SetCmd( x, sLockFile, sLockCmd, 'P', po ); + + } catch (xbInt16 iRc3 ){ iRc = iRc3; if( po > 0 ) @@ -533,6 +647,7 @@ int main( int argCnt, char **av ) SetCmd( x, sLockFile, sLockCmd, 'P', po ); MyFile->Close(); delete MyFile; + } #elif defined (HAVE_CREATEPROCESSW_F) @@ -578,7 +693,7 @@ int main( int argCnt, char **av ) GetCmd( x, sLockFile, sResult, 'P', po ); while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ GetCmd( x, sLockFile, sResult, 'P', po ); - x.xbSleep( 250 ); + x.xbSleep( 300 ); iLoopCtr++; } @@ -600,7 +715,7 @@ int main( int argCnt, char **av ) GetCmd( x, sLockFile, sResult, 'P', po ); while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ GetCmd( x, sLockFile, sResult, 'P', po ); - x.xbSleep( 250 ); + x.xbSleep( 310 ); iLoopCtr++; } @@ -610,19 +725,19 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 200; + iErrorStop = 320; iRc2 = -1; throw iRc2; } // attempt to lock table, should fail if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){ - iErrorStop = 210; + iErrorStop = 330; throw iRc2; } if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){ - iErrorStop = 220; + iErrorStop = 340; throw iRc2; } @@ -658,13 +773,13 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 230; + iErrorStop = 350; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){ - iErrorStop = 240; + iErrorStop = 360; throw iRc2; } if( po > 0 ){ @@ -676,11 +791,11 @@ int main( int argCnt, char **av ) } if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ - iErrorStop = 250; + iErrorStop = 370; throw iRc2; } - /* record lock */ + // record lock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing RL command", av[0] ); x.WriteLogMessage( sMsg ); @@ -693,7 +808,7 @@ int main( int argCnt, char **av ) GetCmd( x, sLockFile, sResult, 'P', po ); while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ GetCmd( x, sLockFile, sResult, 'P', po ); - x.xbSleep( 250 ); + x.xbSleep( 380 ); iLoopCtr++; } @@ -703,19 +818,19 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 260; + iErrorStop = 390; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){ - iErrorStop = 270; + iErrorStop = 400; throw iRc2; } if( po > 0 ) std::cout << "[PASS] LockRecord Test 1\n"; - /* record unlock */ + // record unlock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing RU command", av[0] ); x.WriteLogMessage( sMsg ); @@ -738,24 +853,23 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 280; + iErrorStop = 410; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){ - iErrorStop = 290; + iErrorStop = 420; throw iRc2; } std::cout << "[PASS] LockRecord Test 2\n"; if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){ - iErrorStop = 300; + iErrorStop = 430; throw iRc2; } - - /* memo lock */ + // memo lock #ifdef XB_MEMO_SUPPORT #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing ML command", av[0] ); @@ -779,19 +893,19 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 310; + iErrorStop = 440; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){ - iErrorStop = 320; + iErrorStop = 450; throw iRc2; } if( po > 0 ) std::cout << "[PASS] LockMemo Test 1\n"; - /* memo unlock */ + // memo unlock #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Parent task issuing MU command", av[0] ); x.WriteLogMessage( sMsg ); @@ -814,19 +928,95 @@ int main( int argCnt, char **av ) #endif if( sResult != "OK" ){ - iErrorStop = 330; + iErrorStop = 460; iRc2 = -1; throw iRc2; } if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){ - iErrorStop = 340; + iErrorStop = 470; throw iRc2; } std::cout << "[PASS] LockMemo Test 2\n"; if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ - iErrorStop = 350; + iErrorStop = 480; + throw iRc2; + } + #endif + + // index lock + #ifdef XB_MDX_SUPPORT + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing IL command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "IL"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 490 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 500; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockIndices( XB_LOCK )) == XB_NO_ERROR ){ + iErrorStop = 510; + throw iRc2; + } + if( po > 0 ) + std::cout << "[PASS] LockIndex Test 1\n"; + + // index unlock + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task issuing IU command", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + sLockCmd = "IU"; + SetCmd( x, sLockFile, sLockCmd, 'P', po ); + sResult = ""; + + GetCmd( x, sLockFile, sResult, 'P', po ); + while( sResult != "OK" && sResult != "FAIL" && iLoopCtr < 10 ){ + GetCmd( x, sLockFile, sResult, 'P', po ); + x.xbSleep( 250 ); + iLoopCtr++; + } + + #ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] Parent task retrieved result [%s]", av[0], sResult.Str() ); + x.WriteLogMessage( sMsg ); + #endif + + if( sResult != "OK" ){ + iErrorStop = 520; + iRc2 = -1; + throw iRc2; + } + + if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 530; + throw iRc2; + } + + std::cout << "[PASS] LockIndex Test 2\n"; + if(( iRc2 = MyFile->LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 540; throw iRc2; } #endif @@ -835,6 +1025,7 @@ int main( int argCnt, char **av ) sLockCmd = "EXIT"; SetCmd( x, sLockFile, sLockCmd, 'P', po ); + } catch (xbInt16 iRc3 ){ if( po > 0 ) std::cout << "Parent lock task exiting on failure [" << sLockCmd.Str() << "][" << iErrorStop << "]\n"; @@ -852,6 +1043,7 @@ int main( int argCnt, char **av ) } #else + iRc--; sMsg.Sprintf( "Program [%s] not executed. Library does not support 'fork' or 'CreateProcess' function call", av[0] ); #ifdef XB_LOGGING_SUPPORT diff --git a/src/tests/xb_test_lock2.cpp b/src/tests/xb_test_lock2.cpp index d5534ec..82b4dea 100755 --- a/src/tests/xb_test_lock2.cpp +++ b/src/tests/xb_test_lock2.cpp @@ -38,8 +38,7 @@ int main( int argCnt, char **av ) /* 2 - VERBOSE */ xbBool bDone = xbFalse; -// xbInt16 iErrorStop = 0; - xbString sLockFile = "locktest.txt"; + xbString sLockFile; xbString sLockCmd; xbString sResult; xbInt32 iChildLoop = 0; @@ -74,6 +73,9 @@ int main( int argCnt, char **av ) x.SetDataDirectory( PROJECT_DATA_DIR ); InitTime(); + sLockFile.Sprintf( "%slocktest.txt", PROJECT_DATA_DIR ); + // std::cout << "xb_test_lock2 - lockfile = [ " << sLockFile.Str() << "]\n"; + #ifdef XB_DBF4_SUPPORT MyFile = new xbDbf4( &x ); /* version 4 dbf file */ #else @@ -166,7 +168,31 @@ int main( int argCnt, char **av ) #else sLockCmd = "OK"; #endif + + } else if( sLockCmd == "IL" ){ + // index lock + #ifdef XB_MDX_SUPPORT + if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif + + } else if( sLockCmd == "IU" ){ + // index unlock + #ifdef XB_MDX_SUPPORT + if(( iRc2 = MyFile->LockIndices( XB_UNLOCK )) != XB_NO_ERROR ) + sResult = "FAIL"; + else + sResult = "OK"; + #else + sLockCmd = "OK"; + #endif } + + #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Child task [%s] Result [%s] RC = [%d]", av[0], sLockCmd.Str(), sResult.Str(), iRc2 ); x.WriteLogMessage( sMsg ); @@ -188,7 +214,7 @@ int main( int argCnt, char **av ) if( po > 0 ) std::cout << "Exiting child\n"; - remove( sLockFile ); + remove( sLockFile.Str() ); #ifdef XB_LOGGING_SUPPORT sMsg.Sprintf( "Program [%s] Child task terminating", av[0] ); x.WriteLogMessage( sMsg ); @@ -202,6 +228,6 @@ int main( int argCnt, char **av ) x.WriteLogMessage( sMsg ); #endif - ExitProcess( iRc ); + ExitProcess( (xbUInt32) iRc ); } diff --git a/src/tests/xb_test_log.cpp b/src/tests/xb_test_log.cpp index 28ae552..a586809 100755 --- a/src/tests/xb_test_log.cpp +++ b/src/tests/xb_test_log.cpp @@ -41,7 +41,7 @@ int main( int argCnt, char **av ) } xbXBase x; - x.SetLogDirectory( PROJECT_LOG_DIR ); + x.EnableMsgLogging(); InitTime(); if( po ){ @@ -51,28 +51,58 @@ int main( int argCnt, char **av ) sMsg.Sprintf( "Program [%s] initializing...", av[0] ); x.WriteLogMessage( sMsg ); - xbString sNewLogFileName = "Logfile2.txt"; - sMsg.Sprintf( "Switching to logfile [%s]", sNewLogFileName.Str() ); - x.WriteLogMessage( sMsg ); + // verify first logfile location is correct + xbString sWork; + x.GetHomeDir( sWork ); + std::cout << "Home Dir = " << sWork.Str() << std::endl; + + // verify home directory > 0 length + xbInt32 iLen = (xbInt32) sWork.Len(); + if( sWork.Len() == 0 ) + rc += TestMethod( po, "GetHomeDir()", iLen, 1 ); + else + rc += TestMethod( po, "GetHomeDir()", iLen, iLen ); + + sWork.Sprintf( "%s%cxbase64%clogs", sWork.Str(), + x.GetPathSeparator(), x.GetPathSeparator()); + + // verify path exists + // std::cout << "Home Dir = " << sHomeDir.Str() << std::endl; + xbFile f( &x ); + rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue ); + + + sWork.Sprintf( "%s%c%s", sWork.Str(), + x.GetPathSeparator(), x.GetLogFileName().Str() ); + + // std::cout << "Home Dir = " << sWork.Str() << std::endl; + rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue ); + rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue ); + x.DisableMsgLogging(); - rc += TestMethod( po, "Set/Get Log Status()", x.GetLogStatus(), xbFalse ); - x.SetLogFileName( sNewLogFileName ); + rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbFalse ); + xbString sNewLogFileName = "Logfile2.txt"; + x.SetLogDirectory( PROJECT_LOG_DIR ); + x.SetLogFileName( sNewLogFileName ); + sWork.Sprintf( "%s%c%s", + PROJECT_LOG_DIR, x.GetPathSeparator(), sNewLogFileName.Str()); + + sMsg.Sprintf( "Switching to logfile [%s]", sWork.Str() ); x.EnableMsgLogging(); - rc += TestMethod( po, "Set/Get Log Status()", x.GetLogStatus(), 1 ); - rc += TestMethod( po,"WriteLogMessage()", x.WriteLogMessage( "Test log message........" ), XB_NO_ERROR ); + rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue ); + rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue ); - sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], rc * -1 ); x.WriteLogMessage( sMsg ); + rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue ); + x.WriteLogMessage( "Test Log Message" ); if( po > 0 || rc < 0 ) fprintf( stdout, "Total Errors = %d\n", rc * -1 ); - #endif /* XB_LOGGING_SUPPORT */ - return rc; } diff --git a/src/tests/xb_test_mdx.cpp b/src/tests/xb_test_mdx.cpp index 2ef5ffb..680be5c 100755 --- a/src/tests/xb_test_mdx.cpp +++ b/src/tests/xb_test_mdx.cpp @@ -1,4 +1,4 @@ -/* xb_test_ndx.cpp +/* xb_test_mdx.cpp XBase64 Software Library @@ -280,9 +280,18 @@ int main( int argCnt, char **av ) iRc += TestMethod( iPo, "CheckTagIntegrity(7)", V4DbfX2->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + + iRc2 = V4DbfX2->CreateTag( "MDX", "TAG2", "CHAR27", ".NOT. DELETED()", 0, 1, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(5)", iRc2, 0 ); + + iRc2 += V4DbfX2->Reindex( 1 ); + iRc += TestMethod( iPo, "Reindex()", iRc2, XB_KEY_NOT_UNIQUE ); + + x.CloseAllTables(); - delete V4DbfX1; - delete V4DbfX2; +// delete V4DbfX1; +// delete V4DbfX2; if( iPo > 0 || iRc < 0 ) fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); diff --git a/src/tests/xb_test_ndx.cpp b/src/tests/xb_test_ndx.cpp index 34496b6..320a1c9 100755 --- a/src/tests/xb_test_ndx.cpp +++ b/src/tests/xb_test_ndx.cpp @@ -117,6 +117,14 @@ int main( int argCnt, char **av ) iRc2 = V3Dbf->AssociateIndex( "NDX", "TestNdxN.NDX", 0 ); iRc += TestMethod( iPo, "Associate()", (xbInt32) iRc2, XB_NO_ERROR ); + xbInt16 iTagCnt = ixPtr->GetTagCount(); + iRc += TestMethod( iPo, "GetTagCount()", (xbInt32) iTagCnt, 1 ); + + xbString sTagName; + sTagName = ixPtr->GetTagName( &ndx ); + iRc += TestMethod( iPo, "GetTagName()", sTagName.Str(), "TestNdxN", 8 ); + + #ifdef XB_LOCKING_SUPPORT iRc += TestMethod( iPo, "LockTable()", V3Dbf->LockTable( XB_LOCK ), XB_NO_ERROR ); #endif @@ -307,13 +315,12 @@ int main( int argCnt, char **av ) xbIxList *ixl = V3Dbf->GetIxList(); xbIxNdx *ix; - xbString sTagName; while( ixl ){ if( *ixl->sFmt == "NDX" ){ ix = (xbIxNdx *) ixl->ix; //ix->GetTagName( 0, sTagName ); sMsg.Sprintf( "CheckTagIntegrity() - [%s]", ix->GetTagName(ix->GetCurTag()).Str()); - iRc += TestMethod( iPo, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 2 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 0 ), XB_NO_ERROR ); ixl = ixl->next; } } @@ -382,14 +389,54 @@ int main( int argCnt, char **av ) ix = (xbIxNdx *) ixl->ix; //ix->GetTagName( 0, sTagName ); sMsg.Sprintf( "CheckTagIntegrity() - [%s]", ix->GetTagName(ix->GetCurTag()).Str()); - iRc += TestMethod( iPo, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 2 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, sMsg, ix->CheckTagIntegrity( ix->GetCurTag(), 0 ), XB_NO_ERROR ); } ixl = ixl->next; } -// iRc += TestMethod( iPo, "DeleteTable()", V3Dbf->DeleteTable(), XB_NO_ERROR ); + iRc2 = V3Dbf->Reindex( 1 ); // reindex all tags + iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_NO_ERROR ); + + iRc += TestMethod( iPo, "DeleteTable()", V3Dbf->DeleteTable(), XB_NO_ERROR ); + + + + // test tag delete on unsuccessful reindex + + iRc2 = V3Dbf->CreateTable( "TestNdx.DBF", "TestNdx", MyV3Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( iPo, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + + iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "AAA" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "DFLD", "19611109" ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "PutfieldDouble()", V3Dbf->PutDoubleField( "NFLD", 50 ), XB_NO_ERROR ); + iRc += TestMethod( iPo, "AppendRecord()", V3Dbf->AppendRecord(), XB_NO_ERROR ); + + iRc2 = V3Dbf->Reindex( 1 ); // verify reindex works with no tags + iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_NO_ERROR ); + + + x.SetUniqueKeyOpt( XB_HALT_ON_DUPKEY ); + iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxX.NDX", "CFLD", "", 0, 1, XB_OVERLAY, &ixPtr, &ndx ); + iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR ); + + iRc2 = V3Dbf->Reindex( 1 ); // verify reindex fails with dup key + iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_KEY_NOT_UNIQUE ); x.CloseAllTables(); +// delete V3Dbf; if( iPo > 0 || iRc < 0 ) fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); diff --git a/src/tests/xb_test_sql.cpp b/src/tests/xb_test_sql.cpp index 99a4f0b..3341895 100755 --- a/src/tests/xb_test_sql.cpp +++ b/src/tests/xb_test_sql.cpp @@ -57,11 +57,12 @@ int main( int argCnt, char **av ) { "",0,0,0 } }; - above structure below, depending on how table is created + above structure corresponds to sql below sSql = "CREATE TABLE Address.DBF ( ADDRESS CHAR(30), CITY CHAR(30), STATE CHAR(2), ZIPCODE NUMERIC(9,0), NOTES VARCHAR, LASTUPDATE DATE, ACTIVE LOGICAL )"; */ +/* xbSchema MyZipRecord[] = { { "ZIPCODE", XB_NUMERIC_FLD, 9, 0 }, @@ -69,7 +70,7 @@ int main( int argCnt, char **av ) { "STATE", XB_CHAR_FLD, 2, 0 }, { "",0,0,0 } }; - +*/ xbXBase x; #ifdef XB_LOGGING_SUPPORT @@ -85,54 +86,32 @@ int main( int argCnt, char **av ) x.SetDataDirectory( PROJECT_DATA_DIR ); - InitTime(); xbSql sql( &x ); if( po > 0 ) std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; - - xbDbf4 SqlDbf( &x ); // version 4 dbf file - xbDbf4 SqlDbfZ( &x ); // version 4 dbf file - - - - - - sSql = "DROP TABLE IF EXISTS AddressR.DBF"; +/* + // verify a delete on a non existant table doesn't crash things + sSql = "DROP TABLE IF EXISTS NoTable.DBF"; iRc2 = sql.ExecuteNonQuery( sSql ); iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR ); if( iRc2 ) x.DisplayError( iRc2 ); - - sSql = "DROP TABLE IF EXISTS Address.DBF"; iRc2 = sql.ExecuteNonQuery( sSql ); iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR ); if( iRc2 ) x.DisplayError( iRc2 ); - - sSql = "CREATE TABLE Address.DBF ( ADDRESS CHAR(30), CITY CHAR(30), STATE CHAR(2), ZIPCODE NUMERIC(9,0), NOTES VARCHAR, LASTUPDATE DATE, ACTIVE LOGICAL )"; - iRc2 = sql.ExecuteNonQuery( sSql ); iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); if( iRc2 ) x.DisplayError( iRc2 ); -/* - non sql way to create a table - iRc2 = SqlDbf.CreateTable( "Address.DBF", "Address", MyAddressRecord, XB_OVERLAY, XB_MULTI_USER ); - iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); - if( iRc2 ) - x.DisplayError( iRc2 ); -*/ - -// return 0; - #ifdef XB_MDX_SUPPORT sSql = "CREATE INDEX tag1 ON Address.DBF( CITY, STATE, DTOS( LASTUPDATE )) FILTER .NOT. DELETED()"; // xbString sSql = "CREATE INDEX tag1 ON Address.DBF( CITY, STATE )"; @@ -155,8 +134,13 @@ int main( int argCnt, char **av ) if( iRc2 ) x.DisplayError( iRc2 ); - sSql = "ALTER TABLE Address.DBF RENAME TO AddressR.DBF"; + sSql = "DROP TABLE IF EXISTS AddressR.DBF"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); + sSql = "ALTER TABLE Address.DBF RENAME TO AddressR.DBF"; iRc2 = sql.ExecuteNonQuery( sSql ); iRc += TestMethod( po, "SqlAlterTable()", (xbInt32) iRc2, XB_NO_ERROR ); if( iRc2 ) @@ -186,64 +170,91 @@ int main( int argCnt, char **av ) if( iRc2 ) x.DisplayError( iRc2 ); + sSql = "DELETE FROM AddressR.DBF WHERE BAD='EXPRESSION'"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_INVALID_FIELD_NAME ); - iRc2 = SqlDbfZ.CreateTable( "ZipCode.DBF", "", MyZipRecord, XB_OVERLAY, XB_MULTI_USER ); - iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + + sSql = "DROP TABLE IF EXISTS AddressR.DBF"; + iRc += TestMethod( po, "Drop Table()", sql.ExecuteNonQuery( sSql ), XB_NO_ERROR ); + iRc += TestMethod( po, "Drop Table()", sql.ExecuteNonQuery( sSql ), XB_NO_ERROR ); + +*/ + + + sSql = "DROP TABLE IF EXISTS ZipCode.DBF"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR ); if( iRc2 ) x.DisplayError( iRc2 ); -// sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )"; - + sSql = "CREATE TABLE ZipCode.DBF ( ZIPCODE NUMERIC(9,0), CITY CHAR(30), STATE CHAR(2) )"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); -// std::cout << "---------------------------------------------------------\n"; -// std::cout << sSql.Str() << "\n"; + sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )"; + iRc2 = sql.ExecuteNonQuery( sSql ); - sSql = "INSERT INTO ZipCode ( CITY ) VALUES ( 'city' )"; + sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )"; iRc2 = sql.ExecuteNonQuery( sSql ); - iRc += TestMethod( po, "SqlInsert()", (xbInt32) iRc2, XB_NO_ERROR ); + +/* + + sSql = "CREATE INDEX ZipCode1.NDX ON ZipCode.DBF( ZIPCODE ) ASSOCIATE"; + iRc2 = sql.ExecuteNonQuery( sSql ); if( iRc2 ) x.DisplayError( iRc2 ); + iRc += TestMethod( po, "Create Index()", (xbInt32) iRc2, XB_NO_ERROR ); +*/ + sSql = "CREATE UNIQUE INDEX ZipCode2.NDX ON ZipCode.DBF( ZIPCODE ) ASSOCIATE"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "Create Index()", (xbInt32) iRc2, XB_KEY_NOT_UNIQUE ); + -//**************** work in progress /* - sSql = "DELETE FROM AddressR.DBF WHERE BAD='EXPRESSION'"; + sSql = "DROP TABLE IF EXISTS ZipCode.DBF"; iRc2 = sql.ExecuteNonQuery( sSql ); - iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_INVALID_FIELD_NAME ); + iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); */ -// if( iRc2 ) -// x.DisplayError( iRc2 ); +// sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )"; +// iRc2 = sql.ExecuteNonQuery( sSql ); + + + x.DisplayTableList(); - iRc += TestMethod( po, "Close()", SqlDbf.Close(), XB_NO_ERROR ); - iRc += TestMethod( po, "Close()", SqlDbfZ.Close(), XB_NO_ERROR ); -// return 0; -// std::cout << "---------------------------------------------------------\n"; +/* + sSql = "CREATE INDEX ZipCode.NDX ON Addres.DBF( ZIPCODE )"; - xbStmt sqlQry1( &x ); -// sSql = "SELECT CITY, STATE, ZIPCODE FROM Address.DBF T LEFT JOIN LJ.DBF LJ WHERE CITY IS NOT NULL ORDER BY 2 GROUP BY STATE HAVING ZIPCODE .NOT. NULL"; -// sSql = "SELECT CITY, STATE, ZIPCODE FROM AddressR.DBF T WHERE CITY IS NOT NULL ORDER BY 2 GROUP BY STATE HAVING ZIPCODE .NOT. NULL"; + iRc2 = sql.ExecuteNonQuery( sSql ); + iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR ); + if( iRc2 ) + x.DisplayError( iRc2 ); +*/ -// sSql = "SELECT CITY, STATE, ZIPCODE FROM AddressR A LEFT JOIN ZipCode Z ON A.ZIPCODE = Z.ZIPCODE WHERE CITY IS NOT NULL ORDER BY 2 GROUP BY STATE HAVING ZIPCODE .NOT. NULL"; -// sSql = "SELECT M.ID, M.LEFTFK0, L0.CFLD FROM MAIN0 M LEFT JOIN LEFT0 L0 ON M.LEFTFK0 = L0.LEFTFK0 WHERE M.ID IS NOT NULL"; - iRc += TestMethod( po, "Select()", sqlQry1.ExecuteQuery( sSql ), XB_NO_ERROR ); +// sSql = "DROP TABLE IF EXISTS AddressR.DBF"; +// iRc += TestMethod( po, "Drop Table()", sqlQry1.ExecuteQuery( sSql ), XB_NO_ERROR ); - sqlQry1.DumpStmtInternals(); - // sqlQry1.Test(); - x.DisplayTableList(); + // x.DisplayTableList(); + x.CloseAllTables(); if( po > 0 || iRc < 0 ) fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); diff --git a/src/tests/xb_test_string.cpp b/src/tests/xb_test_string.cpp index 2f01e51..9dd48af 100755 --- a/src/tests/xb_test_string.cpp +++ b/src/tests/xb_test_string.cpp @@ -188,6 +188,19 @@ int main( int argCnt, char **av = NULL ) s2 = "!@#"; rc += TestMethod( po, "s1.Append( '!@#' )", s1.Append( s2 ), "ABCDEFGZ999!@#", 14 ); + xbString sAppend1; + sAppend1.Append( "abc123", 6 ); + rc += TestMethod( po, "Append", sAppend1, "abc123", 6 ); + + xbString sAppend2; + sAppend2.Append( "abc123", 8 ); + rc += TestMethod( po, "Append", sAppend2, "abc123", 6 ); + + xbString sAppend3; + sAppend3.Append( "abc123", 3 ); + rc += TestMethod( po, "Append", sAppend3, "abc", 3 ); + + rc += TestMethod( po, "s1.Assign( 'ABCDE', 3, 2 )", s1.Assign( "ABCDE", 3, 2 ), "CD", 2 ); rc += TestMethod( po, "s1.Assign( 'ABCDE', 2, 7 )", s1.Assign( "ABCDE", 2, 7 ), "BCDE", 4 ); rc += TestMethod( po, "s1.Assign( 'ABCDE', 1, 4 )", s1.Assign( "ABCDE", 1, 4 ), "ABCD", 4 ); @@ -222,10 +235,24 @@ int main( int argCnt, char **av = NULL ) s1.CvtHexString( s2 ); rc += TestMethod( po, "CvtHexString() ", s2, "abcde", 5 ); - s1 = "123"; - s2 = "ABC"; - rc += TestMethod( po, "HasAlphaChars()", s1.HasAlphaChars(), 0 ); - rc += TestMethod( po, "HasAlphaChars()", s2.HasAlphaChars(), 1 ); + s1.ExtractElement( "aaaa|bbbb|cccc|dddd", '|', 2, 0 ); + rc += TestMethod( po, "ExtractElement() ", s1, "bbbb", 4 ); + + s1.ExtractElement( "aaaa|b'bb|c'ccc|dddd", '|', 3, 1 ); + rc += TestMethod( po, "ExtractElement() ", s1, "dddd", 4 ); + + s1 = "aaaa|bbbb|cccc|dddd"; + s2.ExtractElement( s1, '|', 2, 0 ); + rc += TestMethod( po, "ExtractElement() ", s2, "bbbb", 4 ); + + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'b' ), 8 ); + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'x' ), 9 ); + s1 = "abcabcabx"; + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( '$' ), 0 ); + rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( "ab" ), 7 ); + s1 = "\\ABC\\XYZ"; rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '\\' ); @@ -233,14 +260,36 @@ int main( int argCnt, char **av = NULL ) s1 = "/ABC/XYZ"; rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '/' ); + + s1 = "123"; + s2 = "ABC"; + rc += TestMethod( po, "HasAlphaChars()", s1.HasAlphaChars(), 0 ); + rc += TestMethod( po, "HasAlphaChars()", s2.HasAlphaChars(), 1 ); + + s2 = ""; rc += TestMethod( po, "IsEmpty()", s2.IsEmpty(), 1 ); + + s1.SetNum( (long) 123456 ); + + s1 = "abcZZZ123"; + s1.Left( 4 ); + rc += TestMethod( po, "Left(4) ", s1, "abcZ", 4 ); + + s1.Left( 1 ); + rc += TestMethod( po, "Left(1) ", s1, "a", 1 ); + + s1.Left( 0 ); + rc += TestMethod( po, "Left(0) ", s1, "", 0 ); + // trim tests s1 = " ABC "; rc += TestMethod( po, "Ltrim()", s1.Ltrim(), "ABC ", 6 ); + s1 = " ABC "; rc += TestMethod( po, "Rtrim()", s1.Rtrim(), " ABC", 6 ); + s1 = " ABC "; rc += TestMethod( po, "Trim() ", s1.Trim(), "ABC", 3 ); @@ -253,6 +302,20 @@ int main( int argCnt, char **av = NULL ) s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; rc += TestMethod( po, "Mid(4,5) ", s1.Mid(4,5), "DEFGH", 5 ); + s1 = "123"; + s1.PadLeft( '0', 9 ); + rc += TestMethod( po, "PadLeft() ", s1, "000000123", 9 ); + + s1 = "abc"; + s1.PadRight( 'Z', 9 ); + rc += TestMethod( po, "PadRight() ", s1, "abcZZZZZZ", 9 ); + + s1.PadRight( 'Z', 4 ); + rc += TestMethod( po, "PadRight() ", s1, "abcZZZZZZ", 9 ); + + + + s1 = "DEFGHI"; rc += TestMethod( po, "Pos('G') ", (xbInt32) s1.Pos( 'G' ), 4 ); rc += TestMethod( po, "Pos(\"EFG\") ", (xbInt32) s1.Pos( "EFG" ), 2 ); @@ -269,48 +332,32 @@ int main( int argCnt, char **av = NULL ) s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; rc += TestMethod( po, "Remove(3,5) ", s1.Remove( 3, 5 ), "ABHIJKLMNOPQRSTUVWXYZ", 21 ); + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + rc += TestMethod( po, "Remove(22,5) ", s1.Remove( 22, 5 ), "ABCDEFGHIJKLMNOPQRSTU", 21 ); + s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + rc += TestMethod( po, "Remove(24,5) ", s1.Remove( 24, 5 ), "ABCDEFGHIJKLMNOPQRSTUVW", 23 ); - s1 = "ABCABCABZ"; - s1.SwapChars( 'A', '9' ); - rc += TestMethod( po, "SwapChars() ", s1, "9BC9BC9BZ", 9 ); - - s1.ToLowerCase(); - rc += TestMethod( po, "ToLowerCase() ", s1, "9bc9bc9bz", 9 ); - - s1.ToUpperCase(); - rc += TestMethod( po, "ToUpperCase() ", s1, "9BC9BC9BZ", 9 ); - - s1.ZapChar( '9' ); - rc += TestMethod( po, "ZapChar('9') ", s1, "BCBCBZ", 6 ); - - s1.ZapLeadingChar( 'B' ); - rc += TestMethod( po, "ZapLeadingChar('B') ", s1, "CBCBZ", 5 ); - - s1.ZapTrailingChar( 'Z' ); - rc += TestMethod( po, "ZapLeadingChar('B') ", s1, "CBCB", 4 ); - - s1.ExtractElement( "aaaa|bbbb|cccc|dddd", '|', 2, 0 ); - rc += TestMethod( po, "ExtractElement() ", s1, "bbbb", 4 ); - - s1.ExtractElement( "aaaa|b'bb|c'ccc|dddd", '|', 3, 1 ); - rc += TestMethod( po, "ExtractElement() ", s1, "dddd", 4 ); - - s1 = "123"; - s1.PadLeft( '0', 9 ); - rc += TestMethod( po, "PadLeft() ", s1, "000000123", 9 ); - - s1 = "abc"; - s1.PadRight( 'Z', 9 ); - rc += TestMethod( po, "PadRight() ", s1, "abcZZZZZZ", 9 ); + s1.Set( "abcdef.dbf" ); + s1.Replace( "def", "DEF" ); + rc += TestMethod( po, "Replace", s1, "abcDEF.dbf", 10 ); + s1.Replace( ".dbf", ".DBF" ); + rc += TestMethod( po, "Replace", s1, "abcDEF.DBF", 10 ); + s1.Set( "abcdef.dbf" ); + s1.Replace( "def", "DEFG" ); + rc += TestMethod( po, "Replace", s1, "abcDEFG.dbf", 11 ); + s1.Set( "abcdefdef.dbf" ); + s1.Replace( "def", "DEFG" ); + rc += TestMethod( po, "Replace", s1, "abcDEFGDEFG.dbf", 15 ); - s1.Left( 4 ); - rc += TestMethod( po, "Left(4) ", s1, "abcZ", 4 ); - s1.Left( 1 ); - rc += TestMethod( po, "Left(1) ", s1, "a", 1 ); + s1.SetNum( (long) 123456 ); + rc += TestMethod( po, "SetNum() ", s1, "123456", 6 ); - s1.Left( 0 ); - rc += TestMethod( po, "Left(0) ", s1, "", 0 ); + xbFloat f = (xbFloat) 12.35; + // %f format varies depending on compiler + s1.Sprintf( "%6.2f", f ); + s1.Ltrim(); + rc += TestMethod( po, "s1.Sprintf()/s.Trim()", s1, "12.35", 5 ); char buf[5]; buf[0] = 'W'; @@ -319,18 +366,20 @@ int main( int argCnt, char **av = NULL ) buf[3] = 'Z'; buf[4] = 0x00; xbInt32 l = 1234567; - xbFloat f = (xbFloat) 12.35; s2 = "test string"; rc += TestMethod( po, "s1.Sprintf()", s1.Sprintf( "%s %d %s %ld", buf, 12, s2.Str(), l ), "WXYZ 12 test string 1234567", 27 ); - // %f format varies depending on compiler - s1.Sprintf( "%6.2f", f ); - s1.Ltrim(); - rc += TestMethod( po, "s1.Sprintf()/s.Trim()", s1, "12.35", 5 ); - s1.SetNum( (long) 123456 ); - rc += TestMethod( po, "SetNum() ", s1, "123456", 6 ); + s1 = "ABCABCABZ"; + s1.SwapChars( 'A', '9' ); + rc += TestMethod( po, "SwapChars() ", s1, "9BC9BC9BZ", 9 ); + + s1.ToLowerCase(); + rc += TestMethod( po, "ToLowerCase() ", s1, "9bc9bc9bz", 9 ); + + s1.ToUpperCase(); + rc += TestMethod( po, "ToUpperCase() ", s1, "9BC9BC9BZ", 9 ); s1 = "T"; rc += TestMethod( po, "ValidLogicalValue", s1.ValidLogicalValue(), 1 ); @@ -344,13 +393,19 @@ int main( int argCnt, char **av = NULL ) s1 = "ABC-123456.89"; rc += TestMethod( po, "ValidNumericValue", s1.ValidNumericValue(), 0 ); - s1 = "abcabcabx"; - rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'b' ), 8 ); - s1 = "abcabcabx"; - rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( 'x' ), 9 ); - s1 = "abcabcabx"; - rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( '$' ), 0 ); - rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( "ab" ), 7 ); + + s1 = "BC9BC99BZ"; + s1.ZapChar( '9' ); + rc += TestMethod( po, "ZapChar('9') ", s1, "BCBCBZ", 6 ); + + s1.ZapLeadingChar( 'B' ); + rc += TestMethod( po, "ZapLeadingChar('B') ", s1, "CBCBZ", 5 ); + + s1.ZapTrailingChar( 'Z' ); + rc += TestMethod( po, "ZapTrailingChar('Z') ", s1, "CBCB", 4 ); + s1.ZapTrailingChar( 'Z' ); + rc += TestMethod( po, "ZapTrailingChar('Z') ", s1, "CBCB", 4 ); + s1 = ")"; rc += TestMethod( po, "GetLastPos", (xbInt16) s1.GetLastPos( ')' ), 1 ); @@ -385,17 +440,12 @@ int main( int argCnt, char **av = NULL ) s3.Sprintf( "%s and %s", s1.Str(), s2.Str()); rc += TestMethod( po, "Sprintf", s3, "string 1 and string 2.0", 23 ); - s1.Set( "abcdef.dbf" ); - s1.Replace( "def", "DEF" ); - rc += TestMethod( po, "Replace", s1, "abcDEF.dbf", 10 ); - s1.Replace( ".dbf", ".DBF" ); - rc += TestMethod( po, "Replace", s1, "abcDEF.DBF", 10 ); - +/* xbInt16 iErrorStop = 10; xbInt16 iRc = -100; sMsg.Sprintf( "class::method() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, s3.Str() ); - +*/ if( po > 0 || rc < 0 ) fprintf( stdout, "Total Errors = %d\n", rc * -1 ); diff --git a/src/tests/xb_test_tblmgr.cpp b/src/tests/xb_test_tblmgr.cpp index d048594..4e177d0 100755 --- a/src/tests/xb_test_tblmgr.cpp +++ b/src/tests/xb_test_tblmgr.cpp @@ -96,39 +96,6 @@ int main( int argCnt, char **av ) } -std::cout << "cp0\n"; - x.SetDataDirectory( "/ABCDEFG/" ); - - #ifdef WIN32 - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); - #else - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); - #endif -std::cout << "cp1\n"; - x.SetDataDirectory( "/ABCDEFG" ); - #ifdef WIN32 - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); - #else - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); - #endif - - - x.SetDataDirectory( "\\ABCDEFG\\"); - #ifdef WIN32 - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); - #else - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); - #endif - - x.SetDataDirectory( "\\ABCDEFG" ); - #ifdef WIN32 - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); - #else - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); - #endif - - x.SetDataDirectory( "ABCDEFG" ); - iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "ABCDEFG", 7 ); iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), 0 ); iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), XB_NOT_FOUND ); diff --git a/src/tests/xb_test_tdx.cpp b/src/tests/xb_test_tdx.cpp new file mode 100755 index 0000000..5d79f04 --- /dev/null +++ b/src/tests/xb_test_tdx.cpp @@ -0,0 +1,162 @@ +/* xb_test_tdx.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 program tests the class xbIxNdx + +// usage: xb_test_ndx QUITE|NORMAL|VERBOSE + + +#include "xbase.h" + +using namespace xb; + +#include "tstfuncs.cpp" + +int main( int argCnt, char **av ) +{ + int iRc = 0; + int iRc2; + int iPo = 1; /* print option */ + /* 0 - QUIET */ + /* 1 - NORMAL */ + /* 2 - VERBOSE */ + + xbString sMsg; + char c; + xbString s; + + if( argCnt > 1 ) { + if( av[1][0] == 'Q' ) + iPo = 0; + else if( av[1][0] == 'V' ) + iPo = 2; + } + + + xbSchema MyV4Record[] = + { + { "CITY", XB_CHAR_FLD, 100, 0 }, + { "STATE", XB_CHAR_FLD, 2, 0 }, + { "ZIP", XB_NUMERIC_FLD, 9, 0 }, + { "DATE1", XB_DATE_FLD, 8, 0 }, + { "",0,0,0 } + }; + + + + xbXBase x; + + #ifdef XB_LOGGING_SUPPORT + x.SetLogDirectory( PROJECT_LOG_DIR ); + x.EnableMsgLogging(); + if( iPo ){ + std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl; + } + sMsg.Sprintf( "Program [%s] initializing...", av[0] ); + x.WriteLogMessage( sMsg ); + #endif + + x.SetDataDirectory( PROJECT_DATA_DIR ); + x.SetTempDirectory( PROJECT_TEMP_DIR ); + x.SetMultiUser( xbFalse ); + InitTime(); + + if( iPo > 0 ) + std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl; + + + xbFile f( &x ); + xbIx *pIx; + void *pTag; + xbDate dt = "19890209"; + xbString sKey; + + xbDbf *V4DbfX1 = new xbDbf4( &x ); + + + iRc2 = V4DbfX1->CreateTable( "TTDXDB01.DBF", "TestMdxX2", MyV4Record, XB_OVERLAY, XB_MULTI_USER ); + iRc += TestMethod( iPo, "CreateTable(1)", iRc2, 0 ); + + iRc2 = V4DbfX1->CreateTag( "TDX", "CITY_TAGA", "CITY", "", 0, 0, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(1)", iRc2, 0 ); + + std::cout << "Cur Tag Name = " << V4DbfX1->GetCurTagName().Str() << "\n"; + + xbString sFqFileName = pIx->GetFqFileName(); + iRc2 = V4DbfX1->DeleteTag( "TDX", V4DbfX1->GetCurTagName() ); + + iRc += TestMethod( iPo, "DeleteTag(3)", iRc2, 0 ); + + // only one tag, file should not exist anymore + iRc2 = pIx->FileExists(sFqFileName); + iRc += TestMethod( iPo, "FileExists()", iRc2, 0 ); + + iRc2 = V4DbfX1->CreateTag( "TDX", "ZIP_TAG", "ZIP", "", xbTrue, 0, XB_OVERLAY, &pIx, &pTag ); + iRc += TestMethod( iPo, "CreateTag(2)", iRc2, 0 ); + + xbInt32 uZip = 10000; + for( xbUInt16 i = 0; i < 5; i++ ){ + for( xbUInt16 j = 0; j < 4; j++ ){ + + c = j + 65; + s = c; + s.PadRight( c, (xbUInt32) i + 1 ); + //std::cout << "*********** adding s=[" << s.Str() << "] length = " << s.Len() << "\n"; + iRc2 = V4DbfX1->BlankRecord(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "BlankRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutField( "CITY", s ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutLongField( "ZIP", uZip++ ); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->PutDateField( "DATE1", dt ); + dt++; + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->AppendRecord(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "AppendRecord()", iRc2, XB_NO_ERROR ); + + iRc2 = V4DbfX1->Commit(); + if( iRc2 != XB_NO_ERROR ) + iRc += TestMethod( iPo, "Commit()", iRc2, XB_NO_ERROR ); + } + } + + + iRc += TestMethod( iPo, "CheckTagIntegrity(1)", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR ); + + + x.CloseAllTables(); + +// delete V4DbfX1; +// delete V4DbfX2; + + if( iPo > 0 || iRc < 0 ) + fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); + +#ifdef XB_LOGGING_SUPPORT + sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 ); + x.WriteLogMessage( sMsg ); +#endif + + return iRc; +} diff --git a/src/tests/xb_test_xbase.cpp b/src/tests/xb_test_xbase.cpp index 4751c25..a0affd6 100755 --- a/src/tests/xb_test_xbase.cpp +++ b/src/tests/xb_test_xbase.cpp @@ -90,6 +90,119 @@ int main( int argCnt, char **av ) std::cout << "DisplayError Test ==> "; x.DisplayError( 0 ); } + + #ifdef WIN32 + char cSep = '\\'; + iRc += TestMethod( iPo, "GetPathSeparator()", x.GetPathSeparator(), cSep ); + #else + char cSep = '/'; + iRc += TestMethod( iPo, "GetPathSeparator()", x.GetPathSeparator(), cSep ); + #endif + + + + xbString sDir = "\\dir\\path"; + xbString sFile = "myfile"; + xbString sExt = "DBF"; + xbString sFqn; + iRc += TestMethod( iPo, "CreateFQN()", x.CreateFqn( sDir, sFile, sExt, sFqn ), XB_NO_ERROR ); + //std::cout << "FQN = [" << sFqn.Str() << "]\n"; + #ifdef WIN32 + iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "\\dir\\path\\myfile.DBF", 20 ); + #else + iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "/dir/path/myfile.DBF", 20 ); + #endif + + sDir = "/dir/path"; + iRc += TestMethod( iPo, "CreateFQN()", x.CreateFqn( sDir, sFile, sExt, sFqn ), XB_NO_ERROR ); + #ifdef WIN32 + iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "\\dir\\path\\myfile.DBF", 20 ); + #else + iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "/dir/path/myfile.DBF", 20 ); + #endif + + + x.SetDataDirectory( "/ABCDEFG/" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); + #endif + + x.SetDataDirectory( "/ABCDEFG" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); + #else + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); + #endif + + x.SetDataDirectory( "\\ABCDEFG\\"); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 ); + #endif + + x.SetDataDirectory( "\\ABCDEFG" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 ); + #else + iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 ); + #endif + +#ifdef XB_LOGGING_SUPPORT + + x.SetLogDirectory( "ABCDEFG" ); + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "ABCDEFG", 7 ); + x.SetLogDirectory( "/ABCDEFG/" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG/", 9 ); + #endif + + x.SetLogDirectory( "/ABCDEFG" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG", 8 ); + #else + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG", 8 ); + #endif + + x.SetLogDirectory( "\\ABCDEFG\\"); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG/", 9 ); + #endif + + x.SetLogFileName( "LogFileNameTest" ); + iRc += TestMethod( iPo, "Set/GetLogFileName()", x.GetLogFileName(), "LogFileNameTest", 15 ); +#endif + + x.SetTempDirectory( "/ABCDEFG/" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG/", 9 ); + #endif + + x.SetTempDirectory( "/ABCDEFG" ); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG", 8 ); + #else + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG", 8 ); + #endif + + x.SetTempDirectory( "\\ABCDEFG\\"); + #ifdef WIN32 + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG\\", 9 ); + #else + iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG/", 9 ); + #endif + + +// std::cout << "path separator = [" << x.GetPathSeparator() << "]\n"; + if( iPo > 0 || iRc < 0 ) fprintf( stdout, "Total Errors = %d\n", iRc * -1 ); diff --git a/src/utils/xb_cfg_check.cpp b/src/utils/xb_cfg_check.cpp index 5489c51..c979bac 100755 --- a/src/utils/xb_cfg_check.cpp +++ b/src/utils/xb_cfg_check.cpp @@ -152,6 +152,13 @@ int main() fprintf( stdout, "XB_MDX_SUPPORT = [OFF]\n" ); #endif + #ifdef XB_TDX_SUPPORT + fprintf( stdout, "XB_TDX_SUPPORT = [ON]\n" ); + #else + fprintf( stdout, "XB_TDX_SUPPORT = [OFF]\n" ); + #endif + + #ifdef XB_SQL_SUPPORT fprintf( stdout, "XB_SQL_SUPPORT = [ON]\n" ); #else @@ -183,6 +190,7 @@ int main() fprintf( stdout, "PROJECT_BINARY_DIR = [%s]\n", PROJECT_BINARY_DIR ); fprintf( stdout, "PROJECT_DATA_DIR = [%s]\n", PROJECT_DATA_DIR ); fprintf( stdout, "PROJECT_LOG_DIR = [%s]\n", PROJECT_LOG_DIR ); + fprintf( stdout, "PROJECT_TEMP_DIR = [%s]\n", PROJECT_TEMP_DIR ); fprintf( stdout, "PROJECT_DFLT_LOGFILE = [%s]\n", PROJECT_DFLT_LOGFILE ); fprintf( stdout, "PROJECT_SOURCE_DIR = [%s]\n", PROJECT_SOURCE_DIR ); fprintf( stdout, "CMAKE_RUNTIME_OUTPUT_DIRECTORY = [%s]\n\n", CMAKE_RUNTIME_OUTPUT_DIRECTORY ); @@ -191,34 +199,33 @@ int main() fprintf( stdout, "BUILD_SHARED_LIBS = [%s]\n", BUILD_SHARED_LIBS ); fprintf( stdout, "EXTRA_LIBS = [%s]\n\n", EXTRA_LIBS ); - fprintf( stdout, "Field sizes:\n" ); - fprintf( stdout, "SIZEOF_VOID_P = [%s]\n", CMAKE_SIZEOF_VOID_P ); - fprintf( stdout, "sizeof(char *) = [%zd]\n", sizeof( char *)); - fprintf( stdout, "sizeof(int) = [%zd]\n", sizeof( int )); - fprintf( stdout, "sizeof(long) = [%zd]\n", sizeof( long )); - fprintf( stdout, "sizeof(char) = [%zd]\n", sizeof( char )); + + fprintf( stdout, "sizeof(char *) = [%d]\n", (xbInt32) sizeof( char *)); + fprintf( stdout, "sizeof(int) = [%d]\n", (xbInt32) sizeof( int )); + fprintf( stdout, "sizeof(long) = [%d]\n", (xbInt32) sizeof( long )); + fprintf( stdout, "sizeof(char) = [%d]\n", (xbInt32) sizeof( char )); #ifdef HAVE_WCHAR_H - fprintf( stdout, "sizeof(wchar_t) = [%zd]\n", sizeof( wchar_t )); + fprintf( stdout, "sizeof(wchar_t) = [%d]\n", (xbInt32) sizeof( wchar_t )); #endif #ifdef HAVE_WINDOWS_H - fprintf( stdout, "sizeof(DWORD) = [%zd]\n", sizeof( DWORD )); - #endif - - fprintf( stdout, "sizeof(double) = [%zd]\n", sizeof( double )); - fprintf( stdout, "sizeof(float) = [%zd]\n", sizeof( float )); - fprintf( stdout, "sizeof(size_t) = [%zd]\n", sizeof( size_t )); - fprintf( stdout, "sizeof(off_t) = [%zd]\n\n", sizeof( off_t )); - - fprintf( stdout, "sizeof(xbBool) = [%zd]\n", sizeof( xbBool )); - fprintf( stdout, "sizeof(xbInt16) = [%zd]\n", sizeof( xbInt16 )); - fprintf( stdout, "sizeof(xbUInt16) = [%zd]\n", sizeof( xbUInt16 )); - fprintf( stdout, "sizeof(xbInt32) = [%zd]\n", sizeof( xbInt32 )); - fprintf( stdout, "sizeof(xbUInt32) = [%zd]\n", sizeof( xbUInt32 )); - fprintf( stdout, "sizeof(xbInt64) = [%zd]\n", sizeof( xbInt64 )); - fprintf( stdout, "sizeof(xbUInt64) = [%zd]\n", sizeof( xbUInt64 )); - fprintf( stdout, "sizeof(xbFloat) = [%zu]\n", sizeof( xbFloat )); - fprintf( stdout, "sizeof(xbDouble) = [%zu]\n", sizeof( xbDouble )); + fprintf( stdout, "sizeof(DWORD) = [%d]\n", (xbInt32) sizeof( DWORD )); + #endif + + fprintf( stdout, "sizeof(double) = [%d]\n", (xbInt32) sizeof( double )); + fprintf( stdout, "sizeof(float) = [%d]\n", (xbInt32) sizeof( float )); + fprintf( stdout, "sizeof(size_t) = [%d]\n", (xbInt32) sizeof( size_t )); + fprintf( stdout, "sizeof(off_t) = [%d]\n\n", (xbInt32) sizeof( off_t )); + + fprintf( stdout, "sizeof(xbBool) = [%d]\n", (xbInt32) sizeof( xbBool )); + fprintf( stdout, "sizeof(xbInt16) = [%d]\n", (xbInt32) sizeof( xbInt16 )); + fprintf( stdout, "sizeof(xbUInt16) = [%d]\n", (xbInt32) sizeof( xbUInt16 )); + fprintf( stdout, "sizeof(xbInt32) = [%d]\n", (xbInt32) sizeof( xbInt32 )); + fprintf( stdout, "sizeof(xbUInt32) = [%d]\n", (xbInt32) sizeof( xbUInt32 )); + fprintf( stdout, "sizeof(xbInt64) = [%d]\n", (xbInt32) sizeof( xbInt64 )); + fprintf( stdout, "sizeof(xbUInt64) = [%d]\n", (xbInt32) sizeof( xbUInt64 )); + fprintf( stdout, "sizeof(xbFloat) = [%d]\n", (xbInt32) sizeof( xbFloat )); + fprintf( stdout, "sizeof(xbDouble) = [%d]\n", (xbInt32) sizeof( xbDouble )); fprintf( stdout, "\nHeader files:\n" ); diff --git a/src/utils/xb_dbfutil.cpp b/src/utils/xb_dbfutil.cpp index e50495e..76988c0 100755 --- a/src/utils/xb_dbfutil.cpp +++ b/src/utils/xb_dbfutil.cpp @@ -90,6 +90,7 @@ class xbUtil{ void UpdateTableAutoCommit(); void DisplayTableInfo(); void RenameTable(); + void DeleteTable(); // 4 - RecordMenu options void GetRecord(); @@ -776,7 +777,7 @@ void xbUtil::LockRecord(){ std::cin.getline( cBuf, 15 ); //iRc = dActiveTable->LockRecord( XB_LOCK, atol( cBuf )); iRc = dActiveTable->LockRecord( XB_LOCK, strtoul( cBuf, NULL, 0 )); - + x->DisplayError( iRc ); } @@ -1453,6 +1454,24 @@ void xbUtil::DisplayTableInfo(){ } /*************************************************************************************/ +void xbUtil::DeleteTable(){ + + if( !dActiveTable ) + dActiveTable = GetTablePtr( " - select table" ); + + if( !dActiveTable ){ + std::cout << "No table selected" << std::endl; + return; + } + + dActiveTable->DeleteTable(); + dActiveTable = NULL; + std::cout << "Table deleted.\n"; + +} + + +/*************************************************************************************/ void xbUtil::RenameTable(){ if( !dActiveTable ) @@ -1510,26 +1529,29 @@ void xbUtil::DisplayActiveTable() const{ if( dActiveTable ){ std::cout << "Active Table = [" << dActiveTable->GetTblAlias().Str() << "] "; xbUInt32 ulRecCnt = 0; - dActiveTable->ReadHeader( 0, 1 ); - dActiveTable->GetRecordCnt( ulRecCnt ); - std::cout << "Total Records = [" << ulRecCnt << "] "; - std::cout << "Current Record = [" << dActiveTable->GetCurRecNo() << "] "; - - if( dActiveTable->GetAutoCommit()) - std::cout << " Auto Commit = [Enabled]"; - else - std::cout << " Auto Commit = [Disabled]"; + xbInt16 iRc; + if(( iRc = dActiveTable->GetRecordCnt( ulRecCnt )) == XB_NO_ERROR ){ + std::cout << "Total Records = [" << ulRecCnt << "] "; + std::cout << "Current Record = [" << dActiveTable->GetCurRecNo() << "] "; - std::cout << std::endl; - #ifdef XB_INDEX_SUPPORT - xbIx *pIx = dActiveTable->GetCurIx(); - if( pIx ){ - void *vpTag = dActiveTable->GetCurTag(); - std::cout << "Active Tag = [" << pIx->GetTagName( vpTag ).Str() << "] Type = [" << dActiveTable->GetCurIxType().Str() << + if( dActiveTable->GetAutoCommit()) + std::cout << " Auto Commit = [Enabled]"; + else + std::cout << " Auto Commit = [Disabled]"; + + std::cout << std::endl; + #ifdef XB_INDEX_SUPPORT + xbIx *pIx = dActiveTable->GetCurIx(); + if( pIx ){ + void *vpTag = dActiveTable->GetCurTag(); + std::cout << "Active Tag = [" << pIx->GetTagName( vpTag ).Str() << "] Type = [" << dActiveTable->GetCurIxType().Str() << "] \tFile Name = [" << pIx->GetFileName().Str() << "] Key = [" << pIx->GetKeyExpression( vpTag ).Str() << "]" << std::endl; + } + #endif // XB_INDEX_SUPPORT + std::cout << std::endl; + } else { + x->DisplayError( iRc ); } - #endif // XB_INDEX_SUPPORT - std::cout << std::endl; } } /*************************************************************************************/ @@ -1716,7 +1738,7 @@ void xbUtil::DisplayTableStats(){ iOptionNo = 1; } if( iOptionNo < 5 ) { - d->ReadHeader( xbTrue, 0 ); + // d->ReadHeader( xbTrue, 0 ); moved to DumpHeader routine d->DumpHeader( iOptionNo ); } else { // DBMS settings @@ -2192,6 +2214,8 @@ void xbUtil::ProcessOption( const xbString &sOption ){ DisplayTableInfo(); else if( sOption == "=3.15" ) RenameTable(); + else if( sOption == "=3.16" ) + DeleteTable(); else if( sOption == "=4" ) RecordMenu(); else if( sOption == "=4.1" ) @@ -2824,7 +2848,7 @@ void xbUtil::FileMenu() std::cout << "13 - Update Table Auto Commit Setting" << std::endl; std::cout << "14 - Display Table Info" << std::endl; std::cout << "15 - Rename Table" << std::endl; - + std::cout << "16 - Delete Table" << std::endl; std::cout << "99 - Exit Menu" << std::endl; option = GetOption(); @@ -2833,7 +2857,7 @@ void xbUtil::FileMenu() case 0: break; case 1: ListFilesInDataDirectory(); break; case 2: UpdateDataDirectory(); break; - case 3: x->DisplayTableList(); break; + case 3: x->DisplayTableList(); break; case 4: Open(); break; case 5: Close(); break; case 6: CloseAllTables(); break; @@ -2845,13 +2869,13 @@ void xbUtil::FileMenu() case 13: UpdateTableAutoCommit(); break; case 14: DisplayTableInfo(); break; case 15: RenameTable(); break; + case 16: DeleteTable(); break; case 99: break; default: std::cout << "Invalid Option" << std::endl; } } } - /************************************************************************/ #ifdef XB_NDXINF_SUPPORT void xbUtil::InfFileMenu() @@ -3391,7 +3415,7 @@ void xbUtil::FindKey(){ default: std::cout << "Unknown key type [" << cKeyType << "]" << std::endl; return; - break; + // break; } char cBuf[128]; @@ -3457,8 +3481,9 @@ void xbUtil::Reindex(){ std::cout << "Tag not selected" << std::endl; return; } - void *vpCurTag = dActiveTable->GetCurTag(); - xbInt16 iRc = pIx->Reindex( &vpCurTag ); + //void *vpCurTag = dActiveTable->GetCurTag(); + //xbInt16 iRc = pIx->Reindex( &vpCurTag ); + xbInt16 iRc = dActiveTable->Reindex( 0 ); x->DisplayError( iRc ); } diff --git a/src/utils/xb_execsql.cpp b/src/utils/xb_execsql.cpp index daa6b6e..3441e91 100755 --- a/src/utils/xb_execsql.cpp +++ b/src/utils/xb_execsql.cpp @@ -15,12 +15,21 @@ Email Contact: */ #include <xbase.h> - using namespace xb; -xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ); -xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ) -{ +void PrintHelp(); +void PrintHelp(){ + std::cout << "Usage: xb_execsql [-h] [-?] [--help] [-v] [--version] -i filename.SQL -q --quiet" << std::endl << std::endl; + std::cout << "This program processes sql commands from input file 'filename.SQL'" << std::endl << std::endl; +} +void PrintVersion(); +void PrintVersion(){ + std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl; +} + + +xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd, xbBool bQuiet ); +xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd, xbBool bQuiet ){ sCmd = ""; xbString sLine; xbInt16 iRc = XB_NO_ERROR; @@ -31,6 +40,12 @@ xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ) bDone = xbTrue; } else { + if( !bQuiet ){ + std::cout << sLine; + if( sLine.Pos( 0x0a ) == 0 ) + std::cout << std::endl; + } + // don't need CR/LF chars sLine.ZapChar( 0x0a ); sLine.ZapChar( 0x0d ); @@ -50,23 +65,43 @@ xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd ) return iRc; } -int main(int ac,char** av) + +int main(int argc, char* argv[]) { + xbXBase x; + xbSql sql( &x ); + xbFile f( sql.GetXbasePtr() ); + xbInt16 iRc = XB_NO_ERROR; + xbString sFileName = ""; + xbString sSqlLine = ""; + xbString sParm = ""; + xbBool bQuiet = xbFalse; + + x.EnableMsgLogging(); - if (ac <= 1) { - std::cout << "Usage: xb_execsql filename..." << std::endl; + if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) || + x.GetCmdLineOpt( argc, argv, "-?", sParm ) || + x.GetCmdLineOpt( argc, argv, "--help", sParm )){ + PrintHelp(); return 1; } - xbXBase x; - x.EnableMsgLogging(); - xbSql sql( &x ); - xbFile f( sql.GetXbasePtr() ); - xbInt16 iRc = XB_NO_ERROR; - xbString sFileName; - xbString sSqlLine; + if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) || + x.GetCmdLineOpt( argc, argv, "--version", sParm )){ + PrintVersion(); + return 1; + } + + if ( x.GetCmdLineOpt( argc, argv, "-q", sParm ) || + x.GetCmdLineOpt( argc, argv, "--quiet", sParm )){ + bQuiet = xbTrue; + } + + if( !x.GetCmdLineOpt( argc, argv, "-i", sFileName ) || sFileName == "" ){ + PrintHelp(); + return 1; + } - sFileName = av[1]; if(( iRc = f.xbFopen( "r", sFileName, XB_SINGLE_USER )) != XB_NO_ERROR ){ xbString sMsg; @@ -77,12 +112,12 @@ int main(int ac,char** av) } while( iRc == XB_NO_ERROR ){ - iRc = GetNextSqlCmd( f, sSqlLine ); + iRc = GetNextSqlCmd( f, sSqlLine, bQuiet ); if( iRc == XB_NO_ERROR ){ sSqlLine.Trim(); - std::cout << "Processing line [" << sSqlLine.Str() << "]\n"; + // std::cout << "Processing line [" << sSqlLine.Str() << "]\n"; iRc = sql.ExecuteNonQuery( sSqlLine ); if( iRc != XB_NO_ERROR ) x.DisplayError( iRc ); diff --git a/src/utils/xb_import.cpp b/src/utils/xb_import.cpp new file mode 100755 index 0000000..272a0d0 --- /dev/null +++ b/src/utils/xb_import.cpp @@ -0,0 +1,242 @@ +/* xb_import.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2023 Gary A Kunkel + +The xb64 software library is covered under +the terms of the GPL Version 3, 2007 license. + +Email Contact: + + xb64-devel@lists.sourceforge.net + xb64-users@lists.sourceforge.net + +*/ + +#include <xbase.h> +using namespace xb; + + +struct sFldMap{ + xbUInt32 iRecPos; + xbInt16 iFldNo; + char cFldType; + sFldMap *next; +}; + +void PrintHelp(); +void PrintHelp(){ + std::cout << "Usage: xb_execsql [-h] [-?] [--help] [-v] [--version] -i infilename.txt -d delimeter -t table.DBF -q --quiet" << std::endl << std::endl; + std::cout << "This program imports data from a text file into a specified DBF file/table." << std::endl << std::endl; +} +void PrintVersion(); +void PrintVersion(){ + std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl; +} + + + +int main(int argc, char* argv[]) +{ + xbXBase x; + xbSql sql( &x ); + xbFile f( sql.GetXbasePtr() ); + xbInt16 iRc = XB_NO_ERROR; + xbString sFileName = ""; + xbString sTableName = ""; + xbString sSqlLine = ""; + xbString sParm = ""; + xbString sMsg; + xbString sLine; + xbString sFld; + xbBool bQuiet = xbFalse; + char cDelimiter = ','; + char cType = ' '; + sFldMap *fmFldList = 0; + sFldMap *fmTemp = 0; + xbInt16 iFldNo = 0; + xbUInt32 ulRecCtr = 0; + xbBool bRecUpdated; + x.EnableMsgLogging(); + + + if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) || + x.GetCmdLineOpt( argc, argv, "-?", sParm ) || + x.GetCmdLineOpt( argc, argv, "--help", sParm )){ + PrintHelp(); + return 1; + } + + if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) || + x.GetCmdLineOpt( argc, argv, "--version", sParm )){ + PrintVersion(); + return 1; + } + + if ( x.GetCmdLineOpt( argc, argv, "-q", sParm ) || + x.GetCmdLineOpt( argc, argv, "--quiet", sParm )){ + bQuiet = xbTrue; + } + + if ( x.GetCmdLineOpt( argc, argv, "-d", sParm )){ + if( sParm.Len() > 0 ){ + cDelimiter = sParm[1]; + } + } + + if( !x.GetCmdLineOpt( argc, argv, "-i", sFileName ) || sFileName == "" ){ + PrintHelp(); + return 1; + } + + if( !x.GetCmdLineOpt( argc, argv, "-t", sTableName ) || sTableName == "" ){ + PrintHelp(); + return 1; + } + + xbDbf *MyFile = NULL; + if(( iRc = x.OpenHighestVersion( sTableName.Str(), "", &MyFile )) != XB_NO_ERROR ){ + std::cout << "Could not open table/file RC = " << iRc << " file = " << sTableName.Str() << std::endl; + x.DisplayError( iRc ); + return 1; + } + + if(( iRc = f.xbFopen( "r", sFileName, XB_SINGLE_USER )) != XB_NO_ERROR ){ + sMsg.Sprintf( "Error opening [%s]\n", sFileName.Str() ); + std::cout << sMsg.Str(); + x.DisplayError( iRc ); + return 1; + } + + + if(( iRc = f.xbFgets( 1024, sLine )) != XB_NO_ERROR ){ + sMsg.Sprintf( "Error reading [%s]\n", sFileName.Str() ); + std::cout << sMsg.Str(); + x.DisplayError( iRc ); + return 1; + } + + + // determine how many fields in a record + xbUInt32 lFldCnt = sLine.CountChar( cDelimiter, 1 ); + // std::cout << "in rec = [" << sLine.Str() << "]\n"; + // std::cout << "fld cnt = [" << lFldCnt << "]\n"; + + // do the mapping between field names in source data and field numbers in target table + for( xbUInt32 l = 0; l < (lFldCnt + 1); l++ ){ + + // get the field + sFld.ExtractElement( sLine.Str(), cDelimiter, l+1, 1 ); + sFld.ZapTrailingChar( 0x0a ); // eliminate CRLF + sFld.ZapTrailingChar( 0x0d ); // eliminate CRLF + + + // do the lookup + // std::cout << "processing field [" << l << "] [" << sFld.Str() << "]\n"; + // if found, create an entry in the field list structure + // else if not quiet, display a message + + //iRc = MyFile->GetFieldNo( sFld, &iFldNo ); + + if(( iRc = MyFile->GetFieldNo( sFld, iFldNo )) == XB_NO_ERROR ){ + MyFile->GetFieldType( iFldNo, cType ); + fmTemp = (sFldMap *) calloc( 1, sizeof( sFldMap )); + if( !fmTemp ){ + std::cout << "Memory allocation error\n"; + exit(1); + } else { + fmTemp->iRecPos = l; + fmTemp->iFldNo = iFldNo; + fmTemp->cFldType = cType; + fmTemp->next = fmFldList; + fmFldList = fmTemp; + + } + } else { + if( !bQuiet ){ + std::cout << "Field [" << sFld.Str() << "] not found in target table" << std::endl; + } + } + } + + + while( f.xbFgets( 1024, sLine ) == XB_NO_ERROR ){ + bRecUpdated = xbFalse; + // std::cout << sLine.Str() << "\n"; + + if(( iRc = MyFile->BlankRecord()) != XB_NO_ERROR ){ + sMsg.Sprintf( "MyFile->BlankRecord() error [%d]\n", iRc ); + std::cout << sMsg.Str(); + x.DisplayError( iRc ); + } else { + + fmTemp = fmFldList; + while( fmTemp ){ + + // std::cout << "*** RecPos = " << fmTemp->iRecPos << " FldNo = " << fmTemp->iFldNo << " Type = " << fmTemp->cFldType << "\n"; + + sFld.ExtractElement( sLine.Str(), cDelimiter, fmTemp->iRecPos+1, 1 ); + sFld.ZapTrailingChar( 0x0a ); // eliminate CRLF + sFld.ZapTrailingChar( 0x0d ); // eliminate CRLF + + // remove any matching leading and trailing quotes + if( sFld[1] == '\'' && sFld[sFld.Len()] == '\'' ){ + sFld.ZapTrailingChar( '\'' ); + sFld.ZapLeadingChar ( '\'' ); + //std::cout << "DataNq = " << sFld.Str() << "\n"; + } else if( sFld[1] == '"' && sFld[sFld.Len()] == '"' ){ + sFld.ZapTrailingChar( '"' ); + sFld.ZapLeadingChar ( '"' ); + } + + // std::cout << "Data = " << sFld.Str() << "\n"; + if( sFld.Len() > 0 ){ + bRecUpdated = xbTrue; + if( fmTemp->cFldType == 'C' || fmTemp->cFldType == 'L' || fmTemp->cFldType == 'D' || fmTemp->cFldType == 'N' || fmTemp->cFldType == 'F' ){ + iRc = MyFile->PutField( fmTemp->iFldNo, sFld ); + } else if( fmTemp->cFldType == 'M' ){ + iRc = MyFile->UpdateMemoField( fmTemp->iFldNo, sFld ); + } else { + std::cout << "Field type [" << fmTemp->cFldType << "] not built yet" << std::endl; + } + + if( iRc != XB_NO_ERROR && !bQuiet ){ + std::cout << "Error [" << iRc << "] on field [" << fmTemp->iFldNo << "] on record [" << ulRecCtr << "]" << std::endl; + } + } + fmTemp = fmTemp->next; + } + + if( bRecUpdated ){ + iRc = MyFile->AppendRecord(); + if( iRc != XB_NO_ERROR ){ + if( !bQuiet ){ + std::cout << "Error [" << iRc << "] on appending record [" << ulRecCtr << "]" << std::endl; + } + MyFile->Abort(); + } else { + iRc = MyFile->Commit(); + if( iRc != XB_NO_ERROR ){ + if( !bQuiet ){ + std::cout << "Error [" << iRc << "] on appending record [" << ulRecCtr << "]" << std::endl; + } + MyFile->Abort(); + } + } + } + + } + + ulRecCtr++; + + } + + + + + + f.xbFclose(); + return 0; +} + |