/**************************************************************************** Desc : Suspend the selected index from doing any key updates on records that are equal or higher than the next record ID value in the container that the index references. If the index is offline then the background process will be suspended. If the index is online then it will be suspended. If the index is already suspended FERR_OK will be returned. A suspended index is not persistant if the database goes down. Notes: An update transaction will be started if necessary. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmIndexSuspend( HFDB hDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; IXD * pIxd; FLMUINT uiHighestRecId; FLMUINT uiContainerNum; FLMBOOL bSuspended; FLMBOOL bStartedTrans = FALSE; LFILE * pLFile; if( IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_INDEX, FCS_OP_INDEX_SUSPEND))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans))) { goto Exit; } // See if the index is valid if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if( pIxd->uiFlags & IXD_UNIQUE) { // Can't suspend unique indexes rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if( pIxd->uiFlags & IXD_SUSPENDED) { // Index is already suspended. goto Exit; } // Get the current index info from the tracker if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, &uiContainerNum, &uiHighestRecId, NULL, &bSuspended))) { goto Exit; } flmAssert( !bSuspended); // Get information about the container(s) being indexed if( !(pIxd->uiFlags & IXD_OFFLINE)) { if ((uiContainerNum = pIxd->uiContainerNum) == 0) { // The index was on-line and up-to-date. For an index that // crosses all containers, we will suspend on the highest DRN of // the FLM_DATA_CONTAINER. uiContainerNum = FLM_DATA_CONTAINER; } if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile))) { goto Exit; } uiHighestRecId = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiHighestRecId))) { goto Exit; } // Decrement uiHighestRecId by 1 to correctly reflect the // last record that was indexed. flmAssert( uiHighestRecId != 0); uiHighestRecId--; } // There may be a background thread still assigned to the // index even though the index may be "on-line." This is because // the background thread may have just commited a transaction that // transitioned the index from off-line to on-line, but the thread // has not yet exited (even though it will not do any more work // to update the index). We want to wait for the thread to terminate // before our transaction is allowed to commit. This is so that if // we immediately call resume, it won't find the yet-to-terminate // thread still running in the background. if( !(pDb->uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = flmAddToStopList( pDb, uiIndexNum))) { goto Exit; } } flmAssert( uiContainerNum != 0xFFFFFFFF); if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIndexNum, uiContainerNum, uiHighestRecId, TRANS_ID_OFFLINE, TRUE))) { goto Exit; } // Create a new dictionary if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } // Get a pointer to the new IXD if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } } // Update the IXDs flags so that the current update // transaction will see the correct state of the index. // Old read transactions will continue to use a prior // version of the dictionary. pIxd->uiFlags |= (IXD_SUSPENDED | IXD_OFFLINE); // Log the suspend packet to the RFL if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSuspendOrResume( uiIndexNum, RFL_INDEX_SUSPEND_PACKET))) { goto Exit; } Exit: if( bStartedTrans) { rc = flmEndAutoTrans( pDb, rc); } flmExit( FLM_INDEX_SUSPEND, pDb, rc); return( rc); }
/**************************************************************************** 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); }
/************************************************************************ Desc : Deletes a record from a container. Notes: This is the internal call that corresponds to FlmRecordDelete. It may also be called by the database checking code when repairing an index. *************************************************************************/ RCODE flmDeleteRecord( FDB * pDb, // Operation context LFILE * pLFile, // LFILE of container to delete from. FLMUINT uiDrn, // DRN of the record to be deleted. FlmRecord ** ppOldRecord, // Returns old record, if not null. FLMBOOL bMissingKeysOK // Is it OK for keys to be missing in the // indexes that are to be updated? // should we report when index entries // aren't found? TRUE=don't report, // FALSE=do report. ) { RCODE rc = FERR_OK; FlmRecord * pOldRecord = NULL; FLMUINT uiContainer = pLFile->uiLfNum; FLMUINT uiAction; FLMBOOL bHadUniqueKeys; FLMBOOL bProcessedKeys = FALSE; if( pDb->uiFlags & FDB_COMMITTING_TRANS) { flmAssert( 0); rc = RC_SET( FERR_ILLEGAL_TRANS_OP); goto Exit; } if( RC_BAD( rc = KrefCntrlCheck( pDb))) { goto Exit; } if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord))) { if (rc != FERR_NOT_FOUND) { goto Exit; } if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pOldRecord, NULL, NULL))) { goto Exit; } } if( uiContainer == FLM_LOCAL_DICT_CONTAINER ) { if( RC_OK( rc = flmLFileDictUpdate( pDb, &pLFile, &uiDrn, NULL, pOldRecord, FALSE, FALSE, NULL))) { rc = flmRcaRemoveRec( pDb, uiContainer, uiDrn); } goto Exit; } // First read the record, then delete it's keys and then it. uiAction = (FLMUINT)((bMissingKeysOK) ? (FLMUINT)(KREF_DEL_KEYS | KREF_MISSING_KEYS_OK) : (FLMUINT)(KREF_DEL_KEYS)); bProcessedKeys = TRUE; bHadUniqueKeys = FALSE; if( RC_BAD(rc = flmProcessRecFlds( pDb, NULL, uiContainer, uiDrn, pOldRecord, uiAction, TRUE, &bHadUniqueKeys))) { goto Exit; } // NOTE: The LFile table may have changed locations if the dictionary // was updated because of a change in a field state if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) { goto Exit; } if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys))) { goto Exit; } if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE))) { goto Exit; } // Remove the record from cache if( RC_BAD( rc = flmRcaRemoveRec( pDb, uiContainer, uiDrn))) { (void)FSRecUpdate( pDb, pLFile, pOldRecord, uiDrn, REC_UPD_ADD); 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); } if( ppOldRecord) { *ppOldRecord = pOldRecord; } else if( pOldRecord) { pOldRecord->Release(); } // Add the BLOB entries to the blob list. rc = FB_OperationEnd( pDb, rc); return( rc); }
/**************************************************************************** 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); }
/**************************************************************************** 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); }
/**************************************************************************** Desc : Adds a record to a container. Notes: If an index definition record is added to the dictionary container, the index will be built automatically when the transaction commits. When a unique index is added to the database, 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 built. If FLAIM discovers that the keys in an index are not unique, the transaction commit will fail and return an error. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmRecordAdd( HFDB hDb, FLMUINT uiContainer, FLMUINT * puiDrn, FlmRecord * pRecord, FLMUINT uiAutoTrans) { RCODE rc = FERR_OK; FLMUINT uiDrn = 0; FDB * pDb = (FDB *)hDb; LFILE * pLFile; FLMBOOL bStartedAutoTrans = FALSE; FLMBOOL bLogCompleteIndexSet = FALSE; DB_STATS * pDbStats = NULL; F_TMSTAMP StartTime; if( puiDrn) { uiDrn = *puiDrn; } if( uiContainer == FLM_TRACKER_CONTAINER) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (IsInCSMode( hDb)) { fdbInitCS( pDb); rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_ADD, uiContainer, &uiDrn, pRecord, uiAutoTrans); goto ExitCS; } if( RC_BAD( rc = fdbInit( (FDB *)hDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK, uiAutoTrans, &bStartedAutoTrans))) { 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 add a record that is marked read-only, because it // is probably already in the cache under a different record ID. if( pRecord->isReadOnly()) { flmAssert( 0); rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) { goto Exit; } rc = flmAddRecord( pDb, pLFile, &uiDrn, pRecord, FALSE, (uiAutoTrans & FLM_DO_IN_BACKGROUND) ? TRUE : FALSE, (uiAutoTrans & FLM_SUSPENDED) ? TRUE : FALSE, (FLMBOOL)((uiAutoTrans & FLM_DONT_INSERT_IN_CACHE) ? FALSE : TRUE), &bLogCompleteIndexSet); Exit: rc = FB_OperationEnd( pDb, rc); if( RC_OK( rc)) { if( RC_OK( rc = pDb->pFile->pRfl->logUpdate( uiContainer, uiDrn, uiAutoTrans, NULL, 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->RecordAdds.ui64ElapMilli); pDbStats->RecordAdds.ui64Count++; pDbStats->bHaveStats = TRUE; } if( gv_FlmSysData.UpdateEvents.pEventCBList) { flmUpdEventCallback( pDb, F_EVENT_ADD_RECORD, hDb, rc, uiDrn, uiContainer, pRecord, NULL); } // If started an automatic transaction end it. if( bStartedAutoTrans) { rc = flmEndAutoTrans( pDb, rc); } ExitCS: if( puiDrn) { *puiDrn = uiDrn; } flmExit( FLM_RECORD_ADD, pDb, rc); return( rc); }
/**************************************************************************** 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); }
/******************************************************************** Desc: ? *********************************************************************/ FLMINT ViewGetKey( void) { RCODE rc = FERR_OK; FlmRecord * pKey = NULL; void * pvFld; char Prompt [80]; FLMUINT Num; FLMUINT ValEntered; FLMUINT Len; char TempBuf [80]; FLMUINT NumFields; FLMUINT i; FLMINT GetOK; FLMBYTE FieldName [80]; FLMBYTE FieldType [16]; FLMINT KeyEntered = FALSE; FLMBYTE LFH[ LFH_SIZE]; FLMUINT FileOffset; LFILE * pLFile = NULL; IXD * pIxd; IFD * pIfd; FLMUINT uiRootBlkAddress; FLMBOOL bTruncated; if (!gv_bViewHdrRead) ViewReadHdr(); /* See if we can get dictionary information. */ ViewGetDictInfo(); if (gv_bViewHaveDictInfo) { /* Find the logical file */ if ((RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict, ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, gv_uiViewSearchLfNum, &pLFile, NULL))) && (RC_BAD( fdictGetContainer( ((FDB *)gv_hViewDb)->pDict, gv_uiViewSearchLfNum, &pLFile)))) { pLFile = NULL; } } /* See if we have a valid logical file */ if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) || (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) || (pLFile)) { /* Get the LFH information for the logical file */ if (!ViewGetLFH( gv_uiViewSearchLfNum, LFH, &FileOffset)) { ViewShowError( "Could not get LFH for logical file"); return( FALSE); } uiRootBlkAddress = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET]); if (uiRootBlkAddress == 0xFFFFFFFF) { ViewShowError( "Logical file is empty"); return( FALSE); } } else { ViewShowError( "Logical file not defined"); return( FALSE); } if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) || (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) || ((pLFile) && (pLFile->uiLfType == LF_CONTAINER))) { if (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) f_strcpy( TempBuf, "Enter Dictionary Record Number: "); else if (gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) f_strcpy( TempBuf, "Enter Data Container Record Number: "); else f_sprintf( (char *)TempBuf, "Enter Record Number For Container %u: ", (unsigned)gv_uiViewSearchLfNum); if ((!ViewGetNum( TempBuf, &Num, FALSE, 4, 0xFFFFFFFF, &ValEntered)) || (!ValEntered)) return( FALSE); f_UINT32ToBigEndian( (FLMUINT32)Num, gv_ucViewSearchKey); gv_uiViewSearchKeyLen = 4; return( TRUE); } /* At this point, we are dealing with an index. */ if (gv_uiViewSearchLfNum == FLM_DICT_INDEX) { FLMUINT wTagType = 0; FLMUINT wElmLen; while (!wTagType) { if ((!ViewEditText( "Enter Type:", TempBuf, sizeof( TempBuf), &ValEntered)) || (!ValEntered)) return( FALSE); else if ((f_stricmp( TempBuf, "F") == 0) || (f_stricmp( TempBuf, "FIELD") == 0)) { wTagType = FLM_FIELD_TAG; } else if ((f_stricmp( TempBuf, "I") == 0) || (f_stricmp( TempBuf, "INDEX") == 0)) { wTagType = FLM_INDEX_TAG; } else if ((f_stricmp( TempBuf, "C") == 0) || (f_stricmp( TempBuf, "CONTAINER") == 0)) { wTagType = FLM_CONTAINER_TAG; } else if ((f_stricmp( TempBuf, "A") == 0) || (f_stricmp( TempBuf, "AREA") == 0)) { wTagType = FLM_AREA_TAG; } else { ViewShowError( "Illegal type, must be F)ield, I)ndex, C)ontainer, R)ecord, or A)rea"); wTagType = 0; } } gv_ucViewSearchKey [0] = KY_CONTEXT_PREFIX; f_UINT16ToBigEndian( (FLMUINT16)wTagType, &gv_ucViewSearchKey [1]); gv_uiViewSearchKeyLen += KY_CONTEXT_LEN; gv_ucViewSearchKey [gv_uiViewSearchKeyLen++] = COMPOUND_MARKER; if (!ViewEditText( "Enter Name:", TempBuf, sizeof( TempBuf), &ValEntered)) return( FALSE); /* Collate the name. */ wElmLen = MAX_KEY_SIZ - gv_uiViewSearchKeyLen; if (RC_BAD( rc = KYCollateValue( &gv_ucViewSearchKey [gv_uiViewSearchKeyLen], &wElmLen, (const FLMBYTE *)TempBuf, (FLMUINT)f_strlen( TempBuf), FLM_TEXT_TYPE, MAX_KEY_SIZ, NULL, NULL, gv_ViewHdrInfo.FileHdr.uiDefaultLanguage, FALSE, FALSE, FALSE, &bTruncated))) { ViewShowRCError( "collating name", rc); return( FALSE); } gv_uiViewSearchKeyLen += wElmLen; return( TRUE); } else if (!pLFile) { ViewShowError( "Cannot get logical file information"); return( FALSE); } else if (RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict, ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, gv_uiViewSearchLfNum, &pLFile, &pIxd))) { ViewShowError( "Cannot get index field information"); return( FALSE); } else { pIfd = pIxd->pFirstIfd; NumFields = pIxd->uiNumFlds; if (!(pIfd->uiFlags & IFD_COMPOUND)) { NumFields = 1; } if( (pKey = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); ViewShowRCError( "creating key", rc); goto Exit_False; } if (RC_BAD( rc = pKey->insertLast( 0, FLM_KEY_TAG, FLM_CONTEXT_TYPE, &pvFld))) { ViewShowRCError( "adding key tag", rc); goto Exit_False; } /* Ask for data for each field and link into key tree */ i = 0; while (i < NumFields) { /* Get the name of the field and its type */ f_sprintf( (char *)FieldName, "FIELD %u", (unsigned)pIfd->uiFldNum); switch( IFD_GET_FIELD_TYPE( pIfd)) { case FLM_TEXT_TYPE: f_strcpy( (char *)FieldType, "TEXT"); break; case FLM_NUMBER_TYPE: f_strcpy( (char *)FieldType, "NUMBER"); break; case FLM_BINARY_TYPE: f_strcpy( (char *)FieldType, "BINARY"); break; case FLM_CONTEXT_TYPE: f_strcpy( (char *)FieldType, "CONTEXT"); break; default: f_sprintf( (char *)FieldType, "UNK: %u!", (unsigned)IFD_GET_FIELD_TYPE( pIfd)); break; } if (pIfd->uiFlags & IFD_OPTIONAL) f_sprintf( (char *)Prompt, "%s (%s-OPTIONAL): ", FieldName, FieldType); else f_sprintf( (char *)Prompt, "%s (%s-REQUIRED): ", FieldName, FieldType); switch( IFD_GET_FIELD_TYPE( pIfd)) { case FLM_TEXT_TYPE: if (!ViewEditText( Prompt, TempBuf, sizeof( TempBuf), &ValEntered)) goto Exit_False; break; case FLM_NUMBER_TYPE: case FLM_CONTEXT_TYPE: if (!ViewGetNum( Prompt, &Num, FALSE, 4, 0xFFFFFFFF, &ValEntered)) goto Exit_False; break; case FLM_BINARY_TYPE: Len = sizeof( TempBuf); if (!ViewEditBinary( Prompt, TempBuf, &Len, &ValEntered)) goto Exit_False; break; } if (!ValEntered) { i++; } else { FLMUINT uiDataType; /* See if the entered data can be converted to the */ /* correct type */ uiDataType = IFD_GET_FIELD_TYPE( pIfd); if (RC_BAD( rc = pKey->insertLast( 1, pIfd->uiFldNum, uiDataType, &pvFld))) { ViewShowRCError( "creating field", rc); } else { switch( IFD_GET_FIELD_TYPE( pIfd)) { case FLM_TEXT_TYPE: rc = pKey->setNative( pvFld, TempBuf); break; case FLM_NUMBER_TYPE: rc = pKey->setUINT( pvFld, Num); break; case FLM_CONTEXT_TYPE: rc = pKey->setRecPointer( pvFld, Num); break; case FLM_BINARY_TYPE: rc = pKey->setBinary( pvFld, TempBuf, Len); break; } if (RC_BAD( rc)) { ViewShowRCError( "putting data in field", rc); } } if (RC_OK(rc)) { i++; pIfd++; KeyEntered = TRUE; } } } // If index is on all containers, prompt for container number. if (!pIxd->uiContainerNum) { f_strcpy( Prompt, "CONTAINER: "); if (!ViewGetNum( Prompt, &Num, FALSE, sizeof( Num), 0xFFFF, &ValEntered)) { goto Exit_False; } if (ValEntered) { pKey->setContainerID( Num); KeyEntered = TRUE; } } /* Convert the key to binary format */ if (!KeyEntered) goto Exit_False; if ((rc = FlmKeyBuild( gv_hViewDb, gv_uiViewSearchLfNum, pKey->getContainerID(), pKey, 0, gv_ucViewSearchKey, &gv_uiViewSearchKeyLen)) != FERR_OK) ViewShowRCError( "building key", rc); else { GetOK = TRUE; goto Exit_GetKey; } } Exit_False: GetOK = FALSE; Exit_GetKey: if (pKey) { pKey->Release(); } return( GetOK); }