示例#1
0
/****************************************************************************
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 F_Db::abortTrans(
	FLMBOOL			bOkToLogAbort)
{
	RCODE					rc = NE_XFLM_OK;
	eDbTransType		eSaveTransType;
	XFLM_DB_HDR *		pLastCommittedDbHdr;
	XFLM_DB_HDR *		pUncommittedDbHdr;
	FLMBOOL				bDumpedCache = FALSE;
	FLMBOOL				bKeepAbortedTrans;
	FLMUINT64			ui64TransId;
	F_Rfl *				pRfl = m_pDatabase->m_pRfl;
	RCODE					tmpRc;

	// Should never be calling on a temporary database.

	flmAssert( !m_pDatabase->m_bTempDb);

	// Get transaction type

	if (m_eTransType == XFLM_NO_TRANS)
	{
		goto Exit;	// Will return SUCCESS.
	}

	// No recovery required if it is a read transaction.

	if (m_eTransType == XFLM_READ_TRANS)
	{
		if (m_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();
		}

		goto Unlink_From_Trans;
	}

#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID,
			0, 0, NE_XFLM_OK, "TAbrt");
#endif

	// Disable DB header writes

	pRfl->clearDbHdrs();

	// End any pending input operations

	m_pDatabase->endPendingInput();

	// Clear the document list

	m_pDatabase->m_DocumentList.clearNodes();

	// If the transaction had no update operations, restore it
	// to its pre-transaction state - make it appear that no
	// transaction ever happened.

	pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr;
	pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr;
	ui64TransId = m_ui64CurrTransID;

	// 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();

	if (m_bHadUpdOper)
	{

		// Dump any start and stop indexing stubs that should be aborted.

		indexingAfterAbort();

		// 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 (!pRfl->seeIfRflVolumeOk())
		{
			bKeepAbortedTrans = FALSE;
		}
		else
		{
			bKeepAbortedTrans =
				(pUncommittedDbHdr->ui8RflKeepAbortedTrans)
				? 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)
	{
#ifdef FLM_DEBUG
		if( pRfl->isLoggingEnabled())
		{
			flmAssert( m_ui64CurrTransID == pRfl->getCurrTransID());
		}
#endif

		if (RC_BAD( rc = pRfl->logEndTransaction(
			this, 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( pRfl->getCurrTransID() == 0);
	}
#endif

	// If there were no operations in the transaction, restore
	// everything as if the transaction never happened.

	// Even empty transactions can have modified nodes to clean up
	// so we need to call this no matter what.

	m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1);

	if (!m_bHadUpdOper)
	{

		// 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 flmGetDbHdrInfo
	// below, because that call will change pDb->m_ui64CurrTransID,
	// and that value is used by freeModifiedNodes.

	m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID);
	bDumpedCache = TRUE;

	// Reset the Db header from the last committed DB header in pFile.

	getDbHdrInfo( pLastCommittedDbHdr);
	if (RC_BAD( rc = physRollback( (FLMUINT)pUncommittedDbHdr->ui32RblEOF,
				 m_pDatabase->m_uiFirstLogBlkAddress, FALSE, 0)))
	{
		goto Exit1;
	}

	m_pDatabase->lockMutex();

	// 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.

	pLastCommittedDbHdr->ui64CurrTransID = ui64TransId;

	// 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.

	pLastCommittedDbHdr->ui32RflCurrFileNum =
		pUncommittedDbHdr->ui32RflCurrFileNum;
	pLastCommittedDbHdr->ui32RflLastTransOffset =
		pUncommittedDbHdr->ui32RflLastTransOffset;
	f_memcpy( pLastCommittedDbHdr->ucLastTransRflSerialNum,
				 pUncommittedDbHdr->ucLastTransRflSerialNum,
				 XFLM_SERIAL_NUM_SIZE);
	f_memcpy( pLastCommittedDbHdr->ucNextRflSerialNum,
				 pUncommittedDbHdr->ucNextRflSerialNum,
				 XFLM_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.

	pLastCommittedDbHdr->ui32RblEOF =
		pUncommittedDbHdr->ui32RblEOF;
	pLastCommittedDbHdr->ui32RblFirstCPBlkAddr =
		pUncommittedDbHdr->ui32RblFirstCPBlkAddr;

	m_pDatabase->unlockMutex();

	pRfl->commitDbHdrs( pLastCommittedDbHdr,
							&m_pDatabase->m_checkpointDbHdr);

Exit1:

	// Dump cache, if not done above.

	if (!bDumpedCache)
	{
		m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID);
		m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1);
		bDumpedCache = TRUE;
	}

	// Throw away IXD_FIXUPs

	if (m_pIxdFixups)
	{
		IXD_FIXUP *	pIxdFixup;
		IXD_FIXUP *	pDeleteIxdFixup;

		pIxdFixup = m_pIxdFixups;
		while (pIxdFixup)
		{
			pDeleteIxdFixup = pIxdFixup;
			pIxdFixup = pIxdFixup->pNext;
			f_free( &pDeleteIxdFixup);
		}
		m_pIxdFixups = NULL;
	}

	if (m_eTransType == XFLM_UPDATE_TRANS &&
		 gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList)
	{
		flmTransEventCallback( XFLM_EVENT_ABORT_TRANS, this, rc,
						ui64TransId);
	}

Unlink_From_Trans:

	eSaveTransType = m_eTransType;

	if (m_uiFlags & FDB_HAS_WRITE_LOCK)
	{
		if (RC_BAD( tmpRc = pRfl->completeTransWrites( this, FALSE, FALSE)))
		{
			if (RC_OK( rc))
			{
				rc = tmpRc;
			}
		}
	}

	if (eSaveTransType == XFLM_UPDATE_TRANS)
	{

		// Before unlocking, restore collection information.

		if (m_uiFlags & FDB_UPDATED_DICTIONARY)
		{
			m_pDatabase->lockMutex();
			flmAssert( m_pDict);
			unlinkFromDict();
			if (m_pDatabase->m_pDictList)
			{

				// Link the F_Db to the right F_Dict object so it will
				// fixup the correct F_COLLECTION structures.

				linkToDict( m_pDatabase->m_pDictList);
			}
			m_pDatabase->unlockMutex();
		}

		if (m_pDict)
		{
			F_COLLECTION *	pCollection;
			FLMUINT			uiLfNum;
#ifdef FLM_DEBUG
			IXD *				pIxd;
			FLMUINT			uiRootBlk;
#endif

			// Only need to do collections.  Nothing from the LFH of
			// an index is stored in memory except for the root block
			// address, and whenever that is changed, we get a new
			// dictionary.  Since the new dictionary will be discarded
			// in that case, there is nothing to restore for an index.

			uiLfNum = 0;
			while ((pCollection = m_pDict->getNextCollection(
											uiLfNum, TRUE)) != NULL)
			{
#ifdef FLM_DEBUG
				uiRootBlk = pCollection->lfInfo.uiRootBlk;
#endif
				if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this,
							&pCollection->lfInfo, pCollection)))
				{
					if (RC_OK( rc))
					{
						rc = tmpRc;
					}
				}
#ifdef FLM_DEBUG
				else
				{
					// Make sure root block did not change - should not
					// have because root block changes are done by creating
					// a new dictionary, and we have already discarded
					// any new dictionary.  Hence, root block address should
					// be the same in memory as it is no disk.

					flmAssert( uiRootBlk == pCollection->lfInfo.uiRootBlk);
				}
#endif
				uiLfNum = pCollection->lfInfo.uiLfNum;
			}

			// Do indexes in debug mode to make sure uiRootBlk is correct

#ifdef FLM_DEBUG
			uiLfNum = 0;
			while ((pIxd = m_pDict->getNextIndex( uiLfNum, TRUE)) != NULL)
			{
				uiRootBlk = pIxd->lfInfo.uiRootBlk;
				if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this,
							&pIxd->lfInfo, NULL)))
				{
					if (RC_OK( rc))
					{
						rc = tmpRc;
					}
				}
				else
				{
					// Make sure root block did not change - should not
					// have because root block changes are done by creating
					// a new dictionary, and we have already discarded
					// any new dictionary.  Hence, root block address should
					// be the same in memory as it is no disk.

					flmAssert( uiRootBlk == pIxd->lfInfo.uiRootBlk);
				}
				uiLfNum = pIxd->lfInfo.uiLfNum;
			}
#endif
		}
	}

	// Unlink the database from the transaction list.

	unlinkFromTransList( FALSE);

	if (m_pDbStats)
	{
		FLMUINT64	ui64ElapMilli = 0;

		flmAddElapTime( &m_TransStartTime, &ui64ElapMilli);
		m_pDbStats->bHaveStats = TRUE;
		if (eSaveTransType == XFLM_READ_TRANS)
		{
			m_pDbStats->ReadTransStats.AbortedTrans.ui64Count++;
			m_pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli +=
					ui64ElapMilli;
		}
		else
		{
			m_pDbStats->UpdateTransStats.AbortedTrans.ui64Count++;
			m_pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli +=
					ui64ElapMilli;
		}
	}

	if (m_pStats)
	{
		(void)flmStatUpdate( &m_Stats);
	}

Exit:

	m_AbortRc = NE_XFLM_OK;
	return( rc);
}
示例#2
0
/****************************************************************************
Desc : Modifies a record within a container.
Notes: If an index definition record is modified in the dictionary container,
		 the index B-TREE will be deleted and rebuilt automatically when the
		 transaction commits.  If an index definition record is changed into
		 a field definition record or a container definition record, the index
		 will be automatically deleted when the transaction commits.  When a
		 non-unique index is changed to a unique index, or the fields in a
		 unique index are changed, FLAIM needs to verify that each key in the
		 proposed index is indeed a unique key.  However, this verification
		 does NOT occur until the dictionary transaction commits and the
		 index is actually rebuilt.  If FLAIM discovers that the keys in the
		 proposed index are not unique, the transaction commit will fail and
		 return FERR_NOT_UNIQUE.

		 If a container definition record is changed into a field definition
		 record, the container will be deleted automatically when the
		 transaction commits.  If a container definition record is changed
		 into an index definition record, the container will be automatically
		 deleted and the index will be automatically built when the transaction
		 commits.

		 Only the name and state of field definition records can be modified.
		 Changing a field type or changing a field definition record into an
		 index definition record is not allowed.  For information on changing
		 the state of a field, see the Dictionary Syntax document.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRecordModify(
	HFDB		   hDb,
	FLMUINT		uiContainer,
	FLMUINT		uiDrn,
	FlmRecord *	pRecord,
	FLMUINT		uiAutoTrans
	)
{
	RCODE			rc = FERR_OK;
	RCODE			rc1;
	FDB *			pDb = (FDB *)hDb;
	FlmRecord *	pOldRecord = NULL;
	LFILE *		pLFile;
	FLMBOOL		bStartedAutoTrans = FALSE;
	FLMBOOL		bProcessedKeys = FALSE;
	FLMBOOL		bLogCompleteIndexSet = FALSE;
	FLMBOOL		bHadUniqueKeys;
	DB_STATS *	pDbStats = NULL;
	F_TMSTAMP	StartTime;

	if( uiContainer == FLM_TRACKER_CONTAINER)
	{
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	if (IsInCSMode( hDb))
	{
		fdbInitCS( pDb);
		rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_MODIFY, uiContainer,
									&uiDrn, pRecord, uiAutoTrans);
		goto ExitCS;
	}

	if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
			FDB_TRANS_GOING_OK, uiAutoTrans, &bStartedAutoTrans)))
	{
		goto Exit;
	}

	if( pDb->uiFlags & FDB_COMMITTING_TRANS)
	{
		flmAssert( 0);
		rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
		goto Exit;
	}

	if ((pDbStats = pDb->pDbStats) != NULL)
	{
		f_timeGetTimeStamp( &StartTime);
	}

	// Make sure we have a valid record

	if( !pRecord)
	{
		rc = RC_SET( FERR_INVALID_PARM);
		goto Exit;
	}

	// We cannot modify a record that is marked read-only.

	if( pRecord->isReadOnly())
	{
		flmAssert( 0);
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	if( !uiDrn || (uiDrn == (FLMUINT) DRN_LAST_MARKER))
	{
		rc = RC_SET( FERR_BAD_DRN);
		goto Exit;
	}

	if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = KrefCntrlCheck( pDb)))
	{
		goto Exit;
	}

	// DICTIONARY RECORD MODIFY

	if( uiContainer == FLM_LOCAL_DICT_CONTAINER) 
	{
		if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
			uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord)))
		{

			// NOTE: Deliberately not reading in to cache if not found.

			if (rc != FERR_NOT_FOUND)
			{
				goto Exit;
			}
			if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pOldRecord, NULL, NULL)))
			{
				goto Exit;
			}
		}

		// Sanity check -- make sure that the new and old records point at
		// different objects.

		flmAssert( pRecord != pOldRecord);

		if( RC_BAD( rc = flmLFileDictUpdate( pDb, &pLFile, &uiDrn, 
				pRecord, pOldRecord,
				(uiAutoTrans & FLM_DO_IN_BACKGROUND) ? TRUE : FALSE, 
				(uiAutoTrans & FLM_SUSPENDED) ? TRUE : FALSE,
				&bLogCompleteIndexSet)))
		{
			goto Exit;
		}

		pRecord->setID( uiDrn);
		pRecord->setContainerID( uiContainer);
		if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
		{
			goto Exit;
		}

		goto Exit;
	}

	// First read the old record, and delete it's keys.
	// To do this we need to be able to read any purged fields and delete any
	// keys they build.

	if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
		uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord)))
	{

		// NOTE: Deliberately not reading in to cache if not found.

		if (rc != FERR_NOT_FOUND)
		{
			goto Exit;
		}
		
		if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pOldRecord, NULL, NULL)))
		{
			goto Exit;
		}
	}

	// Sanity check -- make sure that the new and old records point at
	// different objects.

	flmAssert( pRecord != pOldRecord);

	bProcessedKeys = TRUE;
	bHadUniqueKeys = FALSE;
	if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, uiContainer, uiDrn, pOldRecord,
													KREF_DEL_KEYS | KREF_IN_MODIFY,
													TRUE,		// Purged Field OK
													&bHadUniqueKeys)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, uiContainer, uiDrn, pRecord,
													KREF_ADD_KEYS | KREF_IN_MODIFY,
													FALSE,
													&bHadUniqueKeys)))
	{
		goto Exit;
	}
	
	// NOTE: The LFile table may have changed locations if the dictionary 
	// was update because of a change in a field state
	
	if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, pRecord, uiDrn, REC_UPD_MODIFY)))
	{
		 goto Exit;
	}

	// Finish up the keys adding the unique keys to the indexes.

	if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys)))
	{
		// Undo the record that was modified - replace with original record.
		if( RC_BAD( rc1 = FSRecUpdate( pDb, pLFile, 
			pOldRecord, uiDrn, REC_UPD_MODIFY)))
		{
			rc = (rc == FERR_NOT_UNIQUE) ? rc1 : rc;
		}
		goto Exit;
	}

	// Insert record into cache
	
	pRecord->setID( uiDrn);
	pRecord->setContainerID( uiContainer);
	
	if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
	{
		if ( rc != FERR_MEM)
		{
			flmAssert( 0);
		}
		
		// Undo the record that was modified - replace with original record.
		
		FSRecUpdate( pDb, pLFile, pOldRecord, uiDrn, REC_UPD_MODIFY);
		goto Exit;
	}

	// Don't make this call until we are sure of success - because we want to
	// be able to back things out of KREF table.

	KYFinishCurrentRecord( pDb);

Exit:

	if( RC_BAD( rc) && bProcessedKeys)
	{
		KYAbortCurrentRecord( pDb);
	}

	// Add the BLOB entries to the blob list.
	
	rc = FB_OperationEnd( pDb, rc);

	if( RC_OK( rc))
	{
		if( RC_OK( rc = pDb->pFile->pRfl->logUpdate( 
				uiContainer, uiDrn, uiAutoTrans, pOldRecord, pRecord)) && 
			 bLogCompleteIndexSet &&
			 pDb->pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_51)
		{
			// Log the fact that we indexed everything so the redo will also
			// index all data records in the container.

			rc = pDb->pFile->pRfl->logIndexSet( uiDrn, 0, 1, 0xFFFFFFFF);
		}
	}

	if( pDbStats)
	{
		flmAddElapTime( &StartTime, &pDbStats->RecordModifies.ui64ElapMilli);
		pDbStats->RecordModifies.ui64Count++;
		pDbStats->bHaveStats = TRUE;
	}

	if( gv_FlmSysData.UpdateEvents.pEventCBList)
	{
		flmUpdEventCallback( pDb, F_EVENT_MODIFY_RECORD, hDb, rc, uiDrn,
								uiContainer, pRecord, pOldRecord);
	}

#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( pDb->pFile->uiFFileId, 
		pDb->LogHdr.uiCurrTransID, uiContainer, uiDrn, rc, "RMod");
#endif

	// If started an automatic transaction, end it.

	if( bStartedAutoTrans)
	{
		rc = flmEndAutoTrans( pDb, rc);
	}

	if( pOldRecord)
	{
		pOldRecord->Release();
		pOldRecord = NULL;
	}

ExitCS:

	flmExit( FLM_RECORD_MODIFY, pDb, rc);
	return( rc);
}
示例#3
0
/****************************************************************************
Desc : Deletes a record from a container.
Notes: If an index definition record or a container definition record is
		 deleted from the dictionary container, the index B-TREE container or
		 container B-TREE will be deleted automatically when the transaction
		 commits.  Field definition records can only be deleted from the
		 dictionary using this routine if the field is not in use.  For more
		 information on deletion of field definitions, see the Dictionary Syntax
		 document.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRecordDelete(
	HFDB	 		hDb,
	FLMUINT		uiContainer,
	FLMUINT		uiDrn,
	FLMUINT		uiAutoTrans
	)
{
	RCODE				rc = FERR_OK;
	LFILE *			pLFile;
	FDB *				pDb = (FDB *)hDb;
	FLMBOOL			bStartedAutoTrans;
	FlmRecord *		pOldRecord;
	FlmRecord **	ppOldRecord;
	DB_STATS *		pDbStats = NULL;
	F_TMSTAMP		StartTime;

	if( uiContainer == FLM_TRACKER_CONTAINER)
	{
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	if (IsInCSMode( hDb))
	{
		fdbInitCS( pDb);
		rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_DELETE, uiContainer,
									&uiDrn, NULL, uiAutoTrans);
		goto ExitCS;
	}

	bStartedAutoTrans = FALSE;
	pOldRecord = NULL;
	ppOldRecord = NULL;

	if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK,
											uiAutoTrans, &bStartedAutoTrans)))
	{
		goto Exit;
	}

	if( (pDbStats = pDb->pDbStats) != NULL)
	{
		f_timeGetTimeStamp( &StartTime);
	}

	if( !uiDrn || uiDrn == (FLMUINT) DRN_LAST_MARKER)
	{
		rc = RC_SET( FERR_BAD_DRN);
		goto Exit;
	}

	if( RC_BAD(rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
	{
		goto Exit;
	}

	if( gv_FlmSysData.UpdateEvents.pEventCBList)
	{
		// Do not have flmDeleteRecord fetch the old version of the record
		// unless an event callback is registered.
		
		ppOldRecord = &pOldRecord;
	}
	
	// NOTE: pLFile should NOT be used after this call, because flmDeleteRecord
	// could actually change its position in memory due to field changes.

	if (RC_BAD( rc = flmDeleteRecord( pDb, pLFile, uiDrn, ppOldRecord, FALSE)))
	{
		goto Exit;
	}

Exit:

	if( RC_OK( rc))
	{
		rc = pDb->pFile->pRfl->logUpdate( uiContainer, uiDrn, 
			uiAutoTrans, NULL, NULL);
	}

	if( pDbStats)
	{
		flmAddElapTime( &StartTime, &pDbStats->RecordDeletes.ui64ElapMilli);
		pDbStats->RecordDeletes.ui64Count++;
		pDbStats->bHaveStats = TRUE;
	}

	if( gv_FlmSysData.UpdateEvents.pEventCBList)
	{
		flmUpdEventCallback( pDb, F_EVENT_DELETE_RECORD, hDb, rc, uiDrn,
								uiContainer, NULL, pOldRecord);
	}

#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( pDb->pFile->uiFFileId, 
		pDb->LogHdr.uiCurrTransID, uiContainer, uiDrn, rc, "RDel");
#endif

	// If started an automatic transaction, end it.

	if( bStartedAutoTrans)
	{
		rc = flmEndAutoTrans( pDb, rc);
	}

	if( pOldRecord)
	{
		pOldRecord->Release();
		pOldRecord = NULL;
	}

ExitCS:

	flmExit( FLM_RECORD_DELETE, pDb, rc);

	return( rc);
}
示例#4
0
/****************************************************************************
Desc:		Internal interface routine for database add operation.
			Must be in a valid transaction.
Note:		Internal name is used so that later we can call this within FLAIM.
****************************************************************************/
RCODE	flmAddRecord(
	FDB *	 		pDb,
	LFILE *		pLFile,
	FLMUINT *	puiDrn,						// Record Number to set AND return.
	FlmRecord * pRecord,						// Record to add, must NOT be NULL.
	FLMBOOL		bBatchProcessing,			// Set to TRUE if called by REBUILD.
	FLMBOOL		bDoInBackground,
	FLMBOOL		bCreateSuspended,
	FLMBOOL		bKeepInCache,
	FLMBOOL *	pbLogCompleteIndexSet)
{
	RCODE			rc = FERR_OK;
	FLMUINT		uiDrn = 0;
	FLMBOOL		bProcessedKeys = FALSE;
	FLMUINT		uiLfNum = pLFile->uiLfNum;
	FLMUINT		uiAddAppendFlags = REC_UPD_ADD;
	FLMBOOL		bHadUniqueKeys;

	if( puiDrn)
	{
		uiDrn = *puiDrn;
	}

	if( pDb->uiFlags & FDB_COMMITTING_TRANS)
	{
		flmAssert( 0);
		rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
		goto Exit;
	}

	// Assert that the record is not read-only

	flmAssert( !pRecord->isReadOnly());

	// Set up for indexing.

	if( RC_BAD( rc = KrefCntrlCheck( pDb)))
	{
		goto Exit;
	}

	if( !pRecord)
	{
		rc = RC_SET( FERR_NULL_RECORD);
		goto Exit;
	}

	// If this is a dictionary record then one routine takes care of it.

	if( pLFile->uiLfNum == FLM_LOCAL_DICT_CONTAINER)
	{
		if( RC_OK( rc = flmLFileDictUpdate( pDb, &pLFile, &uiDrn,
					pRecord, NULL, bDoInBackground, bCreateSuspended,
					pbLogCompleteIndexSet)))
		{
			if( puiDrn)
			{
				*puiDrn = uiDrn;
			}
		}

		goto Exit;
	}
	else
	{
		if( !uiDrn || uiDrn == DRN_LAST_MARKER)
		{
			if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiDrn)))
			{
#ifdef FLM_DBG_LOG
				uiDrn = 0;
#endif
				goto Exit;
			}
			
			uiAddAppendFlags |= REC_UPD_NEW_RECORD;
		}
	}

	// Add the records keys, and then the record.  NOTE: If the bBatchProcessing
	// flag is set to TRUE, we are being called as part of a rebuild.  In this
	// case, we do NOT want BLOBs to be reprocessed.  Also, we do not want
	// the QF job list to be generated (it could get very large - and all of
	// the entries are stored in a single record).  So we set the
	// KREF_INDEXING_ONLY flag.  This flag will prohibit the processing of
	// BLOB data.  Also, it will prohibit constructing a QF job list.  Instead
	// of the QF job list, the entries will be fed directly to QuickFinder and
	// then processed when KYKeysCommit is called.

	bProcessedKeys = TRUE;
	bHadUniqueKeys = FALSE;
	if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, pLFile->uiLfNum, uiDrn, pRecord,
										(FLMUINT)((bBatchProcessing)
											? (KREF_ADD_KEYS | KREF_INDEXING_ONLY)
											: KREF_ADD_KEYS),
											FALSE, &bHadUniqueKeys)))
	{
		goto Exit;
	}
											
	// NOTE: The LFile table may have changed locations if the dictionary 
	// was update because of a change in a field state
	
	if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiLfNum, &pLFile)))
	{
		goto Exit;
	}

	if( RC_BAD(	rc = FSRecUpdate( pDb, pLFile, pRecord,
											uiDrn, uiAddAppendFlags)))
	{
		goto Exit;
	}

	if( puiDrn)
	{
		*puiDrn = uiDrn;
	}

	// Sort and check keys for uniqueness.
	
	if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys)))
	{
		// Remove the record that was added because of the error.

		RCODE		rc1 = FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE);
		if( RC_BAD(rc1))
		{
			rc = (rc == FERR_NOT_UNIQUE) ? rc1 : rc;
		}
		goto Exit;
	}

	// Insert record into cache

	pRecord->setID( uiDrn);
	pRecord->setContainerID( pLFile->uiLfNum);
	if (bKeepInCache)
	{
		if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
		{
			// Remove the record that was added because of the error.

			FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE);
			goto Exit;
		}
	}

	// Don't make this call until we are sure of success - because we want to
	// be able to back things out of KREF table.

	KYFinishCurrentRecord( pDb);

