v8::Handle<v8::Value> ThreadReturnData(V8Scope* scope, const v8::Arguments& args) { BSONObj data = thisConfig(scope, args)->returnData(); return scope->mongoToV8Element(data.firstElement(), true); }
Status MMAPV1DatabaseCatalogEntry::renameCollection(OperationContext* txn, StringData fromNS, StringData toNS, bool stayTemp) { Status s = _renameSingleNamespace(txn, fromNS, toNS, stayTemp); if (!s.isOK()) return s; NamespaceDetails* details = _namespaceIndex.details(toNS); invariant(details); RecordStoreV1Base* systemIndexRecordStore = _getIndexRecordStore(); auto cursor = systemIndexRecordStore->getCursor(txn); while (auto record = cursor->next()) { BSONObj oldIndexSpec = record->data.releaseToBson(); if (fromNS != oldIndexSpec["ns"].valuestrsafe()) continue; BSONObj newIndexSpec; { BSONObjBuilder b; BSONObjIterator i(oldIndexSpec); while (i.more()) { BSONElement e = i.next(); if (strcmp(e.fieldName(), "ns") != 0) b.append(e); else b << "ns" << toNS; } newIndexSpec = b.obj(); } StatusWith<RecordId> newIndexSpecLoc = systemIndexRecordStore->insertRecord( txn, newIndexSpec.objdata(), newIndexSpec.objsize(), false); if (!newIndexSpecLoc.isOK()) return newIndexSpecLoc.getStatus(); const std::string& indexName = oldIndexSpec.getStringField("name"); { // Fix the IndexDetails pointer. int indexI = getCollectionCatalogEntry(toNS)->_findIndexNumber(txn, indexName); IndexDetails& indexDetails = details->idx(indexI); *txn->recoveryUnit()->writing(&indexDetails.info) = DiskLoc::fromRecordId(newIndexSpecLoc.getValue()); } { // Move the underlying namespace. std::string oldIndexNs = IndexDescriptor::makeIndexNamespace(fromNS, indexName); std::string newIndexNs = IndexDescriptor::makeIndexNamespace(toNS, indexName); Status s = _renameSingleNamespace(txn, oldIndexNs, newIndexNs, false); if (!s.isOK()) return s; } systemIndexRecordStore->deleteRecord(txn, record->id); } return Status::OK(); }
BSONObj* DBController::readBSON(StreamType* stream) { auto_ptr<BSONInputStream> is(new BSONInputStream(stream)); BSONObj* res = is->readBSON(); if (_logger->isDebug()) _logger->debug(3, "DBController read bson from disc: %s", res->toChar()); return res; }
TEST(MatchExpressionParserTest, ParseIntegerElementToNonNegativeLongRejectsNegative) { BSONObj query = BSON("" << -2LL); ASSERT_NOT_OK( MatchExpressionParser::parseIntegerElementToNonNegativeLong(query.firstElement())); }
void operator()( DBClientCursorBatchIterator &i ) { mongolock l( true ); if ( context ) { context->relocked(); } while( i.moreInCurrentBatch() ) { if ( n % 128 == 127 /*yield some*/ ) { time_t now = time(0); if( now - lastLog >= 60 ) { // report progress if( lastLog ) log() << "clone " << to_collection << ' ' << n << endl; lastLog = now; } mayInterrupt( _mayBeInterrupted ); dbtempreleaseif t( _mayYield ); } BSONObj tmp = i.nextSafe(); /* assure object is valid. note this will slow us down a little. */ if ( !tmp.valid() ) { stringstream ss; ss << "Cloner: skipping corrupt object from " << from_collection; BSONElement e = tmp.firstElement(); try { e.validate(); ss << " firstElement: " << e; } catch( ... ) { ss << " firstElement corrupt"; } out() << ss.str() << endl; continue; } ++n; BSONObj js = tmp; if ( isindex ) { assert( strstr(from_collection, "system.indexes") ); js = fixindex(tmp); storedForLater->push_back( js.getOwned() ); continue; } try { theDataFileMgr.insertWithObjMod(to_collection, js); if ( logForRepl ) logOp("i", to_collection, js); getDur().commitIfNeeded(); } catch( UserException& e ) { log() << "warning: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n'; } RARELY if ( time( 0 ) - saveLast > 60 ) { log() << n << " objects cloned so far from collection " << from_collection << endl; saveLast = time( 0 ); } } }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsTooLargeNegativeDouble) { BSONObj query = BSON("" << std::numeric_limits<double>::min()); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsNonIntegralDecimal) { BSONObj query = BSON("" << Decimal128("2.5")); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
bool run( const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool ) { string ns; if ( !FieldParser::extract( cmdObj, nsField, &ns, &errmsg ) ) { return false; } if ( ns.size() == 0 ) { errmsg = "no namespace specified"; return false; } vector<BSONObj> bounds; if ( !FieldParser::extract( cmdObj, boundsField, &bounds, &errmsg ) ) { return false; } if ( bounds.size() == 0 ) { errmsg = "no bounds were specified"; return false; } if ( bounds.size() != 2 ) { errmsg = "only a min and max bound may be specified"; return false; } BSONObj minKey = bounds[0]; BSONObj maxKey = bounds[1]; if ( minKey.isEmpty() ) { errmsg = "no min key specified"; return false; } if ( maxKey.isEmpty() ) { errmsg = "no max key specified"; return false; } // // This might be the first call from mongos, so we may need to pass the config and shard // information to initialize the shardingState. // string config; FieldParser::FieldState extracted = FieldParser::extract( cmdObj, configField, &config, &errmsg ); if ( !extracted ) return false; if ( extracted != FieldParser::FIELD_NONE ) { ShardingState::initialize( config ); } else if ( !shardingState.enabled() ) { errmsg = "sharding state must be enabled or config server specified to merge chunks"; return false; } // ShardName is optional, but might not be set yet string shardName; extracted = FieldParser::extract( cmdObj, shardNameField, &shardName, &errmsg ); if ( !extracted ) return false; if ( extracted != FieldParser::FIELD_NONE ) { shardingState.gotShardName( shardName ); } // // Epoch is optional, and if not set indicates we should use the latest epoch // OID epoch; if ( !FieldParser::extract( cmdObj, epochField, &epoch, &errmsg ) ) { return false; } return mergeChunks( NamespaceString( ns ), minKey, maxKey, epoch, true, &errmsg ); }
/* **************************************************************************** * * mongoUnsubscribeContextAvailability - */ HttpStatusCode mongoUnsubscribeContextAvailability ( UnsubscribeContextAvailabilityRequest* requestP, UnsubscribeContextAvailabilityResponse* responseP, const std::string& tenant ) { bool reqSemTaken; std::string err; reqSemTake(__FUNCTION__, "ngsi9 unsubscribe request", SemWriteOp, &reqSemTaken); LM_T(LmtMongo, ("Unsubscribe Context Availability")); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; /* Look for document */ BSONObj sub; OID id; if (!safeGetSubId(requestP->subscriptionId, &id, &(responseP->statusCode))) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (safeGetSubId fail)", reqSemTaken); if (responseP->statusCode.code == SccContextElementNotFound) { // FIXME: doubt: invalid OID format? Or, subscription not found? std::string details = std::string("invalid OID format: '") + requestP->subscriptionId.get() + "'"; alarmMgr.badInput(clientIp, details); } else // SccReceiverInternalError { LM_E(("Runtime Error (exception getting OID: %s)", responseP->statusCode.details.c_str())); } return SccOk; } if (!collectionFindOne(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << id), &sub, &err)) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } alarmMgr.dbErrorReset(); if (sub.isEmpty()) { responseP->statusCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (no subscriptions)", reqSemTaken); return SccOk; } /* Remove document in MongoDB */ // FIXME: I would prefer to do the find and remove in a single operation. Is the some similar // to findAndModify for this? if (!collectionRemove(getSubscribeContextAvailabilityCollectionName(tenant), BSON("_id" << OID(requestP->subscriptionId.get())), &err)) { reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)", reqSemTaken); responseP->statusCode.fill(SccReceiverInternalError, err); return SccOk; } reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request", reqSemTaken); responseP->statusCode.fill(SccOk); return SccOk; }
UpdateResult update(UpdateRequest& request, UpdateDriver* driver) { const NamespaceString& nsString = request.getNamespaceString(); validateUpdate( nsString.ns().c_str(), request.getUpdates(), request.getQuery() ); NamespaceDetails* nsDetails = nsdetails( nsString.ns() ); NamespaceDetailsTransient* nsDetailsTransient = &NamespaceDetailsTransient::get( nsString.ns().c_str() ); OpDebug& debug = request.getDebug(); // TODO: This seems a bit circuitious. debug.updateobj = request.getUpdates(); driver->refreshIndexKeys( nsDetailsTransient->indexKeys() ); shared_ptr<Cursor> cursor = getOptimizedCursor( nsString.ns(), request.getQuery(), BSONObj(), request.getQueryPlanSelectionPolicy() ); // If the update was marked with '$isolated' (a.k.a '$atomic'), we are not allowed to // yield while evaluating the update loop below. // // TODO: Old code checks this repeatedly within the update loop. Is that necessary? It seems // that once atomic should be always atomic. const bool isolated = cursor->ok() && cursor->matcher() && cursor->matcher()->docMatcher().atomic(); // The 'cursor' the optimizer gave us may contain query plans that generate duplicate // diskloc's. We set up here the mechanims that will prevent us from processing those // twice if we see them. We also set up a 'ClientCursor' so that we can support // yielding. // // TODO: Is it valid to call this on a non-ok cursor? const bool dedupHere = cursor->autoDedup(); // // We'll start assuming we have one or more documents for this update. (Othwerwise, // we'll fallback to upserting.) // // We record that this will not be an upsert, in case a mod doesn't want to be applied // when in strict update mode. driver->setContext( ModifierInterface::ExecInfo::UPDATE_CONTEXT ); // Let's fetch each of them and pipe them through the update expression, making sure to // keep track of the necessary stats. Recall that we'll be pulling documents out of // cursors and some of them do not deduplicate the entries they generate. We have // deduping logic in here, too -- for now. unordered_set<DiskLoc, DiskLoc::Hasher> seenLocs; int numMatched = 0; debug.nscanned = 0; Client& client = cc(); mutablebson::Document doc; // If we are going to be yielding, we will need a ClientCursor scoped to this loop. We // only loop as long as the underlying cursor is OK. for ( auto_ptr<ClientCursor> clientCursor; cursor->ok(); ) { // If we haven't constructed a ClientCursor, and if the client allows us to throw // page faults, and if we are referring to a location that is likely not in // physical memory, then throw a PageFaultException. The entire operation will be // restarted. if ( clientCursor.get() == NULL && client.allowedToThrowPageFaultException() && !cursor->currLoc().isNull() && !cursor->currLoc().rec()->likelyInPhysicalMemory() ) { // We should never throw a PFE if we have already updated items. dassert((numMatched == 0) || (numMatched == debug.nupdateNoops)); throw PageFaultException( cursor->currLoc().rec() ); } if ( !isolated && debug.nscanned != 0 ) { // We are permitted to yield. To do so we need a ClientCursor, so create one // now if we have not yet done so. if ( !clientCursor.get() ) clientCursor.reset( new ClientCursor( QueryOption_NoCursorTimeout, cursor, nsString.ns() ) ); // Ask the client cursor to yield. We get two bits of state back: whether or not // we yielded, and whether or not we correctly recovered from yielding. bool yielded = false; const bool recovered = clientCursor->yieldSometimes( ClientCursor::WillNeed, &yielded ); if ( !recovered ) { // If we failed to recover from the yield, then the ClientCursor is already // gone. Release it so we don't destroy it a second time. clientCursor.release(); break; } if ( !cursor->ok() ) { // If the cursor died while we were yielded, just get out of the update loop. break; } if ( yielded ) { // We yielded and recovered OK, and our cursor is still good. Details about // our namespace may have changed while we were yielded, so we re-acquire // them here. If we can't do so, escape the update loop. Otherwise, refresh // the driver so that it knows about what is currently indexed. nsDetails = nsdetails( nsString.ns() ); if ( !nsDetails ) break; nsDetailsTransient = &NamespaceDetailsTransient::get( nsString.ns().c_str() ); // TODO: This copies the index keys, but it may not need to do so. driver->refreshIndexKeys( nsDetailsTransient->indexKeys() ); } } // Let's fetch the next candidate object for this update. Record* record = cursor->_current(); DiskLoc loc = cursor->currLoc(); const BSONObj oldObj = loc.obj(); // We count how many documents we scanned even though we may skip those that are // deemed duplicated. The final 'numUpdated' and 'nscanned' numbers may differ for // that reason. debug.nscanned++; // Skips this document if it: // a) doesn't match the query portion of the update // b) was deemed duplicate by the underlying cursor machinery // // Now, if we are going to update the document, // c) we don't want to do so while the cursor is at it, as that may invalidate // the cursor. So, we advance to next document, before issuing the update. MatchDetails matchDetails; matchDetails.requestElemMatchKey(); if ( !cursor->currentMatches( &matchDetails ) ) { // a) cursor->advance(); continue; } else if ( cursor->getsetdup( loc ) && dedupHere ) { // b) cursor->advance(); continue; } else if (!driver->isDocReplacement() && request.isMulti()) { // c) cursor->advance(); if ( dedupHere ) { if ( seenLocs.count( loc ) ) { continue; } } // There are certain kind of cursors that hold multiple pointers to data // underneath. $or cursors is one example. In a $or cursor, it may be the case // that when we did the last advance(), we finished consuming documents from // one of $or child and started consuming the next one. In that case, it is // possible that the last document of the previous child is the same as the // first document of the next (see SERVER-5198 and jstests/orp.js). // // So we advance the cursor here until we see a new diskloc. // // Note that we won't be yielding, and we may not do so for a while if we find // a particularly duplicated sequence of loc's. That is highly unlikely, // though. (See SERVER-5725, if curious, but "stage" based $or will make that // ticket moot). while( cursor->ok() && loc == cursor->currLoc() ) { cursor->advance(); } } // For some (unfortunate) historical reasons, not all cursors would be valid after // a write simply because we advanced them to a document not affected by the write. // To protect in those cases, not only we engaged in the advance() logic above, but // we also tell the cursor we're about to write a document that we've just seen. // prepareToTouchEarlierIterate() requires calling later // recoverFromTouchingEarlierIterate(), so we make a note here to do so. bool touchPreviousDoc = request.isMulti() && cursor->ok(); if ( touchPreviousDoc ) { if ( clientCursor.get() ) clientCursor->setDoingDeletes( true ); cursor->prepareToTouchEarlierIterate(); } // Found a matching document numMatched++; // Ask the driver to apply the mods. It may be that the driver can apply those "in // place", that is, some values of the old document just get adjusted without any // change to the binary layout on the bson layer. It may be that a whole new // document is needed to accomodate the new bson layout of the resulting document. doc.reset( oldObj, mutablebson::Document::kInPlaceEnabled ); BSONObj logObj; // If there was a matched field, obtain it. string matchedField; if (matchDetails.hasElemMatchKey()) matchedField = matchDetails.elemMatchKey(); Status status = driver->update( matchedField, &doc, &logObj ); if ( !status.isOK() ) { uasserted( 16837, status.reason() ); } // If the driver applied the mods in place, we can ask the mutable for what // changed. We call those changes "damages". :) We use the damages to inform the // journal what was changed, and then apply them to the original document // ourselves. If, however, the driver applied the mods out of place, we ask it to // generate a new, modified document for us. In that case, the file manager will // take care of the journaling details for us. // // This code flow is admittedly odd. But, right now, journaling is baked in the file // manager. And if we aren't using the file manager, we have to do jounaling // ourselves. bool objectWasChanged = false; BSONObj newObj; const char* source = NULL; mutablebson::DamageVector damages; bool inPlace = doc.getInPlaceUpdates(&damages, &source); if ( inPlace && !damages.empty() && !driver->modsAffectIndices() ) { nsDetails->paddingFits(); // All updates were in place. Apply them via durability and writing pointer. mutablebson::DamageVector::const_iterator where = damages.begin(); const mutablebson::DamageVector::const_iterator end = damages.end(); for( ; where != end; ++where ) { const char* sourcePtr = source + where->sourceOffset; void* targetPtr = getDur().writingPtr( const_cast<char*>(oldObj.objdata()) + where->targetOffset, where->size); std::memcpy(targetPtr, sourcePtr, where->size); } newObj = oldObj; debug.fastmod = true; objectWasChanged = true; } else { // The updates were not in place. Apply them through the file manager. newObj = doc.getObject(); DiskLoc newLoc = theDataFileMgr.updateRecord(nsString.ns().c_str(), nsDetails, nsDetailsTransient, record, loc, newObj.objdata(), newObj.objsize(), debug); // If we've moved this object to a new location, make sure we don't apply // that update again if our traversal picks the objecta again. // // We also take note that the diskloc if the updates are affecting indices. // Chances are that we're traversing one of them and they may be multi key and // therefore duplicate disklocs. if ( newLoc != loc || driver->modsAffectIndices() ) { seenLocs.insert( newLoc ); } objectWasChanged = true; } // Log Obj if ( request.shouldUpdateOpLog() ) { if ( driver->isDocReplacement() || !logObj.isEmpty() ) { BSONObj idQuery = driver->makeOplogEntryQuery(newObj, request.isMulti()); logOp("u", nsString.ns().c_str(), logObj , &idQuery, NULL, request.isFromMigration(), &newObj); } } // If it was noop since the document didn't change, record that. if (!objectWasChanged) debug.nupdateNoops++; if (!request.isMulti()) { break; } // If we used the cursor mechanism that prepares an earlier seen document for a // write we need to tell such mechanisms that the write is over. if ( touchPreviousDoc ) { cursor->recoverFromTouchingEarlierIterate(); } getDur().commitIfNeeded(); } // TODO: Can this be simplified? if ((numMatched > 0) || (numMatched == 0 && !request.isUpsert()) ) { debug.nupdated = numMatched; return UpdateResult( numMatched > 0 /* updated existing object(s) */, !driver->isDocReplacement() /* $mod or obj replacement */, numMatched /* # of docments update, even no-ops */, BSONObj() ); } // // We haven't found any existing document so an insert is done // (upsert is true). // debug.upsert = true; // Since this is an insert (no docs found and upsert:true), we will be logging it // as an insert in the oplog. We don't need the driver's help to build the // oplog record, then. We also set the context of the update driver to the INSERT_CONTEXT. // Some mods may only work in that context (e.g. $setOnInsert). driver->setLogOp( false ); driver->setContext( ModifierInterface::ExecInfo::INSERT_CONTEXT ); BSONObj baseObj; // Reset the document we will be writing to doc.reset( baseObj, mutablebson::Document::kInPlaceDisabled ); if ( request.getQuery().hasElement("_id") ) { uassertStatusOK(doc.root().appendElement(request.getQuery().getField("_id"))); } // If this is a $mod base update, we need to generate a document by examining the // query and the mods. Otherwise, we can use the object replacement sent by the user // update command that was parsed by the driver before. // In the following block we handle the query part, and then do the regular mods after. if ( *request.getUpdates().firstElementFieldName() == '$' ) { uassertStatusOK(UpdateDriver::createFromQuery(request.getQuery(), doc)); debug.fastmodinsert = true; } // Apply the update modifications and then log the update as an insert manually. Status status = driver->update( StringData(), &doc, NULL /* no oplog record */); if ( !status.isOK() ) { uasserted( 16836, status.reason() ); } BSONObj newObj = doc.getObject(); theDataFileMgr.insertWithObjMod( nsString.ns().c_str(), newObj, false, request.isGod() ); if ( request.shouldUpdateOpLog() ) { logOp( "i", nsString.ns().c_str(), newObj, NULL, NULL, request.isFromMigration(), &newObj ); } debug.nupdated = 1; return UpdateResult( false /* updated a non existing document */, !driver->isDocReplacement() /* $mod or obj replacement? */, 1 /* count of updated documents */, newObj /* object that was upserted */ ); }
// static Status ClearFilters::clear(OperationContext* txn, QuerySettings* querySettings, PlanCache* planCache, const std::string& ns, const BSONObj& cmdObj) { invariant(querySettings); // According to the specification, the planCacheClearFilters command runs in two modes: // - clear all hints; or // - clear hints for single query shape when a query shape is described in the // command arguments. if (cmdObj.hasField("query")) { auto statusWithCQ = PlanCacheCommand::canonicalize(txn, ns, cmdObj); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); querySettings->removeAllowedIndices(planCache->computeKey(*cq)); // Remove entry from plan cache planCache->remove(*cq); LOG(0) << "Removed index filter on " << ns << " " << cq->toStringShort(); return Status::OK(); } // If query is not provided, make sure sort and projection are not in arguments. // We do not want to clear the entire cache inadvertently when the user // forgot to provide a value for "query". if (cmdObj.hasField("sort") || cmdObj.hasField("projection")) { return Status(ErrorCodes::BadValue, "sort or projection provided without query"); } // Get entries from query settings. We need to remove corresponding entries from the plan // cache shortly. OwnedPointerVector<AllowedIndexEntry> entries; entries.mutableVector() = querySettings->getAllAllowedIndices(); // OK to proceed with clearing entire cache. querySettings->clearAllowedIndices(); const NamespaceString nss(ns); const ExtensionsCallbackReal extensionsCallback(txn, &nss); // Remove corresponding entries from plan cache. // Admin hints affect the planning process directly. If there were // plans generated as a result of applying index filter, these need to be // invalidated. This allows the planner to re-populate the plan cache with // non-filtered indexed solutions next time the query is run. // Resolve plan cache key from (query, sort, projection) in query settings entry. // Concurrency note: There's no harm in removing plan cache entries one at at time. // Only way that PlanCache::remove() can fail is when the query shape has been removed from // the cache by some other means (re-index, collection info reset, ...). This is OK since // that's the intended effect of calling the remove() function with the key from the hint entry. for (vector<AllowedIndexEntry*>::const_iterator i = entries.begin(); i != entries.end(); ++i) { AllowedIndexEntry* entry = *i; invariant(entry); // Create canonical query. auto qr = stdx::make_unique<QueryRequest>(nss); qr->setFilter(entry->query); qr->setSort(entry->sort); qr->setProj(entry->projection); auto statusWithCQ = CanonicalQuery::canonicalize(txn, std::move(qr), extensionsCallback); invariantOK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); // Remove plan cache entry. planCache->remove(*cq); } LOG(0) << "Removed all index filters for collection: " << ns; return Status::OK(); }
void handle_modify(DBClientBase *client, DBOperation *operation) { // First, we have to format our findandmodify. BSONObjBuilder sets; bool has_sets = false; BSONObjBuilder unsets; bool has_unsets = false; for(auto it = operation->set_fields().begin(); it != operation->set_fields().end(); ++it) { stringstream fieldname; fieldname << "fields." << it->first->get_name(); if(it->second.empty()) { unsets << fieldname.str() << true; has_unsets = true; } else { DatagramPtr dg = Datagram::create(); dg->add_data(it->second); DatagramIterator dgi(dg); sets << fieldname.str() << bamboo2bson(it->first->get_type(), dgi)["_"]; has_sets = true; } } BSONObjBuilder updates_b; if(has_sets) { updates_b << "$set" << sets.obj(); } if(has_unsets) { updates_b << "$unset" << unsets.obj(); } BSONObj updates = updates_b.obj(); // Also format any criteria for the change: BSONObjBuilder query_b; query_b << "_id" << operation->doid(); for(auto it = operation->criteria_fields().begin(); it != operation->criteria_fields().end(); ++it) { stringstream fieldname; fieldname << "fields." << it->first->get_name(); if(it->second.empty()) { query_b << fieldname.str() << BSON("$exists" << false); } else { DatagramPtr dg = Datagram::create(); dg->add_data(it->second); DatagramIterator dgi(dg); query_b << fieldname.str() << bamboo2bson(it->first->get_type(), dgi)["_"]; } } BSONObj query = query_b.obj(); m_log->trace() << "Performing updates to " << operation->doid() << ": " << updates << endl; m_log->trace() << "Query is: " << query << endl; BSONObj result; bool success; try { success = client->runCommand( m_db, BSON("findandmodify" << "astron.objects" << "query" << query << "update" << updates), result); } catch(mongo::DBException &e) { m_log->error() << "Unexpected error while modifying " << operation->doid() << ": " << e.what() << endl; operation->on_failure(); return; } m_log->trace() << "Update result: " << result << endl; BSONObj obj; if(!success || result["value"].isNull()) { // Okay, something didn't work right. If we had criteria, let's // try to fetch the object without the criteria to see if it's a // criteria mismatch or a missing DOID. if(!operation->criteria_fields().empty()) { try { obj = client->findOne(m_obj_collection, BSON("_id" << operation->doid())); } catch(mongo::DBException &e) { m_log->error() << "Unexpected error while modifying " << operation->doid() << ": " << e.what() << endl; operation->on_failure(); return; } if(!obj.isEmpty()) { // There's the problem. Now we can send back a snapshot: DBObjectSnapshot *snap = format_snapshot(operation->doid(), obj); if(snap && operation->verify_class(snap->m_dclass)) { operation->on_criteria_mismatch(snap); return; } else { // Something else weird happened with our snapshot; // either the class wasn't recognized or it was the // wrong class. Either way, an error has been logged, // and we need to fail the operation. operation->on_failure(); return; } } } // Nope, not that. We're missing the DOID. m_log->error() << "Attempted to modify unknown DOID: " << operation->doid() << endl; operation->on_failure(); return; } // If we've gotten to this point: Hooray! The change has gone // through to the database. // Let's, however, double-check our changes. (Specifically, we should // run verify_class so that we know the frontend is happy with what // kind of object we just modified.) obj = result["value"].Obj(); try { string dclass_name = obj["dclass"].String(); const dclass::Class *dclass = g_dcf->get_class_by_name(dclass_name); if(!dclass) { m_log->error() << "Encountered unknown database object: " << dclass_name << "(" << operation->doid() << ")" << endl; } else if(operation->verify_class(dclass)) { // Yep, it all checks out. Complete the operation: operation->on_complete(); return; } } catch(mongo::DBException &e) { } // If we've gotten here, something is seriously wrong. We've just // mucked with an object without knowing the consequences! What have // we done?! We've created an abomination in the database! Kill it! // Kill it with fire! // All we really can do to mitigate this is scream at the user (which // the above verification has already done by now) and revert the // object back to how it was when we found it. // NOTE: This DOES have the potential for data loss, because we're // wiping out any changes that conceivably could have happened // between the findandmodify and now. In dev environments, (which we // are probably in right now, if other components are making // outlandish requests like this) this shouldn't be a huge issue. m_log->trace() << "Reverting changes made to " << operation->doid() << endl; try { client->update( m_obj_collection, BSON("_id" << operation->doid()), obj); } catch(mongo::DBException &e) { // Wow, we REALLY fail at life. m_log->error() << "Could not revert corrupting changes to " << operation->doid() << ": " << e.what() << endl; } operation->on_failure(); }
void operator()( DBClientCursorBatchIterator &i ) { Lock::GlobalWrite lk; if ( context ) { context->relocked(); } while( i.moreInCurrentBatch() ) { if ( n % 128 == 127 /*yield some*/ ) { time_t now = time(0); if( now - lastLog >= 60 ) { // report progress if( lastLog ) log() << "clone " << to_collection << ' ' << n << endl; lastLog = now; } mayInterrupt( _mayBeInterrupted ); dbtempreleaseif t( _mayYield ); } BSONObj tmp = i.nextSafe(); /* assure object is valid. note this will slow us down a little. */ if ( !tmp.valid() ) { stringstream ss; ss << "Cloner: skipping corrupt object from " << from_collection; BSONElement e = tmp.firstElement(); try { e.validate(); ss << " firstElement: " << e; } catch( ... ) { ss << " firstElement corrupt"; } out() << ss.str() << endl; continue; } ++n; BSONObj js = tmp; if ( isindex ) { verify( strstr(from_collection, "system.indexes") ); js = fixindex(tmp); storedForLater->push_back( js.getOwned() ); continue; } try { // add keys for presorting DiskLoc loc = theDataFileMgr.insertWithObjMod(to_collection, js); loc.assertOk(); if (_sortersForIndex != NULL) { // add key to SortersForNS for (SortersForIndex::iterator iSorter = _sortersForIndex->begin(); iSorter != _sortersForIndex->end(); ++iSorter) { iSorter->second.preSortPhase.addKeys(iSorter->second.spec, js, loc, false); } } if ( logForRepl ) logOp("i", to_collection, js); getDur().commitIfNeeded(); } catch( UserException& e ) { error() << "error: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n'; throw; } RARELY if ( time( 0 ) - saveLast > 60 ) { log() << n << " objects cloned so far from collection " << from_collection << endl; saveLast = time( 0 ); } } }
void FTSIndexFormat::getKeys(const FTSSpec& spec, const BSONObj& obj, BSONObjSet* keys) { int extraSize = 0; vector<BSONElement> extrasBefore; vector<BSONElement> extrasAfter; // Compute the non FTS key elements for the prefix. for (unsigned i = 0; i < spec.numExtraBefore(); i++) { auto indexedElement = extractNonFTSKeyElement(obj, spec.extraBefore(i)); extrasBefore.push_back(indexedElement); extraSize += indexedElement.size(); } // Compute the non FTS key elements for the suffix. for (unsigned i = 0; i < spec.numExtraAfter(); i++) { auto indexedElement = extractNonFTSKeyElement(obj, spec.extraAfter(i)); extrasAfter.push_back(indexedElement); extraSize += indexedElement.size(); } TermFrequencyMap term_freqs; spec.scoreDocument(obj, &term_freqs); // create index keys from raw scores // only 1 per string // TODO SERVER-36440: Completely remove this limit in 4.3. if (serverGlobalParams.featureCompatibility.isVersionInitialized() && serverGlobalParams.featureCompatibility.getVersion() == ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo40) { uassert(16732, str::stream() << "too many unique keys for a single document to" << " have a text index, max is " << term_freqs.size() << obj["_id"], term_freqs.size() <= 400000); } long long keyBSONSize = 0; const int MaxKeyBSONSizeMB = 4; for (TermFrequencyMap::const_iterator i = term_freqs.begin(); i != term_freqs.end(); ++i) { const string& term = i->first; double weight = i->second; // guess the total size of the btree entry based on the size of the weight, term tuple int guess = 5 /* bson overhead */ + 10 /* weight */ + 8 /* term overhead */ + /* term size (could be truncated/hashed) */ guessTermSize(term, spec.getTextIndexVersion()) + extraSize; BSONObjBuilder b(guess); // builds a BSON object with guess length. for (unsigned k = 0; k < extrasBefore.size(); k++) { b.appendAs(extrasBefore[k], ""); } _appendIndexKey(b, weight, term, spec.getTextIndexVersion()); for (unsigned k = 0; k < extrasAfter.size(); k++) { b.appendAs(extrasAfter[k], ""); } BSONObj res = b.obj(); verify(guess >= res.objsize()); keys->insert(res); keyBSONSize += res.objsize(); // TODO SERVER-36440: Completely remove this limit in 4.3. if (serverGlobalParams.featureCompatibility.isVersionInitialized() && serverGlobalParams.featureCompatibility.getVersion() == ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo40) { uassert(16733, str::stream() << "trying to index text where term list is too big, max is " << MaxKeyBSONSizeMB << "mb " << obj["_id"], keyBSONSize <= (MaxKeyBSONSizeMB * 1024 * 1024)); } } }
Status ChunkManagerTargeter::targetUpdate(OperationContext* txn, const BatchedUpdateDocument& updateDoc, vector<ShardEndpoint*>* endpoints) const { // // Update targeting may use either the query or the update. This is to support save-style // updates, of the form: // // coll.update({ _id : xxx }, { _id : xxx, shardKey : 1, foo : bar }, { upsert : true }) // // Because drivers do not know the shard key, they can't pull the shard key automatically // into the query doc, and to correctly support upsert we must target a single shard. // // The rule is simple - If the update is replacement style (no '$set'), we target using the // update. If the update is replacement style, we target using the query. // // If we have the exact shard key in either the query or replacement doc, we target using // that extracted key. // BSONObj query = updateDoc.getQuery(); BSONObj updateExpr = updateDoc.getUpdateExpr(); UpdateType updateType = getUpdateExprType(updateDoc.getUpdateExpr()); if (updateType == UpdateType_Unknown) { return Status(ErrorCodes::UnsupportedFormat, stream() << "update document " << updateExpr << " has mixed $operator and non-$operator style fields"); } BSONObj shardKey; if (_manager) { // // Sharded collections have the following futher requirements for targeting: // // Upserts must be targeted exactly by shard key. // Non-multi updates must be targeted exactly by shard key *or* exact _id. // // Get the shard key if (updateType == UpdateType_OpStyle) { // Target using the query StatusWith<BSONObj> status = _manager->getShardKeyPattern().extractShardKeyFromQuery(query); // Bad query if (!status.isOK()) return status.getStatus(); shardKey = status.getValue(); } else { // Target using the replacement document shardKey = _manager->getShardKeyPattern().extractShardKeyFromDoc(updateExpr); } // // Extra sharded update validation // if (updateDoc.getUpsert()) { // Sharded upserts *always* need to be exactly targeted by shard key if (shardKey.isEmpty()) { return Status(ErrorCodes::ShardKeyNotFound, stream() << "upsert " << updateDoc.toBSON() << " does not contain shard key for pattern " << _manager->getShardKeyPattern().toString()); } // Also check shard key size on upsert Status status = ShardKeyPattern::checkShardKeySize(shardKey); if (!status.isOK()) return status; } // Validate that single (non-multi) sharded updates are targeted by shard key or _id if (!updateDoc.getMulti() && shardKey.isEmpty() && !isExactIdQuery(updateDoc.getQuery())) { return Status(ErrorCodes::ShardKeyNotFound, stream() << "update " << updateDoc.toBSON() << " does not contain _id or shard key for pattern " << _manager->getShardKeyPattern().toString()); } } // Target the shard key, query, or replacement doc if (!shardKey.isEmpty()) { // We can't rely on our query targeting to be exact ShardEndpoint* endpoint = NULL; Status result = targetShardKey(txn, shardKey, (query.objsize() + updateExpr.objsize()), &endpoint); endpoints->push_back(endpoint); return result; } else if (updateType == UpdateType_OpStyle) { return targetQuery(query, endpoints); } else { return targetDoc(updateExpr, endpoints); } }
DiskLoc _repairExtent( Database* db , string ns, bool forward , DiskLoc eLoc , Writer& w ) { LogIndentLevel lil; if ( eLoc.getOfs() <= 0 ) { error() << "invalid extent ofs: " << eLoc.getOfs() << endl; return DiskLoc(); } MongoDataFile * mdf = db->getFile( eLoc.a() ); Extent * e = mdf->debug_getExtent( eLoc ); if ( ! e->isOk() ) { warning() << "Extent not ok magic: " << e->magic << " going to try to continue" << endl; } log() << "length:" << e->length << endl; LogIndentLevel lil2; set<DiskLoc> seen; DiskLoc loc = forward ? e->firstRecord : e->lastRecord; while ( ! loc.isNull() ) { if ( ! seen.insert( loc ).second ) { error() << "infinite loop in extend, seen: " << loc << " before" << endl; break; } if ( loc.getOfs() <= 0 ) { error() << "offset is 0 for record which should be impossible" << endl; break; } log(1) << loc << endl; Record* rec = loc.rec(); BSONObj obj; try { obj = loc.obj(); assert( obj.valid() ); LOG(1) << obj << endl; w( obj ); } catch ( std::exception& e ) { log() << "found invalid document @ " << loc << " " << e.what() << endl; if ( ! obj.isEmpty() ) { try { BSONElement e = obj.firstElement(); stringstream ss; ss << "first element: " << e; log() << ss.str(); } catch ( std::exception& ) { } } } loc = forward ? rec->getNext( loc ) : rec->getPrev( loc ); } return forward ? e->xnext : e->xprev; }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsTooLargeDouble) { BSONObj query = BSON("" << MatchExpressionParser::kLongLongMaxPlusOneAsDouble); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
int run() { if ( hasParam( "repair" ) ) { warning() << "repair is a work in progress" << endl; return repair(); } { string q = getParam("query"); if ( q.size() ) _query = fromjson( q ); } string opLogName = ""; unsigned long long opLogStart = 0; if (hasParam("oplog")) { if (hasParam("query") || hasParam("db") || hasParam("collection")) { cout << "oplog mode is only supported on full dumps" << endl; return -1; } BSONObj isMaster; conn("true").simpleCommand("admin", &isMaster, "isMaster"); if (isMaster.hasField("hosts")) { // if connected to replica set member opLogName = "local.oplog.rs"; } else { opLogName = "local.oplog.$main"; if ( ! isMaster["ismaster"].trueValue() ) { cout << "oplog mode is only supported on master or replica set member" << endl; return -1; } } auth("local"); BSONObj op = conn(true).findOne(opLogName, Query().sort("$natural", -1), 0, QueryOption_SlaveOk); if (op.isEmpty()) { cout << "No operations in oplog. Please ensure you are connecting to a master." << endl; return -1; } assert(op["ts"].type() == Timestamp); opLogStart = op["ts"]._numberLong(); } // check if we're outputting to stdout string out = getParam("out"); if ( out == "-" ) { if ( _db != "" && _coll != "" ) { writeCollectionStdout( _db+"."+_coll ); return 0; } else { cout << "You must specify database and collection to print to stdout" << endl; return -1; } } _usingMongos = isMongos(); path root( out ); string db = _db; if ( db == "" ) { cout << "all dbs" << endl; auth( "admin" ); BSONObj res = conn( true ).findOne( "admin.$cmd" , BSON( "listDatabases" << 1 ) ); if ( ! res["databases"].isABSONObj() ) { error() << "output of listDatabases isn't what we expected, no 'databases' field:\n" << res << endl; return -2; } BSONObj dbs = res["databases"].embeddedObjectUserCheck(); set<string> keys; dbs.getFieldNames( keys ); for ( set<string>::iterator i = keys.begin() ; i != keys.end() ; i++ ) { string key = *i; if ( ! dbs[key].isABSONObj() ) { error() << "database field not an object key: " << key << " value: " << dbs[key] << endl; return -3; } BSONObj dbobj = dbs[key].embeddedObjectUserCheck(); const char * dbName = dbobj.getField( "name" ).valuestr(); if ( (string)dbName == "local" ) continue; go ( dbName , root / dbName ); } } else { auth( db ); go( db , root / db ); } if (!opLogName.empty()) { BSONObjBuilder b; b.appendTimestamp("$gt", opLogStart); _query = BSON("ts" << b.obj()); writeCollectionFile( opLogName , root / "oplog.bson" ); } return 0; }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsString) { BSONObj query = BSON("" << "1"); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { if ( cmdObj.firstElement().type() != Array ) { errmsg = "ops has to be an array"; return false; } BSONObj ops = cmdObj.firstElement().Obj(); { // check input BSONObjIterator i( ops ); while ( i.more() ) { BSONElement e = i.next(); if (!_checkOperation(e, errmsg)) { return false; } } } // SERVER-4328 todo : is global ok or does this take a long time? i believe multiple // ns used so locking individually requires more analysis ScopedTransaction scopedXact(txn, MODE_X); Lock::GlobalWrite globalWriteLock(txn->lockState()); if (!fromRepl && !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbname)) { return appendCommandStatus(result, Status(ErrorCodes::NotMaster, str::stream() << "Not primary while applying ops to database " << dbname)); } // Preconditions check reads the database state, so needs to be done locked if ( cmdObj["preCondition"].type() == Array ) { BSONObjIterator i( cmdObj["preCondition"].Obj() ); while ( i.more() ) { BSONObj f = i.next().Obj(); DBDirectClient db( txn ); BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() ); // Apply-ops would never have a $where matcher, so use the default callback, // which will throw an error if $where is found. Matcher m(f["res"].Obj()); if ( ! m.matches( realres ) ) { result.append( "got" , realres ); result.append( "whatFailed" , f ); errmsg = "pre-condition failed"; return false; } } } // apply int num = 0; int errors = 0; BSONObjIterator i( ops ); BSONArrayBuilder ab; const bool alwaysUpsert = cmdObj.hasField("alwaysUpsert") ? cmdObj["alwaysUpsert"].trueValue() : true; while ( i.more() ) { BSONElement e = i.next(); const BSONObj& temp = e.Obj(); // Ignore 'n' operations. const char *opType = temp["op"].valuestrsafe(); if (*opType == 'n') continue; const string ns = temp["ns"].String(); // Run operations under a nested lock as a hack to prevent yielding. // // The list of operations is supposed to be applied atomically; yielding // would break atomicity by allowing an interruption or a shutdown to occur // after only some operations are applied. We are already locked globally // at this point, so taking a DBLock on the namespace creates a nested lock, // and yields are disallowed for operations that hold a nested lock. // // We do not have a wrapping WriteUnitOfWork so it is possible for a journal // commit to happen with a subset of ops applied. // TODO figure out what to do about this. Lock::GlobalWrite globalWriteLockDisallowTempRelease(txn->lockState()); // Ensures that yielding will not happen (see the comment above). DEV { Locker::LockSnapshot lockSnapshot; invariant(!txn->lockState()->saveLockStateAndUnlock(&lockSnapshot)); }; OldClientContext ctx(txn, ns); Status status(ErrorCodes::InternalError, ""); while (true) { try { // We assume that in the WriteConflict retry case, either the op rolls back // any changes it makes or is otherwise safe to rerun. status = repl::applyOperation_inlock(txn, ctx.db(), temp, false, alwaysUpsert); break; } catch (const WriteConflictException& wce) { LOG(2) << "WriteConflictException in applyOps command, retrying."; txn->recoveryUnit()->commitAndRestart(); continue; } } ab.append(status.isOK()); if (!status.isOK()) { errors++; } num++; WriteUnitOfWork wuow(txn); logOpForDbHash(txn, ns.c_str()); wuow.commit(); } result.append( "applied" , num ); result.append( "results" , ab.arr() ); if ( ! fromRepl ) { // We want this applied atomically on slaves // so we re-wrap without the pre-condition for speed string tempNS = str::stream() << dbname << ".$cmd"; // TODO: possibly use mutable BSON to remove preCondition field // once it is available BSONObjIterator iter(cmdObj); BSONObjBuilder cmdBuilder; while (iter.more()) { BSONElement elem(iter.next()); if (strcmp(elem.fieldName(), "preCondition") != 0) { cmdBuilder.append(elem); } } const BSONObj cmdRewritten = cmdBuilder.done(); // We currently always logOp the command regardless of whether the individial ops // succeeded and rely on any failures to also happen on secondaries. This isn't // perfect, but it's what the command has always done and is part of its "correct" // behavior. while (true) { try { WriteUnitOfWork wunit(txn); getGlobalEnvironment()->getOpObserver()->onApplyOps(txn, tempNS, cmdRewritten); wunit.commit(); break; } catch (const WriteConflictException& wce) { LOG(2) << "WriteConflictException while logging applyOps command, retrying."; txn->recoveryUnit()->commitAndRestart(); continue; } } } if (errors != 0) { return false; } return true; }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsLargestDecimal) { BSONObj query = BSON("" << Decimal128(Decimal128::kLargestPositive)); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
long long DeleteExecutor::execute(OperationContext* txn, Database* db) { uassertStatusOK(prepare()); uassert(17417, mongoutils::str::stream() << "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(), _isQueryParsed); const bool logop = _request->shouldCallLogOp(); const NamespaceString& ns(_request->getNamespaceString()); if (!_request->isGod()) { if (ns.isSystem()) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns.ns(), true)); } if (ns.ns().find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted( 10100, "cannot delete from collection with reserved $ in name" ); } } Collection* collection = db->getCollection(ns.ns()); if (NULL == collection) { return 0; } uassert(10101, str::stream() << "cannot remove from a capped collection: " << ns.ns(), !collection->isCapped()); uassert(ErrorCodes::NotMaster, str::stream() << "Not primary while removing from " << ns.ns(), !logop || replset::isMasterNs(ns.ns().c_str())); long long nDeleted = 0; Runner* rawRunner; if (_canonicalQuery.get()) { uassertStatusOK(getRunner(collection, _canonicalQuery.release(), &rawRunner)); } else { CanonicalQuery* ignored; uassertStatusOK(getRunner(collection, ns.ns(), _request->getQuery(), &rawRunner, &ignored)); } auto_ptr<Runner> runner(rawRunner); ScopedRunnerRegistration safety(runner.get()); DiskLoc rloc; Runner::RunnerState state; CurOp* curOp = cc().curop(); int oldYieldCount = curOp->numYields(); while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &rloc))) { if (oldYieldCount != curOp->numYields()) { uassert(ErrorCodes::NotMaster, str::stream() << "No longer primary while removing from " << ns.ns(), !logop || replset::isMasterNs(ns.ns().c_str())); oldYieldCount = curOp->numYields(); } BSONObj toDelete; // TODO: do we want to buffer docs and delete them in a group rather than // saving/restoring state repeatedly? runner->saveState(); collection->deleteDocument(txn, rloc, false, false, logop ? &toDelete : NULL ); runner->restoreState(txn); nDeleted++; if (logop) { if ( toDelete.isEmpty() ) { problem() << "Deleted object without id in collection " << collection->ns() << ", not logging."; } else { bool replJustOne = true; replset::logOp(txn, "d", ns.ns().c_str(), toDelete, 0, &replJustOne); } } if (!_request->isMulti()) { break; } if (!_request->isGod()) { txn->recoveryUnit()->commitIfNeeded(); } if (debug && _request->isGod() && nDeleted == 100) { log() << "warning high number of deletes with god=true " << " which could use significant memory b/c we don't commit journal"; } } return nDeleted; }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongAcceptsNegative) { BSONObj query = BSON("" << -2LL); auto result = MatchExpressionParser::parseIntegerElementToLong(query.firstElement()); ASSERT_OK(result.getStatus()); ASSERT_EQ(-2LL, result.getValue()); }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { ScopedTransaction transaction(txn, MODE_X); Lock::GlobalWrite globalWriteLock(txn->lockState()); string source = cmdObj.getStringField( name.c_str() ); string target = cmdObj.getStringField( "to" ); if (!fromRepl && !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbname)) { return appendCommandStatus(result, Status(ErrorCodes::NotMaster, str::stream() << "Not primary while renaming collection " << source << " to " << target)); } // We stay in source context the whole time. This is mostly to set the CurOp namespace. Client::Context ctx(txn, source); if ( !NamespaceString::validCollectionComponent(target.c_str()) ) { errmsg = "invalid collection name: " + target; return false; } if ( source.empty() || target.empty() ) { errmsg = "invalid command syntax"; return false; } if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != repl::ReplicationCoordinator::modeNone)) { if (NamespaceString(source).isOplog()) { errmsg = "can't rename live oplog while replicating"; return false; } if (NamespaceString(target).isOplog()) { errmsg = "can't rename to live oplog while replicating"; return false; } } if (NamespaceString::oplog(source) != NamespaceString::oplog(target)) { errmsg = "If either the source or target of a rename is an oplog name, both must be"; return false; } if (!fromRepl) { // If it got through on the master, need to allow it here too Status sourceStatus = userAllowedWriteNS(source); if (!sourceStatus.isOK()) { errmsg = "error with source namespace: " + sourceStatus.reason(); return false; } Status targetStatus = userAllowedWriteNS(target); if (!targetStatus.isOK()) { errmsg = "error with target namespace: " + targetStatus.reason(); return false; } } if (NamespaceString(source).coll() == "system.indexes" || NamespaceString(target).coll() == "system.indexes") { errmsg = "renaming system.indexes is not allowed"; return false; } Database* const sourceDB = dbHolder().get(txn, nsToDatabase(source)); Collection* const sourceColl = sourceDB ? sourceDB->getCollection(source) : NULL; if (!sourceColl) { errmsg = "source namespace does not exist"; return false; } { // Ensure that collection name does not exceed maximum length. // Ensure that index names do not push the length over the max. // Iterator includes unfinished indexes. IndexCatalog::IndexIterator sourceIndIt = sourceColl->getIndexCatalog()->getIndexIterator( txn, true ); int longestIndexNameLength = 0; while ( sourceIndIt.more() ) { int thisLength = sourceIndIt.next()->indexName().length(); if ( thisLength > longestIndexNameLength ) longestIndexNameLength = thisLength; } unsigned int longestAllowed = min(int(NamespaceString::MaxNsCollectionLen), int(NamespaceString::MaxNsLen) - 2/*strlen(".$")*/ - longestIndexNameLength); if (target.size() > longestAllowed) { StringBuilder sb; sb << "collection name length of " << target.size() << " exceeds maximum length of " << longestAllowed << ", allowing for index names"; errmsg = sb.str(); return false; } } const std::vector<BSONObj> indexesInProg = stopIndexBuilds(txn, sourceDB, cmdObj); // Dismissed on success ScopeGuard indexBuildRestorer = MakeGuard(IndexBuilder::restoreIndexes, txn, indexesInProg); Database* const targetDB = dbHolder().openDb(txn, nsToDatabase(target)); { WriteUnitOfWork wunit(txn); // Check if the target namespace exists and if dropTarget is true. // If target exists and dropTarget is not true, return false. if (targetDB->getCollection(target)) { if (!cmdObj["dropTarget"].trueValue()) { errmsg = "target namespace exists"; return false; } Status s = targetDB->dropCollection(txn, target); if ( !s.isOK() ) { errmsg = s.toString(); return false; } } // If we are renaming in the same database, just // rename the namespace and we're done. if (sourceDB == targetDB) { Status s = targetDB->renameCollection(txn, source, target, cmdObj["stayTemp"].trueValue() ); if (!s.isOK()) { return appendCommandStatus(result, s); } if (!fromRepl) { repl::logOp(txn, "c", (dbname + ".$cmd").c_str(), cmdObj); } wunit.commit(); indexBuildRestorer.Dismiss(); return true; } wunit.commit(); } // If we get here, we are renaming across databases, so we must copy all the data and // indexes, then remove the source collection. // Create the target collection. It will be removed if we fail to copy the collection. // TODO use a temp collection and unset the temp flag on success. Collection* targetColl = NULL; { CollectionOptions options; options.setNoIdIndex(); if (sourceColl->isCapped()) { const CollectionOptions sourceOpts = sourceColl->getCatalogEntry()->getCollectionOptions(txn); options.capped = true; options.cappedSize = sourceOpts.cappedSize; options.cappedMaxDocs = sourceOpts.cappedMaxDocs; } WriteUnitOfWork wunit(txn); // No logOp necessary because the entire renameCollection command is one logOp. targetColl = targetDB->createCollection(txn, target, options); if (!targetColl) { errmsg = "Failed to create target collection."; return false; } wunit.commit(); } // Dismissed on success ScopeGuard targetCollectionDropper = MakeGuard(dropCollection, txn, targetDB, target); MultiIndexBlock indexer(txn, targetColl); indexer.allowInterruption(); // Copy the index descriptions from the source collection, adjusting the ns field. { std::vector<BSONObj> indexesToCopy; IndexCatalog::IndexIterator sourceIndIt = sourceColl->getIndexCatalog()->getIndexIterator( txn, true ); while (sourceIndIt.more()) { const BSONObj currIndex = sourceIndIt.next()->infoObj(); // Process the source index. BSONObjBuilder newIndex; newIndex.append("ns", target); newIndex.appendElementsUnique(currIndex); indexesToCopy.push_back(newIndex.obj()); } indexer.init(indexesToCopy); } { // Copy over all the data from source collection to target collection. boost::scoped_ptr<RecordIterator> sourceIt(sourceColl->getIterator(txn)); while (!sourceIt->isEOF()) { txn->checkForInterrupt(); const Snapshotted<BSONObj> obj = sourceColl->docFor(txn, sourceIt->getNext()); WriteUnitOfWork wunit(txn); // No logOp necessary because the entire renameCollection command is one logOp. Status status = targetColl->insertDocument(txn, obj.value(), &indexer, true).getStatus(); if (!status.isOK()) return appendCommandStatus(result, status); wunit.commit(); } } Status status = indexer.doneInserting(); if (!status.isOK()) return appendCommandStatus(result, status); { // Getting here means we successfully built the target copy. We now remove the // source collection and finalize the rename. WriteUnitOfWork wunit(txn); Status status = sourceDB->dropCollection(txn, source); if (!status.isOK()) return appendCommandStatus(result, status); indexer.commit(); if (!fromRepl) { repl::logOp(txn, "c", (dbname + ".$cmd").c_str(), cmdObj); } wunit.commit(); } indexBuildRestorer.Dismiss(); targetCollectionDropper.Dismiss(); return true; }
std::string runQuery(OperationContext* txn, QueryMessage& q, const NamespaceString& nss, Message& result) { CurOp& curop = *CurOp::get(txn); // Validate the namespace. uassert(16256, str::stream() << "Invalid ns [" << nss.ns() << "]", nss.isValid()); invariant(!nss.isCommand()); // Set curop information. beginQueryOp(txn, nss, q.query, q.ntoreturn, q.ntoskip); // Parse the qm into a CanonicalQuery. auto statusWithCQ = CanonicalQuery::canonicalize(q, WhereCallbackReal(txn, nss.db())); if (!statusWithCQ.isOK()) { uasserted( 17287, str::stream() << "Can't canonicalize query: " << statusWithCQ.getStatus().toString()); } unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); invariant(cq.get()); LOG(5) << "Running query:\n" << cq->toString(); LOG(2) << "Running query: " << cq->toStringShort(); // Parse, canonicalize, plan, transcribe, and get a plan executor. AutoGetCollectionForRead ctx(txn, nss); Collection* collection = ctx.getCollection(); const int dbProfilingLevel = ctx.getDb() ? ctx.getDb()->getProfilingLevel() : serverGlobalParams.defaultProfile; // We have a parsed query. Time to get the execution plan for it. std::unique_ptr<PlanExecutor> exec = uassertStatusOK( getExecutorFind(txn, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO)); const LiteParsedQuery& pq = exec->getCanonicalQuery()->getParsed(); // If it's actually an explain, do the explain and return rather than falling through // to the normal query execution loop. if (pq.isExplain()) { BufBuilder bb; bb.skip(sizeof(QueryResult::Value)); BSONObjBuilder explainBob; Explain::explainStages(exec.get(), ExplainCommon::EXEC_ALL_PLANS, &explainBob); // Add the resulting object to the return buffer. BSONObj explainObj = explainBob.obj(); bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); // TODO: Does this get overwritten/do we really need to set this twice? curop.debug().query = q.query; // Set query result fields. QueryResult::View qr = bb.buf(); bb.decouple(); qr.setResultFlagsToOk(); qr.msgdata().setLen(bb.len()); curop.debug().responseLength = bb.len(); qr.msgdata().setOperation(opReply); qr.setCursorId(0); qr.setStartingFrom(0); qr.setNReturned(1); result.setData(qr.view2ptr(), true); return ""; } ShardingState* const shardingState = ShardingState::get(txn); // We freak out later if this changes before we're done with the query. const ChunkVersion shardingVersionAtStart = shardingState->getVersion(nss.ns()); // Handle query option $maxTimeMS (not used with commands). curop.setMaxTimeMicros(static_cast<unsigned long long>(pq.getMaxTimeMS()) * 1000); txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. // uassert if we are not on a primary, and not a secondary with SlaveOk query parameter set. bool slaveOK = pq.isSlaveOk() || pq.hasReadPref(); Status serveReadsStatus = repl::getGlobalReplicationCoordinator()->checkCanServeReadsFor(txn, nss, slaveOK); uassertStatusOK(serveReadsStatus); // Run the query. // bb is used to hold query results // this buffer should contain either requested documents per query or // explain information, but not both BufBuilder bb(32768); bb.skip(sizeof(QueryResult::Value)); // How many results have we obtained from the executor? int numResults = 0; // If we're replaying the oplog, we save the last time that we read. Timestamp slaveReadTill; BSONObj obj; PlanExecutor::ExecState state; // uint64_t numMisplacedDocs = 0; // Get summary info about which plan the executor is using. curop.debug().planSummary = Explain::getPlanSummary(exec.get()); while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) { // Add result to output buffer. bb.appendBuf((void*)obj.objdata(), obj.objsize()); // Count the result. ++numResults; // Possibly note slave's position in the oplog. if (pq.isOplogReplay()) { BSONElement e = obj["ts"]; if (Date == e.type() || bsonTimestamp == e.type()) { slaveReadTill = e.timestamp(); } } if (FindCommon::enoughForFirstBatch(pq, numResults, bb.len())) { LOG(5) << "Enough for first batch, wantMore=" << pq.wantMore() << " ntoreturn=" << pq.getNToReturn().value_or(0) << " numResults=" << numResults << endl; break; } } // If we cache the executor later, we want to deregister it as it receives notifications // anyway by virtue of being cached. // // If we don't cache the executor later, we are deleting it, so it must be deregistered. // // So, no matter what, deregister the executor. exec->deregisterExec(); // Caller expects exceptions thrown in certain cases. if (PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state) { const unique_ptr<PlanStageStats> stats(exec->getStats()); error() << "Plan executor error during find: " << PlanExecutor::statestr(state) << ", stats: " << Explain::statsToBSON(*stats); uasserted(17144, "Executor error: " + WorkingSetCommon::toStatusString(obj)); } // TODO: Currently, chunk ranges are kept around until all ClientCursors created while the // chunk belonged on this node are gone. Separating chunk lifetime management from // ClientCursor should allow this check to go away. if (!shardingState->getVersion(nss.ns()).isWriteCompatibleWith(shardingVersionAtStart)) { // if the version changed during the query we might be missing some data and its safe to // send this as mongols can resend at this point throw SendStaleConfigException(nss.ns(), "version changed during initial query", shardingVersionAtStart, shardingState->getVersion(nss.ns())); } // Fill out curop based on query results. If we have a cursorid, we will fill out curop with // this cursorid later. long long ccId = 0; if (shouldSaveCursor(txn, collection, state, exec.get())) { // We won't use the executor until it's getMore'd. exec->saveState(); exec->detachFromOperationContext(); // Allocate a new ClientCursor. We don't have to worry about leaking it as it's // inserted into a global map by its ctor. ClientCursor* cc = new ClientCursor(collection->getCursorManager(), exec.release(), nss.ns(), txn->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(), pq.getOptions(), pq.getFilter()); ccId = cc->cursorid(); LOG(5) << "caching executor with cursorid " << ccId << " after returning " << numResults << " results" << endl; // TODO document if (pq.isOplogReplay() && !slaveReadTill.isNull()) { cc->slaveReadTill(slaveReadTill); } // TODO document if (pq.isExhaust()) { curop.debug().exhaust = true; } cc->setPos(numResults); // If the query had a time limit, remaining time is "rolled over" to the cursor (for // use by future getmore ops). cc->setLeftoverMaxTimeMicros(curop.getRemainingMaxTimeMicros()); endQueryOp(txn, collection, *cc->getExecutor(), dbProfilingLevel, numResults, ccId); } else { LOG(5) << "Not caching executor but returning " << numResults << " results.\n"; endQueryOp(txn, collection, *exec, dbProfilingLevel, numResults, ccId); } // Add the results from the query into the output buffer. result.appendData(bb.buf(), bb.len()); bb.decouple(); // Fill out the output buffer's header. QueryResult::View qr = result.header().view2ptr(); qr.setCursorId(ccId); qr.setResultFlagsToOk(); qr.msgdata().setOperation(opReply); qr.setStartingFrom(0); qr.setNReturned(numResults); // curop.debug().exhaust is set above. return curop.debug().exhaust ? nss.ns() : ""; }
/** * The core config write functionality. * * Config writes run in two passes - the first is a quick check to ensure the config servers * are all reachable, the second runs the actual write. * * TODO: Upgrade and move this logic to the config servers, a state machine implementation * is probably the next step. */ void ConfigCoordinator::executeBatch(const BatchedCommandRequest& clientRequest, BatchedCommandResponse* clientResponse) { const NamespaceString nss(clientRequest.getNS()); // Should never use it for anything other than DBs residing on the config server dassert(nss.db() == "config" || nss.db() == "admin"); dassert(clientRequest.sizeWriteOps() == 1u); // This is an opportunistic check that all config servers look healthy by calling // getLastError on each one of them. If there was some form of write/journaling error, get // last error would fail. { for (vector<ConnectionString>::iterator it = _configHosts.begin(); it != _configHosts.end(); ++it) { _dispatcher->addCommand(*it, "admin", RawBSONSerializable(BSON("getLastError" << true << "fsync" << true))); } _dispatcher->sendAll(); bool error = false; while (_dispatcher->numPending()) { ConnectionString host; RawBSONSerializable response; Status status = _dispatcher->recvAny(&host, &response); if (status.isOK()) { BSONObj obj = response.toBSON(); LOG(3) << "Response " << obj.toString(); // If the ok field is anything other than 1, count it as error if (!obj["ok"].trueValue()) { error = true; log() << "Config server check for host " << host << " returned error: " << response; } } else { error = true; log() << "Config server check for host " << host << " failed with status: " << status; } } // All responses should have been gathered by this point if (error) { clientResponse->setOk(false); clientResponse->setErrCode(ErrorCodes::RemoteValidationError); clientResponse->setErrMessage("Could not verify that config servers were active" " and reachable before write"); return; } } if (!_checkConfigString(clientResponse)) { return; } // // Do the actual writes // BatchedCommandRequest configRequest( clientRequest.getBatchType() ); clientRequest.cloneTo( &configRequest ); configRequest.setNS( nss.coll() ); OwnedPointerVector<ConfigResponse> responsesOwned; vector<ConfigResponse*>& responses = responsesOwned.mutableVector(); // // Send the actual config writes // // Get as many batches as we can at once for (vector<ConnectionString>::const_iterator it = _configHosts.begin(); it != _configHosts.end(); ++it) { const ConnectionString& configHost = *it; _dispatcher->addCommand(configHost, nss.db(), configRequest); } // Send them all out _dispatcher->sendAll(); // // Recv side // while (_dispatcher->numPending() > 0) { // Get the response responses.push_back(new ConfigResponse()); ConfigResponse& configResponse = *responses.back(); Status dispatchStatus = _dispatcher->recvAny(&configResponse.configHost, &configResponse.response); if (!dispatchStatus.isOK()) { buildErrorFrom(dispatchStatus, &configResponse.response); } } combineResponses(responses, clientResponse); }
Status MMAPV1DatabaseCatalogEntry::_renameSingleNamespace(OperationContext* txn, StringData fromNS, StringData toNS, bool stayTemp) { // some sanity checking NamespaceDetails* fromDetails = _namespaceIndex.details(fromNS); if (!fromDetails) return Status(ErrorCodes::BadValue, "from namespace doesn't exist"); if (_namespaceIndex.details(toNS)) return Status(ErrorCodes::BadValue, "to namespace already exists"); // at this point, we haven't done anything destructive yet // ---- // actually start moving // ---- // this could throw, but if it does we're ok _namespaceIndex.add_ns(txn, toNS, fromDetails); NamespaceDetails* toDetails = _namespaceIndex.details(toNS); try { toDetails->copyingFrom(txn, toNS, _namespaceIndex, fromDetails); // fixes extraOffset } catch (DBException&) { // could end up here if .ns is full - if so try to clean up / roll back a little _namespaceIndex.kill_ns(txn, toNS); throw; } // at this point, code .ns stuff moved _namespaceIndex.kill_ns(txn, fromNS); fromDetails = NULL; // fix system.namespaces BSONObj newSpec; RecordId oldSpecLocation = getCollectionCatalogEntry(fromNS)->getNamespacesRecordId(); invariant(!oldSpecLocation.isNull()); { BSONObj oldSpec = _getNamespaceRecordStore()->dataFor(txn, oldSpecLocation).releaseToBson(); invariant(!oldSpec.isEmpty()); BSONObjBuilder b; BSONObjIterator i(oldSpec.getObjectField("options")); while (i.more()) { BSONElement e = i.next(); if (strcmp(e.fieldName(), "create") != 0) { if (stayTemp || (strcmp(e.fieldName(), "temp") != 0)) b.append(e); } else { b << "create" << toNS; } } newSpec = b.obj(); } RecordId rid = _addNamespaceToNamespaceCollection(txn, toNS, newSpec.isEmpty() ? 0 : &newSpec); _getNamespaceRecordStore()->deleteRecord(txn, oldSpecLocation); Entry*& entry = _collections[toNS.toString()]; invariant(entry == NULL); txn->recoveryUnit()->registerChange(new EntryInsertion(toNS, this)); entry = new Entry(); _removeFromCache(txn->recoveryUnit(), fromNS); _insertInCache(txn, toNS, rid, entry); return Status::OK(); }
bool ClientInfo::getLastError( const BSONObj& options , BSONObjBuilder& result , bool fromWriteBackListener ) { set<string> * shards = getPrev(); if ( shards->size() == 0 ) { result.appendNull( "err" ); return true; } vector<WBInfo> writebacks; // handle single server if ( shards->size() == 1 ) { string theShard = *(shards->begin() ); ShardConnection conn( theShard , "" ); BSONObj res; bool ok = false; try { ok = conn->runCommand( "admin" , options , res ); } catch( std::exception &e ) { warning() << "could not get last error from shard " << theShard << causedBy( e ) << endl; // Catch everything that happens here, since we need to ensure we return our connection when we're // finished. conn.done(); return false; } res = res.getOwned(); conn.done(); _addWriteBack( writebacks , res ); // hit other machines just to block for ( set<string>::const_iterator i=sinceLastGetError().begin(); i!=sinceLastGetError().end(); ++i ) { string temp = *i; if ( temp == theShard ) continue; ShardConnection conn( temp , "" ); try { _addWriteBack( writebacks , conn->getLastErrorDetailed() ); } catch( std::exception &e ) { warning() << "could not clear last error from shard " << temp << causedBy( e ) << endl; } conn.done(); } clearSinceLastGetError(); if ( writebacks.size() ) { vector<BSONObj> v = _handleWriteBacks( writebacks , fromWriteBackListener ); if ( v.size() == 0 && fromWriteBackListener ) { // ok } else { // this will usually be 1 // it can be greater than 1 if a write to a different shard // than the last write op had a writeback // all we're going to report is the first // since that's the current write // but we block for all assert( v.size() >= 1 ); result.appendElements( v[0] ); result.appendElementsUnique( res ); result.append( "writebackGLE" , v[0] ); result.append( "initialGLEHost" , theShard ); } } else { result.append( "singleShard" , theShard ); result.appendElements( res ); } return ok; } BSONArrayBuilder bbb( result.subarrayStart( "shards" ) ); BSONObjBuilder shardRawGLE; long long n = 0; int updatedExistingStat = 0; // 0 is none, -1 has but false, 1 has true // hit each shard vector<string> errors; vector<BSONObj> errorObjects; for ( set<string>::iterator i = shards->begin(); i != shards->end(); i++ ) { string theShard = *i; bbb.append( theShard ); ShardConnection conn( theShard , "" ); BSONObj res; bool ok = false; try { ok = conn->runCommand( "admin" , options , res ); shardRawGLE.append( theShard , res ); } catch( std::exception &e ) { // Safe to return here, since we haven't started any extra processing yet, just collecting // responses. warning() << "could not get last error from a shard " << theShard << causedBy( e ) << endl; conn.done(); return false; } _addWriteBack( writebacks, res ); string temp = DBClientWithCommands::getLastErrorString( res ); if ( conn->type() != ConnectionString::SYNC && ( ok == false || temp.size() ) ) { errors.push_back( temp ); errorObjects.push_back( res ); } n += res["n"].numberLong(); if ( res["updatedExisting"].type() ) { if ( res["updatedExisting"].trueValue() ) updatedExistingStat = 1; else if ( updatedExistingStat == 0 ) updatedExistingStat = -1; } conn.done(); } bbb.done(); result.append( "shardRawGLE" , shardRawGLE.obj() ); result.appendNumber( "n" , n ); if ( updatedExistingStat ) result.appendBool( "updatedExisting" , updatedExistingStat > 0 ); // hit other machines just to block for ( set<string>::const_iterator i=sinceLastGetError().begin(); i!=sinceLastGetError().end(); ++i ) { string temp = *i; if ( shards->count( temp ) ) continue; ShardConnection conn( temp , "" ); try { _addWriteBack( writebacks, conn->getLastErrorDetailed() ); } catch( std::exception &e ) { warning() << "could not clear last error from a shard " << temp << causedBy( e ) << endl; } conn.done(); } clearSinceLastGetError(); if ( errors.size() == 0 ) { result.appendNull( "err" ); _handleWriteBacks( writebacks , fromWriteBackListener ); return true; } result.append( "err" , errors[0].c_str() ); { // errs BSONArrayBuilder all( result.subarrayStart( "errs" ) ); for ( unsigned i=0; i<errors.size(); i++ ) { all.append( errors[i].c_str() ); } all.done(); } { // errObjects BSONArrayBuilder all( result.subarrayStart( "errObjects" ) ); for ( unsigned i=0; i<errorObjects.size(); i++ ) { all.append( errorObjects[i] ); } all.done(); } _handleWriteBacks( writebacks , fromWriteBackListener ); return true; }
bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) { if ( ! okForConfigChanges( errmsg ) ) return false; ShardConnection::sync(); string ns = cmdObj.firstElement().valuestrsafe(); if ( ns.size() == 0 ) { errmsg = "no ns"; return false; } DBConfigPtr config = grid.getDBConfig( ns ); if ( ! config->isSharded( ns ) ) { config->reload(); if ( ! config->isSharded( ns ) ) { errmsg = "ns not sharded. have to shard before can split"; return false; } } BSONObj find = cmdObj.getObjectField( "find" ); if ( find.isEmpty() ) { find = cmdObj.getObjectField( "middle" ); if ( find.isEmpty() ) { errmsg = "need to specify find or middle"; return false; } } ChunkManagerPtr info = config->getChunkManager( ns ); ChunkPtr chunk = info->findChunk( find ); BSONObj middle = cmdObj.getObjectField( "middle" ); assert( chunk.get() ); log() << "splitting: " << ns << " shard: " << chunk << endl; BSONObj res; bool worked; if ( middle.isEmpty() ) { BSONObj ret = chunk->singleSplit( true /* force a split even if not enough data */ , res ); worked = !ret.isEmpty(); } else { // sanity check if the key provided is a valid split point if ( ( middle == chunk->getMin() ) || ( middle == chunk->getMax() ) ) { errmsg = "cannot split on initial or final chunk's key"; return false; } if (!fieldsMatch(middle, info->getShardKey().key())){ errmsg = "middle has different fields (or different order) than shard key"; return false; } vector<BSONObj> splitPoints; splitPoints.push_back( middle ); worked = chunk->multiSplit( splitPoints , res ); } if ( !worked ) { errmsg = "split failed"; result.append( "cause" , res ); return false; } config->getChunkManager( ns , true ); return true; }
/* **************************************************************************** * * runCollectionCommand - * * NOTE * Different from other functions in this module, this function can get the connection * in the params, instead of using getMongoConnection(). * This is only done from DB connection bootstrapping code . * */ bool runCollectionCommand ( DBClientBase* connection, const std::string& col, const BSONObj& command, BSONObj* result, std::string* err ) { bool releaseConnection = false; // // The call to TIME_STAT_MONGO_COMMAND_WAIT_START must be on toplevel so that local variables // are visible for TIME_STAT_MONGO_COMMAND_WAIT_STOP() // TIME_STAT_MONGO_COMMAND_WAIT_START(); if (connection == NULL) { connection = getMongoConnection(); releaseConnection = true; if (connection == NULL) { TIME_STAT_MONGO_COMMAND_WAIT_STOP(); LM_E(("Fatal Error (null DB connection)")); return false; } } LM_T(LmtMongo, ("runCommand() in '%s' collection: '%s'", col.c_str(), command.toString().c_str())); try { connection->runCommand(col.c_str(), command, *result); if (releaseConnection) { releaseMongoConnection(connection); TIME_STAT_MONGO_COMMAND_WAIT_STOP(); } LM_I(("Database Operation Successful (command: %s)", command.toString().c_str())); } catch (const std::exception &e) { if (releaseConnection) { releaseMongoConnection(connection); TIME_STAT_MONGO_COMMAND_WAIT_STOP(); } std::string msg = std::string("collection: ") + col.c_str() + " - runCommand(): " + command.toString() + " - exception: " + e.what(); *err = "Database Error (" + msg + ")"; LM_E((err->c_str())); return false; } catch (...) { if (releaseConnection) { releaseMongoConnection(connection); TIME_STAT_MONGO_COMMAND_WAIT_STOP(); } std::string msg = std::string("collection: ") + col.c_str() + " - runCommand(): " + command.toString() + " - exception: generic"; *err = "Database Error (" + msg + ")"; LM_E((err->c_str())); return false; } return true; }