bool BatchedCommandRequest::isValidIndexRequest( string* errMsg ) const { string dummy; if ( !errMsg ) errMsg = &dummy; dassert( isInsertIndexRequest() ); if ( sizeWriteOps() != 1 ) { *errMsg = "invalid batch request for index creation"; return false; } NamespaceString targetNSS( getTargetingNS() ); if ( !targetNSS.isValid() ) { *errMsg = targetNSS.ns() + " is not a valid namespace to index"; return false; } NamespaceString reqNSS( getNS() ); if ( reqNSS.db().compare( targetNSS.db() ) != 0 ) { *errMsg = targetNSS.ns() + " namespace is not in the request database " + reqNSS.db().toString(); return false; } return true; }
/** * Validate authentication on the server for the given dbname. */ void Tool::auth() { if (toolGlobalParams.username.empty()) { // Make sure that we don't need authentication to connect to this db // findOne throws an AssertionException if it's not authenticated. if (toolGlobalParams.coll.size() > 0) { // BSONTools don't have a collection conn().findOne(getNS(), Query("{}"), 0, QueryOption_SlaveOk); } return; } BSONObjBuilder authParams; authParams << saslCommandUserDBFieldName << getAuthenticationDatabase() << saslCommandUserFieldName << toolGlobalParams.username << saslCommandPasswordFieldName << toolGlobalParams.password << saslCommandMechanismFieldName << toolGlobalParams.authenticationMechanism; if (!toolGlobalParams.gssapiServiceName.empty()) { authParams << saslCommandServiceNameFieldName << toolGlobalParams.gssapiServiceName; } if (!toolGlobalParams.gssapiHostName.empty()) { authParams << saslCommandServiceHostnameFieldName << toolGlobalParams.gssapiHostName; } _conn->auth(authParams.obj()); }
StatusWith<std::vector<ShardEndpoint>> ChunkManagerTargeter::_targetQuery( OperationContext* opCtx, const BSONObj& query, const BSONObj& collation) const { if (!_routingInfo->db().primary() && !_routingInfo->cm()) { return {ErrorCodes::NamespaceNotFound, str::stream() << "could not target query in " << getNS().ns() << "; no metadata found"}; } std::set<ShardId> shardIds; if (_routingInfo->cm()) { try { _routingInfo->cm()->getShardIdsForQuery(opCtx, query, collation, &shardIds); } catch (const DBException& ex) { return ex.toStatus(); } } else { shardIds.insert(_routingInfo->db().primary()->getId()); } std::vector<ShardEndpoint> endpoints; for (auto&& shardId : shardIds) { const auto version = _routingInfo->cm() ? _routingInfo->cm()->getVersion(shardId) : ChunkVersion::UNSHARDED(); endpoints.emplace_back(std::move(shardId), version); } return endpoints; }
/** * Validate authentication on the server for the given dbname. populates * level (if supplied) with the user's credentials. */ void Tool::auth( string dbname, Auth::Level * level ) { if ( ! dbname.size() ) dbname = _db; if ( ! ( _username.size() || _password.size() ) ) { // Make sure that we don't need authentication to connect to this db // findOne throws an AssertionException if it's not authenticated. if (_coll.size() > 0) { // BSONTools don't have a collection conn().findOne(getNS(), Query("{}"), 0, QueryOption_SlaveOk); } // set write-level access if authentication is disabled if ( level != NULL ) *level = Auth::WRITE; return; } string errmsg; if (dbname.size()) { if ( _conn->auth( dbname , _username , _password , errmsg, true, level ) ) { return; } } // try against the admin db if ( _conn->auth( "admin" , _username , _password , errmsg, true, level ) ) { return; } throw UserException( 9997 , (string)"authentication failed: " + errmsg ); }
AutoGetCollectionForRead::~AutoGetCollectionForRead() { // Report time spent in read lock auto currentOp = CurOp::get(_txn); Top::get(_txn->getClient()->getServiceContext()) .record(currentOp->getNS(), currentOp->getOp(), -1, // "read locked" _timer.micros(), currentOp->isCommand()); }
void Model::save( bool check ){ ScopedDbConnection conn( modelServer() ); BSONObjBuilder b; serialize( b ); if ( _id.isEmpty() ){ OID oid; oid.init(); b.appendOID( "_id" , &oid ); BSONObj o = b.obj(); conn->insert( getNS() , o ); _id = o["_id"].wrap().getOwned(); log(4) << "inserted new model " << getNS() << " " << o << endl; } else { BSONElement id = _id["_id"]; b.append( id ); BSONObjBuilder qb; qb.append( id ); BSONObj q = qb.obj(); BSONObj o = b.obj(); log(4) << "updated old model" << getNS() << " " << q << " " << o << endl; conn->update( getNS() , q , o ); } string errmsg = ""; if ( check ) errmsg = conn->getLastError(); conn.done(); if ( check && errmsg.size() ) throw UserException( (string)"error on Model::save: " + errmsg ); }
AutoStatsTracker::~AutoStatsTracker() { auto curOp = CurOp::get(_opCtx); Top::get(_opCtx->getServiceContext()) .record(_opCtx, curOp->getNS(), curOp->getLogicalOp(), _lockType, durationCount<Microseconds>(curOp->elapsedTimeExcludingPauses()), curOp->isCommand(), curOp->getReadWriteType()); }
OldClientContext::~OldClientContext() { // Lock must still be held invariant(_txn->lockState()->isLocked()); auto currentOp = CurOp::get(_txn); Top::get(_txn->getClient()->getServiceContext()) .record(currentOp->getNS(), currentOp->getOp(), _txn->lockState()->isWriteLocked() ? 1 : -1, _timer.micros(), currentOp->isCommand()); }
bool Model::load(BSONObj& query){ ScopedDbConnection conn( modelServer() ); BSONObj b = conn->findOne(getNS(), query); conn.done(); if ( b.isEmpty() ) return false; unserialize(b); _id = b["_id"].wrap().getOwned(); return true; }
void recordStatsForTopCommand(OperationContext* txn) { auto curOp = CurOp::get(txn); const int writeLocked = 1; Top::get(txn->getClient()->getServiceContext()) .record(txn, curOp->getNS(), curOp->getLogicalOp(), writeLocked, curOp->elapsedMicros(), curOp->isCommand(), curOp->getReadWriteType()); }
OldClientContext::~OldClientContext() { // Lock must still be held invariant(_opCtx->lockState()->isLocked()); auto currentOp = CurOp::get(_opCtx); Top::get(_opCtx->getClient()->getServiceContext()) .record(_opCtx, currentOp->getNS(), currentOp->getLogicalOp(), _opCtx->lockState()->isWriteLocked() ? Top::LockType::WriteLocked : Top::LockType::ReadLocked, _timer.micros(), currentOp->isCommand(), currentOp->getReadWriteType()); }
void Model::remove( bool safe ){ uassert( 10016 , "_id isn't set - needed for remove()" , _id["_id"].type() ); ScopedDbConnection conn( modelServer() ); conn->remove( getNS() , _id ); string errmsg = ""; if ( safe ) errmsg = conn->getLastError(); conn.done(); if ( safe && errmsg.size() ) throw UserException( 9002 , (string)"error on Model::remove: " + errmsg ); }
BSONObj ChunkType::toBSON() const { BSONObjBuilder builder; if (_name) builder.append(name.name(), getName()); if (_ns) builder.append(ns.name(), getNS()); if (_min) builder.append(min.name(), getMin()); if (_max) builder.append(max.name(), getMax()); if (_shard) builder.append(shard.name(), getShard()); if (_version) { // For now, write both the deprecated *and* the new fields _version->addToBSON(builder, version()); _version->addToBSON(builder, DEPRECATED_lastmod()); } if (_jumbo) builder.append(jumbo.name(), getJumbo()); return builder.obj(); }
/** * Validate authentication on the server for the given dbname. */ void Tool::auth() { if ( _username.empty() ) { // Make sure that we don't need authentication to connect to this db // findOne throws an AssertionException if it's not authenticated. if (_coll.size() > 0) { // BSONTools don't have a collection conn().findOne(getNS(), Query("{}"), 0, QueryOption_SlaveOk); } return; } _conn->auth( BSON( saslCommandUserSourceFieldName << getAuthenticationDatabase() << saslCommandUserFieldName << _username << saslCommandPasswordFieldName << _password << saslCommandMechanismFieldName << _authenticationMechanism ) ); }
BSONObj ChunkType::toConfigBSON() const { BSONObjBuilder builder; if (_nss && _min) builder.append(name.name(), getName()); if (_nss) builder.append(ns.name(), getNS().ns()); if (_min) builder.append(min.name(), getMin()); if (_max) builder.append(max.name(), getMax()); if (_shard) builder.append(shard.name(), getShard().toString()); if (_version) _version->appendForChunk(&builder); if (_jumbo) builder.append(jumbo.name(), getJumbo()); return builder.obj(); }
BSONObj ChangeLogType::toBSON() const { BSONObjBuilder builder; if (_changeId) builder.append(changeId.name(), getChangeId()); if (_server) builder.append(server.name(), getServer()); if (_shard) builder.append(shard.name(), getShard()); if (_clientAddr) builder.append(clientAddr.name(), getClientAddr()); if (_time) builder.append(time.name(), getTime()); if (_what) builder.append(what.name(), getWhat()); if (_ns) builder.append(ns.name(), getNS()); if (_details) builder.append(details.name(), getDetails()); return builder.obj(); }
StatusWith<std::vector<ShardEndpoint>> ChunkManagerTargeter::targetCollection() const { if (!_routingInfo->db().primary() && !_routingInfo->cm()) { return {ErrorCodes::NamespaceNotFound, str::stream() << "could not target full range of " << getNS().ns() << "; metadata not found"}; } std::set<ShardId> shardIds; if (_routingInfo->cm()) { _routingInfo->cm()->getAllShardIds(&shardIds); } else { shardIds.insert(_routingInfo->db().primary()->getId()); } std::vector<ShardEndpoint> endpoints; for (auto&& shardId : shardIds) { const auto version = _routingInfo->cm() ? _routingInfo->cm()->getVersion(shardId) : ChunkVersion::UNSHARDED(); endpoints.emplace_back(std::move(shardId), version); } return endpoints; }
void Model::save( bool safe ) { scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getScopedDbConnection (modelServer() ) ); BSONObjBuilder b; serialize( b ); BSONElement myId; { BSONObjIterator i = b.iterator(); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp( e.fieldName() , "_id" ) == 0 ) { myId = e; break; } } } if ( myId.type() ) { if ( _id.isEmpty() ) { _id = myId.wrap(); } else if ( myId.woCompare( _id.firstElement() ) ) { stringstream ss; ss << "_id from serialize and stored differ: "; ss << '[' << myId << "] != "; ss << '[' << _id.firstElement() << ']'; throw UserException( 13121 , ss.str() ); } } if ( _id.isEmpty() ) { OID oid; oid.init(); b.appendOID( "_id" , &oid ); BSONObj o = b.obj(); conn->get()->insert( getNS() , o ); _id = o["_id"].wrap().getOwned(); LOG(4) << "inserted new model " << getNS() << " " << o << endl; } else { if ( myId.eoo() ) { myId = _id["_id"]; b.append( myId ); } verify( ! myId.eoo() ); BSONObjBuilder qb; qb.append( myId ); BSONObj q = qb.obj(); BSONObj o = b.obj(); LOG(4) << "updated model" << getNS() << " " << q << " " << o << endl; conn->get()->update( getNS() , q , o , true ); } string errmsg = ""; if ( safe ) errmsg = conn->get()->getLastError(); conn->done(); if ( safe && errmsg.size() ) throw UserException( 9003 , (string)"error on Model::save: " + errmsg ); }
string BatchedCommandRequest::getTargetingNS() const { if ( !isInsertIndexRequest() ) return getNS(); NamespaceString nss; extractIndexNSS( getInsertRequest()->getDocumentsAt( 0 ), &nss ); return nss.toString(); }
StatusWith<std::vector<ShardEndpoint>> ChunkManagerTargeter::targetAllShards( OperationContext* opCtx) const { if (!_routingInfo->db().primary() && !_routingInfo->cm()) { return {ErrorCodes::NamespaceNotFound, str::stream() << "could not target every shard with versions for " << getNS().ns() << "; metadata not found"}; } std::vector<ShardId> shardIds; Grid::get(opCtx)->shardRegistry()->getAllShardIdsNoReload(&shardIds); std::vector<ShardEndpoint> endpoints; for (auto&& shardId : shardIds) { const auto version = _routingInfo->cm() ? _routingInfo->cm()->getVersion(shardId) : ChunkVersion::UNSHARDED(); endpoints.emplace_back(std::move(shardId), version); } return endpoints; }
bool BatchedCommandRequest::isInsertIndexRequest() const { if ( _batchType != BatchedCommandRequest::BatchType_Insert ) return false; return NamespaceString( getNS() ).isSystemDotIndexes(); }
StatusWith<std::vector<ShardEndpoint>> ChunkManagerTargeter::targetDelete( OperationContext* opCtx, const write_ops::DeleteOpEntry& deleteDoc) const { BSONObj shardKey; if (_routingInfo->cm()) { // // Sharded collections have the following further requirements for targeting: // // Limit-1 deletes must be targeted exactly by shard key *or* exact _id // // Get the shard key StatusWith<BSONObj> status = _routingInfo->cm()->getShardKeyPattern().extractShardKeyFromQuery(opCtx, deleteDoc.getQ()); // Bad query if (!status.isOK()) return status.getStatus(); shardKey = status.getValue(); } const auto collation = write_ops::collationOf(deleteDoc); // Target the shard key or delete query if (!shardKey.isEmpty()) { try { return std::vector<ShardEndpoint>{_targetShardKey(shardKey, collation, 0)}; } catch (const DBException&) { // This delete is potentially not constrained to a single shard } } // We failed to target a single shard. // Parse delete query. auto qr = stdx::make_unique<QueryRequest>(getNS()); qr->setFilter(deleteDoc.getQ()); if (!collation.isEmpty()) { qr->setCollation(collation); } const boost::intrusive_ptr<ExpressionContext> expCtx; auto cq = CanonicalQuery::canonicalize(opCtx, std::move(qr), expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); if (!cq.isOK()) { return cq.getStatus().withContext(str::stream() << "Could not parse delete query " << deleteDoc.getQ()); } // Single deletes must target a single shard or be exact-ID. if (_routingInfo->cm() && !deleteDoc.getMulti() && !isExactIdQuery(opCtx, *cq.getValue(), _routingInfo->cm().get())) { return Status(ErrorCodes::ShardKeyNotFound, str::stream() << "A single delete on a sharded collection must contain an exact " "match on _id (and have the collection default collation) or " "contain the shard key (and have the simple collation). Delete " "request: " << deleteDoc.toBSON() << ", shard key pattern: " << _routingInfo->cm()->getShardKeyPattern().toString()); } return _targetQuery(opCtx, deleteDoc.getQ(), collation); }
StatusWith<std::vector<ShardEndpoint>> ChunkManagerTargeter::targetUpdate( OperationContext* opCtx, const write_ops::UpdateOpEntry& updateDoc) 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 not replacement style, we target using the query. Because mongoD // will automatically propagate '_id' from an existing document, and will extract it from an // exact-match in the query in the case of an upsert, we augment the replacement doc with the // query's '_id' for targeting purposes, if it exists. // // Once we have determined the correct component to target on, we attempt to extract an exact // shard key from it. If one is present, we target using it. // const auto updateType = getUpdateExprType(updateDoc); if (!updateType.isOK()) { return updateType.getStatus(); } // If the collection is not sharded, forward the update to the primary shard. if (!_routingInfo->cm()) { if (!_routingInfo->db().primary()) { return {ErrorCodes::NamespaceNotFound, str::stream() << "could not target update on " << getNS().ns() << "; no metadata found"}; } return std::vector<ShardEndpoint>{ {_routingInfo->db().primaryId(), ChunkVersion::UNSHARDED()}}; } const auto& shardKeyPattern = _routingInfo->cm()->getShardKeyPattern(); const auto collation = write_ops::collationOf(updateDoc); const auto updateExpr = getUpdateExpr(opCtx, shardKeyPattern, updateType.getValue(), updateDoc); const bool isUpsert = updateDoc.getUpsert(); const auto query = updateDoc.getQ(); if (!updateExpr.isOK()) { return updateExpr.getStatus(); } // We first attempt to target by exact match on the shard key. const auto swTarget = _targetUpdateByShardKey( opCtx, updateType.getValue(), query, collation, updateExpr.getValue(), isUpsert); // Return the status if we were successful in targeting by shard key. If this is an upsert, then // we return the status regardless of whether or not we succeeded, since an upsert must always // target an exact shard key or fail. If this is *not* an upsert and we were unable to target an // exact shard key, then we proceed to try other means of targeting the update. if (isUpsert || swTarget.isOK()) { return swTarget; } // If we could not target by shard key, attempt to route the update by query or replacement doc. auto shardEndPoints = (updateType.getValue() == UpdateType::kOpStyle ? _targetQuery(opCtx, query, collation) : _targetDoc(opCtx, updateExpr.getValue(), collation)); // Single (non-multi) updates must target a single shard, or an exact _id. if (!updateDoc.getMulti() && shardEndPoints.isOK() && shardEndPoints.getValue().size() > 1) { if (!isExactIdQuery(opCtx, getNS(), query, collation, _routingInfo->cm().get())) { return {ErrorCodes::InvalidOptions, str::stream() << "A {multi:false} update on a sharded collection must either " "contain an exact match on _id (and have the collection " "default collation) or must target a single shard (and have " "the simple collation), but this update targeted " << shardEndPoints.getValue().size() << " shards. Update request: " << updateDoc.toBSON() << ", shard key pattern: " << shardKeyPattern.toString()}; } } return shardEndPoints; }
StatusWith<ShardEndpoint> ChunkManagerTargeter::targetInsert(OperationContext* opCtx, const BSONObj& doc) const { BSONObj shardKey; if (_routingInfo->cm()) { // // Sharded collections have the following requirements for targeting: // // Inserts must contain the exact shard key. // shardKey = _routingInfo->cm()->getShardKeyPattern().extractShardKeyFromDoc(doc); // Check shard key exists if (shardKey.isEmpty()) { return {ErrorCodes::ShardKeyNotFound, str::stream() << "document " << doc << " does not contain shard key for pattern " << _routingInfo->cm()->getShardKeyPattern().toString()}; } // Check shard key size on insert Status status = ShardKeyPattern::checkShardKeySize(shardKey); if (!status.isOK()) return status; } // Target the shard key or database primary if (!shardKey.isEmpty()) { return _targetShardKey(shardKey, CollationSpec::kSimpleSpec, doc.objsize()); } else { if (!_routingInfo->db().primary()) { return Status(ErrorCodes::NamespaceNotFound, str::stream() << "could not target insert in collection " << getNS().ns() << "; no metadata found"); } return ShardEndpoint(_routingInfo->db().primary()->getId(), ChunkVersion::UNSHARDED()); } return Status::OK(); }