/**************************************************************************** Desc : Adds a value to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddValue( HFCURSOR hCursor, QTYPES eValType, void * pVal, FLMUINT uiValLen ) { RCODE rc = FERR_OK; FLMUINT uiVal; void * pTmpVal = pVal; CURSOR * pCursor = (CURSOR *)hCursor; F_Pool pool; FLMBOOL bPoolInitialized = FALSE; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } switch (eValType) { // Convert all string types to FLM_TEXT_VALUE // in order to handle pure unicode coming in. case FLM_UNICODE_VAL: case FLM_STRING_VAL: { NODE node; f_memset( &node, 0, sizeof(NODE)); pool.poolInit( 512); bPoolInitialized = TRUE; rc = (eValType == FLM_UNICODE_VAL) ? GedPutUNICODE( &pool, &node, (FLMUNICODE *) pVal) : GedPutNATIVE( &pool, &node, (const char *)pVal); if (RC_BAD( rc)) { goto Exit; } pTmpVal = GedValPtr( &node); uiValLen = GedValLen( &node); eValType = FLM_TEXT_VAL; break; } case FLM_BOOL_VAL: if (!pVal) { uiVal = FLM_UNK; } else { FLMBOOL bTrueFalse = (FLMBOOL)*(FLMBOOL *)pVal; uiVal = (bTrueFalse) ? FLM_TRUE : FLM_FALSE; } pTmpVal = &uiVal; eValType = FLM_BOOL_VAL; break; case FLM_INT32_VAL: case FLM_UINT32_VAL: case FLM_REC_PTR_VAL: case FLM_UINT64_VAL: case FLM_INT64_VAL: case FLM_TEXT_VAL: case FLM_BINARY_VAL: // pTmpVal is already pointing to pVal, and // eValType does not need to be changed. break; default: flmAssert( 0); rc = RC_SET( FERR_CURSOR_SYNTAX); break; } if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, eValType, pTmpVal, uiValLen, pCursor->QTInfo.uiFlags, &(pCursor->QTInfo.pCurAtomNode)))) { if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } if (bPoolInitialized) { pool.poolFree(); } return( rc); }
/**************************************************************************** Desc: Release the existing cache block and setup and alloc new blk. ****************************************************************************/ RCODE F_BtreeRoot::newCacheBlk( FLMUINT uiCachePos, F_BtreeBlk ** ppBlk, eDynRSetBlkTypes eBlkType) { RCODE rc = NE_FLM_OK; F_BtreeBlk * pNewBlk = NULL; if (m_CacheBlks[uiCachePos].pBlk) { if (m_CacheBlks[uiCachePos].pBlk->isDirty()) { if (RC_BAD( rc = writeBlk( uiCachePos))) { goto Exit; } } } if (m_CacheBlks[uiCachePos].pBlk != NULL && m_CacheBlks[uiCachePos].pBlk->blkType() == eBlkType) { // If block is of the same type then reset it and use it. pNewBlk = m_CacheBlks[uiCachePos].pBlk; pNewBlk->reset( eBlkType); *ppBlk = pNewBlk; goto Exit; } if (m_CacheBlks[uiCachePos].pBlk) { m_CacheBlks[uiCachePos].pBlk->Release(); } if (eBlkType == ACCESS_BTREE_LEAF) { F_BtreeLeaf * pLeafBlk; if ((pLeafBlk = f_new F_BtreeLeaf) == NULL) { rc = RC_SET( NE_FLM_MEM); goto Exit; } if (RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize))) { pLeafBlk->Release(); goto Exit; } pLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); pNewBlk = (F_BtreeBlk *) pLeafBlk; } else { F_BtreeNonLeaf * pNonLeafBlk; if ((pNonLeafBlk = f_new F_BtreeNonLeaf) == NULL) { rc = RC_SET( NE_FLM_MEM); goto Exit; } if (RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize))) { pNonLeafBlk->Release(); goto Exit; } pNonLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); pNewBlk = (F_BtreeBlk *) pNonLeafBlk; } m_CacheBlks[uiCachePos].pBlk = pNewBlk; *ppBlk = pNewBlk; Exit: return( rc); }
/**************************************************************************** Desc: ****************************************************************************/ JNIEXPORT jlong JNICALL Java_xflaim_Backup__1backup( JNIEnv * pEnv, jobject, // obj, jlong lThis, jstring sBackupPath, jstring sPassword, jobject backupClient, jobject backupStatus) { RCODE rc = NE_XFLM_OK; IF_Backup * pBackup = THIS_BACKUP(); FLMUINT uiSeqNum = 0; JavaVM * pJvm; JNIBackupClient * pClient = NULL; JNIBackupStatus * pStatus = NULL; FLMBYTE ucBackupPath [F_PATH_MAX_SIZE]; F_DynaBuf backupPathBuf( ucBackupPath, sizeof( ucBackupPath)); FLMBYTE ucPassword [100]; F_DynaBuf passwordBuf( ucPassword, sizeof( ucPassword)); if (RC_BAD( rc = getUTF8String( pEnv, sBackupPath, &backupPathBuf))) { ThrowError( rc, pEnv); goto Exit; } if (RC_BAD( rc = getUTF8String( pEnv, sPassword, &passwordBuf))) { ThrowError( rc, pEnv); goto Exit; } flmAssert( backupClient); pEnv->GetJavaVM( &pJvm); if( (pClient = f_new JNIBackupClient( backupClient, pJvm)) == NULL) { rc = RC_SET( NE_XFLM_MEM); ThrowError( rc, pEnv); goto Exit; } if (backupStatus) { if( (pStatus = f_new JNIBackupStatus( backupStatus, pJvm)) == NULL) { rc = RC_SET( NE_XFLM_MEM); ThrowError( rc, pEnv); goto Exit; } } if (RC_BAD( rc = pBackup->backup( (const char *)(backupPathBuf.getDataLength() > 1 ? (const char *)backupPathBuf.getBufferPtr() : (const char *)NULL), (const char *)(passwordBuf.getDataLength() > 1 ? (const char *)passwordBuf.getBufferPtr() : (const char *)NULL), pClient, pStatus, &uiSeqNum))) { ThrowError( rc, pEnv); goto Exit; } Exit: if (pClient) { pClient->Release(); } if (pStatus) { pStatus->Release(); } return( uiSeqNum); }
/**************************************************************************** Desc: Searches the SCache for the block referenced by the parameters. If found, it copies the data into pLocalSCache. Assumes that the mutex has already been locked! ****************************************************************************/ RCODE F_SCacheBase::locateSCacheBlock( FLMUINT uiNumParams, const char ** ppszParams, SCACHE * pLocalSCache, FLMUINT * puiBlkAddress, FLMUINT * puiLowTransID, FLMUINT * puiHighTransID, FFILE * * ppFile) { RCODE rc = FERR_OK; FLMUINT uiSigBitsInBlkSize; SCACHE * pSCache; SCACHE ** ppSCache; #define MAXPARAMLEN 15 char szBlkAddress[MAXPARAMLEN]; char szLowTransID[MAXPARAMLEN]; char szHighTransID[MAXPARAMLEN]; char szFile[MAXPARAMLEN]; // Grab the block address, low and high trans id's and FFile pointer, which // we need to uniquely identify an scache block... if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "BlockAddress", sizeof( szBlkAddress), &szBlkAddress[0]))) { goto Exit; } *puiBlkAddress = f_atoi( szBlkAddress); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "LowTransID", sizeof( szLowTransID), &szLowTransID[0]))) { goto Exit; } *puiLowTransID = f_atoi( szLowTransID); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "HighTransID", sizeof( szHighTransID), &szHighTransID[0]))) { goto Exit; } *puiHighTransID = f_atoi( szHighTransID); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "File", sizeof( szFile), &szFile[0]))) { goto Exit; } *ppFile = (FFILE *)f_atoud( szFile); flmAssert( *ppFile); uiSigBitsInBlkSize = (*ppFile)->FileHdr.uiSigBitsInBlkSize; // ScaHash actually returns a pointer to the first scache in the hash // bucket. It's up to us to traverse this list to find the proper block // address and FFile (and potentially high and low trans id) ppSCache = ScaHash( uiSigBitsInBlkSize, *puiBlkAddress); pSCache = *ppSCache; while ( pSCache && ( (pSCache->uiBlkAddress != *puiBlkAddress) || (pSCache->pFile != *ppFile) ) ) { pSCache = pSCache->pNextInHashBucket; } // Ok - we've found the right address and ffile. Do we need a different // version? while ( (pSCache) && (pSCache->uiHighTransID != *puiHighTransID) && (scaGetLowTransID( pSCache) != *puiLowTransID) ) { pSCache = pSCache->pNextInVersionList; } // Now, if we've found the right block, copy it's contents to local memory... if (pSCache) { f_memcpy( pLocalSCache, pSCache, sizeof( SCACHE)); } else { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: return( rc); }
/**************************************************************************** Desc: Search a single block tree. Position for get* or for insert. Do a binary search on all of the entries to find a match. If no match then position to the entry where an insert will take place. ****************************************************************************/ RCODE F_BtreeBlk::searchEntry( void * pvEntry, FLMUINT * puiChildAddr, void * pvFoundEntry) { RCODE rc = RC_SET( NE_FLM_NOT_FOUND); FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblSize; FLMINT iCompare; // check for zero entries. if (!entryCount()) { uiMid = 0; goto Exit; } uiHigh = uiTblSize = entryCount() - 1; uiLow = 0; for(;;) { uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 // Use compare routine if (m_fnCompare) { iCompare = m_fnCompare( pvEntry, ENTRY_POS( uiMid), m_pvUserData); } else { iCompare = f_memcmp( pvEntry, ENTRY_POS( uiMid), m_uiEntrySize); } if (iCompare == 0) { if (pvFoundEntry) { f_memcpy( pvFoundEntry, ENTRY_POS( uiMid), m_uiEntrySize); } rc = NE_FLM_OK; goto Exit; } // Check if we are done - where wLow equals uiHigh or mid is at end. if (iCompare < 0) { if (uiMid == uiLow || uiLow == uiHigh) { break; } uiHigh = uiMid - 1; // Too high } else { if (uiMid == uiHigh || uiLow == uiHigh) { // Go up one for the correct position? uiMid++; break; } uiLow = uiMid + 1; // Too low } } Exit: m_uiPosition = uiMid; if (puiChildAddr && blkType() != ACCESS_BTREE_LEAF) { if (uiMid == entryCount()) { *puiChildAddr = lemBlk(); } else { FLMBYTE * pucChildAddr = ENTRY_POS(uiMid) + m_uiEntrySize; *puiChildAddr = (FLMUINT)FB2UD( pucChildAddr); } } return( rc); }
/**************************************************************************** Desc : Checks for physical corruption in a FLAIM database. DNote: The routine verifies the database by first reading through the database to count certain block types which are in linked lists. It then verifies the linked lists. It also verifies the B-TREEs in the database. The reason for the first pass is so that when we verify the linked lists, we can keep ourselves from getting into an infinite loop if there is a loop in the lists. ****************************************************************************/ RCODE F_DbCheck::dbCheck( const char * pszDbFileName, // [IN] Full path and file name of the database which // is to be checked. NULL can be passed as the value of // this parameter if pDb is non-NULL. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory. NULL can be passed as the value of // this parameter to indicate that the log files are located // in the same directory as the database or if pDb is non-NULL. const char * pszPassword, // [IN] Database password. Needed to open the database if the database // key has been wrapped in a password. NULL by default. FLMUINT uiFlags, // [IN] Check flags. Possible flags include: // // XFLM_ONLINE. This flag instructs the check to repair any // index corruptions it finds. The database must have been // opened in read/write mode in order for the check to // successfully repair corruptions. An update transaction // will be started whenever a corruption is repaired. // // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to // perform a logical check of the databases's indexes // in addition to the structural check. // // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip // verifying the DOM links. This check can take quite a long time // to execute. // // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow // the database to be opened in limited mode if the database key is // wrapped in a password and the password we pass is incorrect // (or non-existent). IF_DbInfo ** ppDbInfo, // [IN] Pointer to a DB_INFO structure which is used to store // statistics collected during the database check. IF_DbCheckStatus * pDbCheckStatus // [IN] Status interface. Functions in this interface are called // periodically to iform the calling application of the progress // being made. This allows the application to monitor and/or display // the progress of the database check. NULL may be passed as the // value of this parameter if the callback feature is not needed. ) { RCODE rc = NE_XFLM_OK; FLMBYTE * pBlk = NULL; FLMUINT uiFileEnd; FLMUINT uiBlockSize; FLMUINT uiLoop; FLMUINT64 ui64TmpSize; FLMBOOL bStartOver; FLMBOOL bOkToCloseTrans = FALSE; FLMBOOL bAllowLimitedMode = ( uiFlags & XFLM_ALLOW_LIMITED_MODE) ? TRUE : FALSE; if (RC_BAD( rc = gv_pXFlmDbSystem->dbOpen( pszDbFileName, pszDataDir, pszRflDir, pszPassword, bAllowLimitedMode, (IF_Db **)&m_pDb))) { goto Exit; } if ((m_pDbInfo = f_new F_DbInfo) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (ppDbInfo) { *ppDbInfo = m_pDbInfo; (*ppDbInfo)->AddRef(); } m_pDbCheckStatus = pDbCheckStatus; m_LastStatusRc = NE_XFLM_OK; // Get the file size... if (uiFlags & XFLM_SKIP_DOM_LINK_CHECK) { m_bSkipDOMLinkCheck = TRUE; } // Initialize the information block and Progress structure. // Since we know that the check will start read transactions // during its processing, set the flag to indicate that the KRef table // should be cleaned up on exit if we are still in a read transaction. bOkToCloseTrans = TRUE; uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); // Allocate memory to use for reading through the data blocks. if( RC_BAD( rc = f_alloc( uiBlockSize, &pBlk))) { goto Exit; } if ((m_pBtPool = f_new F_BtPool) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = m_pBtPool->btpInit())) { goto Exit; } // Setup the result set database. if( RC_BAD( rc = FlmAllocRandomGenerator( &m_pRandGen))) { goto Exit; } m_pRandGen->setSeed( 9768); if (RC_BAD( rc = createAndOpenResultSetDb())) { goto Exit; } Begin_Check: // Initialize all statistics in the DB_INFO structure. rc = NE_XFLM_OK; bStartOver = FALSE; m_pDbInfo->m_ui64FileSize = 0; m_pDbInfo->freeLogicalFiles(); m_bPhysicalCorrupt = FALSE; m_bIndexCorrupt = FALSE; m_uiFlags = uiFlags; m_bStartedUpdateTrans = FALSE; f_memset( &m_pDbInfo->m_AvailBlocks, 0, sizeof( BLOCK_INFO)); f_memset( &m_pDbInfo->m_LFHBlocks, 0, sizeof( BLOCK_INFO)); f_memset( &m_Progress, 0, sizeof( XFLM_PROGRESS_CHECK_INFO)); /* Get the dictionary information for the file. */ if (RC_BAD( rc = getDictInfo())) { goto Exit; } m_Progress.ui64BytesExamined = 0; for (uiLoop = 1; uiLoop <= MAX_DATA_BLOCK_FILE_NUMBER; uiLoop++) { if (RC_BAD( m_pDb->m_pSFileHdl->getFileSize( uiLoop, &ui64TmpSize))) { break; } m_Progress.ui64FileSize += ui64TmpSize; } // See if we have a valid end of file uiFileEnd = m_pDb->m_uiLogicalEOF; if (FSGetFileOffset( uiFileEnd) % uiBlockSize != 0) { if (RC_BAD( rc = chkReportError( FLM_BAD_FILE_SIZE, XFLM_LOCALE_NONE, 0, 0, 0xFF, (FLMUINT32)uiFileEnd, 0, 0, 0))) { goto Exit; } } else if (m_Progress.ui64FileSize < FSGetSizeInBytes( m_pDb->m_pDatabase-> getMaxFileSize(), uiFileEnd)) { m_Progress.ui64FileSize = FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(), uiFileEnd); } m_pDbInfo->m_ui64FileSize = m_Progress.ui64FileSize; // Verify the LFH blocks, B-Trees, and the AVAIL list. if( RC_BAD( rc = verifyLFHBlocks( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } // Check the b-trees. if (RC_BAD( rc = verifyBTrees( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } // Check the avail list. if (RC_BAD( rc = verifyAvailList( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } Exit: if ((m_bPhysicalCorrupt || m_bIndexCorrupt) && !gv_pXFlmDbSystem->errorIsFileCorrupt( rc)) { rc = RC_SET( NE_XFLM_DATA_ERROR); } if (RC_OK( rc) && RC_BAD( m_LastStatusRc)) { rc = m_LastStatusRc; } if (m_pDb) { // Close down the transaction, if one is going if( bOkToCloseTrans && m_pDb->getTransType( ) == XFLM_READ_TRANS) { m_pDb->krefCntrlFree(); m_pDb->transAbort(); } } // Free memory, if allocated if (pBlk) { f_free( &pBlk); } // Close the FLAIM database we opened. if (m_pDb) { m_pDb->Release(); m_pDb = NULL; } return( rc); }
/**************************************************************************** Desc: Checks for physical corruption in a FLAIM database. Note: The routine verifies the database by first reading through the database to count certain block types which are in linked lists. It then verifies the linked lists. It also verifies the B-TREEs in the database. The reason for the first pass is so that when we verify the linked lists, we can keep ourselves from getting into an infinite loop if there is a loop in the lists. ****************************************************************************/ RCODE XFLAPI F_DbSystem::dbCheck( const char * pszDbFileName, // [IN] Full path and file name of the database which // is to be checked. NULL can be passed as the value of // this parameter if pDb is non-NULL. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory. NULL can be passed as the value of // this parameter to indicate that the log files are located // in the same directory as the database or if pDb is non-NULL. const char * pszPassword, // [IN] Database password. This is necessary to open the database if // the key has been wrapped in a password. NULL by default. FLMUINT uiFlags, // [IN] Check flags. Possible flags include: // // XFLM_ONLINE. This flag instructs the check to repair any // index corruptions it finds. The database must have been // opened in read/write mode in order for the check to // successfully repair corruptions. An update transaction // will be started whenever a corruption is repaired. // // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to // perform a logical check of the databases's indexes // in addition to the structural check. // // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip // verifying the DOM links. This check can take quite a long time // to execute. // // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow // the database to be opened in limited mode if the database key is // wrapped in a password and the password we pass is incorrect // (or non-existent). IF_DbInfo ** ppDbInfo, // [IN] Pointer to a DB_INFO structure which is used to store // statistics collected during the database check. IF_DbCheckStatus * pDbCheckStatus // [IN] Status interface. Functions in this interface are called // periodically to iform the calling application of the progress // being made. This allows the application to monitor and/or display // the progress of the database check. NULL may be passed as the // value of this parameter if the callback feature is not needed. ) { RCODE rc = NE_XFLM_OK; F_DbCheck * pCheckObj = NULL; if ((pCheckObj = f_new F_DbCheck) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } rc = pCheckObj->dbCheck( pszDbFileName, pszDataDir, pszRflDir, pszPassword, uiFlags, ppDbInfo, pDbCheckStatus); Exit: if (pCheckObj) { pCheckObj->Release(); } return( rc); }
/**************************************************************************** Desc: Starts a transaction. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbTransBegin( HFDB hDb, FLMUINT uiTransType, FLMUINT uiMaxLockWait, FLMBYTE * pucHeader) { RCODE rc = FERR_OK; FLMBOOL bIgnore; FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( uiTransType); FDB * pDb = (FDB *)hDb; uiTransType = FLM_GET_TRANS_TYPE( uiTransType); if (IsInCSMode( hDb)) { fdbInitCS( pDb); FCL_WIRE Wire( pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { if( RC_BAD( rc = Wire.doTransOp( FCS_OP_TRANSACTION_BEGIN, uiTransType, uiFlags, uiMaxLockWait, pucHeader))) { goto Exit; } } goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } // Verify the transaction type. if (( uiTransType != FLM_UPDATE_TRANS) && ( uiTransType != FLM_READ_TRANS)) { rc = RC_SET( FERR_ILLEGAL_TRANS); goto Exit; } // Verify the transaction flags if( (uiFlags & FLM_DONT_KILL_TRANS) && uiTransType != FLM_READ_TRANS) { rc = RC_SET( FERR_ILLEGAL_TRANS); goto Exit; } // Can't start an update transaction on a database that // is locked in shared mode. if ((uiTransType == FLM_UPDATE_TRANS) && (pDb->uiFlags & FDB_FILE_LOCK_SHARED)) { rc = RC_SET( FERR_PERMISSION); goto Exit; } // If the database has an invisible transaction going, abort it // before going any further - we don't want application transactions // to be nested under invisible transactions. Application transactions // take precedence over invisible transactions. if ((pDb->uiTransType != FLM_NO_TRANS) && (pDb->uiFlags & FDB_INVISIBLE_TRANS)) { if (RC_BAD( rc = flmAbortDbTrans( pDb))) { goto Exit; } } // If the database is not running a transaction, start one. // Otherwise, start a nested transaction - first verifying that // the transation type matches. if (pDb->uiTransType == FLM_NO_TRANS) { FLMUINT uiBytesRead; if( pucHeader) { if( RC_BAD( rc = pDb->pSFileHdl->readBlock( 0, 2048, pucHeader, &uiBytesRead))) { goto Exit; } } if (RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType, uiMaxLockWait, uiFlags, pucHeader ? &pucHeader [16] : NULL))) { goto Exit; } pDb->bHadUpdOper = FALSE; } else { // Cannot nest transactions. rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } Exit: flmExit( FLM_DB_TRANS_BEGIN, pDb, rc); return( rc); }
/**************************************************************************** Desc: Retrieves the last commit sequence number of a database. Notes: Whenever a transaction is committed, FLAIM increments the commit sequence number to indicate that the database has been modified. An application may use this routine to determine if the database has been modified. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbGetCommitCnt( HFDB hDb, FLMUINT * puiCommitCount) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bIgnore; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); // Send a request to get the commit count if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_GET_COMMIT_CNT))) { goto Exit; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } *puiCommitCount = (FLMUINT)Wire.getCount(); rc = Wire.getRCode(); goto ExitCS; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto ExitCS; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } // See if we have a transaction going which should be aborted. if (pDb->uiTransType != FLM_NO_TRANS) { if (flmCheckBadTrans( pDb)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } } f_mutexLock( gv_FlmSysData.hShareMutex); *puiCommitCount = (FLMUINT)FB2UD( &pDb->pFile->ucLastCommittedLogHdr [LOG_COMMIT_COUNT]); f_mutexUnlock( gv_FlmSysData.hShareMutex); Exit: ExitCS: flmExit( FLM_DB_GET_COMMIT_CNT, pDb, rc); return( rc); }
/**************************************************************************** Desc: Commits an active transaction. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbTransCommit( HFDB hDb, FLMBOOL * pbEmpty) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bIgnore; if (IsInCSMode( hDb)) { fdbInitCS( pDb); FCL_WIRE Wire( pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { rc = Wire.doTransOp( FCS_OP_TRANSACTION_COMMIT, 0, 0, 0); } goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore))) { goto Exit; } // If there is an invisible transaction going, it should not be // commitable by an application. if ((pDb->uiTransType == FLM_NO_TRANS) || (pDb->uiFlags & FDB_INVISIBLE_TRANS)) { rc = RC_SET( FERR_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if( RC_BAD( pDb->AbortRc)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } if (pbEmpty) { *pbEmpty = FALSE; } rc = flmCommitDbTrans( pDb, 0, FALSE, pbEmpty); Exit: if( RC_OK( rc)) { rc = flmCheckDatabaseState( pDb); } flmExit( FLM_DB_TRANS_COMMIT, pDb, rc); return( rc); }
/**************************************************************************** Desc : Returns the type of the current database transaction. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbGetTransType( HFDB hDb, FLMUINT * puiTransTypeRV) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pDb->pCSContext, pDb); // Send a request to get the transaction type. if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_TRANS, FCS_OP_TRANSACTION_GET_TYPE))) { goto Exit; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } *puiTransTypeRV = Wire.getTransType(); rc = Wire.getRCode(); goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (!pDb) { rc = RC_SET( FERR_BAD_HDL); goto Exit; } fdbUseCheck( pDb); pDb->uiInitNestLevel++; (void)flmResetDiag( pDb); // If the transaction is an internal transaction that is invisible to // the application, return FLM_NO_TRANS. Application is not supposed // see invisible transactions. *puiTransTypeRV = (FLMUINT)(((pDb->uiTransType == FLM_NO_TRANS) || (pDb->uiFlags & FDB_INVISIBLE_TRANS)) ? (FLMUINT)FLM_NO_TRANS : pDb->uiTransType); // See if the database is being forced to close if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } Exit: flmExit( FLM_DB_GET_TRANS_TYPE, pDb, rc); return( rc); }
/**************************************************************************** Desc: This routine commits an active transaction for a particular database. If the database is open via a server, a message is sent to the server to commit the transaction. Otherwise, the transaction is committed locally. ****************************************************************************/ RCODE flmCommitDbTrans( FDB * pDb, FLMUINT uiNewLogicalEOF, FLMBOOL bForceCheckpoint, FLMBOOL * pbEmpty) { RCODE rc = FERR_OK; FLMBYTE * pucUncommittedLogHdr; FFILE * pFile = pDb->pFile; FLMUINT uiCPFileNum = 0; FLMUINT uiCPOffset = 0; FLMUINT uiTransId = 0; FLMBOOL bTransEndLogged; FLMBOOL bForceCloseOnError = FALSE; FLMBOOL bOkToLogAbort = TRUE; DB_STATS * pDbStats = pDb->pDbStats; FLMUINT uiTransType; FLMBOOL bInvisibleTrans = FALSE; FLMBOOL bIndexAfterCommit = FALSE; pDb->uiFlags |= FDB_COMMITTING_TRANS; // See if we even have a transaction going. if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS) { goto Exit; // Will return FERR_OK. } // See if we have a transaction going which should be aborted. if (flmCheckBadTrans( pDb)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } // If we are in a read transaction we can skip most of the stuff // below because no updates would have occurred. This will help // improve performance. if (uiTransType == FLM_READ_TRANS) { if( pDb->KrefCntrl.bKrefSetup) { // KrefCntrlFree could be called w/o checking bKrefSetup because // it checks the flag, but it is more optimal to check the // flag before making the call because most of the time it will // be false. KrefCntrlFree( pDb); } goto Exit1; } // At this point, we know we have an update transaction. pFile->pRfl->clearLogHdrs(); #ifdef FLM_DBG_LOG flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, 0, 0, FERR_OK, "TCmit"); #endif uiTransId = pDb->LogHdr.uiCurrTransID; // If the transaction had no update operations, restore it // to its pre-transaction state - make it appear that no // transaction ever happened. if (!pDb->bHadUpdOper) { bOkToLogAbort = FALSE; rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, TRUE); // Even though we didn't have any update operations, there may have // been operations during the transaction (i.e., query operations) // that initialized the KREF in order to generate keys. KrefCntrlFree( pDb); // Restore everything as if the transaction never happened. f_mutexLock( gv_FlmSysData.hShareMutex); pFile->uiUpdateTransID = 0; f_mutexUnlock( gv_FlmSysData.hShareMutex); if (pbEmpty) { *pbEmpty = TRUE; } goto Exit1; } // Log commit record to roll-forward log bOkToLogAbort = FALSE; if (RC_BAD( rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged))) { goto Exit1; } bForceCloseOnError = TRUE; // Commit any keys in the KREF buffers. if (RC_BAD( rc = KYKeysCommit( pDb, TRUE))) { flmLogError( rc, "calling KYKeysCommit from flmCommitDbTrans"); goto Exit1; } if (RC_BAD( rc = FSCommitIxCounts( pDb))) { flmLogError( rc, "calling FSCommitIxCounts from flmCommitDbTrans"); goto Exit1; } // Reinitialize the log header. If the local dictionary was updated // during the transaction, increment the local dictionary ID so that // other concurrent users will know that it has been modified and // that they need to re-read it into memory. // If we are in recovery mode, see if we need to force // a checkpoint with what we have so far. We force a // checkpoint on one of two conditions: // 1. If it appears that we have a buildup of dirty cache // blocks. We force a checkpoint on this condition // because it will be more efficient than replacing // cache blocks one at a time. // We check for this condition by looking to see if // our LRU block is not used and it is dirty. That is // a pretty good indicator that we have a buildup // of dirty cache blocks. // 2. We are at the end of the roll-forward log. We // want to force a checkpoint here to complete the // recovery phase. if ( pDb->uiFlags & FDB_REPLAYING_RFL) { // If we are in the middle of upgrading, and are forcing // a checkpoint, use the file number and offset that were // set in the FDB. if ((pDb->uiFlags & FDB_UPGRADING) && bForceCheckpoint) { uiCPFileNum = pDb->uiUpgradeCPFileNum; uiCPOffset = pDb->uiUpgradeCPOffset; } else { SCACHE * pTmpSCache; F_Rfl * pRfl = pFile->pRfl; f_mutexLock( gv_FlmSysData.hShareMutex); pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache; // Test for buildup of dirty cache blocks. if( (pTmpSCache && !pTmpSCache->uiUseCount && (pTmpSCache->ui16Flags & (CA_DIRTY | CA_LOG_FOR_CP | CA_WRITE_TO_LOG))) || pRfl->atEndOfLog() || bForceCheckpoint) { bForceCheckpoint = TRUE; uiCPFileNum = pRfl->getCurrFileNum(); uiCPOffset = pRfl->getCurrReadOffset(); } f_mutexUnlock( gv_FlmSysData.hShareMutex); } } // Move information collected in the pDb->LogHdr into the // uncommitted log header. Other things that need to be // set have already been set in the uncommitted log header // at various places in the code. // Mutex does not have to be locked while we do this because // the update transaction is the only one that ever accesses // the uncommitted log header buffer. pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; // Set the new logical EOF if passed in. if( uiNewLogicalEOF) { pDb->LogHdr.uiLogicalEOF = uiNewLogicalEOF; } UD2FBA( (FLMUINT32)pDb->LogHdr.uiLogicalEOF, &pucUncommittedLogHdr [LOG_LOGICAL_EOF]); // Increment the commit counter. flmIncrUint( &pucUncommittedLogHdr [LOG_COMMIT_COUNT], 1); // Set the last committed transaction ID if( (bTransEndLogged || (pDb->uiFlags & FDB_REPLAYING_COMMIT)) && pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) { UD2FBA( (FLMUINT32)uiTransId, &pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); } // Write the header pFile->pRfl->commitLogHdrs( pucUncommittedLogHdr, pFile->ucCheckpointLogHdr); // Commit any record cache. flmRcaCommitTrans( pDb); // Push the IXD_FIXUP values back into the IXD if (pDb->pIxdFixups) { IXD_FIXUP * pIxdFixup; IXD_FIXUP * pDeleteIxdFixup; IXD * pIxd; pIxdFixup = pDb->pIxdFixups; while (pIxdFixup) { if( RC_BAD( fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE))) { flmAssert( 0); pIxd = NULL; } if( pIxd) { pIxd->uiLastContainerIndexed = pIxdFixup->uiLastContainerIndexed; pIxd->uiLastDrnIndexed = pIxdFixup->uiLastDrnIndexed; } pDeleteIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; f_free( &pDeleteIxdFixup); } pDb->pIxdFixups = NULL; } // Set the update transaction ID back to zero only // AFTER we know the transaction has safely committed. f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy( pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, LOG_HEADER_SIZE); pFile->uiUpdateTransID = 0; ScaReleaseLogBlocks( pFile); if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) { // Link the new local dictionary to its file. // Since the new local dictionary will be linked at the head // of the list of FDICT structures, see if the FDICT currently // at the head of the list is unused and can be unlinked. if ((pFile->pDictList) && (!pFile->pDictList->uiUseCount)) { flmUnlinkDict( pFile->pDictList); } flmLinkDictToFile( pFile, pDb->pDict); } f_mutexUnlock( gv_FlmSysData.hShareMutex); Exit1: // If the local dictionary was updated during this transaction, // link the new local dictionary structures to their file - or free // them if there was an error. if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) { if( RC_BAD( rc) && pDb->pDict) { // Unlink the FDB from the FDICT. - Shouldn't have // to lock semaphore, because the DICT is NOT linked // to the FFILE. flmAssert( pDb->pDict->pFile == NULL); flmUnlinkFdbFromDict( pDb); } } if (RC_BAD( rc)) { // Since we failed to commit, do an abort. We are purposely not // checking the return code from flmAbortDbTrans because we already // have an error return code. If we attempted to log the transaction // to the RFL and failed, we don't want to try to log an abort packet. // The RFL code has already reset the log back to the starting point // of the transaction, thereby discarding all operations. pDb->uiFlags &= ~FDB_COMMITTING_TRANS; (void)flmAbortDbTrans( pDb, bOkToLogAbort); uiTransType = FLM_NO_TRANS; // Do we need to force all handles to close? if( bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all FDBs linked to the FFILE // and set the FFILE's "must close" flag. This will cause any // subsequent operations on the database to fail until all // handles have been closed. flmSetMustCloseFlags( pFile, rc, FALSE); } } else { bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE; if (uiTransType == FLM_UPDATE_TRANS) { if (gv_FlmSysData.UpdateEvents.pEventCBList) { flmTransEventCallback( F_EVENT_COMMIT_TRANS, (HFDB)pDb, rc, uiTransId); } // Do the BLOB and indexing work before we unlock the db. FBListAfterCommit( pDb); if (pDb->pIxStopList || pDb->pIxStartList) { // Must not call flmIndexingAfterCommit until after // completeTransWrites. Otherwise, there is a potential // deadlock condition where flmIndexingAfterCommit is // waiting on an indexing thread to quit, but that // thread is waiting to be signaled by this thread that // writes are completed. However, flmIndexingAfterCommit // also must only be called while the database is still // locked. If we were to leave the database locked for // every call to completeTransWrites, however, we would // lose the group commit capability. Hence, we opt to // only lose it when there are actual indexing operations // to start or stop - which should be very few transactions. // That is what the bIndexAfterCommit flag is for. bIndexAfterCommit = TRUE; } } } // Unlock the database, if the update transaction is still going. // NOTE: We check uiTransType because it may have been reset // to FLM_NO_TRANS up above if flmAbortDbTrans was called. if (uiTransType == FLM_UPDATE_TRANS) { if (RC_BAD( rc)) { // SHOULD NEVER HAPPEN - because it would have been taken // care of above - flmAbortDbTrans would have been called and // uiTransType would no longer be FLM_UPDATE_TRANS. flmAssert( 0); (void)pFile->pRfl->completeTransWrites( pDb, FALSE, TRUE); } else if( !bForceCheckpoint) { if( bIndexAfterCommit) { rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE); flmIndexingAfterCommit( pDb); flmUnlinkDbFromTrans( pDb, TRUE); } else { rc = pFile->pRfl->completeTransWrites( pDb, TRUE, TRUE); } } else { // Do checkpoint, if forcing. Before doing the checkpoint // we have to make sure the roll-forward log writes // complete. We don't want to unlock the DB while the // writes are happening in this case - thus, the FALSE // parameter to completeTransWrites. if (RC_OK( rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE))) { bForceCloseOnError = FALSE; rc = ScaDoCheckpoint( pDbStats, pDb->pSFileHdl, pFile, (pDb->uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE, TRUE, CP_TIME_INTERVAL_REASON, uiCPFileNum, uiCPOffset); } if (bIndexAfterCommit) { flmIndexingAfterCommit( pDb); } flmUnlinkDbFromTrans( pDb, TRUE); } if (RC_BAD( rc) && bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all FDBs linked to the FFILE // and set the FFILE's "must close" flag. This will cause any // subsequent operations on the database to fail until all // handles have been closed. flmSetMustCloseFlags( pFile, rc, FALSE); } } else { // Unlink the database from the transaction // structure as well as from the FDICT structure. flmUnlinkDbFromTrans( pDb, FALSE); } if (pDbStats && uiTransType != FLM_NO_TRANS) { FLMUINT64 ui64ElapMilli = 0; flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli); pDbStats->bHaveStats = TRUE; if (uiTransType == FLM_READ_TRANS) { pDbStats->ReadTransStats.CommittedTrans.ui64Count++; pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; if (bInvisibleTrans) { pDbStats->ReadTransStats.InvisibleTrans.ui64Count++; pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli += ui64ElapMilli; } } else { pDbStats->UpdateTransStats.CommittedTrans.ui64Count++; pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; } } // Update stats if (pDb->pStats) { (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); } Exit: pDb->uiFlags &= ~FDB_COMMITTING_TRANS; return( rc); }
/**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::verifyQuery( const char * pszQuery, char* ppszNames[][ 2], IF_Query * pQuery, FLMUINT uiNumNames, FLMBOOL bFirstAscending, FLMBOOL bLastAscending, FLMBOOL bDocsIndexed, FLMBOOL bIxFirstAscending, FLMBOOL bIxLastAscending) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pResultNode = NULL; IF_DOMNode * pFirstNameNode = NULL; IF_DOMNode * pLastNameNode = NULL; FLMUINT uiLoop; FLMUINT uiPos; char szBuf[ 100]; IF_DataVector * pSearchKey = NULL; char szTestName[ 200]; static FLMUINT uiCallCount = 0; const char * pszTestNameFormat = "Verify Query #%u " "(%s/First Name Asc == %u/Last Name Asc ==%u/%s)"; uiCallCount++; f_sprintf( szTestName, pszTestNameFormat, uiCallCount, pszQuery, (unsigned)bFirstAscending, (unsigned)bLastAscending, "positioning"); beginTest( szTestName, "Ensure queries return proper results in proper order", "No Additional Info", ""); // positioning tests if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, pszQuery))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( bDocsIndexed && (bFirstAscending == bIxFirstAscending && bLastAscending == bIxLastAscending)) { pQuery->setIndex( IX_NUM); } if ( RC_BAD( rc = pQuery->enablePositioning())) { MAKE_FLM_ERROR_STRING( "enablePositioning failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = createSortKey( pQuery, bFirstAscending, bLastAscending))) { goto Exit; } for( uiLoop = 0; uiLoop < uiNumNames; uiLoop++) { if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, uiLoop))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResultNode->getFirstChild( m_pDb, &pFirstNameNode))) { MAKE_FLM_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getNextSibling( m_pDb, &pLastNameNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][0]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pLastNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][1]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getPosition( m_pDb, &uiPos))) { MAKE_FLM_ERROR_STRING( "getPosition failed.", m_szDetails, rc); goto Exit; } if ( uiPos != uiLoop) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected position", m_szDetails, rc); goto Exit; } } endTest("PASS"); f_sprintf( szTestName, pszTestNameFormat, uiCallCount, pszQuery, (unsigned)bFirstAscending, (unsigned)bLastAscending, "search key positioning"); beginTest( szTestName, "Ensure queries return proper results in proper order", "No Additional Info", ""); if( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { goto Exit; } // positioning tests 2 for( uiLoop = 0; uiLoop < uiNumNames; uiLoop++) { if ( uiLoop == 0) { if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, pSearchKey, XFLM_FIRST))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } } else { if ( RC_BAD( rc = pSearchKey->setUTF8( 0, (FLMBYTE *)ppszNames[uiLoop][0]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pSearchKey->setUTF8( 1, (FLMBYTE *)ppszNames[uiLoop][1]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, pSearchKey, XFLM_EXACT))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = pResultNode->getFirstChild( m_pDb, &pFirstNameNode))) { MAKE_FLM_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getNextSibling( m_pDb, &pLastNameNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][0]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pLastNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][1]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getPosition( m_pDb, &uiPos))) { MAKE_FLM_ERROR_STRING( "getPosition failed.", m_szDetails, rc); goto Exit; } if ( uiPos != uiLoop) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected position", m_szDetails, rc); goto Exit; } } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( pResultNode) { pResultNode->Release(); } if( pFirstNameNode) { pFirstNameNode->Release(); } if( pLastNameNode) { pLastNameNode->Release(); } if( pSearchKey) { pSearchKey->Release(); } return rc; }
/**************************************************************************** Desc: Adds a field ID to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddField( HFCURSOR hCursor, FLMUINT uiFldId, FLMUINT uiFlags) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FQNODE * pTmpQNode; FLMUINT uiPath [2]; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (uiFldId == FLM_RECID_FIELD) { uiFlags |= FLM_SINGLE_VALUED; } // Set up a field path with one element in it. uiPath [0] = uiFldId; uiPath [1] = 0; if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, FLM_FLD_PATH, &uiPath [0], 0, pCursor->QTInfo.uiFlags, &pTmpQNode))) { pTmpQNode->pQAtom->uiFlags |= uiFlags; pCursor->QTInfo.pCurAtomNode = pTmpQNode; if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } return( rc); }
/**************************************************************************** Desc: Finds the location of a wildcard in the internal text string, if any. ****************************************************************************/ FSTATIC RCODE flmUTF8FindWildcard( const FLMBYTE * pucValue, FLMUINT * puiCharPos, FLMUINT * puiCompareRules) { RCODE rc = NE_SFLM_OK; const FLMBYTE * pucSaveVal; const FLMBYTE * pucStart = pucValue; FLMUNICODE uzChar; FLMUINT uiCompareRules = *puiCompareRules; flmAssert( pucValue); *puiCharPos = FLM_MAX_UINT; for( ;;) { pucSaveVal = pucValue; if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar))) { goto Exit; } if (!uzChar) { break; } if ((uzChar = f_convertChar( uzChar, uiCompareRules)) == 0) { continue; } if (uzChar == ASCII_WILDCARD) { *puiCharPos = (FLMUINT)(pucSaveVal - pucStart); goto Exit; } if (uzChar != ASCII_SPACE) { // Once we hit a non-space character - except for the wildcard, // we can remove the ignore leading space rule. uiCompareRules &= (~(FLM_COMP_IGNORE_LEADING_SPACE)); if (uzChar == ASCII_BACKSLASH) { // Skip the escaped character if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar))) { goto Exit; } if (!uzChar) { rc = RC_SET( NE_SFLM_Q_BAD_SEARCH_STRING); goto Exit; } } } } Exit: *puiCompareRules = uiCompareRules; return( rc); }
/**************************************************************************** Desc: Forces a checkpoint on the database. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbCheckpoint( HFDB hDb, FLMUINT uiTimeout) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bStartedTrans; bStartedTrans = FALSE; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_CHECKPOINT))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiTimeout))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } // Start an update transaction. Must not already be one going. if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, uiTimeout | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } // Commit the transaction, forcing it to be checkpointed. bStartedTrans = FALSE; pDb->bHadUpdOper = FALSE; if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) { goto Exit; } Exit: if (bStartedTrans) { (void)flmAbortDbTrans( pDb); } flmExit( FLM_DB_CHECKPOINT, pDb, rc); return( rc); }
/**************************************************************************** Desc: Method to get a new F_BtResultSet object. This method will create a new collection for the result set to use before handing it off. ****************************************************************************/ RCODE F_DbCheck::getBtResultSet( F_BtResultSet ** ppBtRSet ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; F_BtResultSet * pBtRSet = NULL; F_Database * pDatabase = NULL; // If there is already a BtResultSet, let's get rid of it. if (*ppBtRSet) { (*ppBtRSet)->Release(); *ppBtRSet = NULL; } // Create the new BtResultSet object first. if ((pBtRSet = f_new F_BtResultSet( m_pResultSetDb, m_pBtPool)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } pDatabase = m_pResultSetDb->m_pDatabase; for (;;) { // Now create a new collection. Randomly select a collection number to use. uiCollection = m_pRandGen->getUINT32( 100, XFLM_MAX_COLLECTION_NUM); // Check to see if it already exists. if (RC_OK( rc = pDatabase->lFileCreate( m_pResultSetDb, &pBtRSet->m_Collection.lfInfo, &pBtRSet->m_Collection, uiCollection, XFLM_LF_COLLECTION, FALSE, TRUE))) { break; } if (rc != NE_XFLM_EXISTS) { goto Exit; } rc = NE_XFLM_OK; } *ppBtRSet = pBtRSet; pBtRSet = NULL; Exit: if (pBtRSet) { pBtRSet->Release(); } return rc; }
/**************************************************************************** Desc: This routine starts a transaction for the specified database. The transaction may be part of an overall larger transaction. ****************************************************************************/ RCODE flmBeginDbTrans( FDB * pDb, FLMUINT uiTransType, FLMUINT uiMaxLockWait, FLMUINT uiFlags, FLMBYTE * pucLogHdr) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMBOOL bMutexLocked = FALSE; FLMBYTE * pucLastCommittedLogHdr; DB_STATS * pDbStats = pDb->pDbStats; if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } // Initialize a few things - as few as is necessary to avoid // unnecessary overhead. pDb->eAbortFuncId = FLM_UNKNOWN_FUNC; pDb->AbortRc = FERR_OK; pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; pDb->KrefCntrl.bKrefSetup = FALSE; pDb->uiTransType = uiTransType; pDb->uiThreadId = (FLMUINT)f_threadId(); pDb->uiTransCount++; // Link the FDB to the file's most current FDICT structure, // if there is one. // // Also, if it is a read transaction, link the FDB // into the list of read transactions off of // the FFILE structure. f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; if (pFile->pDictList) { // Link the FDB to the FDICT. flmLinkFdbToDict( pDb, pFile->pDictList); } // If it is a read transaction, link into the list of // read transactions off of the FFILE structure. Until we // get the log header transaction ID below, we set uiCurrTransID // to zero and link this transaction in at the beginning of the // list. if (uiTransType == FLM_READ_TRANS) { flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); // Link in at the end of the transaction list. pDb->pNextReadTrans = NULL; if ((pDb->pPrevReadTrans = pFile->pLastReadTrans) != NULL) { // Make sure transaction IDs are always in ascending order. They // should be at this point. flmAssert( pFile->pLastReadTrans->LogHdr.uiCurrTransID <= pDb->LogHdr.uiCurrTransID); pFile->pLastReadTrans->pNextReadTrans = pDb; } else { pFile->pFirstReadTrans = pDb; } pFile->pLastReadTrans = pDb; pDb->uiInactiveTime = 0; if( uiFlags & FLM_DONT_KILL_TRANS) { pDb->uiFlags |= FDB_DONT_KILL_TRANS; } else { pDb->uiFlags &= ~FDB_DONT_KILL_TRANS; } if (pucLogHdr) { f_memcpy( pucLogHdr, &pDb->pFile->ucLastCommittedLogHdr[0], LOG_HEADER_SIZE); } } f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; if( uiFlags & FLM_DONT_POISON_CACHE) { pDb->uiFlags |= FDB_DONT_POISON_CACHE; } else { pDb->uiFlags &= ~FDB_DONT_POISON_CACHE; } // Put an exclusive lock on the database if we are not in a read // transaction. Read transactions require no lock. if (uiTransType != FLM_READ_TRANS) { flmAssert( pDb->pIxStats == NULL); // Set the bHadUpdOper to TRUE for all transactions to begin with. // Many calls to flmBeginDbTrans are internal, and we WANT the // normal behavior at the end of the transaction when it is // committed or aborted. The only time this flag will be set // to FALSE is when the application starts the transaction as // opposed to an internal starting of the transaction. pDb->bHadUpdOper = TRUE; // Initialize the count of blocks changed to be 0 pDb->uiBlkChangeCnt = 0; if (RC_BAD( rc = dbLock( pDb, uiMaxLockWait))) { goto Exit; } // If there was a problem with the RFL volume, we must wait // for a checkpoint to be completed before continuing. // The checkpoint thread looks at this same flag and forces // a checkpoint. If it completes one successfully, it will // reset this flag. // // Also, if the last forced checkpoint had a problem // (pFile->CheckpointRc != FERR_OK), we don't want to // start up a new update transaction until it is resolved. if (!pFile->pRfl->seeIfRflVolumeOk() || RC_BAD( pFile->CheckpointRc)) { rc = RC_SET( FERR_MUST_WAIT_CHECKPOINT); goto Exit; } // Set the first log block address to zero. pFile->uiFirstLogBlkAddress = 0; // Header must be read before opening roll forward log file to make // sure we have the most current log file and log options. f_memcpy( pFile->ucUncommittedLogHdr, pucLastCommittedLogHdr, LOG_HEADER_SIZE); flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); // Need to increment the current checkpoint for update transactions // so that it will be correct when we go to mark cache blocks. if (pDb->uiFlags & FDB_REPLAYING_RFL) { // During recovery we need to set the transaction ID to the // transaction ID that was logged. pDb->LogHdr.uiCurrTransID = pFile->pRfl->getCurrTransID(); } else { pDb->LogHdr.uiCurrTransID++; } f_mutexLock( gv_FlmSysData.hShareMutex); // Link FDB to the most current local dictionary, if there // is one. if (pFile->pDictList != pDb->pDict && pFile->pDictList) { flmLinkFdbToDict( pDb, pFile->pDictList); } pFile->uiUpdateTransID = pDb->LogHdr.uiCurrTransID; f_mutexUnlock( gv_FlmSysData.hShareMutex); // Set the transaction EOF to the current file EOF pDb->uiTransEOF = pDb->LogHdr.uiLogicalEOF; // Put the transaction ID into the uncommitted log header. UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, &pFile->ucUncommittedLogHdr [LOG_CURR_TRANS_ID]); if (pucLogHdr) { f_memcpy( pucLogHdr, &pDb->pFile->ucUncommittedLogHdr [0], LOG_HEADER_SIZE); } } if (pDbStats) { f_timeGetTimeStamp( &pDb->TransStartTime); } // If we do not have a dictionary, read it in from disk. // NOTE: This should only happen when we are first opening // the database. if (!pDb->pDict) { flmAssert( pDb->pFile->uiFlags & DBF_BEING_OPENED); if (RC_BAD( rc = fdictRebuild( pDb))) { if (pDb->pDict) { flmFreeDict( pDb->pDict); pDb->pDict = NULL; } goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); // At this point, we will not yet have opened the database for // general use, so there is no way that any other thread can have // created a dictionary yet. flmAssert( pDb->pFile->pDictList == NULL); // Link the new local dictionary to its file structure. flmLinkDictToFile( pDb->pFile, pDb->pDict); f_mutexUnlock( gv_FlmSysData.hShareMutex); } Exit: if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } if (uiTransType != FLM_READ_TRANS) { if (RC_OK( rc)) { rc = pFile->pRfl->logBeginTransaction( pDb); } #ifdef FLM_DBG_LOG flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, 0, 0, rc, "TBeg"); #endif } if( uiTransType == FLM_UPDATE_TRANS && gv_FlmSysData.UpdateEvents.pEventCBList) { flmTransEventCallback( F_EVENT_BEGIN_TRANS, (HFDB)pDb, rc, (FLMUINT)(RC_OK( rc) ? pDb->LogHdr.uiCurrTransID : (FLMUINT)0)); } if (RC_BAD( rc)) { // If there was an error, unlink the database from the transaction // structure as well as from the FDICT structure. flmUnlinkDbFromTrans( pDb, FALSE); if (pDb->pStats) { (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); } } return( rc); }
/******************************************************************** Desc: This routine follows all of the blocks in a chain, verifying that they are properly linked. It also verifies each block's header. *********************************************************************/ RCODE F_DbCheck::verifyBlkChain( BLOCK_INFO * pBlkInfo, FLMUINT uiLocale, FLMUINT uiFirstBlkAddr, FLMUINT uiBlkType, FLMBOOL * pbStartOverRV ) { RCODE rc = NE_XFLM_OK; FLMINT32 i32VerifyCode = 0; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiPrevBlkAddress; FLMUINT uiBlkCount = 0; STATE_INFO StateInfo; FLMBOOL bStateInitialized = FALSE; FLMUINT64 ui64SaveBytesExamined; FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); FLMUINT uiMaxBlocks = (FLMUINT)(FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(), m_pDb->m_uiLogicalEOF) / (FLMUINT64)uiBlockSize); uiPrevBlkAddress = 0; /* There must be at least ONE block if it is the LFH chain. */ if ((uiBlkType == BT_LFH_BLK) && (uiFirstBlkAddr == 0)) { i32VerifyCode = FLM_BAD_LFH_LIST_PTR; (void)chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, 0, 0, 0, 0); goto Exit; } /* Read through all of the blocks, verifying them as we go. */ Restart_Chain: uiBlkCount = 0; flmInitReadState( &StateInfo, &bStateInitialized, (FLMUINT)m_pDb->m_pDatabase-> m_lastCommittedDbHdr.ui32DbVersion, m_pDb, NULL, (FLMUINT)((uiBlkType == BT_FREE) ? (FLMUINT)0xFF : (FLMUINT)0), uiBlkType, NULL); ui64SaveBytesExamined = m_Progress.ui64BytesExamined; StateInfo.ui32BlkAddress = (FLMUINT32)uiFirstBlkAddr; while ((StateInfo.ui32BlkAddress != 0) && (uiBlkCount < uiMaxBlocks)) { StateInfo.pBlkHdr = NULL; if( RC_BAD( rc = blkRead( StateInfo.ui32BlkAddress, &pBlkHdr, &pSCache, &i32VerifyCode))) { if (rc == NE_XFLM_OLD_VIEW) { FLMUINT uiSaveDictSeq = m_pDb->m_pDict->getDictSeq(); if (RC_BAD( rc = getDictInfo())) goto Exit; // If the dictionary ID changed, start over. if (m_pDb->m_pDict->getDictSeq() != uiSaveDictSeq) { *pbStartOverRV = TRUE; goto Exit; } m_Progress.ui64BytesExamined = ui64SaveBytesExamined; goto Restart_Chain; } pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; rc = chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, StateInfo.ui32BlkAddress, 0, 0, 0); } StateInfo.pBlkHdr = pBlkHdr; uiBlkCount++; m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; if (RC_BAD( rc = chkCallProgFunc())) { goto Exit; } f_yieldCPU(); if ((i32VerifyCode = flmVerifyBlockHeader( &StateInfo, pBlkInfo, uiBlockSize, 0xFFFFFFFF, uiPrevBlkAddress, TRUE)) != 0) { pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, StateInfo.ui32BlkAddress, 0, 0, 0); goto Exit; } uiPrevBlkAddress = StateInfo.ui32BlkAddress; StateInfo.ui32BlkAddress = pBlkHdr->ui32NextBlkInChain; } if (StateInfo.ui32BlkAddress != 0 && RC_OK( m_LastStatusRc)) { switch (uiBlkType) { case BT_LFH_BLK: i32VerifyCode = FLM_BAD_LFH_LIST_END; break; case BT_FREE: i32VerifyCode = FLM_BAD_AVAIL_LIST_END; break; } pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, (FLMUINT32)uiPrevBlkAddress, 0, 0, 0); goto Exit; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } else if( pBlkHdr) { f_free( &pBlkHdr); } if (RC_OK(rc) && (i32VerifyCode != 0)) { rc = RC_SET( NE_XFLM_DATA_ERROR); } return( rc); }
/**************************************************************************** Desc: Opens an existing 64-bit file ****************************************************************************/ RCODE F_MultiFileHdl::openFile( const char * pszPath) { RCODE rc = NE_FLM_OK; IF_FileSystem * pFileSystem = f_getFileSysPtr(); IF_DirHdl * pDir = NULL; FLMUINT uiTmp; FLMUINT uiHighFileNum = 0; FLMUINT64 ui64HighOffset = 0; if( m_bOpen) { rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); goto Exit; } if( RC_BAD( pFileSystem->doesFileExist( pszPath)) || !pFileSystem->isDir( pszPath)) { rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); goto Exit; } f_strcpy( m_szPath, pszPath); // Create the lock file if( RC_BAD( rc = createLockFile( m_szPath))) { goto Exit; } // Need to determine the current EOF if( RC_BAD( rc = pFileSystem->openDir( m_szPath, (char *)"*.64", &pDir))) { goto Exit; } // Find all data files to determine the EOF for( rc = pDir->next(); !RC_BAD( rc); rc = pDir->next()) { if( RC_OK( getFileNum( pDir->currentItemName(), &uiTmp))) { if( uiTmp >= uiHighFileNum) { uiHighFileNum = uiTmp; ui64HighOffset = pDir->currentItemSize(); } } } rc = NE_FLM_OK; m_ui64EOF = (((FLMUINT64)uiHighFileNum) * m_uiMaxFileSize) + ui64HighOffset; m_bOpen = TRUE; Exit: if( pDir) { pDir->Release(); } // Release the lock file if( RC_BAD( rc)) { releaseLockFile( m_szPath, FALSE); } return( rc); }
/**************************************************************************** Desc: This is the function that the HTTP server calls when it wants to display one of our pages ****************************************************************************/ int flmHttpCallback( HRequest * pHRequest, void * //pvUserData ) { RCODE rc = FERR_OK; F_WebPage * pPage = NULL; char * pszPath = NULL; char * pszQuery = NULL; char * pszTemp = NULL; const char * pszConstTemp = NULL; #define MAX_PARAMS 10 const char * pszParams[ MAX_PARAMS]; FLMUINT uiNumParams; // If we get a NULL for the pHRequest object, then we are shutting down... if (pHRequest == NULL) { // Remove the globals that enable the secure pages... gv_FlmSysData.HttpConfigParms.fnSetGblValue( FLM_SECURE_PASSWORD, "", 0); gv_FlmSysData.HttpConfigParms.fnSetGblValue( FLM_SECURE_EXPIRATION, "", 0); // Delete the web page factory object if (gv_pWPFact) { gv_pWPFact->Release( NULL); } gv_pWPFact = NULL; goto Exit; } // Increment the use count (helps ensure that the function pointers // that display() references don't go away while display() still needs // them. f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); gv_FlmSysData.HttpConfigParms.uiUseCount++; f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); // Must not access any HRequest function pointers prior to incrementing the // use count. if( !gv_FlmSysData.HttpConfigParms.fnReqPath) { flmAssert( 0); rc = RC_SET( FERR_FAILURE); goto Exit; } // If the web page factory does not exist yet, then we need to create it. if (!gv_pWPFact) { f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); // In the time it took us to get the lock, some other thread might // have come along and created the factory already... if (!gv_pWPFact) { if ((gv_pWPFact = f_new F_WebPageFactory) == NULL) { rc = RC_SET( FERR_MEM); f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); goto Exit; } } f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); } pszConstTemp = gv_FlmSysData.HttpConfigParms.fnReqPath( pHRequest); flmAssert( pszConstTemp); if( RC_BAD( rc = f_alloc( f_strlen( pszConstTemp) + 1, &pszPath))) { goto Exit; } f_strcpy( pszPath, pszConstTemp); pszConstTemp = gv_FlmSysData.HttpConfigParms.fnReqQuery( pHRequest); if( pszConstTemp) { if( RC_BAD( rc = f_alloc( f_strlen( pszConstTemp) + 1, &pszQuery))) { goto Exit; } f_strcpy( pszQuery, pszConstTemp); pszConstTemp = pszQuery; } else // This URL had no query string... { // If pszQuery is NULL, it causes problems further down, so we'll // make it a pointer to a null string... if( RC_BAD( rc = f_alloc( 1, &pszQuery))) { goto Exit; } pszQuery[0] = '\0'; } // Strip off pszURLString (and the next '/', if there is one) from the request and store // what's left as pszParams[0]. // (ie: /coredb/FlmSysData --> FlmSysData) // Note: The reason we're checking for the URL string first is because if // we're using our own http stack, then this callback is called for every // http request and we don't want to crash if we've got a short URI. // When we're running under DS, we're guarenteed that the URLString will // be part of the URI. if( f_strlen( pszPath) >= gv_FlmSysData.HttpConfigParms.uiURLStringLen) { pszConstTemp = pszPath + gv_FlmSysData.HttpConfigParms.uiURLStringLen; if( *pszConstTemp == '/') { pszConstTemp++; } } else { pszConstTemp = pszPath; } pszParams[0] = pszConstTemp; uiNumParams = 1; // Parse parameters in the query string // Note that it's technically incorrect to have more than one ? in a // URL, but we didn't know that when we first started creating some of // these pages and as a result, some queries are in the form of: // ?name1=value1?name2=value2?name3=value3... (which is improper) and // some have the form: // ?name1=value1&name2=value2&name3=value3... (which is correct). pszTemp = pszQuery; while( *pszTemp != 0) { flmAssert( uiNumParams < MAX_PARAMS); pszParams[ uiNumParams] = pszTemp; uiNumParams++; pszTemp = tokenizer( pszTemp, '?', '&'); if (*pszTemp) { *pszTemp = '\0'; pszTemp++; } } // Tell the factory to create the page if (RC_BAD( rc = gv_pWPFact->create( pszParams[0], &pPage, pHRequest))) { goto Exit; } pPage->setMembers( pHRequest); // display the page if( RC_BAD( rc = pPage->display (uiNumParams, &pszParams[0]))) { goto Exit; } Exit: // Decrement the use count if( pHRequest) { f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); if( gv_FlmSysData.HttpConfigParms.uiUseCount > 0) { gv_FlmSysData.HttpConfigParms.uiUseCount--; } else { flmAssert( 0); } f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); } if (pPage) { gv_pWPFact->Release( &pPage); } if (pszPath) { f_free( &pszPath); } if (pszQuery) { f_free( &pszQuery); } return (int)rc; }
/**************************************************************************** Desc: Reads data from the file ****************************************************************************/ RCODE F_MultiFileHdl::read( FLMUINT64 ui64Offset, // Offset to begin reading FLMUINT uiLength, // Number of bytes to read void * pvBuffer, // Buffer FLMUINT * puiBytesRead) // [out] Number of bytes read { RCODE rc = NE_FLM_OK; FLMUINT uiFileNum = getFileNum( ui64Offset); FLMUINT uiFileOffset = getFileOffset( ui64Offset); FLMUINT uiTmp; FLMUINT uiTotalBytesRead = 0; FLMUINT uiBytesToRead; FLMUINT uiMaxReadLen; IF_FileHdl * pFileHdl; // Handle the case of a 0-byte read if( !uiLength) { if( ui64Offset >= m_ui64EOF) { rc = RC_SET( NE_FLM_IO_END_OF_FILE); } goto Exit; } // Read the data file(s), moving to new files as needed. for( ;;) { if( ui64Offset >= m_ui64EOF) { rc = RC_SET( NE_FLM_IO_END_OF_FILE); goto Exit; } uiMaxReadLen = m_uiMaxFileSize - uiFileOffset; f_assert( uiMaxReadLen != 0); uiTmp = (uiLength >= uiMaxReadLen ? uiMaxReadLen : uiLength); uiBytesToRead = (((FLMUINT64)uiTmp > (FLMUINT64)(m_ui64EOF - ui64Offset)) ? (FLMUINT)(m_ui64EOF - ui64Offset) : uiTmp); if( RC_BAD( rc = getFileHdl( uiFileNum, FALSE, &pFileHdl))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND) { // Handle the case of a sparse file by filling the unread // portion of the buffer with zeros. f_memset( pvBuffer, 0, uiBytesToRead); uiTmp = uiBytesToRead; rc = NE_FLM_OK; } else { goto Exit; } } else { if( RC_BAD( rc = pFileHdl->read( uiFileOffset, uiBytesToRead, pvBuffer, &uiTmp))) { if( rc == NE_FLM_IO_END_OF_FILE) { // Handle the case of a sparse file by filling the unread // portion of the buffer with zeros. f_memset( &(((FLMBYTE *)(pvBuffer))[ uiTmp]), 0, (FLMUINT)(uiBytesToRead - uiTmp)); uiTmp = uiBytesToRead; rc = NE_FLM_OK; } else { goto Exit; } } } uiTotalBytesRead += uiTmp; uiLength -= uiTmp; if( !uiLength) { break; } // Set up for next read pvBuffer = ((FLMBYTE *)pvBuffer) + uiTmp; ui64Offset += uiTmp; uiFileNum = getFileNum( ui64Offset); uiFileOffset = getFileOffset( ui64Offset); } Exit: *puiBytesRead = uiTotalBytesRead; return( rc); }
/**************************************************************************** Desc: Returns specific information about the most recent error that occured within FLAIM. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmGetDiagInfo( HFDB hDb, eDiagInfoType eDiagCode, void * pvDiagInfo) { RCODE rc = FERR_OK; FDB * pDb; if ((pDb = (FDB *)hDb) == NULL) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } fdbUseCheck( pDb); /* Now, copy over the data into the users variable */ switch( eDiagCode) { case FLM_GET_DIAG_INDEX_NUM : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_INDEX_NUM)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiIndexNum; } break; case FLM_GET_DIAG_DRN : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_DRN)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiDrn; } break; case FLM_GET_DIAG_FIELD_NUM : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_FIELD_NUM)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiFieldNum; } break; case FLM_GET_DIAG_FIELD_TYPE : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_FIELD_TYPE)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiFieldType; } break; case FLM_GET_DIAG_ENC_ID : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_ENC_ID)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiEncId; } break; default: flmAssert( 0); rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: if( pDb) { fdbUnuse( pDb); } return( rc); }
/**************************************************************************** Desc: This routine obtains exclusive access to a 64-bit file by creating a .lck file. The object holds the .lck file open as long as the 64-bit file is open. ****************************************************************************/ RCODE F_MultiFileHdl::createLockFile( const char * pszBasePath) { RCODE rc = NE_FLM_OK; char szLockPath [F_PATH_MAX_SIZE]; F_FileHdl * pLockFileHdl = NULL; FLMUINT uiIoFlags = FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYRW; IF_FileSystem * pFileSystem = f_getFileSysPtr(); f_strcpy( szLockPath, pszBasePath); pFileSystem->pathAppend( szLockPath, "64.LCK"); // Attempt to create the lock file. If it fails, the lock file // may have been left because of a crash. Hence, we first try // to delete the file. If that succeeds, we then attempt to // create the file again. If it, or the 2nd create fail, we simply // return an access denied error. if( RC_BAD( rc = f_allocFileHdl( &pLockFileHdl))) { goto Exit; } #ifndef FLM_UNIX // On Unix, we do not want to delete the file because it // will succeed even if someone else has the file open. uiIoFlags |= FLM_IO_DELETE_ON_RELEASE; #endif if( RC_BAD( pLockFileHdl->createFile( szLockPath, uiIoFlags))) { #ifndef FLM_UNIX if (RC_BAD( pFileSystem->deleteFile( szLockPath))) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } else if (RC_BAD( pLockFileHdl->createFile( szLockPath, uiIoFlags))) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } #else if( RC_BAD( pLockFileHdl->openFile( szLockPath, uiIoFlags))) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } #endif } #ifdef FLM_UNIX if( RC_BAD( pLockFileHdl->lock())) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } #endif m_pLockFileHdl = pLockFileHdl; pLockFileHdl = NULL; Exit: if (pLockFileHdl) { (void)pLockFileHdl->closeFile(); pLockFileHdl->Release(); pLockFileHdl = NULL; } return( rc); }
/**************************************************************************** Desc: Insert the entry into the btree - should be positioned ****************************************************************************/ RCODE F_BtreeRoot::insert( void * pvEntry) { RCODE rc = NE_FLM_OK; FLMUINT uiCurLevel; FLMBYTE ucEntryBuf[FBTREE_MAX_LEVELS][DYNSSET_MAX_FIXED_ENTRY_SIZE]; FLMUINT uiNewBlkAddr; if (RC_OK( rc = m_BTStack[0]->insert( pvEntry))) { goto Exit; } // Failed to input at the left level. Do block split(s). // This is an iterative and NOT a recursive split algorithm. // The debugging, and cases to test should be lots easier this way. f_memcpy( ucEntryBuf[0], pvEntry, m_uiEntrySize); uiCurLevel = 0; uiNewBlkAddr = FBTREE_END; for(;;) { // Split while adding the element. if (RC_BAD( rc = (m_BTStack[uiCurLevel])->split( this, ucEntryBuf[ uiCurLevel], uiNewBlkAddr, ucEntryBuf[ uiCurLevel+1], &uiNewBlkAddr))) { goto Exit; } uiCurLevel++; flmAssert( uiCurLevel < m_uiLevels); if (RC_OK( rc = m_BTStack[uiCurLevel]->insertEntry( ucEntryBuf[uiCurLevel], uiNewBlkAddr))) { goto Exit; } // Only returns NE_FLM_OK or FAILURE. // Root split? if (uiCurLevel + 1 == m_uiLevels) { flmAssert( m_uiLevels + 1 <= FBTREE_MAX_LEVELS); if (m_uiLevels + 1 > FBTREE_MAX_LEVELS) { rc = RC_SET( NE_FLM_BTREE_FULL); goto Exit; } // Need to split the root block. rc = ((F_BtreeRoot *)m_BTStack[uiCurLevel])->split( ucEntryBuf[uiCurLevel], uiNewBlkAddr ); break; } } Exit: if (RC_OK(rc)) { m_uiTotalEntries++; } return( rc); }
/**************************************************************************** Desc: Parse a single line from the ini file into its name, value and comment parts. ****************************************************************************/ RCODE F_IniFile::parseBuffer( char * pszBuf, FLMUINT uiNumBytes) { RCODE rc = NE_FLM_OK; FLMUINT uiCurrentChar = 0; char * pszNameStart = NULL; char * pszNameEnd = NULL; char * pszValStart = NULL; char * pszValEnd = NULL; char * pszCommentStart = NULL; INI_LINE * pLine = NULL; FLMUINT uiStrLen = 0; f_assert( pszBuf); f_assert( uiNumBytes); // Start looking for the parameter name... while (uiCurrentChar < uiNumBytes) { if( !isWhiteSpace( pszBuf[uiCurrentChar])) { if (pszBuf[uiCurrentChar] == '#') { goto Comment; } else { pszNameStart = &pszBuf[uiCurrentChar]; break; } } uiCurrentChar++; } // We've found a param name, now mark the end of it // We determine the end by looking for whitespace or '=' // or '#' while (uiCurrentChar < uiNumBytes) { if( isWhiteSpace( pszBuf[uiCurrentChar]) || (pszBuf[uiCurrentChar] == '=') || (pszBuf[uiCurrentChar] == '#')) { pszNameEnd = &pszBuf[uiCurrentChar-1]; break; } uiCurrentChar++; } if( (uiCurrentChar == uiNumBytes) && (pszNameEnd == NULL) ) { pszNameEnd = &pszBuf[uiCurrentChar - 1]; } // Now, there may be a value part or a comment part next. If there's a // value, it had better be preceeded by an '=' while( (uiCurrentChar < uiNumBytes) && isWhiteSpace( pszBuf[uiCurrentChar]) ) { uiCurrentChar++; } if( uiCurrentChar < uiNumBytes && pszBuf[ uiCurrentChar] == '#') { goto Comment; } if( uiCurrentChar < uiNumBytes && pszBuf[uiCurrentChar] != '=' ) { rc = RC_SET( NE_FLM_SYNTAX); goto Exit; } // Ok - at this point pszBuf[uiCurrentChar] contains an =. Skip over // the = and any whitespace that follows. while( uiCurrentChar < uiNumBytes) { uiCurrentChar++; if( !isWhiteSpace( pszBuf[uiCurrentChar])) { pszValStart = &pszBuf[uiCurrentChar]; break; } } // Now mark the end of the value. // We determine the end by looking for whitespace or '#' while( uiCurrentChar < uiNumBytes) { if( isWhiteSpace( pszBuf[uiCurrentChar]) || (pszBuf[uiCurrentChar] == '#')) { pszValEnd = &pszBuf[uiCurrentChar-1]; break; } uiCurrentChar++; } if( uiCurrentChar == uiNumBytes && !pszValEnd) { pszValEnd = &pszBuf[uiCurrentChar-1]; } Comment: // Check out the rest of the line to see if there's a comment while( uiCurrentChar < uiNumBytes) { if( !isWhiteSpace( pszBuf[ uiCurrentChar]) && pszBuf[ uiCurrentChar] != '#') { rc = RC_SET( NE_FLM_SYNTAX); goto Exit; } else if( pszBuf[ uiCurrentChar] == '#') { // Comment found. Set pszCommentStart to the next char pszCommentStart = &pszBuf[uiCurrentChar+1]; break; } uiCurrentChar++; } // Done parsing. Now, assuming the line had any info in it, // store all the strings... if( pszNameStart || pszCommentStart) { if( RC_BAD( rc = m_pool.poolCalloc( sizeof( INI_LINE), (void **)&pLine))) { goto Exit; } if( pszNameStart) { uiStrLen = pszNameEnd - pszNameStart + 1; if( RC_BAD( rc = m_pool.poolAlloc( uiStrLen + 1, (void **)&pLine->pszParamName))) { goto Exit; } f_memcpy( pLine->pszParamName, pszNameStart, uiStrLen); pLine->pszParamName[uiStrLen] = '\0'; } if( pszValStart) { uiStrLen = pszValEnd - pszValStart + 1; if( RC_BAD( rc = m_pool.poolAlloc( uiStrLen + 1, (void **)&pLine->pszParamValue))) { goto Exit; } f_memcpy(pLine->pszParamValue, pszValStart, uiStrLen); pLine->pszParamValue[uiStrLen] = '\0'; } if (pszCommentStart) { uiStrLen = uiNumBytes-(pszCommentStart-pszBuf); if (RC_BAD( rc = m_pool.poolAlloc( uiStrLen + 1, (void **)&pLine->pszComment))) { goto Exit; } f_memcpy(pLine->pszComment, pszCommentStart, uiStrLen); pLine->pszComment[uiStrLen] = '\0'; } // Insert this struct into the linked list if( m_pLastLine) { m_pLastLine->pNext = pLine; } pLine->pPrev = m_pLastLine; pLine->pNext = NULL; m_pLastLine = pLine; if( !m_pFirstLine) { m_pFirstLine = pLine; } } Exit: return( rc); }
/**************************************************************************** Desc: Build a collated key value piece. ****************************************************************************/ RCODE KYCollateValue( FLMBYTE * pucDest, FLMUINT * puiDestLen, IF_PosIStream * pIStream, FLMUINT uiDataType, FLMUINT uiFlags, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT * puiCollationLen, FLMUINT * puiLuLen, FLMUINT uiLanguage, FLMBOOL bFirstSubstring, FLMBOOL bDataTruncated, FLMBOOL * pbDataTruncated, FLMBOOL * pbOriginalCharsLost) { RCODE rc = NE_XFLM_OK; FLMUINT uiDestLen; IF_BufferIStream * pBufferIStream = NULL; FLMUINT uiCharLimit; FLMUINT uiLength; FLMBYTE * pucTmpDest; FLMUINT uiBytesRead; FLMBOOL bHaveData = TRUE; FLMUNICODE uChar; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); if (puiLuLen) { *puiLuLen = 0; } if ((uiDestLen = *puiDestLen) == 0) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } if (uiDataType != XFLM_TEXT_TYPE) { if( !pIStream->remainingSize()) { bHaveData = FALSE; } } else { FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); if( RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if (rc == NE_XFLM_EOF_HIT) { bHaveData = FALSE; rc = NE_XFLM_OK; } else { goto Exit; } } if( RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) { goto Exit; } // The text is expected to be 0-terminated UTF-8 if ((uiFlags & ICD_ESC_CHAR) || (uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_NO_WHITESPACE | XFLM_COMP_NO_UNDERSCORES | XFLM_COMP_NO_DASHES | XFLM_COMP_WHITESPACE_AS_SPACE | XFLM_COMP_IGNORE_LEADING_SPACE | XFLM_COMP_IGNORE_TRAILING_SPACE))) { dynaBuf.truncateData( 0); if (RC_BAD( rc = KYFormatUTF8Text( pIStream, uiFlags, uiCompareRules, &dynaBuf))) { goto Exit; } if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)dynaBuf.getBufferPtr(), dynaBuf.getDataLength()))) { goto Exit; } pIStream = pBufferIStream; } uiCharLimit = uiLimit ? uiLimit : ICD_DEFAULT_LIMIT; if( (uiLanguage >= FLM_FIRST_DBCS_LANG ) && (uiLanguage <= FLM_LAST_DBCS_LANG)) { if( RC_BAD( rc = f_asiaUTF8ToColText( pIStream, pucDest, &uiDestLen, (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE, puiCollationLen, puiLuLen, uiCharLimit, bFirstSubstring, bDataTruncated, pbDataTruncated))) { goto Exit; } } else { if( RC_BAD( rc = flmUTF8ToColText( pIStream, pucDest, &uiDestLen, (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE, puiCollationLen, puiLuLen, uiLanguage, uiCharLimit, bFirstSubstring, bDataTruncated, pbOriginalCharsLost, pbDataTruncated))) { goto Exit; } } } // TRICKY: uiDestLen could be set to zero if text and no value. if (!bHaveData || !uiDestLen) { uiDestLen = 0; goto Exit; } switch (uiDataType) { case XFLM_TEXT_TYPE: break; case XFLM_NUMBER_TYPE: { FLMBYTE ucTmpBuf [FLM_MAX_NUM_BUF_SIZE]; uiLength = (FLMUINT)pIStream->remainingSize(); flmAssert( uiLength <= sizeof( ucTmpBuf)); if (RC_BAD( rc = pIStream->read( ucTmpBuf, uiLength, &uiBytesRead))) { goto Exit; } flmAssert( uiBytesRead == uiLength); if (RC_BAD( rc = flmStorageNum2CollationNum( ucTmpBuf, uiBytesRead, pucDest, &uiDestLen))) { goto Exit; } break; } case XFLM_BINARY_TYPE: { uiLength = (FLMUINT)pIStream->remainingSize(); pucTmpDest = pucDest; if (uiLength >= uiLimit) { uiLength = uiLimit; bDataTruncated = TRUE; } // We don't want any single key piece to "pig out" more // than 256 bytes of the key if (uiDestLen > 256) { uiDestLen = 256; } if (uiLength > uiDestLen) { // Compute length so will not overflow uiLength = uiDestLen; bDataTruncated = TRUE; } else { uiDestLen = uiLength; } // Store as is. if (RC_BAD( rc = pIStream->read( pucTmpDest, uiDestLen, &uiBytesRead))) { goto Exit; } if (bDataTruncated && pbDataTruncated) { *pbDataTruncated = TRUE; } break; } default: { rc = RC_SET( NE_XFLM_CANNOT_INDEX_DATA_TYPE); break; } } Exit: if( pBufferIStream) { pBufferIStream->Release(); } *puiDestLen = uiDestLen; return( rc); }
/************************************************************************** Desc: Get the Flaim collating string and convert back to a text string Ret: Length of new wpStr Notes: Allocates the area for the word string buffer if will be over 256. ***************************************************************************/ RCODE flmColText2StorageText( const FLMBYTE * pucColStr, // Points to the collated string FLMUINT uiColStrLen, // Length of the collated string FLMBYTE * pucStorageBuf, // Output string to build - TEXT string FLMUINT * puiStorageLen, // In: Size of buffer, Out: Bytes used FLMUINT uiLang, FLMBOOL * pbDataTruncated, // Sets to TRUE if data had been truncated FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring { #define LOCAL_CHARS 150 FLMBYTE ucWPStr[ LOCAL_CHARS * 2 + LOCAL_CHARS / 5 ]; // Sample + 20% FLMBYTE * pucWPPtr = NULL; FLMBYTE * pucAllocatedWSPtr = NULL; FLMUINT uiWPStrLen; FLMBYTE * pucStoragePtr; FLMUINT uiUnconvChars; FLMUINT uiTmp; FLMUINT uiMaxStorageBytes = *puiStorageLen; FLMUINT uiMaxWPBytes; FLMUINT uiStorageOffset; FLMBYTE ucTmpSen[ 5]; FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; RCODE rc = NE_FLM_OK; if( uiColStrLen > LOCAL_CHARS) { // If it won't fit, allocate a new buffer if( RC_BAD( rc = f_alloc( SFLM_MAX_KEY_SIZE * 2, &pucWPPtr))) { goto Exit; } pucAllocatedWSPtr = pucWPPtr; uiMaxWPBytes = uiWPStrLen = SFLM_MAX_KEY_SIZE * 2; } else { pucWPPtr = &ucWPStr[ 0]; uiMaxWPBytes = uiWPStrLen = sizeof( ucWPStr); } if( (uiLang >= FLM_FIRST_DBCS_LANG) && (uiLang <= FLM_LAST_DBCS_LANG)) { if( RC_BAD( rc = f_asiaColStr2WPStr( pucColStr, uiColStrLen, pucWPPtr, &uiWPStrLen, &uiUnconvChars, pbDataTruncated, pbFirstSubstring))) { goto Exit; } } else { if( RC_BAD( rc = f_colStr2WPStr( pucColStr, uiColStrLen, pucWPPtr, &uiWPStrLen, uiLang, &uiUnconvChars, pbDataTruncated, pbFirstSubstring))) { goto Exit; } } // Copy word string to the storage string area uiWPStrLen >>= 1; // Convert # of bytes to # of words pucStoragePtr = pucStorageBuf; uiStorageOffset = 0; // Encode the number of characters as a SEN. If pucEncPtr is // NULL, the caller is only interested in the length of the encoded // string, so a temporary buffer is used to call f_encodeSEN. uiTmp = f_encodeSEN( uiWPStrLen - uiUnconvChars, &pucTmpSen); if( (uiStorageOffset + uiTmp) >= uiMaxStorageBytes) { rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucStoragePtr, &ucTmpSen[ 0], uiTmp); uiStorageOffset += uiTmp; // Encode each of the WP characters into UTF-8 while( uiWPStrLen--) { FLMBYTE ucChar; FLMBYTE ucCharSet; FLMUNICODE uChar; // Put the character in a local variable for speed ucChar = *pucWPPtr++; ucCharSet = *pucWPPtr++; if( ucCharSet == 0xFF && ucChar == 0xFF) { uChar = (((FLMUNICODE)*(pucWPPtr + 1)) << 8) | *pucWPPtr; pucWPPtr += 2; uiWPStrLen--; // Skip past 4 bytes for UNICODE } else { if( RC_BAD( rc = f_wpToUnicode( (((FLMUINT16)ucCharSet) << 8) + ucChar, &uChar))) { goto Exit; } } uiTmp = uiMaxStorageBytes - uiStorageOffset; if( RC_BAD( rc = f_uni2UTF8( uChar, &pucStorageBuf[ uiStorageOffset], &uiTmp))) { goto Exit; } uiStorageOffset += uiTmp; } if( uiStorageOffset >= uiMaxStorageBytes) { rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); goto Exit; } // Tack on a trailing NULL byte pucStorageBuf[ uiStorageOffset++] = 0; // Return the length of the storage buffer *puiStorageLen = uiStorageOffset; Exit: if( pucAllocatedWSPtr) { f_free( &pucAllocatedWSPtr); } return( rc); }
/******************************************************************************* Desc: Renames a database *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRename( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszNewDbName, FLMBOOL bOverwriteDestOk, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; IF_FileHdl * pFileHdl = NULL; FLMUINT uiFileNumber; FILE_HDR FileHdr; LOG_HDR LogHdr; DBRenameInfo * pRenameList = NULL; FLMBOOL bFileFound; FLMBYTE * pucBuffer = NULL; FLMBYTE * pucLogHdr; char * pszOldName; char * pszNewName; char * pszOldDataName; char * pszNewDataName; char * pszFullNewName; char szOldBase[ F_FILENAME_SIZE]; char szNewBase[ F_FILENAME_SIZE]; char * pszExtOld; char * pszExtNew; char * pszDataExtOld; char * pszDataExtNew; // Cannot handle empty database name. flmAssert( pszDbName && *pszDbName); flmAssert( pszNewDbName && *pszNewDbName); // Allocate memory for a read buffer, the log header, and various // file names. if( RC_BAD( rc = f_allocAlignedBuffer( 2048 + LOG_HEADER_SIZE + F_PATH_MAX_SIZE * 5, &pucBuffer))) { goto Exit; } pucLogHdr = pucBuffer + 2048; pszOldName = (char *)(pucLogHdr + LOG_HEADER_SIZE); pszNewName = pszOldName + F_PATH_MAX_SIZE; pszOldDataName = pszNewName + F_PATH_MAX_SIZE; pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; // There must be either no directory specified for the new name, or // it must be identical to the old directory. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) { goto Exit; } // Directories must be the same. if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } f_strcpy( pszNewName, pszOldName); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewName, szNewBase))) { goto Exit; } f_strcpy( pszFullNewName, pszNewName); f_strcpy( pszOldName, pszDbName); if( pszDataDir && *pszDataDir) { f_strcpy( pszOldDataName, pszDataDir); f_strcpy( pszNewDataName, pszDataDir); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszOldDataName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewDataName, szNewBase))) { goto Exit; } } else { f_strcpy( pszNewDataName, pszNewName); f_strcpy( pszOldDataName, pszOldName); } // First make sure we have closed the databases and gotten rid of // them from our internal memory tables - in case they had been open. if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszDbName, (void *)pszDataDir))) { goto Exit; } if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszFullNewName, (void *)pszDataDir))) { goto Exit; } gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Open the file so we can get the log header. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszDbName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } // Read the header to get the low and high RFL log // file numbers. if (RC_BAD( flmReadAndVerifyHdrInfo( NULL, pFileHdl, pucBuffer, &FileHdr, &LogHdr, pucLogHdr))) { goto Exit; } // Close the file. pFileHdl->Release(); pFileHdl = NULL; // Start renaming files, beginning with the main DB file. if( RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, bOverwriteDestOk, FALSE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Find where the extension of the old and new database names are pszExtOld = pszOldName + f_strlen( pszOldName) - 1; pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; while (pszExtOld != pszOldName && *pszExtOld != '.') { pszExtOld--; // Both the old db name and old data name have the same // base name, so we can decrement pszDataExtOld // at the same time we decrement pszExtOld. pszDataExtOld--; } if (*pszExtOld != '.') { pszExtOld = pszOldName + f_strlen( pszOldName); pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); } pszExtNew = pszNewName + f_strlen( pszNewName) - 1; pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; while (pszExtNew != pszOldName && *pszExtNew != '.') { pszExtNew--; // Both the new db name and new data name have the same // base name, so we can decrement pszDataExtNew // at the same time we decrement pszExtNew. pszDataExtNew--; } if (*pszExtNew != '.') { pszExtNew = pszNewName + f_strlen( pszNewName); pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); } // Rename the .lck file, if any. This is necessary for UNIX. f_strcpy( pszExtOld, ".lck"); f_strcpy( pszExtNew, ".lck"); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Rename block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtNew); if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER (FileHdr.uiVersionNum); for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtNew); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename roll-forward log files. if (FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to rename one RFL file. if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszDbName, pszRflDir, 1, pszOldName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, 1, pszNewName))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } else { // For 4.3 and greater, rename the RFL directory. if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszDbName, pszRflDir, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, pszNewName, szNewBase))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } Exit: if( pFileHdl) { pFileHdl->Release(); } if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } // Free the list of renamed files. while( pRenameList) { DBRenameInfo * pRenameFile; pRenameFile = pRenameList; pRenameList = pRenameList->pNext; // If we had an error of some sort, attempt to un-rename // the file that had been renamed. if (RC_BAD( rc)) { gv_FlmSysData.pFileSystem->renameFile( pRenameFile->Info.szDstFileName, pRenameFile->Info.szSrcFileName); } f_free( &pRenameFile); } return( rc); }
/**************************************************************************** Desc : Adds an operator to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddOp( HFCURSOR hCursor, QTYPES eOperator, FLMBOOL bResolveUnknown ) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FQNODE * pTmpQNode; FQNODE * pTmpGraftNode; FQNODE * pTmpChildNode; FLMBOOL bDecrementNestLvl = FALSE; FLMUINT uiFlags = bResolveUnknown ? FLM_RESOLVE_UNK : 0; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // If the operator is a left paren, link it as the last sibling in the // argument list of the current operator. if (eOperator == FLM_LPAREN_OP) { (pCursor->QTInfo.uiNestLvl)++; goto Exit; } // If it is a right paren, find the left paren and close it out if (eOperator == FLM_RPAREN_OP) { if (!pCursor->QTInfo.uiNestLvl) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } (pCursor->QTInfo.uiNestLvl)--; goto Exit; } // If it is not an operator, return an error if (!IS_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // If an operator is not expected, bail out if (!(pCursor->QTInfo.uiExpecting & FLM_Q_OPERATOR) && eOperator != FLM_NEG_OP && eOperator != FLM_NOT_OP) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Make a QNODE and find a place for it in the query tree if (RC_BAD( rc = flmCurMakeQNode( &pCursor->QueryPool, eOperator, NULL, 0, uiFlags, &pTmpQNode))) { goto Exit; } pTmpQNode->uiNestLvl = pCursor->QTInfo.uiNestLvl; // If this is the first operator in the query, set the current operator // to it and graft in the current operand as its child. NOTE: there // should always be a current operand at this point. if (!pCursor->QTInfo.pTopNode) { pCursor->QTInfo.pTopNode = pTmpQNode; pCursor->QTInfo.pCurOpNode = pTmpQNode; if (pCursor->QTInfo.pCurAtomNode) { // If the current operand node is a user predicate, the only // thing that can become its parent is a logical operator. if (GET_QNODE_TYPE( pCursor->QTInfo.pCurAtomNode) == FLM_USER_PREDICATE && !IS_LOG_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } flmCurLinkLastChild( pTmpQNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND; goto Exit; } // Go up the stack until an operator whose nest level or precedence is < // this one's is encountered, then link this one in as the last child for (pTmpChildNode = NULL, pTmpGraftNode = pCursor->QTInfo.pCurOpNode; ; pTmpChildNode = pTmpGraftNode, pTmpGraftNode = pTmpGraftNode->pParent) { if (pTmpGraftNode->uiNestLvl < pTmpQNode->uiNestLvl || (pTmpGraftNode->uiNestLvl == pTmpQNode->uiNestLvl && PRECEDENCE( pTmpGraftNode->eOpType) < PRECEDENCE( eOperator))) { // If the node under which this operator is to be grafted already // has two children, or if its child is at a greater nesting level, // link the child as the last child of this operator. Example: // ((A - B) == C) && (((D + E) * F) == G). // When the '*' operator in this expression is added, it will be // grafted as the last child of the '&&' operator. But the '+' // must first be unlinked from the '&&' and then linked as the child // of the '*'. Otherwise, they will be siblings, and the expression // will be evaluated incorrectly. if (pTmpChildNode && (pTmpChildNode->uiNestLvl > pTmpQNode->uiNestLvl || pTmpChildNode->pPrevSib != NULL || pTmpGraftNode->eOpType == FLM_NEG_OP || pTmpGraftNode->eOpType == FLM_NOT_OP)) { flmCurLinkLastChild( pTmpQNode, pTmpChildNode); } // If this operator is to be grafted into the query tree at the leaf // level, link the current operand as its last child. Examples: // in A * (B + C), we want B to be linked to +; // in A + B * C, we want B linked to *. if (pTmpGraftNode == pCursor->QTInfo.pCurOpNode && eOperator != FLM_NEG_OP && eOperator != FLM_NOT_OP) { // If the current operand node is a user predicate, the only // thing that can become its parent is a logical operator. if (pCursor->QTInfo.pCurAtomNode && GET_QNODE_TYPE( pCursor->QTInfo.pCurAtomNode) == FLM_USER_PREDICATE && !IS_LOG_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } flmCurLinkLastChild( pTmpQNode, pCursor->QTInfo.pCurAtomNode); } flmCurLinkLastChild( pTmpGraftNode, pTmpQNode); break; } if (!pTmpGraftNode->pParent) { pCursor->QTInfo.pTopNode = pTmpQNode; flmCurLinkLastChild( pTmpQNode, pTmpGraftNode); break; } } pCursor->QTInfo.pCurOpNode = pTmpQNode; pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND; Exit: if (bDecrementNestLvl) { (pCursor->QTInfo.uiNestLvl)--; } if (pCursor) { pCursor->rc = rc; } return( rc); }