/** * @param arrayNestedArray - set if the returned element is an array nested directly within arr. */ BSONElement extractNextElement( const BSONObj &obj, const BSONObj &arr, const char *&field, bool &arrayNestedArray ) const { string firstField = mongoutils::str::before( field, '.' ); bool haveObjField = !obj.getField( firstField ).eoo(); BSONElement arrField = arr.getField( firstField ); bool haveArrField = !arrField.eoo(); // An index component field name cannot exist in both a document array and one of that array's children. uassert( 15855, mongoutils::str::stream() << "Ambiguous field name found in array (do not use numeric field names in " "embedded elements in an array), field: '" << arrField.fieldName() << "' for array: " << arr, !haveObjField || !haveArrField ); arrayNestedArray = false; if ( haveObjField ) { return obj.getFieldDottedOrArray( field ); } else if ( haveArrField ) { if ( arrField.type() == Array ) { arrayNestedArray = true; } return arr.getFieldDottedOrArray( field ); } return BSONElement(); }
// static void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj, const string& hashedField, HashSeed seed, int hashVersion, bool isSparse, const CollatorInterface* collator, BSONObjSet* keys) { const char* cstr = hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); // Convert strings to comparison keys. BSONObj fieldValObj; if (!fieldVal.eoo()) { BSONObjBuilder bob; CollationIndexKey::collationAwareIndexKeyAppend(fieldVal, collator, &bob); fieldValObj = bob.obj(); fieldVal = fieldValObj.firstElement(); } uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array); if (!fieldVal.eoo()) { BSONObj key = BSON("" << makeSingleHashKey(fieldVal, seed, hashVersion)); keys->insert(key); } else if (!isSparse) { BSONObj nullObj = BSON("" << BSONNULL); keys->insert(BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion))); } }
/** * Checks if phrase is exactly matched in obj, returns true if so, false otherwise * @param phrase, the string to be matched * @param obj, document in the collection to match against */ bool FTSMatcher::phraseMatch( const string& phrase, const BSONObj& obj ) const { if ( _spec.wildcard() ) { // case where everything is indexed (all fields) return _phraseRecurse( phrase, obj ); } for ( Weights::const_iterator i = _spec.weights().begin(); i != _spec.weights().end(); ++i ) { // figure out what the indexed field is.. ie. is it "field" or "field.subfield" etc. const char * leftOverName = i->first.c_str(); BSONElement e = obj.getFieldDottedOrArray(leftOverName); if ( e.type() == Array ) { BSONObjIterator j( e.Obj() ); while ( j.more() ) { BSONElement x = j.next(); if ( leftOverName[0] && x.isABSONObj() ) x = x.Obj().getFieldDotted( leftOverName ); if ( x.type() == String ) if ( _phraseMatches( phrase, x.String() ) ) return true; } } else if ( e.type() == String ) { if ( _phraseMatches( phrase, e.String() ) ) return true; } } return false; }
BSONElement BtreeKeyGeneratorV1::extractNextElement(const BSONObj& obj, const PositionalPathInfo& positionalInfo, const char** field, bool* arrayNestedArray) const { std::string firstField = mongoutils::str::before(*field, '.'); bool haveObjField = !obj.getField(firstField).eoo(); BSONElement arrField = positionalInfo.positionallyIndexedElt; // An index component field name cannot exist in both a document // array and one of that array's children. uassert(16746, mongoutils::str::stream() << "Ambiguous field name found in array (do not use numeric field names in " "embedded elements in an array), field: '" << arrField.fieldName() << "' for array: " << positionalInfo.arrayObj, !haveObjField || !positionalInfo.hasPositionallyIndexedElt()); *arrayNestedArray = false; if (haveObjField) { return obj.getFieldDottedOrArray(*field); } else if (positionalInfo.hasPositionallyIndexedElt()) { if (arrField.type() == Array) { *arrayNestedArray = true; } *field = positionalInfo.remainingPath; return positionalInfo.dottedElt; } return BSONElement(); }
void HashAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) { const char* cstr = _hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array ); if (!fieldVal.eoo()) { BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); keys->insert( key ); } else if (!_descriptor->isSparse()) { keys->insert( _missingKey.copy() ); } }
void HashedIndexType::getKeys( const BSONObj &obj, BSONObjSet &keys ) const { string hashedFieldCopy = string( _hashedField ); const char* hashedFieldCopyPtr = hashedFieldCopy.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray( hashedFieldCopyPtr ); uassert( 16244 , "Error: hashed indexes do not currently support array values" , fieldVal.type() != Array ); if ( ! fieldVal.eoo() ) { BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); keys.insert( key ); } else if (! _isSparse ) { keys.insert( _missingKey.copy() ); } }
// static void HashAccessMethod::getKeysImpl(const BSONObj& obj, const string& hashedField, HashSeed seed, int hashVersion, bool isSparse, BSONObjSet* keys) { const char* cstr = hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array ); if (!fieldVal.eoo()) { BSONObj key = BSON( "" << makeSingleKey(fieldVal, seed, hashVersion)); keys->insert(key); } else if (!isSparse) { BSONObj nullObj = BSON("" << BSONNULL); keys->insert(BSON("" << makeSingleKey(nullObj.firstElement(), seed, hashVersion))); } }
/* * Calculates the score for all terms in a document of a collection * @param obj, the document in the collection being parsed * @param term_freqs, map<string,double> to fill up */ void FTSSpec::scoreDocument( const BSONObj& obj, TermFrequencyMap* term_freqs ) const { string language = getLanguageToUse( obj ); Stemmer stemmer(language); Tools tools(language); tools.stemmer = &stemmer; tools.stopwords = StopWords::getStopWords( language ); if ( wildcard() ) { // if * is specified for weight, we can recurse over all fields. _scoreRecurse(tools, obj, term_freqs); return; } // otherwise, we need to remember the different weights for each field // and act accordingly (in other words, call _score) for ( Weights::const_iterator i = _weights.begin(); i != _weights.end(); i++ ) { const char * leftOverName = i->first.c_str(); // name of field BSONElement e = obj.getFieldDottedOrArray(leftOverName); // weight associated to name of field double weight = i->second; if ( e.eoo() ) { // do nothing } else if ( e.type() == Array ) { BSONObjIterator j( e.Obj() ); while ( j.more() ) { BSONElement x = j.next(); if ( leftOverName[0] && x.isABSONObj() ) x = x.Obj().getFieldDotted( leftOverName ); if ( x.type() == String ) _scoreString( tools, x.valuestr(), term_freqs, weight ); } } else if ( e.type() == String ) { _scoreString( tools, e.valuestr(), term_freqs, weight ); } } }
/* * Checks if the obj contains any of the negTerms, if so returns true, otherwise false * @param obj, object to be checked */ bool FTSMatcher::hasNegativeTerm(const BSONObj& obj ) const { // called during search. deals with the case in which we have a term // flagged for exclusion, i.e. "hello -world" we want to remove all // results that include "world" if ( _query.getNegatedTerms().size() == 0 ) return false; if ( _spec.wildcard() ) { return _hasNegativeTerm_recurse(obj); } /* otherwise look at fields where weights are defined */ for ( Weights::const_iterator i = _spec.weights().begin(); i != _spec.weights().end(); i++ ) { const char * leftOverName = i->first.c_str(); BSONElement e = obj.getFieldDottedOrArray(leftOverName); if ( e.type() == Array ) { BSONObjIterator j( e.Obj() ); while ( j.more() ) { BSONElement x = j.next(); if ( leftOverName[0] && x.isABSONObj() ) x = x.Obj().getFieldDotted( leftOverName ); if ( x.type() == String ) if ( _hasNegativeTerm_string( x.String() ) ) return true; } } else if ( e.type() == String ) { if ( _hasNegativeTerm_string( e.String() ) ) return true; } } return false; }
void IndexSpec::_getKeys( vector<const char*> fieldNames , vector<BSONElement> fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const { BSONElement arrElt; unsigned arrIdx = ~0; for( unsigned i = 0; i < fieldNames.size(); ++i ) { if ( *fieldNames[ i ] == '\0' ) continue; BSONElement e = obj.getFieldDottedOrArray( fieldNames[ i ] ); if ( e.eoo() ) e = _nullElt; // no matching field if ( e.type() != Array ) fieldNames[ i ] = ""; // no matching field or non-array match if ( *fieldNames[ i ] == '\0' ) fixed[ i ] = e; // no need for further object expansion (though array expansion still possible) if ( e.type() == Array && arrElt.eoo() ) { // we only expand arrays on a single path -- track the path here arrIdx = i; arrElt = e; } // enforce single array path here uassert( 10088 , "cannot index parallel arrays", e.type() != Array || e.rawdata() == arrElt.rawdata() ); } bool allFound = true; // have we found elements for all field names in the key spec? for( vector<const char*>::const_iterator i = fieldNames.begin(); i != fieldNames.end(); ++i ){ if ( **i != '\0' ){ allFound = false; break; } } bool insertArrayNull = false; if ( allFound ) { if ( arrElt.eoo() ) { // no terminal array element to expand BSONObjBuilder b(_sizeTracker); for( vector< BSONElement >::iterator i = fixed.begin(); i != fixed.end(); ++i ) b.appendAs( *i, "" ); keys.insert( b.obj() ); } else { // terminal array element to expand, so generate all keys BSONObjIterator i( arrElt.embeddedObject() ); if ( i.more() ){ while( i.more() ) { BSONObjBuilder b(_sizeTracker); for( unsigned j = 0; j < fixed.size(); ++j ) { if ( j == arrIdx ) b.appendAs( i.next(), "" ); else b.appendAs( fixed[ j ], "" ); } keys.insert( b.obj() ); } } else if ( fixed.size() > 1 ){ insertArrayNull = true; } } } else { // nonterminal array element to expand, so recurse assert( !arrElt.eoo() ); BSONObjIterator i( arrElt.embeddedObject() ); if ( i.more() ){ while( i.more() ) { BSONElement e = i.next(); if ( e.type() == Object ){ _getKeys( fieldNames, fixed, e.embeddedObject(), keys ); } } } else { insertArrayNull = true; } } if ( insertArrayNull ) { // x : [] - need to insert undefined BSONObjBuilder b(_sizeTracker); for( unsigned j = 0; j < fixed.size(); ++j ) { if ( j == arrIdx ){ b.appendUndefined( "" ); } else { BSONElement e = fixed[j]; if ( e.eoo() ) b.appendNull( "" ); else b.appendAs( e , "" ); } } keys.insert( b.obj() ); } }
void BtreeKeyGeneratorV0::getKeysImpl(vector<const char*> fieldNames, vector<BSONElement> fixed, const BSONObj &obj, BSONObjSet *keys) const { BSONElement arrElt; unsigned arrIdx = ~0; unsigned numNotFound = 0; for ( unsigned i = 0; i < fieldNames.size(); ++i ) { if ( *fieldNames[ i ] == '\0' ) continue; BSONElement e = obj.getFieldDottedOrArray( fieldNames[ i ] ); if ( e.eoo() ) { e = _nullElt; // no matching field numNotFound++; } if ( e.type() != Array ) fieldNames[ i ] = ""; // no matching field or non-array match if ( *fieldNames[ i ] == '\0' ) // no need for further object expansion (though array expansion still possible) fixed[ i ] = e; if ( e.type() == Array && arrElt.eoo() ) { // we only expand arrays on a single path -- track the path here arrIdx = i; arrElt = e; } // enforce single array path here if ( e.type() == Array && e.rawdata() != arrElt.rawdata() ) { assertParallelArrays( e.fieldName(), arrElt.fieldName() ); } } bool allFound = true; // have we found elements for all field names in the key spec? for (vector<const char*>::const_iterator i = fieldNames.begin(); i != fieldNames.end(); ++i ) { if ( **i != '\0' ) { allFound = false; break; } } if ( _isSparse && numNotFound == _fieldNames.size()) { // we didn't find any fields // so we're not going to index this document return; } bool insertArrayNull = false; if ( allFound ) { if ( arrElt.eoo() ) { // no terminal array element to expand BSONObjBuilder b(_sizeTracker); for( vector< BSONElement >::iterator i = fixed.begin(); i != fixed.end(); ++i ) b.appendAs( *i, "" ); keys->insert( b.obj() ); } else { // terminal array element to expand, so generate all keys BSONObjIterator i( arrElt.embeddedObject() ); if ( i.more() ) { while( i.more() ) { BSONObjBuilder b(_sizeTracker); for( unsigned j = 0; j < fixed.size(); ++j ) { if ( j == arrIdx ) b.appendAs( i.next(), "" ); else b.appendAs( fixed[ j ], "" ); } keys->insert( b.obj() ); } } else if ( fixed.size() > 1 ) { insertArrayNull = true; } } } else { // nonterminal array element to expand, so recurse verify( !arrElt.eoo() ); BSONObjIterator i( arrElt.embeddedObject() ); if ( i.more() ) { while( i.more() ) { BSONElement e = i.next(); if ( e.type() == Object ) { getKeysImpl( fieldNames, fixed, e.embeddedObject(), keys ); } } } else { insertArrayNull = true; } } if ( insertArrayNull ) { // x : [] - need to insert undefined BSONObjBuilder b(_sizeTracker); for( unsigned j = 0; j < fixed.size(); ++j ) { if ( j == arrIdx ) { b.appendUndefined( "" ); } else { BSONElement e = fixed[j]; if ( e.eoo() ) b.appendNull( "" ); else b.appendAs( e , "" ); } } keys->insert( b.obj() ); } }
// PD_TRACE_DECLARE_FUNCTION ( SDB__IXMKEYGEN__GETKEYS, "_ixmKeyGenerator::_getKeys" ) INT32 _getKeys( vector<const CHAR *> &fieldNames, const BSONObj &obj, BSONObjSet &keys, BSONElement *arrEle ) const { INT32 rc = SDB_OK ; PD_TRACE_ENTRY ( SDB__IXMKEYGEN__GETKEYS ); #define IXM_DEFAULT_FIELD_NUM 3 BSONElement eleOnStack[IXM_DEFAULT_FIELD_NUM] ; BSONElement *keyEles = NULL ; const CHAR *arrEleName = NULL ; UINT32 arrElePos = 0 ; UINT32 eooNum = 0 ; if ( IXM_DEFAULT_FIELD_NUM < fieldNames.size() ) { keyEles = new(std::nothrow) BSONElement[fieldNames.size()] ; if ( NULL == keyEles ) { PD_LOG( PDERROR, "failed to allocalte mem." ) ; rc = SDB_OOM ; goto error ; } } else { keyEles = ( BSONElement* )eleOnStack ; } for ( UINT32 i = 0; i < fieldNames.size(); i++ ) { const CHAR *name = fieldNames.at( i ) ; SDB_ASSERT( '\0' != name[0], "can not be empty" ) ; BSONElement &e = keyEles[i] ; e = obj.getFieldDottedOrArray( name ) ; if ( e.eoo() ) { ++eooNum ; continue ; } else if ( Array == e.type() ) { if ( !arrEle->eoo() ) { PD_LOG( PDERROR, "At most one array can be in the key:", arrEle->fieldName(), e.fieldName() ) ; rc = SDB_IXM_MULTIPLE_ARRAY ; goto error ; } else { *arrEle = e ; arrEleName = name ; arrElePos = i ; } } else { continue ; } } if ( fieldNames.size() == eooNum ) { rc = SDB_OK ; goto done ; } else if ( !arrEle->eoo() ) { rc = _genKeyWithArrayEle( keyEles, fieldNames.size(), arrEle, arrEleName, arrElePos, keys ) ; if ( SDB_OK != rc ) { PD_LOG( PDERROR, "failed to gen keys with array element:%d", rc ) ; goto error ; } } else { rc = _genKeyWithNormalEle( keyEles, fieldNames.size(), keys ) ; if ( SDB_OK != rc ) { PD_LOG( PDERROR, "failed to gen keys with normal element:%d", rc ) ; goto error ; } } done: if ( IXM_DEFAULT_FIELD_NUM < fieldNames.size() && NULL != keyEles ) { delete []keyEles ; } PD_TRACE_EXITRC ( SDB__IXMKEYGEN__GETKEYS, rc ); return rc ; error: goto done ; }
void BtreeKeyGeneratorV1::getKeysImplWithArray( std::vector<const char*> fieldNames, std::vector<BSONElement> fixed, const BSONObj& obj, BSONObjSet* keys, unsigned numNotFound, const std::vector<PositionalPathInfo>& positionalInfo, MultikeyPaths* multikeyPaths) const { BSONElement arrElt; // A set containing the position of any indexed fields in the key pattern that traverse through // the 'arrElt' array value. std::set<size_t> arrIdxs; // A vector with size equal to the number of elements in the index key pattern. Each element in // the vector, if initialized, refers to the component within the indexed field that traverses // through the 'arrElt' array value. We say that this component within the indexed field // corresponds to a path that causes the index to be multikey if the 'arrElt' array value // contains multiple elements. // // For example, consider the index {'a.b': 1, 'a.c'} and the document // {a: [{b: 1, c: 'x'}, {b: 2, c: 'y'}]}. The path "a" causes the index to be multikey, so we'd // have a std::vector<boost::optional<size_t>>{{0U}, {0U}}. // // Furthermore, due to how positional key patterns are specified, it's possible for an indexed // field to cause the index to be multikey at a different component than another indexed field // that also traverses through the 'arrElt' array value. It's then also possible for an indexed // field not to cause the index to be multikey, even if it traverses through the 'arrElt' array // value, because only a particular element would be indexed. // // For example, consider the index {'a.b': 1, 'a.b.0'} and the document {a: {b: [1, 2]}}. The // path "a.b" causes the index to be multikey, but the key pattern "a.b.0" only indexes the // first element of the array, so we'd have a // std::vector<boost::optional<size_t>>{{1U}, boost::none}. std::vector<boost::optional<size_t>> arrComponents(fieldNames.size()); bool mayExpandArrayUnembedded = true; for (size_t i = 0; i < fieldNames.size(); ++i) { if (*fieldNames[i] == '\0') { continue; } bool arrayNestedArray; // Extract element matching fieldName[ i ] from object xor array. BSONElement e = extractNextElement(obj, positionalInfo[i], &fieldNames[i], &arrayNestedArray); if (e.eoo()) { // if field not present, set to null fixed[i] = nullElt; // done expanding this field name fieldNames[i] = ""; numNotFound++; } else if (e.type() == Array) { arrIdxs.insert(i); if (arrElt.eoo()) { // we only expand arrays on a single path -- track the path here arrElt = e; } else if (e.rawdata() != arrElt.rawdata()) { // enforce single array path here assertParallelArrays(e.fieldName(), arrElt.fieldName()); } if (arrayNestedArray) { mayExpandArrayUnembedded = false; } } else { // not an array - no need for further expansion fixed[i] = e; } } if (arrElt.eoo()) { // No array, so generate a single key. if (_isSparse && numNotFound == fieldNames.size()) { return; } BSONObjBuilder b(_sizeTracker); for (std::vector<BSONElement>::iterator i = fixed.begin(); i != fixed.end(); ++i) { b.appendAs(*i, ""); } keys->insert(b.obj()); } else if (arrElt.embeddedObject().firstElement().eoo()) { // Empty array, so set matching fields to undefined. _getKeysArrEltFixed(&fieldNames, &fixed, undefinedElt, keys, numNotFound, arrElt, arrIdxs, true, _emptyPositionalInfo, multikeyPaths); } else { BSONObj arrObj = arrElt.embeddedObject(); // For positional key patterns, e.g. {'a.1.b': 1}, we lookup the indexed array element // and then traverse the remainder of the field path up front. This prevents us from // having to look up the indexed element again on each recursive call (i.e. once per // array element). std::vector<PositionalPathInfo> subPositionalInfo(fixed.size()); for (size_t i = 0; i < fieldNames.size(); ++i) { const bool fieldIsArray = arrIdxs.find(i) != arrIdxs.end(); if (*fieldNames[i] == '\0') { // We've reached the end of the path. if (multikeyPaths && fieldIsArray && mayExpandArrayUnembedded) { // The 'arrElt' array value isn't expanded into multiple elements when the last // component of the indexed field is positional and 'arrElt' contains nested // array values. In all other cases, the 'arrElt' array value may be expanded // into multiple element and can therefore cause the index to be multikey. arrComponents[i] = _pathLengths[i] - 1; } continue; } // The earlier call to BSONObj::getFieldDottedOrArray(fieldNames[i]) modified // fieldNames[i] to refer to the suffix of the path immediately following the 'arrElt' // array value. If we haven't reached the end of this indexed field yet, then we must // have traversed through 'arrElt'. invariant(fieldIsArray); StringData part = fieldNames[i]; part = part.substr(0, part.find('.')); subPositionalInfo[i].positionallyIndexedElt = arrObj[part]; if (subPositionalInfo[i].positionallyIndexedElt.eoo()) { // We aren't indexing a particular element of the 'arrElt' array value, so it may be // expanded into multiple elements. It can therefore cause the index to be multikey. if (multikeyPaths) { // We need to determine which component of the indexed field causes the index to // be multikey as a result of the 'arrElt' array value. Since // // NumComponents("<pathPrefix>") + NumComponents("<pathSuffix>") // = NumComponents("<pathPrefix>.<pathSuffix>"), // // we can compute the number of components in a prefix of the indexed field by // subtracting the number of components in the suffix 'fieldNames[i]' from the // number of components in the indexed field '_fieldNames[i]'. // // For example, consider the indexed field "a.b.c" and the suffix "c". The path // "a.b.c" has 3 components and the suffix "c" has 1 component. Subtracting the // latter from the former yields the number of components in the prefix "a.b", // i.e. 2. size_t fullPathLength = _pathLengths[i]; size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts(); invariant(suffixPathLength < fullPathLength); arrComponents[i] = fullPathLength - suffixPathLength - 1; } continue; } // We're indexing an array element by its position. Traverse the remainder of the // field path now. // // Indexing an array element by its position selects a particular element of the // 'arrElt' array value when generating keys. It therefore cannot cause the index to be // multikey. subPositionalInfo[i].arrayObj = arrObj; subPositionalInfo[i].remainingPath = fieldNames[i]; subPositionalInfo[i].dottedElt = arrObj.getFieldDottedOrArray(subPositionalInfo[i].remainingPath); } // Generate a key for each element of the indexed array. size_t nArrObjFields = 0; for (const auto arrObjElem : arrObj) { _getKeysArrEltFixed(&fieldNames, &fixed, arrObjElem, keys, numNotFound, arrElt, arrIdxs, mayExpandArrayUnembedded, subPositionalInfo, multikeyPaths); ++nArrObjFields; } if (multikeyPaths && nArrObjFields > 1) { // The 'arrElt' array value contains multiple elements, so we say that it causes the // index to be multikey. for (size_t i = 0; i < arrComponents.size(); ++i) { if (auto arrComponent = arrComponents[i]) { (*multikeyPaths)[i].insert(*arrComponent); } } } } }
int dmsFile::insert ( BSONObj &record, BSONObj &outRecord, dmsRecordID &rid ) { int rc = EDB_OK ; PAGEID pageID = 0 ; char *page = NULL ; dmsPageHeader *pageHeader = NULL ; int recordSize = 0 ; SLOTOFF offsetTemp = 0 ; const char *pGKeyFieldName = NULL ; dmsRecord recordHeader ; recordSize = record.objsize() ; // when we attempt to insert record, first we have to verify it include _id field if ( (unsigned int)recordSize > DMS_MAX_RECORD ) { rc = EDB_INVALIDARG ; PD_LOG ( PDERROR, "record cannot bigger than 4MB" ) ; goto error ; } pGKeyFieldName = gKeyFieldName ; // make sure _id exists if ( record.getFieldDottedOrArray ( pGKeyFieldName ).eoo () ) { rc = EDB_INVALIDARG ; PD_LOG ( PDERROR, "record must be with _id" ) ; goto error ; } retry : // lock the database _mutex.get() ; // and then we should get the required record size pageID = _findPage ( recordSize + sizeof(dmsRecord) ) ; // if there's not enough space in any existing pages, let's release db lock if ( DMS_INVALID_PAGEID == pageID ) { _mutex.release () ; // if there's not enough space in any existing pages, let's release db lock and // try to allocate a new segment by calling _extendSegment if ( _extendMutex.try_get() ) { // calling _extendSegment rc = _extendSegment () ; if ( rc ) { PD_LOG ( PDERROR, "Failed to extend segment, rc = %d", rc ) ; _extendMutex.release () ; goto error ; } } else { // if we cannot get the extendmutex, that means someone else is trying to extend // so let's wait until getting the mutex, and release it and try again _extendMutex.get() ; } _extendMutex.release () ; goto retry ; } // find the in-memory offset for the page page = pageToOffset ( pageID ) ; // if something wrong, let's return error if ( !page ) { rc = EDB_SYS ; PD_LOG ( PDERROR, "Failed to find the page" ) ; goto error_releasemutex ; } // set page header pageHeader = (dmsPageHeader *)page ; if ( memcmp ( pageHeader->_eyeCatcher, DMS_PAGE_EYECATCHER, DMS_PAGE_EYECATCHER_LEN ) != 0 ) { rc = EDB_SYS ; PD_LOG ( PDERROR, "Invalid page header" ) ; goto error_releasemutex ; } // slot offset is the last byte of slots // free offset is the first byte of data // so freeOffset - slotOffset is the actual free space excluding holes if ( // make sure there's still holes to recover ( pageHeader->_freeSpace > pageHeader->_freeOffset - pageHeader->_slotOffset ) && // if there's no free space excluding holes ( pageHeader->_slotOffset + recordSize + sizeof(dmsRecord) + sizeof(SLOTID) > pageHeader->_freeOffset ) ) { // recover empty hole from page _recoverSpace ( page ) ; } if ( // make sure there is enough free space ( pageHeader->_freeSpace < recordSize + sizeof(dmsRecord) + sizeof(SLOTID) ) || ( pageHeader->_freeOffset - pageHeader->_slotOffset < recordSize + sizeof(dmsRecord) + sizeof(SLOTID) ) ) { PD_LOG ( PDERROR, "Something big wrong!!" ) ; rc = EDB_SYS ; goto error_releasemutex ; } offsetTemp = pageHeader->_freeOffset - recordSize - sizeof(dmsRecord) ; recordHeader._size = recordSize + sizeof( dmsRecord ) ; recordHeader._flag = DMS_RECORD_FLAG_NORMAL ; // copy the slot *(SLOTOFF*)( page + sizeof( dmsPageHeader ) + pageHeader->_numSlots * sizeof(SLOTOFF) ) = offsetTemp ; // copy the record header memcpy ( page + offsetTemp, ( char* )&recordHeader, sizeof(dmsRecord) ) ; // copy the record body memcpy ( page + offsetTemp + sizeof(dmsRecord), record.objdata(), recordSize ) ; outRecord = BSONObj ( page + offsetTemp + sizeof(dmsRecord) ) ; rid._pageID = pageID ; rid._slotID = pageHeader->_numSlots ; // modify metadata in page pageHeader->_numSlots ++ ; pageHeader->_slotOffset += sizeof(SLOTID) ; pageHeader->_freeOffset = offsetTemp ; // modify database metadata _updateFreeSpace ( pageHeader, -(recordSize+sizeof(SLOTID)+sizeof(dmsRecord)), pageID ) ; // release lock for database _mutex.release () ; done : return rc ; error_releasemutex : _mutex.release() ; error : goto done ; }