/**************************************************************************** Desc: Insert the entry into the buffer. ****************************************************************************/ RCODE F_BtreeBlk::insertEntry( void * pvEntry, FLMUINT uiChildAddr) { RCODE rc = NE_FLM_OK; FLMBYTE * pucCurEntry; FLMUINT uiShiftBytes; // Always shift down if( entryCount() >= m_uiNumSlots) { rc = RC_SET( NE_FLM_FAILURE); goto Exit; } flmAssert( m_uiPosition != DYNSSET_POSITION_NOT_SET); pucCurEntry = ENTRY_POS( m_uiPosition); if ((uiShiftBytes = (entryCount() - m_uiPosition) * (m_uiEntrySize + m_uiEntryOvhd)) != 0) { // Big hairy assert. Finds coding bugs and corruptions. flmAssert( m_uiPosition * (m_uiEntrySize + m_uiEntryOvhd) + uiShiftBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); f_memmove( pucCurEntry + m_uiEntrySize + m_uiEntryOvhd, pucCurEntry, uiShiftBytes); } f_memcpy( pucCurEntry, pvEntry, m_uiEntrySize); if( m_uiEntryOvhd) { UD2FBA( (FLMUINT32)uiChildAddr, &pucCurEntry[m_uiEntrySize]); } entryCount( entryCount() + 1); m_uiPosition++; m_bDirty = TRUE; Exit: return( rc); }
/******************************************************************** Desc: Edit a field *********************************************************************/ FLMBOOL ViewEdit( FLMBOOL bWriteEntireBlock ) { FLMUINT uiBytesToWrite; FLMUINT uiBytesWritten; FLMUINT uiNum; FLMUINT64 ui64Num; FLMUINT32 ui32Num; FLMUINT16 ui16Num; char szTempBuf[ 100]; F_BLK_HDR * pBlkHdr = NULL; RCODE rc; FLMUINT uiFileOffset; FLMUINT uiFileNumber; FLMBOOL bValEntered; FLMUINT uiBytesRead; IF_FileHdl * pFileHdl; FLMUINT32 ui32CRC; FLMUINT uiBlockOffset; FLMUINT uiBlkAddress = 0; FLMBYTE ucSENValue[ 9]; FLMBYTE * pucSENBuffer = &ucSENValue[0]; FLMUINT uiSENLen = 0; if ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_DISABLED) { ViewShowError( "Cannot modify this value"); return( FALSE); } uiFileOffset = gv_pViewMenuCurrItem->uiModFileOffset; uiFileNumber = gv_pViewMenuCurrItem->uiModFileNumber; switch (gv_pViewMenuCurrItem->uiModType & 0x0F) { case MOD_SEN5: // The SEN value is at most 5 bytes. ui64Num = 0; if (!ViewEditNum( &ui64Num, ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 5, ~((FLMUINT64)0))) { return( FALSE); } // Need to know make a SEN out of this first. uiSENLen = f_encodeSEN( ui64Num, &pucSENBuffer); break; case MOD_SEN9: // The SEN value is at most 5 bytes. ui64Num = 0; if (!ViewEditNum( &ui64Num, ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 9, ~((FLMUINT64)0))) { return( FALSE); } // Need to know make a SEN out of this first. uiSENLen = f_encodeSEN( ui64Num, &pucSENBuffer); break; case MOD_FLMUINT64: uiBytesToWrite = 8; if (!ViewEditNum( &ui64Num, ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 8, ~((FLMUINT64)0))) { return( FALSE); } if (gv_pViewMenuCurrItem->uiModType & MOD_NATIVE) { f_memcpy( szTempBuf, &ui64Num, 8); } else { U642FBA( ui64Num, (FLMBYTE *)szTempBuf); } break; case MOD_FLMUINT32: uiBytesToWrite = 4; if (!ViewEditNum( &ui32Num, ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 4, (FLMUINT64)0xFFFFFFFF)) { return( FALSE); } if (gv_pViewMenuCurrItem->uiModType & MOD_NATIVE) { f_memcpy( szTempBuf, &ui32Num, 4); } else { UD2FBA( ui32Num, (FLMBYTE *)szTempBuf); } break; case MOD_FLMUINT16: uiBytesToWrite = 2; if (!ViewEditNum( &ui16Num, ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 2, (FLMUINT64)0xFFFF)) { return( FALSE); } if (gv_pViewMenuCurrItem->uiModType & MOD_NATIVE) { f_memcpy( szTempBuf, &ui16Num, 2); } else { UW2FBA( ui16Num, (FLMBYTE *)szTempBuf); } break; case MOD_FLMBYTE: uiBytesToWrite = 1; if (!ViewEditNum( &szTempBuf [0], ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), 1, (FLMUINT64)0xFF)) { return( FALSE); } break; case MOD_BINARY: uiBytesToWrite = gv_pViewMenuCurrItem->uiModBufLen; if (HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) { uiFileOffset += gv_pViewMenuCurrItem->uiHorizCurPos; uiBytesToWrite -= gv_pViewMenuCurrItem->uiHorizCurPos; } if (!ViewEditBinary( NULL, (FLMBYTE *)szTempBuf, &uiBytesToWrite, &bValEntered) || !bValEntered) { return( FALSE); } break; case MOD_TEXT: if (!ViewEditText( "Enter Value: ", szTempBuf, gv_pViewMenuCurrItem->uiModBufLen, &bValEntered) || !bValEntered) { return( FALSE); } uiBytesToWrite = gv_pViewMenuCurrItem->uiModBufLen; break; case MOD_LANGUAGE: if( !ViewEditLanguage( &uiNum)) { return( FALSE); } szTempBuf[0] = (FLMBYTE)uiNum; uiBytesToWrite = 1; break; case MOD_CHILD_BLK: if( !ViewEditNum( &uiNum, TRUE, 4, (FLMUINT64)0xFFFFFFFF)) { return( FALSE); } uiBytesToWrite = 4; UD2FBA( (FLMUINT32)uiNum, (FLMBYTE *)szTempBuf); break; case MOD_BITS: if (!ViewEditBits( (FLMBYTE *)&szTempBuf[ 0], ((gv_pViewMenuCurrItem->uiModType & 0xF0) == MOD_HEX), (FLMBYTE)gv_pViewMenuCurrItem->uiModBufLen)) { return( FALSE); } uiBytesToWrite = 1; break; } // Read in the block if necessary if (!bWriteEntireBlock) { pBlkHdr = (F_BLK_HDR *)(&szTempBuf [0]); } else { uiBlockOffset = (FLMUINT)(uiFileOffset % (FLMUINT)gv_ViewDbHdr.ui16BlockSize); uiBlkAddress = FSBlkAddress( uiFileNumber, uiFileOffset - uiBlockOffset); uiFileOffset = uiFileOffset - uiBlockOffset; // Don't convert the block if the address is zero - means we // are updating the database header. if (!ViewBlkRead( uiBlkAddress, &pBlkHdr, !uiBlkAddress ? FALSE : TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, NULL, NULL, &uiBytesRead, FALSE)) { return( FALSE); } uiBytesToWrite = uiBytesRead; // Convert to native format if (!uiBlkAddress) { if (hdrIsNonNativeFormat( (XFLM_DB_HDR *)pBlkHdr)) { convertDbHdr( (XFLM_DB_HDR *)pBlkHdr); } } else { if (blkIsNonNativeFormat( pBlkHdr)) { convertBlk( (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkHdr); } } // Put the data in the appropriate place in the block if ((gv_pViewMenuCurrItem->uiModType & 0x0F) == MOD_BITS) { FLMBYTE ucMask = (FLMBYTE)gv_pViewMenuCurrItem->uiModBufLen; FLMBYTE * pucBuf = (FLMBYTE *)pBlkHdr; // Unset the bits, then OR in the new bits pucBuf [uiBlockOffset] &= (~(ucMask)); pucBuf [uiBlockOffset] |= szTempBuf[ 0]; } else if ((gv_pViewMenuCurrItem->uiModType & 0x0F) == MOD_SEN5 || (gv_pViewMenuCurrItem->uiModType & 0x0F) == MOD_SEN9) { // Need to make sure the size of the original SEN is the same as the // new SEN. const FLMBYTE * pucOrigSEN = (FLMBYTE *)pBlkHdr + uiBlockOffset; FLMBYTE ucBuffer[9]; FLMBYTE * pucBuffer = &ucBuffer[0]; FLMUINT64 ui64Value; FLMUINT uiOrigSENLen; if (RC_BAD( rc = f_decodeSEN64( &pucOrigSEN, (FLMBYTE *)pBlkHdr + gv_ViewDbHdr.ui16BlockSize, &ui64Value))) { ViewShowRCError( "Decoding original SEN value", rc); } uiOrigSENLen = f_encodeSEN( ui64Value, &pucBuffer); if (uiOrigSENLen != uiSENLen) { ViewShowRCError( "SEN Length does not match original", NE_XFLM_FAILURE); } else { f_memcpy( (FLMBYTE *)pBlkHdr + uiBlockOffset, ucSENValue, uiSENLen); } } else { f_memcpy( (FLMBYTE *)pBlkHdr + uiBlockOffset, szTempBuf, uiBytesToWrite); } // Calculate CRC if (!uiBlkAddress) { ui32CRC = calcDbHdrCRC( (XFLM_DB_HDR *)pBlkHdr); ((XFLM_DB_HDR *)pBlkHdr)->ui32HdrCRC = ui32CRC; uiBytesToWrite = sizeof( XFLM_DB_HDR); } else { FLMUINT uiBlkLen; uiBlkLen = gv_ViewDbHdr.ui16BlockSize; #if 0 if ((FLMUINT)pBlkHdr->ui16BlkBytesAvail > (FLMUINT)gv_ViewDbHdr.ui16BlockSize - blkHdrSize( pBlkHdr)) { uiBlkLen = blkHdrSize( pBlkHdr); } else { uiBlkLen = (FLMUINT)(gv_ViewDbHdr.ui16BlockSize - pBlkHdr->ui16BlkBytesAvail); } #endif // Calculate and set the block CRC. ui32CRC = calcBlkCRC( pBlkHdr, uiBlkLen); pBlkHdr->ui32BlkCRC = ui32CRC; } } if (RC_BAD( rc = gv_pSFileHdl->getFileHdl( uiFileNumber, TRUE, &pFileHdl))) { ViewShowRCError( "getting file handle", rc); } // Write the data out to the file else if (RC_BAD( rc = pFileHdl->write( uiFileOffset, uiBytesToWrite, pBlkHdr, &uiBytesWritten))) { ViewShowRCError( "updating file", rc); } else if (RC_BAD( rc = pFileHdl->flush())) { ViewShowRCError( "flushing data to file", rc); } else if (bWriteEntireBlock && !uiBlkAddress) { f_memcpy( &gv_ViewDbHdr, pBlkHdr, sizeof( XFLM_DB_HDR)); } // Free any memory used to read in the data block if (pBlkHdr && pBlkHdr != (F_BLK_HDR *)(&szTempBuf [0])) { f_free( &pBlkHdr); } return( TRUE); }
/**************************************************************************** Desc: This routine aborts an active transaction for a particular database. If the database is open via a server, a message is sent to the server to abort the transaction. Otherwise, the transaction is rolled back locally. ****************************************************************************/ RCODE flmAbortDbTrans( FDB * pDb, FLMBOOL bOkToLogAbort) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiTransType; FLMBYTE * pucLastCommittedLogHdr; FLMBYTE * pucUncommittedLogHdr; FLMBOOL bDumpedCache = FALSE; DB_STATS * pDbStats = pDb->pDbStats; FLMBOOL bKeepAbortedTrans; FLMUINT uiTransId; FLMBOOL bInvisibleTrans; // Get transaction type if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS) { goto Exit; } // No recovery required if it is a read transaction. 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 Unlink_From_Trans; } #ifdef FLM_DBG_LOG flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, 0, 0, FERR_OK, "TAbrt"); #endif pFile->pRfl->clearLogHdrs(); // If the transaction had no update operations, restore it // to its pre-transaction state - make it appear that no // transaction ever happened. pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; uiTransId = pDb->LogHdr.uiCurrTransID; // Free up all keys associated with this database. This is done even // if we didn't have any update operations because the KREF may // have been initialized by key generation operations performed // by cursors, etc. KrefCntrlFree( pDb); // Free any index counts we may have allocated. FSFreeIxCounts( pDb); if (pDb->bHadUpdOper) { // Dump any BLOB structures that should be aborted. FBListAfterAbort( pDb); // Dump any start and stop indexing stubs that should be aborted. flmIndexingAfterAbort( pDb); // Log the abort record to the rfl file, or throw away the logged // records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL // flag. If the RFL volume is bad, we will not attempt to keep this // transaction in the RFL. if (!pFile->pRfl->seeIfRflVolumeOk()) { bKeepAbortedTrans = FALSE; } else { bKeepAbortedTrans = (pucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL]) ? TRUE : FALSE; } } else { bKeepAbortedTrans = FALSE; } // Log an abort transaction record to the roll-forward log or // throw away the entire transaction, depending on the // bKeepAbortedTrans flag. // If the transaction is being "dumped" because of a failed commit, // don't log anything to the RFL. if( bOkToLogAbort) { flmAssert( pDb->LogHdr.uiCurrTransID == pFile->pRfl->getCurrTransID()); if (RC_BAD( rc = pFile->pRfl->logEndTransaction( RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans))) { goto Exit1; } } #ifdef FLM_DEBUG else { // If bOkToLogAbort is FALSE, this always means that either a // commit failed while trying to log an end transaction packet or a // commit packet was logged and the transaction commit subsequently // failed for some other reason. In either case, the RFL should be // in a good state, with its current transaction ID reset to 0. If // not, either bOkToLogAbort is being used incorrectly by the caller // or there is a bug in the RFL logic. flmAssert( pFile->pRfl->getCurrTransID() == 0); } #endif // If there were no operations in the transaction, restore // everything as if the transaction never happened. if (!pDb->bHadUpdOper) { f_mutexLock( gv_FlmSysData.hShareMutex); pFile->uiUpdateTransID = 0; f_mutexUnlock( gv_FlmSysData.hShareMutex); // Pretend we dumped cache - shouldn't be any to worry about at // this point. bDumpedCache = TRUE; goto Exit1; } // Dump ALL modified cache blocks associated with the DB. // NOTE: This needs to be done BEFORE the call to flmGetLogHdrInfo // below, because that call will change pDb->LogHdr.uiCurrTransID, // and that value is used by flmRcaAbortTrans. ScaFreeModifiedBlocks( pDb); flmRcaAbortTrans( pDb); bDumpedCache = TRUE; // Reset the LogHdr from the last committed log header in pFile. flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); if (RC_BAD( rc = flmPhysRollback( pDb, (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_ROLLBACK_EOF]), pFile->uiFirstLogBlkAddress, FALSE, 0))) { goto Exit1; } f_mutexLock( gv_FlmSysData.hShareMutex); // Put the new transaction ID into the log header even though // we are not committing. We want to keep the transaction IDs // incrementing even though we aborted. UD2FBA( (FLMUINT32)uiTransId, &pucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); // Preserve where we are at in the roll-forward log. Even though // the transaction aborted, we may have kept it in the RFL instead of // throw it away. f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_FILE_NUM], &pucUncommittedLogHdr [LOG_RFL_FILE_NUM], 4); f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET], &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET], 4); f_memcpy( &pucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], F_SERIAL_NUM_SIZE); f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], F_SERIAL_NUM_SIZE); // The following items tell us where we are at in the roll-back log. // During a transaction we may log blocks for the checkpoint or for // read transactions. So, even though we are aborting this transaction, // there may be other things in the roll-back log that we don't want // to lose. These items should not be reset until we do a checkpoint, // which is when we know it is safe to throw away the entire roll-back log. f_memcpy( &pucLastCommittedLogHdr [LOG_ROLLBACK_EOF], &pucUncommittedLogHdr [LOG_ROLLBACK_EOF], 4); f_memcpy( &pucLastCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], &pucUncommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], 4); f_mutexUnlock( gv_FlmSysData.hShareMutex); pFile->pRfl->commitLogHdrs( pucLastCommittedLogHdr, pFile->ucCheckpointLogHdr); Exit1: // Dump cache, if not done above. if (!bDumpedCache) { ScaFreeModifiedBlocks( pDb); flmRcaAbortTrans( pDb); bDumpedCache = TRUE; } // Throw away IXD_FIXUPs if (pDb->pIxdFixups) { IXD_FIXUP * pIxdFixup; IXD_FIXUP * pDeleteIxdFixup; pIxdFixup = pDb->pIxdFixups; while (pIxdFixup) { pDeleteIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; f_free( &pDeleteIxdFixup); } pDb->pIxdFixups = NULL; } if (uiTransType != FLM_READ_TRANS && gv_FlmSysData.UpdateEvents.pEventCBList) { flmTransEventCallback( F_EVENT_ABORT_TRANS, (HFDB)pDb, rc, uiTransId); } Unlink_From_Trans: bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE; if (pDb->uiFlags & FDB_HAS_WRITE_LOCK) { RCODE tmpRc; if (RC_BAD( tmpRc = pFile->pRfl->completeTransWrites( pDb, FALSE, FALSE))) { if (RC_OK( rc)) { rc = tmpRc; } } } // Unlink the database from the transaction // structure as well as from the FLDICT structure. flmUnlinkDbFromTrans( pDb, FALSE); if (pDbStats) { FLMUINT64 ui64ElapMilli = 0; flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli); pDbStats->bHaveStats = TRUE; if (uiTransType == FLM_READ_TRANS) { pDbStats->ReadTransStats.AbortedTrans.ui64Count++; pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli += ui64ElapMilli; if (bInvisibleTrans) { pDbStats->ReadTransStats.InvisibleTrans.ui64Count++; pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli += ui64ElapMilli; } } else { pDbStats->UpdateTransStats.AbortedTrans.ui64Count++; pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli += ui64ElapMilli; } } if (pDb->pStats) { (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); } Exit: 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 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: Copy a file that is one of the files of the database. *****************************************************************************/ FSTATIC RCODE flmCopyFile( IF_FileSystem * pFileSystem, FLMUINT uiStartOffset, FLMUINT uiEndOffset, DB_COPY_INFO * pDbCopyInfo, COPIED_NAME ** ppCopiedListRV, FLMBYTE * pucInMemLogHdr, FLMBOOL bOkToTruncate, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; FLMBYTE * pucBuffer = NULL; IF_FileHdl * pSrcFileHdl = NULL; IF_FileHdl * pDestFileHdl = NULL; FLMUINT uiBufferSize = 32768; FLMUINT uiBytesToRead; FLMUINT uiBytesRead; FLMUINT uiBytesWritten; FLMUINT uiOffset; FLMBYTE * pucLogHdr = NULL; FLMUINT uiNewChecksum; FLMBOOL bCreatedDestFile = FALSE; // Open the source file. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pDbCopyInfo->szSrcFileName, gv_FlmSysData.uiFileOpenFlags, &pSrcFileHdl))) { goto Exit; } // First attempt to open the destination file. If it does // not exist, attempt to create it. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileOpenFlags, &pDestFileHdl))) { if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile( pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileCreateFlags, &pDestFileHdl))) { goto Exit; } bCreatedDestFile = TRUE; } // Allocate a buffer for reading and writing if( RC_BAD( rc = f_allocAlignedBuffer( uiBufferSize, &pucBuffer))) { goto Exit; } // Allocate a buffer for the log header if( RC_BAD( rc = f_allocAlignedBuffer( 2048, &pucLogHdr))) { goto Exit; } // If uiStartOffset is 2048, it is the special case of // the control file, and we are not copying the first 2K. // However, we need to set up the first 2K so that if // someone reads the first 2K, it will return them a // maintenance in progress error. if (uiStartOffset == 2048) { // Read the first 2K of the source file. if (RC_BAD( rc = pSrcFileHdl->read( 0, 2048, pucBuffer, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { rc = FERR_OK; } else { goto Exit; } } // Zero out whatever part of the 2K we didn't get on the read. if (uiBytesRead < 2048) { f_memset( &pucBuffer[ uiBytesRead], 0, (int)(2048 - uiBytesRead)); } // Attempt to read the log header from the destination file. // It is OK if we can't read it, because if we created the // destination file, these bytes may not be present. if( bCreatedDestFile || RC_BAD( pDestFileHdl->read( 0, 2048, pucLogHdr, &uiBytesRead))) { f_memset( pucLogHdr, 0, sizeof( 2048)); } // Set the transaction ID to zero. MUST ALSO SET THE TRANS ACTIVE FLAG // TO FALSE - OTHERWISE READERS WILL ATTEMPT TO DECREMENT THE // TRANSACTION ID AND WILL END UP WITH 0xFFFFFFFF - very bad! // We must use zero, because it is the only transaction ID that will not // appear on ANY block. UD2FBA( 0, &pucLogHdr[ 16 + LOG_CURR_TRANS_ID]); // Recalculate the log header checksum so that readers will not get a // checksum error. uiNewChecksum = lgHdrCheckSum( &pucLogHdr[ 16], FALSE); UW2FBA( (FLMUINT16)uiNewChecksum, &pucLogHdr[ 16 + LOG_HDR_CHECKSUM]); f_memcpy( &pucBuffer[ 16], &pucLogHdr[ 16], LOG_HEADER_SIZE); // Write this "special" first 2K into the destination file. // The real first 2K from the source file will be copied in // at a later time. if (RC_BAD( rc = pDestFileHdl->write( 0, 2048, pucBuffer, &uiBytesWritten))) { goto Exit; } // Save the log header to the in-memory version of the log // header as well - if pucInMemLogHdr is NULL, it is pointing // to the pFile->ucLastCommittedLogHdr buffer. if (pucInMemLogHdr) { f_memcpy( pucInMemLogHdr, &pucLogHdr[ 16], LOG_HEADER_SIZE); } } // Read from source file until we hit EOF in the file or // we hit the end offset. uiOffset = uiStartOffset; for (;;) { uiBytesToRead = (FLMUINT)((uiEndOffset - uiOffset >= uiBufferSize) ? uiBufferSize : (FLMUINT)(uiEndOffset - uiOffset)); // Read data from source file. if (RC_BAD( rc = pSrcFileHdl->read( uiOffset, uiBytesToRead, pucBuffer, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { rc = FERR_OK; if (!uiBytesRead) { break; } } else { goto Exit; } } // Write data to destination file. if (RC_BAD( rc = pDestFileHdl->write( uiOffset, uiBytesRead, pucBuffer, &uiBytesWritten))) { goto Exit; } // See if we wrote out the buffer that has the log header // If so, we need to copy it back to the in-memory log // header. if ((pucInMemLogHdr) && (uiOffset <= 16) && (uiOffset + uiBytesWritten >= 16 + LOG_HEADER_SIZE)) { f_memcpy( pucInMemLogHdr, &pucBuffer[ 16 - uiOffset], LOG_HEADER_SIZE); } uiOffset += uiBytesWritten; // Do callback to report progress. if (fnStatusCallback) { pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten; if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_COPY_STATUS, (void *)pDbCopyInfo, (void *)0, UserData))) { goto Exit; } pDbCopyInfo->bNewSrcFile = FALSE; } // Quit once we reach the end offset or we read fewer bytes // than we asked for. if (uiOffset >= uiEndOffset || uiBytesRead < uiBytesToRead) { break; } } // If we overwrote the destination file, as opposed to creating // it, truncate it in case it was larger than the number of // bytes we actually copied. if (!bCreatedDestFile && bOkToTruncate) { if (RC_BAD( rc = pDestFileHdl->truncateFile( uiOffset))) { goto Exit; } } // If the copy succeeded, add the destination name to a list // of destination files. This is done so we can clean up // copied files if we fail somewhere in the overall database // copy. if (ppCopiedListRV) { COPIED_NAME * pCopyName; if( RC_BAD( rc = f_alloc( (FLMUINT)sizeof( COPIED_NAME), &pCopyName))) { goto Exit; } f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName); pCopyName->pNext = *ppCopiedListRV; *ppCopiedListRV = pCopyName; } Exit: if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } if( pucLogHdr) { f_freeAlignedBuffer( &pucLogHdr); } if( pSrcFileHdl) { pSrcFileHdl->Release(); } if( pDestFileHdl) { pDestFileHdl->flush(); pDestFileHdl->Release(); } // Attempt to delete the destination file if // we didn't successfully copy it. if( RC_BAD( rc)) { (void)pFileSystem->deleteFile( pDbCopyInfo->szDestFileName); } return( rc); }