/**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI f_rwlockAcquire( F_RWLOCK hReadWriteLock, F_SEM hSem, FLMBOOL bWriter) { RCODE rc = NE_FLM_OK; F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock; FLMBOOL bMutexLocked = FALSE; f_mutexLock( pReadWriteLock->hMutex); bMutexLocked = TRUE; if( bWriter) { if( pReadWriteLock->iRefCnt != 0) { rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)((FLMINT)bWriter), &pReadWriteLock->pNotifyList); } if( RC_OK( rc)) { f_assert( !pReadWriteLock->iRefCnt); pReadWriteLock->iRefCnt = -1; pReadWriteLock->uiWriteThread = f_threadId(); } } else { if( pReadWriteLock->iRefCnt < 0 || pReadWriteLock->pNotifyList) { rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)((FLMINT)bWriter), &pReadWriteLock->pNotifyList); } if( RC_OK( rc)) { pReadWriteLock->iRefCnt++; } } f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt); if( bMutexLocked) { f_mutexUnlock( pReadWriteLock->hMutex); } return( rc); }
/**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI f_rwlockPromote( F_RWLOCK hReadWriteLock, F_SEM hSem) { RCODE rc = NE_FLM_OK; F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock; FLMBOOL bMutexLocked = FALSE; f_mutexLock( pReadWriteLock->hMutex); bMutexLocked = TRUE; if( pReadWriteLock->iRefCnt <= 0) { rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); goto Exit; } pReadWriteLock->iRefCnt--; if( pReadWriteLock->iRefCnt != 0) { rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)TRUE, &pReadWriteLock->pNotifyList); } if( RC_OK( rc)) { f_assert( !pReadWriteLock->iRefCnt); pReadWriteLock->iRefCnt = -1; pReadWriteLock->uiWriteThread = f_threadId(); } Exit: f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt); if( bMutexLocked) { f_mutexUnlock( pReadWriteLock->hMutex); } return( rc); }
/**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI f_rwlockRelease( F_RWLOCK hReadWriteLock) { RCODE rc = NE_FLM_OK; F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock; FLMBOOL bMutexLocked = FALSE; f_mutexLock( pReadWriteLock->hMutex); bMutexLocked = TRUE; if( pReadWriteLock->iRefCnt > 0) { pReadWriteLock->iRefCnt--; } else if( pReadWriteLock->iRefCnt == -1) { f_assert( pReadWriteLock->uiWriteThread == f_threadId()); pReadWriteLock->iRefCnt = 0; } else { rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); goto Exit; } if( !pReadWriteLock->iRefCnt && pReadWriteLock->pNotifyList) { f_rwlockNotify( pReadWriteLock); } Exit: f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt >= 0); if( bMutexLocked) { f_mutexUnlock( pReadWriteLock->hMutex); } return( rc); }
/**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI f_rwlockTryAcquire( F_RWLOCK hReadWriteLock, FLMBOOL bWriter) { RCODE rc = NE_FLM_OK; F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock; f_mutexLock( pReadWriteLock->hMutex); if( bWriter) { if( pReadWriteLock->iRefCnt != 0) { rc = RC_SET( NE_FLM_WAIT_TIMEOUT); } else { pReadWriteLock->iRefCnt = -1; pReadWriteLock->uiWriteThread = f_threadId(); } } else { if( pReadWriteLock->iRefCnt < 0 || pReadWriteLock->pNotifyList) { rc = RC_SET( NE_FLM_WAIT_TIMEOUT); } else { pReadWriteLock->iRefCnt++; } } f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt); f_mutexUnlock( pReadWriteLock->hMutex); return( rc); }
/************************************************************************ Desc : Outputs an update event callback. *************************************************************************/ void flmUpdEventCallback( FDB * pDb, FEventType eEventType, HFDB hDb, RCODE rc, FLMUINT uiDrn, FLMUINT uiContainer, FlmRecord * pNewRecord, FlmRecord * pOldRecord) { FLM_UPDATE_EVENT UpdEvent; UpdEvent.uiThreadId = f_threadId(); UpdEvent.hDb = hDb; UpdEvent.uiTransID = (FLMUINT)( (pDb->uiTransType) ? pDb->LogHdr.uiCurrTransID : 0); UpdEvent.rc = rc; UpdEvent.uiDrn = uiDrn; UpdEvent.uiContainer = uiContainer; UpdEvent.pNewRecord = pNewRecord; UpdEvent.pOldRecord = pOldRecord; flmDoEventCallback( F_EVENT_UPDATES, eEventType, &UpdEvent, NULL); }
/**************************************************************************** Desc: Index a set of documents or until time runs out. ****************************************************************************/ RCODE F_Db::indexSetOfRows( FLMUINT uiIndexNum, FLMUINT64 ui64StartRowId, FLMUINT64 ui64EndRowId, IF_IxStatus * pIxStatus, IF_IxClient * pIxClient, SFLM_INDEX_STATUS * pIndexStatus, FLMBOOL * pbHitEnd, IF_Thread * pThread) { RCODE rc = NE_SFLM_OK; FLMUINT64 ui64RowId; FLMUINT64 ui64LastRowId = 0; F_INDEX * pIndex = NULL; F_TABLE * pTable; IF_LockObject * pDatabaseLockObj = m_pDatabase->m_pDatabaseLockObj; FLMBOOL bHitEnd = FALSE; FLMUINT uiCurrTime; FLMUINT uiLastStatusTime = 0; FLMUINT uiStartTime; FLMUINT uiMinTU; FLMUINT uiStatusIntervalTU; FLMUINT64 ui64RowsProcessed = 0; FLMBOOL bRelinquish = FALSE; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; void * pvTmpPoolMark = m_tempPool.poolMark(); F_Btree * pbtree = NULL; FLMBOOL bNeg; FLMUINT uiBytesProcessed; F_Row * pRow = NULL; uiMinTU = FLM_MILLI_TO_TIMER_UNITS( 500); uiStatusIntervalTU = FLM_SECS_TO_TIMER_UNITS( 10); uiStartTime = FLM_GET_TIMER(); if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } pIndex = m_pDict->getIndex( uiIndexNum); flmAssert( pIndex); flmAssert( !(pIndex->uiFlags & IXD_SUSPENDED)); // Get a btree if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } pTable = m_pDict->getTable( pIndex->uiTableNum); flmAssert( pTable); if (RC_BAD( rc = pbtree->btOpen( this, &pTable->lfInfo, FALSE, TRUE))) { goto Exit; } uiKeyLen = sizeof( ucKey); if (RC_BAD( rc = flmNumber64ToStorage( ui64StartRowId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pbtree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, FLM_INCL))) { if (rc == NE_SFLM_EOF_HIT || rc == NE_SFLM_NOT_FOUND) { rc = NE_SFLM_OK; bHitEnd = TRUE; goto Commit_Keys; } goto Exit; } for (;;) { // See what row we're on if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64RowId, &bNeg, &uiBytesProcessed))) { goto Exit; } if (ui64RowId > ui64EndRowId) { break; } if( RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->retrieveRow( this, pIndex->uiTableNum, ui64RowId, &pRow))) { goto Exit; } if (RC_BAD( rc = buildKeys( pIndex, pTable, pRow, TRUE, NULL))) { goto Exit; } // See if there is an indexing callback if (pIxClient) { if (RC_BAD( rc = pIxClient->doIndexing( this, uiIndexNum, pIndex->uiTableNum, pRow))) { goto Exit; } } ui64LastRowId = ui64RowId; ui64RowsProcessed++; if (pIndexStatus) { pIndexStatus->ui64RowsProcessed++; pIndexStatus->ui64LastRowIndexed = ui64LastRowId; } // Get the current time uiCurrTime = FLM_GET_TIMER(); // Break out if someone is waiting for an update transaction. if (pThread) { if (pThread->getShutdownFlag()) { bRelinquish = TRUE; break; } if (pDatabaseLockObj->getWaiterCount()) { // See if our minimum run time has elapsed if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU) { if (ui64RowsProcessed < 50) { // If there are higher priority waiters in the lock queue, // we want to relinquish. if (pDatabaseLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { bRelinquish = TRUE; break; } } else { bRelinquish = TRUE; break; } } } else { // Even if no one has requested a lock for a long time, we // still want to periodically commit our transaction so // we won't lose more than uiMaxCPInterval timer units worth // of work if we crash. We will run until we exceed the checkpoint // interval and we see that someone (the checkpoint thread) is // waiting for the write lock. if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > gv_SFlmSysData.uiMaxCPInterval && m_pDatabase->m_pWriteLockObj->getWaiterCount()) { bRelinquish = TRUE; break; } } } if (FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >= uiStatusIntervalTU) { uiLastStatusTime = uiCurrTime; if( pIxStatus) { if( RC_BAD( rc = pIxStatus->reportIndex( ui64LastRowId))) { goto Exit; } } // Send indexing completed event notification if( gv_SFlmSysData.EventHdrs[ SFLM_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( SFLM_EVENT_UPDATES, SFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), 0, uiIndexNum, ui64LastRowId, NE_SFLM_OK); } // Log a progress message flmLogIndexingProgress( uiIndexNum, ui64LastRowId); } // Need to go to the next row. if( RC_BAD( rc = pbtree->btNextEntry( ucKey, sizeof( ucKey), &uiKeyLen))) { if (rc == NE_SFLM_EOF_HIT) { rc = NE_SFLM_OK; bHitEnd = TRUE; break; } goto Exit; } } Commit_Keys: if (RC_BAD( rc = keysCommit( TRUE))) { goto Exit; } // If at the end, change index state. if (bHitEnd) { if (RC_BAD( rc = setIxStateInfo( uiIndexNum, 0, 0))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIndex = NULL; } else if (ui64RowsProcessed) { if (RC_BAD( rc = setIxStateInfo( uiIndexNum, ui64LastRowId, IXD_OFFLINE))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIndex is no // good after this point pIndex = NULL; } // Log the rows that were indexed, if any if (ui64LastRowId) { if (RC_BAD( rc = m_pDatabase->m_pRfl->logIndexSet( this, uiIndexNum, ui64StartRowId, ui64LastRowId))) { goto Exit; } } Exit: // We want to make one last call if we are in the foreground or if // we actually did some indexing. if (gv_SFlmSysData.EventHdrs[ SFLM_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( SFLM_EVENT_UPDATES, SFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), 0, uiIndexNum, (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastRowId), NE_SFLM_OK); } flmLogIndexingProgress( uiIndexNum, (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastRowId)); if (pIxStatus) { (void) pIxStatus->reportIndex( ui64LastRowId); } if (pbHitEnd) { *pbHitEnd = bHitEnd; } krefCntrlFree(); m_tempPool.poolReset( pvTmpPoolMark); if (pbtree) { gv_SFlmSysData.pBtPool->btpReturnBtree( &pbtree); } if (pRow) { pRow->ReleaseRow(); } return( rc); }
/**************************************************************************** Desc: This routine links a request into a notification list and then waits to be notified that the event has occurred. The mutex is assumed to protect the notify list. ****************************************************************************/ RCODE FLMAPI f_notifyWait( F_MUTEX hMutex, F_SEM hSem, void * pvData, F_NOTIFY_LIST_ITEM ** ppNotifyList) { RCODE rc = NE_FLM_OK; RCODE tmpRc; F_NOTIFY_LIST_ITEM stackNotify; F_NOTIFY_LIST_ITEM * pNotify = &stackNotify; f_assertMutexLocked( hMutex); f_assert( pNotify != *ppNotifyList); f_memset( &stackNotify, 0, sizeof( F_NOTIFY_LIST_ITEM)); pNotify->uiThreadId = f_threadId(); pNotify->hSem = F_SEM_NULL; if( hSem == F_SEM_NULL) { if( RC_BAD( rc = f_semCreate( &pNotify->hSem))) { goto Exit; } } else { pNotify->hSem = hSem; } pNotify->pRc = &rc; pNotify->pvData = pvData; pNotify->pNext = *ppNotifyList; *ppNotifyList = pNotify; // Unlock the mutex and wait on the semaphore f_mutexUnlock( hMutex); if( RC_BAD( tmpRc = f_semWait( pNotify->hSem, F_WAITFOREVER))) { rc = tmpRc; } // Free the semaphore if( hSem != pNotify->hSem) { f_semDestroy( &pNotify->hSem); } // Relock the mutex f_mutexLock( hMutex); 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: Copy a database's files, including roll-forward log files. *****************************************************************************/ FSTATIC RCODE flmCopyDb( FLMUINT uiDbVersion, const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; DB_COPY_INFO DbCopyInfo; F_SuperFileHdl * pSrcSFileHdl = NULL; F_SuperFileHdl * pDestSFileHdl = NULL; F_SuperFileClient * pSrcSFileClient = NULL; F_SuperFileClient * pDestSFileClient = NULL; FLMUINT uiFileNumber; FLMUINT uiHighFileNumber; FLMUINT uiHighLogFileNumber; FLMUINT64 ui64FileSize; FFILE * pFile = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pLockFileHdl = NULL; IF_FileHdl * pTmpFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; FLMBOOL bFileLocked = FALSE; FLMBOOL bWriteLocked = FALSE; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pFileLockObj = NULL; COPIED_NAME * pCopiedList = NULL; FLMBOOL bUsedFFile = FALSE; FLMBYTE * pucInMemLogHdr = NULL; eLockType currLockType; FLMUINT uiLockThreadId; char * pszActualSrcRflPath = NULL; char * pszSrcPrefix = NULL; char * pszActualDestRflPath = NULL; char * pszDestPrefix = NULL; FLMBOOL bCreatedDestRflDir = FALSE; F_SEM hWaitSem = F_SEM_NULL; f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo)); // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcDbName, pszDestDbName) == 0) { goto Exit; } // Allocate a semaphore if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } // Allocate memory for paths we don't want to push onto the stack. if (RC_BAD( rc = f_calloc( (F_PATH_MAX_SIZE + F_FILENAME_SIZE) * 2, &pszActualSrcRflPath))) { goto Exit; } pszSrcPrefix = &pszActualSrcRflPath[ F_PATH_MAX_SIZE]; pszActualDestRflPath = &pszSrcPrefix[ F_FILENAME_SIZE]; pszDestPrefix = &pszActualDestRflPath[ F_PATH_MAX_SIZE]; // Set up the super file object for the source database. // Must at least open the control file. if( (pSrcSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileClient->setup( pszSrcDbName, pszSrcDataDir, uiDbVersion))) { goto Exit; } if( (pSrcSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileHdl->setup( pSrcSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, 0))) { goto Exit; } // Lock the destination database, if not already locked. // This is so we can overwrite it without necessarily // deleting it. May unlock and re-lock the global mutex. f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; if (RC_BAD( rc = flmFindFile( pszDestDbName, pszDestDataDir, &pFile))) { goto Exit; } // If we didn't find an FFILE structure, get an // exclusive lock on the file. if (!pFile) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl))) { goto Exit; } } else { // The call to flmVerifyFileUse will wait if the file is in // the process of being opened by another thread. if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile))) { goto Exit; } // Increment the use count on the FFILE so it will not // disappear while we are copying the file. if (++pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } bUsedFFile = TRUE; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; pucInMemLogHdr = &pFile->ucLastCommittedLogHdr [0]; // Lock the destination file object and transaction // object, if not already locked. pFile->pFileLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if (currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pFileLockObj = pFile->pFileLockObj; pFileLockObj->AddRef(); if (RC_BAD( rc = pFileLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bFileLocked = TRUE; } // Lock the write object, if not already locked pFile->pWriteLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if( currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pWriteLockObj = pFile->pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread - wait // forever until the checkpoint thread gives it up. if( RC_BAD( rc = pWriteLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bWriteLocked = TRUE; } } // Set up the super file object for the destination database. if( (pDestSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileClient->setup( pszDestDbName, pszDestDataDir, uiDbVersion))) { goto Exit; } if( (pDestSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileHdl->setup( pDestSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, gv_FlmSysData.uiFileCreateFlags))) { goto Exit; } // See how many files we have and calculate the total size. uiHighFileNumber = 0; for (;;) { if( RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighFileNumber, &ui64FileSize)) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { // If the control file doesn't exist, we will return // path not found. if (!uiHighFileNumber) { goto Exit; } uiHighFileNumber--; rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighFileNumber++; } // See how many rollback log files we have, and calculate // their total size. uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); for (;;) { if ((RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { if (uiHighLogFileNumber == FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { uiHighLogFileNumber = 0; } else { uiHighLogFileNumber--; } rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighLogFileNumber++; } // Get the sizes of the roll-forward log files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to copy one RFL file. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, pszActualSrcRflPath))) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszActualSrcRflPath, gv_FlmSysData.uiFileOpenFlags, &pTmpFileHdl))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } else { if (RC_BAD( rc = pTmpFileHdl->size( &ui64FileSize))) { goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; pTmpFileHdl->Release(); pTmpFileHdl = NULL; } } else { if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszSrcDbName, pszSrcRflDir, pszActualSrcRflPath, pszSrcPrefix))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, increment ui64BytesToCopy if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { DbCopyInfo.ui64BytesToCopy += pDirHdl->currentItemSize(); } } pDirHdl->Release(); pDirHdl = NULL; } // Close all file handles in the source and destination pSrcSFileHdl->releaseFiles(); pDestSFileHdl->releaseFiles(); // Copy the database files. for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if( RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if( RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } // For the control file, don't copy first 2K - it will be set up // to show maintenance in progress. Then the first 2K will be copied // later. if (!uiFileNumber) { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 2048, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, pucInMemLogHdr, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } // Copy the additional rollback log files, if any. for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); uiFileNumber <= uiHighLogFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if (RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } // Copy the RFL files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, 1, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { // Create the destination RFL directory, if needed. The purpose of this // code is two-fold: 1) We want to keep track of the fact that we tried // to create the destination RFL directory so we can try to remove it // if the copy fails; 2) If the destination RFL directory path specifies // a directory with existing files, we want to remove them. if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDestDbName, pszDestRflDir, pszActualDestRflPath, pszDestPrefix))) { goto Exit; } if( RC_OK( gv_FlmSysData.pFileSystem->doesFileExist( pszActualDestRflPath))) { if( gv_FlmSysData.pFileSystem->isDir( pszActualDestRflPath)) { // Remove the existing directory and all files, etc. (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath, TRUE); } else { (void)gv_FlmSysData.pFileSystem->deleteFile( pszActualDestRflPath); } } // Try to create the destination RFL directory. This might fail if // another process was accessing the directory for some reason // (i.e., from a command prompt), when we tried to remove it above. // We really don't care if the call to CreateDir is sucessful, because // when we try to create the destination files (below), the FLAIM file // file system code will try to create any necessary directories. (void)gv_FlmSysData.pFileSystem->createDir( pszActualDestRflPath); bCreatedDestRflDir = TRUE; // Copy the RFL files. NOTE: We need to copy all of the RFL files // in the source RFL directory so that they will be available // when performing a database restore operation. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, copy it to the destination if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } pDirHdl->Release(); pDirHdl = NULL; } // Do one final copy on the control file to copy just the first 2K if (RC_BAD( rc = pSrcSFileHdl->getFilePath( 0, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( 0, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = FALSE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 2048, &DbCopyInfo, NULL, pucInMemLogHdr, FALSE, fnStatusCallback, UserData))) { goto Exit; } Exit: if (bUsedFFile) { if (!bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } if (!(--pFile->uiUseCount)) { flmLinkFileToNUList( pFile); } } if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if (bWriteLocked) { if( RC_BAD( rc = pWriteLockObj->unlock())) { goto Exit; } bWriteLocked = FALSE; } if (bFileLocked) { RCODE rc3; if (RC_BAD( rc3 = pFileLockObj->unlock())) { if (RC_OK( rc)) rc = rc3; } bFileLocked = FALSE; } if (pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if (pFileLockObj) { pFileLockObj->Release(); pFileLockObj = NULL; } if (pLockFileHdl) { pLockFileHdl->Release(); pLockFileHdl = NULL; } if( pTmpFileHdl) { pTmpFileHdl->Release(); } if( pDirHdl) { pDirHdl->Release(); } // Free all the names of files that were copied. // If the copy didn't finish, try to delete any files // that were copied. while (pCopiedList) { COPIED_NAME * pNext = pCopiedList->pNext; // If the overall copy failed, delete the copied file. if (RC_BAD( rc)) { (void)gv_FlmSysData.pFileSystem->deleteFile( pCopiedList->szPath); } f_free( &pCopiedList); pCopiedList = pNext; } if( RC_BAD( rc) && bCreatedDestRflDir) { (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath); } if( pszActualSrcRflPath) { f_free( &pszActualSrcRflPath); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } if( pSrcSFileHdl) { pSrcSFileHdl->Release(); } if( pSrcSFileClient) { pSrcSFileClient->Release(); } if( pDestSFileHdl) { pDestSFileHdl->Release(); } if( pDestSFileClient) { pDestSFileClient->Release(); } return( rc); }