Exit:

	if( RC_BAD( rc) && bProcessedKeys)
	{
		KYAbortCurrentRecord( pDb);
	}
	
#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( pDb->pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, 
		uiLfNum, uiDrn, rc, "RAdd");
#endif

	return( rc);
}
示例#5
0
/****************************************************************************
Desc:		Returns the next DRN that record ADD would return.  The database
			must be in an existing update transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmReserveNextDrn(
	HFDB			hDb,
	FLMUINT		uiContainer,
	FLMUINT *	puiDrnRV)
{
	RCODE			rc;
	FDB *			pDb = (FDB *)hDb;
	LFILE *		pLFile;
	FLMBOOL		bIgnore;
	FLMUINT		uiDrn = 0;

	if (IsInCSMode( hDb))
	{
		fdbInitCS( pDb);

		CS_CONTEXT *	pCSContext = pDb->pCSContext;
		FCL_WIRE			Wire( pCSContext, pDb);

		// Send the request

		if( RC_BAD( rc = Wire.sendOp( 
			FCS_OPCLASS_RECORD, FCS_OP_RESERVE_NEXT_DRN)))
		{
			goto ExitCS;
		}

		if( uiContainer)
		{
			if (RC_BAD( rc = Wire.sendNumber(
				WIRE_VALUE_CONTAINER_ID, uiContainer)))
			{
				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 ExitCS;
		}

		*puiDrnRV = Wire.getDrn();
		goto ExitCS;

Transmission_Error:
		pCSContext->bConnectionGood = FALSE;
		goto ExitCS;
	}

	bIgnore = FALSE;					// Set to shut up compiler.

	if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
										FDB_TRANS_GOING_OK,	// byFlags
										0, 						// wAutoTrans
										&bIgnore)))				// bStartedAutoTrans
	{
		goto Exit;
	}

	if( pDb->uiFlags & FDB_COMMITTING_TRANS)
	{
		flmAssert( 0);
		rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
		goto Exit;
	}

	if( RC_BAD( fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
	{
#ifdef FLM_DBG_LOG
		uiDrn = 0;
#endif
		goto Exit;
	}
	uiDrn = (FLMUINT) 0;					// Must initialize before call.
	if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, TRUE, &uiDrn)))
	{
#ifdef FLM_DBG_LOG
		uiDrn = 0;
#endif
		goto Exit;
	}

	*puiDrnRV = uiDrn;						// Set return value.

Exit:

	if (RC_OK( rc))
	{
		rc = pDb->pFile->pRfl->logUpdatePacket( 
			RFL_RESERVE_DRN_PACKET, uiContainer, *puiDrnRV, 0);
	}

	if( gv_FlmSysData.UpdateEvents.pEventCBList)
	{
		flmUpdEventCallback( pDb, F_EVENT_RESERVE_DRN, hDb, rc, *puiDrnRV,
								uiContainer, NULL, NULL);
	}

#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( pDb->pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
			uiContainer, uiDrn, rc, "RDrn");
#endif

ExitCS:

	flmExit( FLM_RESERVE_NEXT_DRN, pDb, rc);

	return( rc);
}
示例#6
0
/****************************************************************************
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);
}
示例#7
0
/****************************************************************************
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);
}
示例#8
0
/****************************************************************************
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);
}