summaryrefslogtreecommitdiff
path: root/src/mongo/client/dbclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/client/dbclient.cpp')
-rw-r--r--src/mongo/client/dbclient.cpp2597
1 files changed, 1292 insertions, 1305 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp
index 4b455a75ac4..f1ebb144944 100644
--- a/src/mongo/client/dbclient.cpp
+++ b/src/mongo/client/dbclient.cpp
@@ -64,1511 +64,1498 @@
namespace mongo {
- using std::unique_ptr;
- using std::endl;
- using std::list;
- using std::map;
- using std::string;
- using std::stringstream;
- using std::vector;
+using std::unique_ptr;
+using std::endl;
+using std::list;
+using std::map;
+using std::string;
+using std::stringstream;
+using std::vector;
namespace {
- const char* const saslCommandUserSourceFieldName = "userSource";
+const char* const saslCommandUserSourceFieldName = "userSource";
#ifdef MONGO_CONFIG_SSL
- static SimpleMutex s_mtx;
- static SSLManagerInterface* s_sslMgr(NULL);
+static SimpleMutex s_mtx;
+static SSLManagerInterface* s_sslMgr(NULL);
- SSLManagerInterface* sslManager() {
- stdx::lock_guard<SimpleMutex> lk(s_mtx);
- if (s_sslMgr) {
- return s_sslMgr;
- }
-
- s_sslMgr = getSSLManager();
+SSLManagerInterface* sslManager() {
+ stdx::lock_guard<SimpleMutex> lk(s_mtx);
+ if (s_sslMgr) {
return s_sslMgr;
}
-#endif
-
-} // namespace
-
- AtomicInt64 DBClientBase::ConnectionIdSequence;
-
- const BSONField<BSONObj> Query::ReadPrefField("$readPreference");
- const BSONField<string> Query::ReadPrefModeField("mode");
- const BSONField<BSONArray> Query::ReadPrefTagsField("tags");
-
- Query::Query( const string &json ) : obj( fromjson( json ) ) {}
-
- Query::Query( const char *json ) : obj( fromjson( json ) ) {}
-
- Query& Query::hint(const string &jsonKeyPatt) { return hint( fromjson( jsonKeyPatt ) ); }
-
- Query& Query::where(const string &jscode, BSONObj scope) {
- /* use where() before sort() and hint() and explain(), else this will assert. */
- verify( ! isComplex() );
- BSONObjBuilder b;
- b.appendElements(obj);
- b.appendWhere(jscode, scope);
- obj = b.obj();
- return *this;
- }
-
- void Query::makeComplex() {
- if ( isComplex() )
- return;
- BSONObjBuilder b;
- b.append( "query", obj );
- obj = b.obj();
- }
-
- Query& Query::sort(const BSONObj& s) {
- appendComplex( "orderby", s );
- return *this;
- }
- Query& Query::hint(BSONObj keyPattern) {
- appendComplex( "$hint", keyPattern );
- return *this;
- }
+ s_sslMgr = getSSLManager();
+ return s_sslMgr;
+}
+#endif
- Query& Query::explain() {
- appendComplex( "$explain", true );
- return *this;
+} // namespace
+
+AtomicInt64 DBClientBase::ConnectionIdSequence;
+
+const BSONField<BSONObj> Query::ReadPrefField("$readPreference");
+const BSONField<string> Query::ReadPrefModeField("mode");
+const BSONField<BSONArray> Query::ReadPrefTagsField("tags");
+
+Query::Query(const string& json) : obj(fromjson(json)) {}
+
+Query::Query(const char* json) : obj(fromjson(json)) {}
+
+Query& Query::hint(const string& jsonKeyPatt) {
+ return hint(fromjson(jsonKeyPatt));
+}
+
+Query& Query::where(const string& jscode, BSONObj scope) {
+ /* use where() before sort() and hint() and explain(), else this will assert. */
+ verify(!isComplex());
+ BSONObjBuilder b;
+ b.appendElements(obj);
+ b.appendWhere(jscode, scope);
+ obj = b.obj();
+ return *this;
+}
+
+void Query::makeComplex() {
+ if (isComplex())
+ return;
+ BSONObjBuilder b;
+ b.append("query", obj);
+ obj = b.obj();
+}
+
+Query& Query::sort(const BSONObj& s) {
+ appendComplex("orderby", s);
+ return *this;
+}
+
+Query& Query::hint(BSONObj keyPattern) {
+ appendComplex("$hint", keyPattern);
+ return *this;
+}
+
+Query& Query::explain() {
+ appendComplex("$explain", true);
+ return *this;
+}
+
+Query& Query::snapshot() {
+ appendComplex("$snapshot", true);
+ return *this;
+}
+
+Query& Query::minKey(const BSONObj& val) {
+ appendComplex("$min", val);
+ return *this;
+}
+
+Query& Query::maxKey(const BSONObj& val) {
+ appendComplex("$max", val);
+ return *this;
+}
+
+bool Query::isComplex(const BSONObj& obj, bool* hasDollar) {
+ if (obj.hasElement("query")) {
+ if (hasDollar)
+ *hasDollar = false;
+ return true;
}
- Query& Query::snapshot() {
- appendComplex( "$snapshot", true );
- return *this;
+ if (obj.hasElement("$query")) {
+ if (hasDollar)
+ *hasDollar = true;
+ return true;
}
- Query& Query::minKey( const BSONObj &val ) {
- appendComplex( "$min", val );
- return *this;
- }
+ return false;
+}
+
+Query& Query::readPref(ReadPreference pref, const BSONArray& tags) {
+ appendComplex(ReadPrefField.name().c_str(), ReadPreferenceSetting(pref, TagSet(tags)).toBSON());
+ return *this;
+}
+
+bool Query::isComplex(bool* hasDollar) const {
+ return isComplex(obj, hasDollar);
+}
+
+bool Query::hasReadPreference(const BSONObj& queryObj) {
+ const bool hasReadPrefOption = queryObj["$queryOptions"].isABSONObj() &&
+ queryObj["$queryOptions"].Obj().hasField(ReadPrefField.name());
+
+ bool canHaveReadPrefField = Query::isComplex(queryObj) ||
+ // The find command has a '$readPreference' option.
+ queryObj.firstElementFieldName() == StringData("find");
+
+ return (canHaveReadPrefField && queryObj.hasField(ReadPrefField.name())) || hasReadPrefOption;
+}
+
+BSONObj Query::getFilter() const {
+ bool hasDollar;
+ if (!isComplex(&hasDollar))
+ return obj;
- Query& Query::maxKey( const BSONObj &val ) {
- appendComplex( "$max", val );
- return *this;
+ return obj.getObjectField(hasDollar ? "$query" : "query");
+}
+BSONObj Query::getSort() const {
+ if (!isComplex())
+ return BSONObj();
+ BSONObj ret = obj.getObjectField("orderby");
+ if (ret.isEmpty())
+ ret = obj.getObjectField("$orderby");
+ return ret;
+}
+BSONObj Query::getHint() const {
+ if (!isComplex())
+ return BSONObj();
+ return obj.getObjectField("$hint");
+}
+bool Query::isExplain() const {
+ return isComplex() && obj.getBoolField("$explain");
+}
+
+string Query::toString() const {
+ return obj.toString();
+}
+
+/* --- dbclientcommands --- */
+
+bool DBClientWithCommands::isOk(const BSONObj& o) {
+ return o["ok"].trueValue();
+}
+
+bool DBClientWithCommands::isNotMasterErrorString(const BSONElement& e) {
+ return e.type() == String && str::contains(e.valuestr(), "not master");
+}
+
+
+enum QueryOptions DBClientWithCommands::availableOptions() {
+ if (!_haveCachedAvailableOptions) {
+ _cachedAvailableOptions = _lookupAvailableOptions();
+ _haveCachedAvailableOptions = true;
}
+ return _cachedAvailableOptions;
+}
+
+enum QueryOptions DBClientWithCommands::_lookupAvailableOptions() {
+ BSONObj ret;
+ if (runCommand("admin", BSON("availablequeryoptions" << 1), ret)) {
+ return QueryOptions(ret.getIntField("options"));
+ }
+ return QueryOptions(0);
+}
+
+rpc::ProtocolSet DBClientWithCommands::getClientRPCProtocols() const {
+ return _clientRPCProtocols;
+}
+
+rpc::ProtocolSet DBClientWithCommands::getServerRPCProtocols() const {
+ return _serverRPCProtocols;
+}
- bool Query::isComplex(const BSONObj& obj, bool* hasDollar) {
- if (obj.hasElement("query")) {
- if (hasDollar) *hasDollar = false;
- return true;
- }
+void DBClientWithCommands::setClientRPCProtocols(rpc::ProtocolSet protocols) {
+ _clientRPCProtocols = std::move(protocols);
+}
- if (obj.hasElement("$query")) {
- if (hasDollar) *hasDollar = true;
+void DBClientWithCommands::_setServerRPCProtocols(rpc::ProtocolSet protocols) {
+ _serverRPCProtocols = std::move(protocols);
+}
+
+void DBClientWithCommands::setRequestMetadataWriter(rpc::RequestMetadataWriter writer) {
+ _metadataWriter = std::move(writer);
+}
+
+const rpc::RequestMetadataWriter& DBClientWithCommands::getRequestMetadataWriter() {
+ return _metadataWriter;
+}
+
+void DBClientWithCommands::setReplyMetadataReader(rpc::ReplyMetadataReader reader) {
+ _metadataReader = std::move(reader);
+}
+
+const rpc::ReplyMetadataReader& DBClientWithCommands::getReplyMetadataReader() {
+ return _metadataReader;
+}
+
+rpc::UniqueReply DBClientWithCommands::runCommandWithMetadata(StringData database,
+ StringData command,
+ const BSONObj& metadata,
+ const BSONObj& commandArgs) {
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Database name '" << database << "' is not valid.",
+ NamespaceString::validDBName(database));
+
+ BSONObjBuilder metadataBob;
+ metadataBob.appendElements(metadata);
+
+ if (_metadataWriter) {
+ uassertStatusOK(_metadataWriter(&metadataBob));
+ }
+
+ auto requestBuilder = rpc::makeRequestBuilder(getClientRPCProtocols(), getServerRPCProtocols());
+
+ requestBuilder->setDatabase(database);
+ requestBuilder->setCommandName(command);
+ requestBuilder->setMetadata(metadataBob.done());
+ requestBuilder->setCommandArgs(commandArgs);
+ auto requestMsg = requestBuilder->done();
+
+ auto replyMsg = stdx::make_unique<Message>();
+ // call oddly takes this by pointer, so we need to put it on the stack.
+ auto host = getServerAddress();
+
+ // We always want to throw if there was a network error, we do it here
+ // instead of passing 'true' for the 'assertOk' parameter so we can construct a
+ // more helpful error message. Note that call() can itself throw a socket exception.
+ uassert(ErrorCodes::HostUnreachable,
+ str::stream() << "network error while attempting to run "
+ << "command '" << command << "' "
+ << "on host '" << host << "' ",
+ call(*requestMsg, *replyMsg, false, &host));
+
+ auto commandReply = rpc::makeReply(replyMsg.get());
+
+ uassert(ErrorCodes::RPCProtocolNegotiationFailed,
+ str::stream() << "Mismatched RPC protocols - request was '"
+ << opToString(requestMsg->operation()) << "' '"
+ << " but reply was '" << opToString(replyMsg->operation()) << "' ",
+ requestBuilder->getProtocol() == commandReply->getProtocol());
+
+ if (ErrorCodes::SendStaleConfig ==
+ getStatusFromCommandResult(commandReply->getCommandReply())) {
+ throw RecvStaleConfigException("stale config in runCommand",
+ commandReply->getCommandReply());
+ }
+
+ if (_metadataReader) {
+ uassertStatusOK(_metadataReader(commandReply->getMetadata(), host));
+ }
+
+ return rpc::UniqueReply(std::move(replyMsg), std::move(commandReply));
+}
+
+bool DBClientWithCommands::runCommand(const string& dbname,
+ const BSONObj& cmd,
+ BSONObj& info,
+ int options) {
+ BSONObj upconvertedCmd;
+ BSONObj upconvertedMetadata;
+
+ // TODO: This will be downconverted immediately if the underlying
+ // requestBuilder is a legacyRequest builder. Not sure what the best
+ // way to get around that is without breaking the abstraction.
+ std::tie(upconvertedCmd, upconvertedMetadata) =
+ uassertStatusOK(rpc::upconvertRequestMetadata(cmd, options));
+
+ auto commandName = upconvertedCmd.firstElementFieldName();
+
+ auto result = runCommandWithMetadata(dbname, commandName, upconvertedMetadata, upconvertedCmd);
+
+ info = result->getCommandReply().getOwned();
+
+ return isOk(info);
+}
+
+/* note - we build a bson obj here -- for something that is super common like getlasterror you
+ should have that object prebuilt as that would be faster.
+*/
+bool DBClientWithCommands::simpleCommand(const string& dbname,
+ BSONObj* info,
+ const string& command) {
+ BSONObj o;
+ if (info == 0)
+ info = &o;
+ BSONObjBuilder b;
+ b.append(command, 1);
+ return runCommand(dbname, b.done(), *info);
+}
+
+bool DBClientWithCommands::runPseudoCommand(StringData db,
+ StringData realCommandName,
+ StringData pseudoCommandCol,
+ const BSONObj& cmdArgs,
+ BSONObj& info,
+ int options) {
+ BSONObjBuilder bob;
+ bob.append(realCommandName, 1);
+ bob.appendElements(cmdArgs);
+ auto cmdObj = bob.done();
+
+ bool success = false;
+
+ if (!(success = runCommand(db.toString(), cmdObj, info, options))) {
+ auto status = getStatusFromCommandResult(info);
+ verify(!status.isOK());
+
+ if (status == ErrorCodes::CommandResultSchemaViolation) {
+ msgasserted(28624,
+ str::stream() << "Received bad " << realCommandName
+ << " response from server: " << info);
+ } else if (status == ErrorCodes::CommandNotFound) {
+ NamespaceString pseudoCommandNss(db, pseudoCommandCol);
+ // if this throws we just let it escape as that's how runCommand works.
+ info = findOne(pseudoCommandNss.ns(), cmdArgs, nullptr, options);
return true;
}
-
- return false;
}
- Query& Query::readPref(ReadPreference pref, const BSONArray& tags) {
- appendComplex(ReadPrefField.name().c_str(),
- ReadPreferenceSetting(pref, TagSet(tags)).toBSON());
- return *this;
+ return success;
+}
+
+unsigned long long DBClientWithCommands::count(
+ const string& myns, const BSONObj& query, int options, int limit, int skip) {
+ BSONObj cmd = _countCmd(myns, query, options, limit, skip);
+ BSONObj res;
+ if (!runCommand(nsToDatabase(myns), cmd, res, options))
+ uasserted(11010, string("count fails:") + res.toString());
+ return res["n"].numberLong();
+}
+
+BSONObj DBClientWithCommands::_countCmd(
+ const string& myns, const BSONObj& query, int options, int limit, int skip) {
+ NamespaceString ns(myns);
+ BSONObjBuilder b;
+ b.append("count", ns.coll());
+ b.append("query", query);
+ if (limit)
+ b.append("limit", limit);
+ if (skip)
+ b.append("skip", skip);
+ return b.obj();
+}
+
+BSONObj DBClientWithCommands::getLastErrorDetailed(bool fsync, bool j, int w, int wtimeout) {
+ return getLastErrorDetailed("admin", fsync, j, w, wtimeout);
+}
+
+BSONObj DBClientWithCommands::getLastErrorDetailed(
+ const std::string& db, bool fsync, bool j, int w, int wtimeout) {
+ BSONObj info;
+ BSONObjBuilder b;
+ b.append("getlasterror", 1);
+
+ if (fsync)
+ b.append("fsync", 1);
+ if (j)
+ b.append("j", 1);
+
+ // only affects request when greater than one node
+ if (w >= 1)
+ b.append("w", w);
+ else if (w == -1)
+ b.append("w", "majority");
+
+ if (wtimeout > 0)
+ b.append("wtimeout", wtimeout);
+
+ runCommand(db, b.obj(), info);
+
+ return info;
+}
+
+string DBClientWithCommands::getLastError(bool fsync, bool j, int w, int wtimeout) {
+ return getLastError("admin", fsync, j, w, wtimeout);
+}
+
+string DBClientWithCommands::getLastError(
+ const std::string& db, bool fsync, bool j, int w, int wtimeout) {
+ BSONObj info = getLastErrorDetailed(db, fsync, j, w, wtimeout);
+ return getLastErrorString(info);
+}
+
+string DBClientWithCommands::getLastErrorString(const BSONObj& info) {
+ if (info["ok"].trueValue()) {
+ BSONElement e = info["err"];
+ if (e.eoo())
+ return "";
+ if (e.type() == Object)
+ return e.toString();
+ return e.str();
+ } else {
+ // command failure
+ BSONElement e = info["errmsg"];
+ if (e.eoo())
+ return "";
+ if (e.type() == Object)
+ return "getLastError command failed: " + e.toString();
+ return "getLastError command failed: " + e.str();
}
+}
- bool Query::isComplex( bool * hasDollar ) const {
- return isComplex(obj, hasDollar);
- }
+const BSONObj getpreverrorcmdobj = fromjson("{getpreverror:1}");
- bool Query::hasReadPreference(const BSONObj& queryObj) {
- const bool hasReadPrefOption = queryObj["$queryOptions"].isABSONObj() &&
- queryObj["$queryOptions"].Obj().hasField(ReadPrefField.name());
+BSONObj DBClientWithCommands::getPrevError() {
+ BSONObj info;
+ runCommand("admin", getpreverrorcmdobj, info);
+ return info;
+}
- bool canHaveReadPrefField = Query::isComplex(queryObj) ||
- // The find command has a '$readPreference' option.
- queryObj.firstElementFieldName() == StringData("find");
+BSONObj getnoncecmdobj = fromjson("{getnonce:1}");
- return (canHaveReadPrefField &&
- queryObj.hasField(ReadPrefField.name())) ||
- hasReadPrefOption;
- }
+string DBClientWithCommands::createPasswordDigest(const string& username,
+ const string& clearTextPassword) {
+ return mongo::createPasswordDigest(username, clearTextPassword);
+}
- BSONObj Query::getFilter() const {
- bool hasDollar;
- if ( ! isComplex( &hasDollar ) )
- return obj;
+namespace {
+class ScopedMetadataWriterRemover {
+ MONGO_DISALLOW_COPYING(ScopedMetadataWriterRemover);
- return obj.getObjectField( hasDollar ? "$query" : "query" );
- }
- BSONObj Query::getSort() const {
- if ( ! isComplex() )
- return BSONObj();
- BSONObj ret = obj.getObjectField( "orderby" );
- if (ret.isEmpty())
- ret = obj.getObjectField( "$orderby" );
- return ret;
- }
- BSONObj Query::getHint() const {
- if ( ! isComplex() )
- return BSONObj();
- return obj.getObjectField( "$hint" );
+public:
+ ScopedMetadataWriterRemover(DBClientWithCommands* cli)
+ : _cli(cli), _oldWriter(cli->getRequestMetadataWriter()) {
+ _cli->setRequestMetadataWriter(rpc::RequestMetadataWriter{});
}
- bool Query::isExplain() const {
- return isComplex() && obj.getBoolField( "$explain" );
+ ~ScopedMetadataWriterRemover() {
+ _cli->setRequestMetadataWriter(_oldWriter);
}
- string Query::toString() const {
- return obj.toString();
- }
+private:
+ DBClientWithCommands* const _cli;
+ rpc::RequestMetadataWriter _oldWriter;
+};
+} // namespace
- /* --- dbclientcommands --- */
+void DBClientWithCommands::_auth(const BSONObj& params) {
+ ScopedMetadataWriterRemover{this};
- bool DBClientWithCommands::isOk(const BSONObj& o) {
- return o["ok"].trueValue();
- }
+ std::string mechanism;
- bool DBClientWithCommands::isNotMasterErrorString( const BSONElement& e ) {
- return e.type() == String && str::contains( e.valuestr() , "not master" );
- }
+ uassertStatusOK(bsonExtractStringField(params, saslCommandMechanismFieldName, &mechanism));
+ uassert(17232,
+ "You cannot specify both 'db' and 'userSource'. Please use only 'db'.",
+ !(params.hasField(saslCommandUserDBFieldName) &&
+ params.hasField(saslCommandUserSourceFieldName)));
- enum QueryOptions DBClientWithCommands::availableOptions() {
- if ( !_haveCachedAvailableOptions ) {
- _cachedAvailableOptions = _lookupAvailableOptions();
- _haveCachedAvailableOptions = true;
- }
- return _cachedAvailableOptions;
+ if (mechanism == StringData("MONGODB-CR", StringData::LiteralTag())) {
+ std::string db;
+ if (params.hasField(saslCommandUserSourceFieldName)) {
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserSourceFieldName, &db));
+ } else {
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserDBFieldName, &db));
+ }
+ std::string user;
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserFieldName, &user));
+ std::string password;
+ uassertStatusOK(bsonExtractStringField(params, saslCommandPasswordFieldName, &password));
+ bool digestPassword;
+ uassertStatusOK(bsonExtractBooleanFieldWithDefault(
+ params, saslCommandDigestPasswordFieldName, true, &digestPassword));
+ BSONObj result;
+ uassert(result["code"].Int(),
+ result.toString(),
+ _authMongoCR(db, user, password, &result, digestPassword));
}
-
- enum QueryOptions DBClientWithCommands::_lookupAvailableOptions() {
- BSONObj ret;
- if ( runCommand( "admin", BSON( "availablequeryoptions" << 1 ), ret ) ) {
- return QueryOptions( ret.getIntField( "options" ) );
+#ifdef MONGO_CONFIG_SSL
+ else if (mechanism == StringData("MONGODB-X509", StringData::LiteralTag())) {
+ std::string db;
+ if (params.hasField(saslCommandUserSourceFieldName)) {
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserSourceFieldName, &db));
+ } else {
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserDBFieldName, &db));
}
- return QueryOptions(0);
- }
+ std::string user;
+ uassertStatusOK(bsonExtractStringField(params, saslCommandUserFieldName, &user));
- rpc::ProtocolSet DBClientWithCommands::getClientRPCProtocols() const {
- return _clientRPCProtocols;
- }
+ uassert(ErrorCodes::AuthenticationFailed,
+ "Please enable SSL on the client-side to use the MONGODB-X509 "
+ "authentication mechanism.",
+ getSSLManager() != NULL);
- rpc::ProtocolSet DBClientWithCommands::getServerRPCProtocols() const {
- return _serverRPCProtocols;
- }
-
- void DBClientWithCommands::setClientRPCProtocols(rpc::ProtocolSet protocols) {
- _clientRPCProtocols = std::move(protocols);
- }
+ uassert(ErrorCodes::AuthenticationFailed,
+ "Username \"" + user + "\" does not match the provided client certificate user \"" +
+ getSSLManager()->getSSLConfiguration().clientSubjectName + "\"",
+ user == getSSLManager()->getSSLConfiguration().clientSubjectName);
- void DBClientWithCommands::_setServerRPCProtocols(rpc::ProtocolSet protocols) {
- _serverRPCProtocols = std::move(protocols);
+ BSONObj result;
+ uassert(result["code"].Int(), result.toString(), _authX509(db, user, &result));
}
-
- void DBClientWithCommands::setRequestMetadataWriter(rpc::RequestMetadataWriter writer) {
- _metadataWriter = std::move(writer);
+#endif
+ else if (saslClientAuthenticate != NULL) {
+ uassertStatusOK(saslClientAuthenticate(this, params));
+ } else {
+ uasserted(ErrorCodes::BadValue,
+ mechanism + " mechanism support not compiled into client library.");
+ }
+};
+
+void DBClientWithCommands::auth(const BSONObj& params) {
+ try {
+ _auth(params);
+ return;
+ } catch (const UserException& ex) {
+ if (getFallbackAuthParams(params).isEmpty() ||
+ (ex.getCode() != ErrorCodes::BadValue && ex.getCode() != ErrorCodes::CommandNotFound)) {
+ throw ex;
+ }
+ }
+
+ // BadValue or CommandNotFound indicates unsupported auth mechanism so fall back to
+ // MONGODB-CR for 2.6 compatibility.
+ _auth(getFallbackAuthParams(params));
+}
+
+bool DBClientWithCommands::auth(const string& dbname,
+ const string& username,
+ const string& password_text,
+ string& errmsg,
+ bool digestPassword) {
+ try {
+ auth(BSON(saslCommandMechanismFieldName
+ << "SCRAM-SHA-1" << saslCommandUserDBFieldName << dbname
+ << saslCommandUserFieldName << username << saslCommandPasswordFieldName
+ << password_text << saslCommandDigestPasswordFieldName << digestPassword));
+ return true;
+ } catch (const UserException& ex) {
+ if (ex.getCode() != ErrorCodes::AuthenticationFailed)
+ throw;
+ errmsg = ex.what();
+ return false;
}
+}
- const rpc::RequestMetadataWriter& DBClientWithCommands::getRequestMetadataWriter() {
- return _metadataWriter;
- }
+bool DBClientWithCommands::_authMongoCR(const string& dbname,
+ const string& username,
+ const string& password_text,
+ BSONObj* info,
+ bool digestPassword) {
+ string password = password_text;
+ if (digestPassword)
+ password = createPasswordDigest(username, password_text);
- void DBClientWithCommands::setReplyMetadataReader(rpc::ReplyMetadataReader reader) {
- _metadataReader = std::move(reader);
+ string nonce;
+ if (!runCommand(dbname, getnoncecmdobj, *info)) {
+ return false;
}
-
- const rpc::ReplyMetadataReader& DBClientWithCommands::getReplyMetadataReader() {
- return _metadataReader;
+ {
+ BSONElement e = info->getField("nonce");
+ verify(e.type() == String);
+ nonce = e.valuestr();
}
- rpc::UniqueReply DBClientWithCommands::runCommandWithMetadata(StringData database,
- StringData command,
- const BSONObj& metadata,
- const BSONObj& commandArgs) {
- uassert(ErrorCodes::InvalidNamespace, str::stream() << "Database name '" << database
- << "' is not valid.",
- NamespaceString::validDBName(database));
-
- BSONObjBuilder metadataBob;
- metadataBob.appendElements(metadata);
-
- if (_metadataWriter) {
- uassertStatusOK(_metadataWriter(&metadataBob));
- }
-
- auto requestBuilder = rpc::makeRequestBuilder(getClientRPCProtocols(),
- getServerRPCProtocols());
-
- requestBuilder->setDatabase(database);
- requestBuilder->setCommandName(command);
- requestBuilder->setMetadata(metadataBob.done());
- requestBuilder->setCommandArgs(commandArgs);
- auto requestMsg = requestBuilder->done();
-
- auto replyMsg = stdx::make_unique<Message>();
- // call oddly takes this by pointer, so we need to put it on the stack.
- auto host = getServerAddress();
-
- // We always want to throw if there was a network error, we do it here
- // instead of passing 'true' for the 'assertOk' parameter so we can construct a
- // more helpful error message. Note that call() can itself throw a socket exception.
- uassert(ErrorCodes::HostUnreachable,
- str::stream() << "network error while attempting to run "
- << "command '" << command << "' "
- << "on host '" << host << "' ",
- call(*requestMsg, *replyMsg, false, &host));
-
- auto commandReply = rpc::makeReply(replyMsg.get());
-
- uassert(ErrorCodes::RPCProtocolNegotiationFailed,
- str::stream() << "Mismatched RPC protocols - request was '"
- << opToString(requestMsg->operation()) << "' '"
- << " but reply was '"
- << opToString(replyMsg->operation()) << "' ",
- requestBuilder->getProtocol() == commandReply->getProtocol());
-
- if (ErrorCodes::SendStaleConfig ==
- getStatusFromCommandResult(commandReply->getCommandReply())) {
- throw RecvStaleConfigException("stale config in runCommand",
- commandReply->getCommandReply());
- }
-
- if (_metadataReader) {
- uassertStatusOK(_metadataReader(commandReply->getMetadata(), host));
+ BSONObj authCmd;
+ BSONObjBuilder b;
+ {
+ b << "authenticate" << 1 << "nonce" << nonce << "user" << username;
+ md5digest d;
+ {
+ md5_state_t st;
+ md5_init(&st);
+ md5_append(&st, (const md5_byte_t*)nonce.c_str(), nonce.size());
+ md5_append(&st, (const md5_byte_t*)username.data(), username.length());
+ md5_append(&st, (const md5_byte_t*)password.c_str(), password.size());
+ md5_finish(&st, d);
}
-
- return rpc::UniqueReply(std::move(replyMsg), std::move(commandReply));
+ b << "key" << digestToString(d);
+ authCmd = b.done();
}
- bool DBClientWithCommands::runCommand(const string& dbname,
- const BSONObj& cmd,
- BSONObj &info,
- int options) {
- BSONObj upconvertedCmd;
- BSONObj upconvertedMetadata;
-
- // TODO: This will be downconverted immediately if the underlying
- // requestBuilder is a legacyRequest builder. Not sure what the best
- // way to get around that is without breaking the abstraction.
- std::tie(upconvertedCmd, upconvertedMetadata) = uassertStatusOK(
- rpc::upconvertRequestMetadata(cmd, options)
- );
-
- auto commandName = upconvertedCmd.firstElementFieldName();
-
- auto result = runCommandWithMetadata(dbname,
- commandName,
- upconvertedMetadata,
- upconvertedCmd);
-
- info = result->getCommandReply().getOwned();
-
- return isOk(info);
+ if (runCommand(dbname, authCmd, *info)) {
+ return true;
}
- /* note - we build a bson obj here -- for something that is super common like getlasterror you
- should have that object prebuilt as that would be faster.
- */
- bool DBClientWithCommands::simpleCommand(const string &dbname, BSONObj *info, const string &command) {
- BSONObj o;
- if ( info == 0 )
- info = &o;
- BSONObjBuilder b;
- b.append(command, 1);
- return runCommand(dbname, b.done(), *info);
- }
+ return false;
+}
- bool DBClientWithCommands::runPseudoCommand(StringData db,
- StringData realCommandName,
- StringData pseudoCommandCol,
- const BSONObj& cmdArgs,
- BSONObj& info,
- int options) {
-
- BSONObjBuilder bob;
- bob.append(realCommandName, 1);
- bob.appendElements(cmdArgs);
- auto cmdObj = bob.done();
-
- bool success = false;
-
- if (!(success = runCommand(db.toString(), cmdObj, info, options))) {
-
- auto status = getStatusFromCommandResult(info);
- verify(!status.isOK());
-
- if (status == ErrorCodes::CommandResultSchemaViolation) {
- msgasserted(28624, str::stream() << "Received bad "
- << realCommandName
- << " response from server: "
- << info);
- } else if (status == ErrorCodes::CommandNotFound) {
- NamespaceString pseudoCommandNss(db, pseudoCommandCol);
- // if this throws we just let it escape as that's how runCommand works.
- info = findOne(pseudoCommandNss.ns(), cmdArgs, nullptr, options);
- return true;
- }
- }
+bool DBClientWithCommands::_authX509(const string& dbname, const string& username, BSONObj* info) {
+ BSONObj authCmd;
+ BSONObjBuilder cmdBuilder;
+ cmdBuilder << "authenticate" << 1 << "mechanism"
+ << "MONGODB-X509"
+ << "user" << username;
+ authCmd = cmdBuilder.done();
- return success;
+ if (runCommand(dbname, authCmd, *info)) {
+ return true;
}
- unsigned long long DBClientWithCommands::count(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
- BSONObj cmd = _countCmd( myns , query , options , limit , skip );
+ return false;
+}
+
+void DBClientWithCommands::logout(const string& dbname, BSONObj& info) {
+ runCommand(dbname, BSON("logout" << 1), info);
+}
+
+BSONObj ismastercmdobj = fromjson("{\"ismaster\":1}");
+
+bool DBClientWithCommands::isMaster(bool& isMaster, BSONObj* info) {
+ BSONObj o;
+ if (info == 0)
+ info = &o;
+ bool ok = runCommand("admin", ismastercmdobj, *info);
+ isMaster = info->getField("ismaster").trueValue();
+ return ok;
+}
+
+bool DBClientWithCommands::createCollection(
+ const string& ns, long long size, bool capped, int max, BSONObj* info) {
+ verify(!capped || size);
+ BSONObj o;
+ if (info == 0)
+ info = &o;
+ BSONObjBuilder b;
+ string db = nsToDatabase(ns);
+ b.append("create", ns.c_str() + db.length() + 1);
+ if (size)
+ b.append("size", size);
+ if (capped)
+ b.append("capped", true);
+ if (max)
+ b.append("max", max);
+ return runCommand(db.c_str(), b.done(), *info);
+}
+
+bool DBClientWithCommands::copyDatabase(const string& fromdb,
+ const string& todb,
+ const string& fromhost,
+ BSONObj* info) {
+ BSONObj o;
+ if (info == 0)
+ info = &o;
+ BSONObjBuilder b;
+ b.append("copydb", 1);
+ b.append("fromhost", fromhost);
+ b.append("fromdb", fromdb);
+ b.append("todb", todb);
+ return runCommand("admin", b.done(), *info);
+}
+
+bool DBClientWithCommands::eval(const string& dbname,
+ const string& jscode,
+ BSONObj& info,
+ BSONElement& retValue,
+ BSONObj* args) {
+ BSONObjBuilder b;
+ b.appendCode("$eval", jscode);
+ if (args)
+ b.appendArray("args", *args);
+ bool ok = runCommand(dbname, b.done(), info);
+ if (ok)
+ retValue = info.getField("retval");
+ return ok;
+}
+
+bool DBClientWithCommands::eval(const string& dbname, const string& jscode) {
+ BSONObj info;
+ BSONElement retValue;
+ return eval(dbname, jscode, info, retValue);
+}
+
+list<string> DBClientWithCommands::getDatabaseNames() {
+ BSONObj info;
+ uassert(10005,
+ "listdatabases failed",
+ runCommand("admin", BSON("listDatabases" << 1), info, QueryOption_SlaveOk));
+ uassert(10006, "listDatabases.databases not array", info["databases"].type() == Array);
+
+ list<string> names;
+
+ BSONObjIterator i(info["databases"].embeddedObjectUserCheck());
+ while (i.more()) {
+ names.push_back(i.next().embeddedObjectUserCheck()["name"].valuestr());
+ }
+
+ return names;
+}
+
+list<string> DBClientWithCommands::getCollectionNames(const string& db) {
+ list<BSONObj> infos = getCollectionInfos(db);
+ list<string> names;
+ for (list<BSONObj>::iterator it = infos.begin(); it != infos.end(); ++it) {
+ names.push_back(db + "." + (*it)["name"].valuestr());
+ }
+ return names;
+}
+
+list<BSONObj> DBClientWithCommands::getCollectionInfos(const string& db, const BSONObj& filter) {
+ list<BSONObj> infos;
+
+ // first we're going to try the command
+ // it was only added in 3.0, so if we're talking to an older server
+ // we'll fail back to querying system.namespaces
+ // TODO(spencer): remove fallback behavior after 3.0
+
+ {
BSONObj res;
- if( !runCommand(nsToDatabase(myns), cmd, res, options) )
- uasserted(11010,string("count fails:") + res.toString());
- return res["n"].numberLong();
- }
-
- BSONObj DBClientWithCommands::_countCmd(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
- NamespaceString ns(myns);
- BSONObjBuilder b;
- b.append( "count" , ns.coll() );
- b.append( "query" , query );
- if ( limit )
- b.append( "limit" , limit );
- if ( skip )
- b.append( "skip" , skip );
- return b.obj();
- }
-
- BSONObj DBClientWithCommands::getLastErrorDetailed(bool fsync, bool j, int w, int wtimeout) {
- return getLastErrorDetailed("admin", fsync, j, w, wtimeout);
- }
-
- BSONObj DBClientWithCommands::getLastErrorDetailed(const std::string& db,
- bool fsync,
- bool j,
- int w,
- int wtimeout) {
- BSONObj info;
- BSONObjBuilder b;
- b.append( "getlasterror", 1 );
-
- if ( fsync )
- b.append( "fsync", 1 );
- if ( j )
- b.append( "j", 1 );
-
- // only affects request when greater than one node
- if ( w >= 1 )
- b.append( "w", w );
- else if ( w == -1 )
- b.append( "w", "majority" );
-
- if ( wtimeout > 0 )
- b.append( "wtimeout", wtimeout );
-
- runCommand(db, b.obj(), info);
-
- return info;
- }
-
- string DBClientWithCommands::getLastError(bool fsync, bool j, int w, int wtimeout) {
- return getLastError("admin", fsync, j, w, wtimeout);
- }
-
- string DBClientWithCommands::getLastError(const std::string& db,
- bool fsync,
- bool j,
- int w,
- int wtimeout) {
- BSONObj info = getLastErrorDetailed(db, fsync, j, w, wtimeout);
- return getLastErrorString( info );
- }
-
- string DBClientWithCommands::getLastErrorString(const BSONObj& info) {
- if (info["ok"].trueValue()) {
- BSONElement e = info["err"];
- if (e.eoo()) return "";
- if (e.type() == Object) return e.toString();
- return e.str();
- } else {
- // command failure
- BSONElement e = info["errmsg"];
- if (e.eoo()) return "";
- if (e.type() == Object) return "getLastError command failed: " + e.toString();
- return "getLastError command failed: " + e.str();
- }
- }
-
- const BSONObj getpreverrorcmdobj = fromjson("{getpreverror:1}");
-
- BSONObj DBClientWithCommands::getPrevError() {
- BSONObj info;
- runCommand("admin", getpreverrorcmdobj, info);
- return info;
- }
-
- BSONObj getnoncecmdobj = fromjson("{getnonce:1}");
-
- string DBClientWithCommands::createPasswordDigest( const string & username , const string & clearTextPassword ) {
- return mongo::createPasswordDigest(username, clearTextPassword);
- }
-
- namespace {
- class ScopedMetadataWriterRemover {
- MONGO_DISALLOW_COPYING(ScopedMetadataWriterRemover);
- public:
- ScopedMetadataWriterRemover(DBClientWithCommands* cli)
- : _cli(cli), _oldWriter(cli->getRequestMetadataWriter()) {
- _cli->setRequestMetadataWriter(rpc::RequestMetadataWriter{});
+ if (runCommand(db,
+ BSON("listCollections" << 1 << "filter" << filter << "cursor" << BSONObj()),
+ res,
+ QueryOption_SlaveOk)) {
+ BSONObj cursorObj = res["cursor"].Obj();
+ BSONObj collections = cursorObj["firstBatch"].Obj();
+ BSONObjIterator it(collections);
+ while (it.more()) {
+ BSONElement e = it.next();
+ infos.push_back(e.Obj().getOwned());
}
- ~ScopedMetadataWriterRemover() {
- _cli->setRequestMetadataWriter(_oldWriter);
- }
- private:
- DBClientWithCommands* const _cli;
- rpc::RequestMetadataWriter _oldWriter;
- };
- } // namespace
-
- void DBClientWithCommands::_auth(const BSONObj& params) {
- ScopedMetadataWriterRemover{this};
-
- std::string mechanism;
-
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandMechanismFieldName,
- &mechanism));
-
- uassert(17232, "You cannot specify both 'db' and 'userSource'. Please use only 'db'.",
- !(params.hasField(saslCommandUserDBFieldName)
- && params.hasField(saslCommandUserSourceFieldName)));
-
- if (mechanism == StringData("MONGODB-CR", StringData::LiteralTag())) {
- std::string db;
- if (params.hasField(saslCommandUserSourceFieldName)) {
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserSourceFieldName,
- &db));
- }
- else {
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserDBFieldName,
- &db));
- }
- std::string user;
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserFieldName,
- &user));
- std::string password;
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandPasswordFieldName,
- &password));
- bool digestPassword;
- uassertStatusOK(bsonExtractBooleanFieldWithDefault(params,
- saslCommandDigestPasswordFieldName,
- true,
- &digestPassword));
- BSONObj result;
- uassert(result["code"].Int(),
- result.toString(),
- _authMongoCR(db, user, password, &result, digestPassword));
- }
-#ifdef MONGO_CONFIG_SSL
- else if (mechanism == StringData("MONGODB-X509", StringData::LiteralTag())){
- std::string db;
- if (params.hasField(saslCommandUserSourceFieldName)) {
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserSourceFieldName,
- &db));
- }
- else {
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserDBFieldName,
- &db));
- }
- std::string user;
- uassertStatusOK(bsonExtractStringField(params,
- saslCommandUserFieldName,
- &user));
-
- uassert(ErrorCodes::AuthenticationFailed,
- "Please enable SSL on the client-side to use the MONGODB-X509 "
- "authentication mechanism.",
- getSSLManager() != NULL);
-
- uassert(ErrorCodes::AuthenticationFailed,
- "Username \"" + user +
- "\" does not match the provided client certificate user \"" +
- getSSLManager()->getSSLConfiguration().clientSubjectName + "\"",
- user == getSSLManager()->getSSLConfiguration().clientSubjectName);
-
- BSONObj result;
- uassert(result["code"].Int(),
- result.toString(),
- _authX509(db, user, &result));
- }
-#endif
- else if (saslClientAuthenticate != NULL) {
- uassertStatusOK(saslClientAuthenticate(this, params));
- }
- else {
- uasserted(ErrorCodes::BadValue,
- mechanism + " mechanism support not compiled into client library.");
- }
- };
-
- void DBClientWithCommands::auth(const BSONObj& params) {
- try {
- _auth(params);
- return;
- } catch(const UserException& ex) {
- if (getFallbackAuthParams(params).isEmpty() ||
- (ex.getCode() != ErrorCodes::BadValue &&
- ex.getCode() != ErrorCodes::CommandNotFound)) {
- throw ex;
- }
- }
-
- // BadValue or CommandNotFound indicates unsupported auth mechanism so fall back to
- // MONGODB-CR for 2.6 compatibility.
- _auth(getFallbackAuthParams(params));
- }
-
- bool DBClientWithCommands::auth(const string &dbname,
- const string &username,
- const string &password_text,
- string& errmsg,
- bool digestPassword) {
- try {
- auth(BSON(saslCommandMechanismFieldName << "SCRAM-SHA-1" <<
- saslCommandUserDBFieldName << dbname <<
- saslCommandUserFieldName << username <<
- saslCommandPasswordFieldName << password_text <<
- saslCommandDigestPasswordFieldName << digestPassword));
- return true;
- } catch(const UserException& ex) {
- if (ex.getCode() != ErrorCodes::AuthenticationFailed)
- throw;
- errmsg = ex.what();
- return false;
- }
- }
-
- bool DBClientWithCommands::_authMongoCR(const string &dbname,
- const string &username,
- const string &password_text,
- BSONObj *info,
- bool digestPassword) {
- string password = password_text;
- if( digestPassword )
- password = createPasswordDigest( username , password_text );
+ const long long id = cursorObj["id"].Long();
- string nonce;
- if( !runCommand(dbname, getnoncecmdobj, *info) ) {
- return false;
- }
- {
- BSONElement e = info->getField("nonce");
- verify( e.type() == String );
- nonce = e.valuestr();
- }
-
- BSONObj authCmd;
- BSONObjBuilder b;
- {
-
- b << "authenticate" << 1 << "nonce" << nonce << "user" << username;
- md5digest d;
- {
- md5_state_t st;
- md5_init(&st);
- md5_append(&st, (const md5_byte_t *) nonce.c_str(), nonce.size() );
- md5_append(&st, (const md5_byte_t *) username.data(), username.length());
- md5_append(&st, (const md5_byte_t *) password.c_str(), password.size() );
- md5_finish(&st, d);
+ if (id != 0) {
+ const std::string ns = cursorObj["ns"].String();
+ unique_ptr<DBClientCursor> cursor = getMore(ns, id, 0, 0);
+ while (cursor->more()) {
+ infos.push_back(cursor->nextSafe().getOwned());
+ }
}
- b << "key" << digestToString( d );
- authCmd = b.done();
- }
- if( runCommand(dbname, authCmd, *info) ) {
- return true;
+ return infos;
}
- return false;
- }
-
- bool DBClientWithCommands::_authX509(const string&dbname,
- const string &username,
- BSONObj *info){
- BSONObj authCmd;
- BSONObjBuilder cmdBuilder;
- cmdBuilder << "authenticate" << 1 << "mechanism" << "MONGODB-X509" << "user" << username;
- authCmd = cmdBuilder.done();
+ // command failed
- if( runCommand(dbname, authCmd, *info) ) {
- return true;
+ int code = res["code"].numberInt();
+ string errmsg = res["errmsg"].valuestrsafe();
+ if (code == ErrorCodes::CommandNotFound || errmsg.find("no such cmd") != string::npos) {
+ // old version of server, ok, fall through to old code
+ } else {
+ uasserted(18630, str::stream() << "listCollections failed: " << res);
}
-
- return false;
}
- void DBClientWithCommands::logout(const string &dbname, BSONObj& info) {
- runCommand(dbname, BSON("logout" << 1), info);
+ // SERVER-14951 filter for old version fallback needs to db qualify the 'name' element
+ BSONObjBuilder fallbackFilter;
+ if (filter.hasField("name") && filter["name"].type() == String) {
+ fallbackFilter.append("name", db + "." + filter["name"].str());
}
+ fallbackFilter.appendElementsUnique(filter);
- BSONObj ismastercmdobj = fromjson("{\"ismaster\":1}");
-
- bool DBClientWithCommands::isMaster(bool& isMaster, BSONObj *info) {
- BSONObj o;
- if ( info == 0 )
- info = &o;
- bool ok = runCommand("admin", ismastercmdobj, *info);
- isMaster = info->getField("ismaster").trueValue();
- return ok;
- }
+ string ns = db + ".system.namespaces";
+ unique_ptr<DBClientCursor> c =
+ query(ns.c_str(), fallbackFilter.obj(), 0, 0, 0, QueryOption_SlaveOk);
+ uassert(28611, str::stream() << "listCollections failed querying " << ns, c.get());
- bool DBClientWithCommands::createCollection(const string &ns, long long size, bool capped, int max, BSONObj *info) {
- verify(!capped||size);
- BSONObj o;
- if ( info == 0 ) info = &o;
+ while (c->more()) {
+ BSONObj obj = c->nextSafe();
+ string ns = obj["name"].valuestr();
+ if (ns.find("$") != string::npos)
+ continue;
BSONObjBuilder b;
- string db = nsToDatabase(ns);
- b.append("create", ns.c_str() + db.length() + 1);
- if ( size ) b.append("size", size);
- if ( capped ) b.append("capped", true);
- if ( max ) b.append("max", max);
- return runCommand(db.c_str(), b.done(), *info);
+ b.append("name", ns.substr(db.size() + 1));
+ b.appendElementsUnique(obj);
+ infos.push_back(b.obj());
}
- bool DBClientWithCommands::copyDatabase(const string &fromdb, const string &todb, const string &fromhost, BSONObj *info) {
- BSONObj o;
- if ( info == 0 ) info = &o;
- BSONObjBuilder b;
- b.append("copydb", 1);
- b.append("fromhost", fromhost);
- b.append("fromdb", fromdb);
- b.append("todb", todb);
- return runCommand("admin", b.done(), *info);
- }
-
- bool DBClientWithCommands::eval(const string &dbname, const string &jscode, BSONObj& info, BSONElement& retValue, BSONObj *args) {
- BSONObjBuilder b;
- b.appendCode("$eval", jscode);
- if ( args )
- b.appendArray("args", *args);
- bool ok = runCommand(dbname, b.done(), info);
- if ( ok )
- retValue = info.getField("retval");
- return ok;
- }
+ return infos;
+}
- bool DBClientWithCommands::eval(const string &dbname, const string &jscode) {
- BSONObj info;
- BSONElement retValue;
- return eval(dbname, jscode, info, retValue);
- }
+bool DBClientWithCommands::exists(const string& ns) {
+ BSONObj filter = BSON("name" << nsToCollectionSubstring(ns));
+ list<BSONObj> results = getCollectionInfos(nsToDatabase(ns), filter);
+ return !results.empty();
+}
- list<string> DBClientWithCommands::getDatabaseNames() {
- BSONObj info;
- uassert(10005, "listdatabases failed", runCommand("admin",
- BSON("listDatabases" << 1),
- info,
- QueryOption_SlaveOk));
- uassert( 10006 , "listDatabases.databases not array" , info["databases"].type() == Array );
-
- list<string> names;
-
- BSONObjIterator i( info["databases"].embeddedObjectUserCheck() );
- while ( i.more() ) {
- names.push_back( i.next().embeddedObjectUserCheck()["name"].valuestr() );
- }
-
- return names;
- }
+/* --- dbclientconnection --- */
- list<string> DBClientWithCommands::getCollectionNames( const string& db ) {
- list<BSONObj> infos = getCollectionInfos( db );
- list<string> names;
- for ( list<BSONObj>::iterator it = infos.begin(); it != infos.end(); ++it ) {
- names.push_back( db + "." + (*it)["name"].valuestr() );
- }
- return names;
+void DBClientConnection::_auth(const BSONObj& params) {
+ if (autoReconnect) {
+ /* note we remember the auth info before we attempt to auth -- if the connection is broken, we will
+ then have it for the next autoreconnect attempt.
+ */
+ authCache[params[saslCommandUserDBFieldName].str()] = params.getOwned();
}
- list<BSONObj> DBClientWithCommands::getCollectionInfos( const string& db,
- const BSONObj& filter ) {
- list<BSONObj> infos;
-
- // first we're going to try the command
- // it was only added in 3.0, so if we're talking to an older server
- // we'll fail back to querying system.namespaces
- // TODO(spencer): remove fallback behavior after 3.0
-
- {
- BSONObj res;
- if (runCommand(db,
- BSON("listCollections" << 1 << "filter" << filter
- << "cursor" << BSONObj()),
- res,
- QueryOption_SlaveOk)) {
- BSONObj cursorObj = res["cursor"].Obj();
- BSONObj collections = cursorObj["firstBatch"].Obj();
- BSONObjIterator it( collections );
- while ( it.more() ) {
- BSONElement e = it.next();
- infos.push_back( e.Obj().getOwned() );
- }
-
- const long long id = cursorObj["id"].Long();
+ DBClientBase::_auth(params);
+}
- if ( id != 0 ) {
- const std::string ns = cursorObj["ns"].String();
- unique_ptr<DBClientCursor> cursor = getMore(ns, id, 0, 0);
- while ( cursor->more() ) {
- infos.push_back(cursor->nextSafe().getOwned());
- }
- }
-
- return infos;
- }
-
- // command failed
-
- int code = res["code"].numberInt();
- string errmsg = res["errmsg"].valuestrsafe();
- if ( code == ErrorCodes::CommandNotFound ||
- errmsg.find( "no such cmd" ) != string::npos ) {
- // old version of server, ok, fall through to old code
- }
- else {
- uasserted( 18630, str::stream() << "listCollections failed: " << res );
- }
-
- }
-
- // SERVER-14951 filter for old version fallback needs to db qualify the 'name' element
- BSONObjBuilder fallbackFilter;
- if ( filter.hasField( "name" ) && filter["name"].type() == String ) {
- fallbackFilter.append( "name", db + "." + filter["name"].str() );
- }
- fallbackFilter.appendElementsUnique( filter );
-
- string ns = db + ".system.namespaces";
- unique_ptr<DBClientCursor> c = query(
- ns.c_str(), fallbackFilter.obj(), 0, 0, 0, QueryOption_SlaveOk);
- uassert(28611, str::stream() << "listCollections failed querying " << ns, c.get());
-
- while ( c->more() ) {
- BSONObj obj = c->nextSafe();
- string ns = obj["name"].valuestr();
- if ( ns.find( "$" ) != string::npos )
- continue;
- BSONObjBuilder b;
- b.append( "name", ns.substr( db.size() + 1 ) );
- b.appendElementsUnique( obj );
- infos.push_back( b.obj() );
- }
-
- return infos;
- }
-
- bool DBClientWithCommands::exists( const string& ns ) {
- BSONObj filter = BSON( "name" << nsToCollectionSubstring( ns ) );
- list<BSONObj> results = getCollectionInfos( nsToDatabase( ns ), filter );
- return !results.empty();
+/** query N objects from the database into an array. makes sense mostly when you want a small number of results. if a huge number, use
+ query() and iterate the cursor.
+ */
+void DBClientInterface::findN(vector<BSONObj>& out,
+ const string& ns,
+ Query query,
+ int nToReturn,
+ int nToSkip,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
+ out.reserve(nToReturn);
+
+ unique_ptr<DBClientCursor> c =
+ this->query(ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions);
+
+ uassert(10276,
+ str::stream() << "DBClientBase::findN: transport error: " << getServerAddress()
+ << " ns: " << ns << " query: " << query.toString(),
+ c.get());
+
+ if (c->hasResultFlag(ResultFlag_ShardConfigStale)) {
+ BSONObj error;
+ c->peekError(&error);
+ throw RecvStaleConfigException("findN stale config", error);
+ }
+
+ for (int i = 0; i < nToReturn; i++) {
+ if (!c->more())
+ break;
+ out.push_back(c->nextSafe().copy());
+ }
+}
+
+BSONObj DBClientInterface::findOne(const string& ns,
+ const Query& query,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
+ vector<BSONObj> v;
+ findN(v, ns, query, 1, 0, fieldsToReturn, queryOptions);
+ return v.empty() ? BSONObj() : v[0];
+}
+
+bool DBClientConnection::connect(const HostAndPort& server, string& errmsg) {
+ _server = server;
+ _serverString = _server.toString();
+ return _connect(errmsg);
+}
+
+bool DBClientConnection::_connect(string& errmsg) {
+ _serverString = _server.toString();
+ _serverAddrString.clear();
+
+ // we keep around SockAddr for connection life -- maybe MessagingPort
+ // requires that?
+ std::unique_ptr<SockAddr> serverSockAddr(new SockAddr(_server.host().c_str(), _server.port()));
+ if (!serverSockAddr->isValid()) {
+ errmsg = str::stream() << "couldn't initialize connection to host "
+ << _server.host().c_str() << ", address is invalid";
+ return false;
}
- /* --- dbclientconnection --- */
-
- void DBClientConnection::_auth(const BSONObj& params) {
-
- if( autoReconnect ) {
- /* note we remember the auth info before we attempt to auth -- if the connection is broken, we will
- then have it for the next autoreconnect attempt.
- */
- authCache[params[saslCommandUserDBFieldName].str()] = params.getOwned();
- }
+ server.reset(serverSockAddr.release());
+ p.reset(new MessagingPort(_so_timeout, _logLevel));
- DBClientBase::_auth(params);
+ if (_server.host().empty()) {
+ errmsg = str::stream() << "couldn't connect to server " << toString() << ", host is empty";
+ return false;
}
- /** query N objects from the database into an array. makes sense mostly when you want a small number of results. if a huge number, use
- query() and iterate the cursor.
- */
- void DBClientInterface::findN(vector<BSONObj>& out, const string& ns, Query query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions) {
- out.reserve(nToReturn);
-
- unique_ptr<DBClientCursor> c =
- this->query(ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions);
-
- uassert( 10276 , str::stream() << "DBClientBase::findN: transport error: " << getServerAddress() << " ns: " << ns << " query: " << query.toString(), c.get() );
-
- if ( c->hasResultFlag( ResultFlag_ShardConfigStale ) ){
- BSONObj error;
- c->peekError( &error );
- throw RecvStaleConfigException( "findN stale config", error );
- }
+ _serverAddrString = server->getAddr();
- for( int i = 0; i < nToReturn; i++ ) {
- if ( !c->more() )
- break;
- out.push_back( c->nextSafe().copy() );
- }
- }
-
- BSONObj DBClientInterface::findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions) {
- vector<BSONObj> v;
- findN(v, ns, query, 1, 0, fieldsToReturn, queryOptions);
- return v.empty() ? BSONObj() : v[0];
+ if (_serverAddrString == "0.0.0.0") {
+ errmsg = str::stream() << "couldn't connect to server " << toString()
+ << ", address resolved to 0.0.0.0";
+ return false;
}
- bool DBClientConnection::connect(const HostAndPort& server, string& errmsg) {
- _server = server;
- _serverString = _server.toString();
- return _connect( errmsg );
+ if (!p->connect(*server)) {
+ errmsg = str::stream() << "couldn't connect to server " << toString()
+ << ", connection attempt failed";
+ _failed = true;
+ return false;
+ } else {
+ LOG(1) << "connected to server " << toString() << endl;
}
- bool DBClientConnection::_connect( string& errmsg ) {
- _serverString = _server.toString();
- _serverAddrString.clear();
-
- // we keep around SockAddr for connection life -- maybe MessagingPort
- // requires that?
- std::unique_ptr<SockAddr> serverSockAddr(new SockAddr(_server.host().c_str(),
- _server.port()));
- if (!serverSockAddr->isValid()) {
- errmsg = str::stream() << "couldn't initialize connection to host "
- << _server.host().c_str() << ", address is invalid";
- return false;
- }
-
- server.reset(serverSockAddr.release());
- p.reset(new MessagingPort( _so_timeout, _logLevel ));
-
- if (_server.host().empty() ) {
- errmsg = str::stream() << "couldn't connect to server " << toString()
- << ", host is empty";
- return false;
- }
-
- _serverAddrString = server->getAddr();
-
- if ( _serverAddrString == "0.0.0.0" ) {
- errmsg = str::stream() << "couldn't connect to server " << toString()
- << ", address resolved to 0.0.0.0";
- return false;
- }
-
- if ( !p->connect(*server) ) {
- errmsg = str::stream() << "couldn't connect to server " << toString()
- << ", connection attempt failed";
- _failed = true;
- return false;
- }
- else {
- LOG( 1 ) << "connected to server " << toString() << endl;
- }
-
#ifdef MONGO_CONFIG_SSL
- int sslModeVal = sslGlobalParams.sslMode.load();
- if (sslModeVal == SSLParams::SSLMode_preferSSL ||
- sslModeVal == SSLParams::SSLMode_requireSSL) {
- return p->secure( sslManager(), _server.host() );
- }
-#endif
-
- return true;
+ int sslModeVal = sslGlobalParams.sslMode.load();
+ if (sslModeVal == SSLParams::SSLMode_preferSSL || sslModeVal == SSLParams::SSLMode_requireSSL) {
+ return p->secure(sslManager(), _server.host());
}
+#endif
- void DBClientConnection::logout(const string& dbname, BSONObj& info){
- authCache.erase(dbname);
- runCommand(dbname, BSON("logout" << 1), info);
- }
+ return true;
+}
- bool DBClientConnection::runCommand(const string &dbname,
- const BSONObj& cmd,
- BSONObj &info,
- int options) {
- if (DBClientWithCommands::runCommand(dbname, cmd, info, options))
- return true;
+void DBClientConnection::logout(const string& dbname, BSONObj& info) {
+ authCache.erase(dbname);
+ runCommand(dbname, BSON("logout" << 1), info);
+}
- if (!_parentReplSetName.empty()) {
- handleNotMasterResponse(info["errmsg"]);
- }
+bool DBClientConnection::runCommand(const string& dbname,
+ const BSONObj& cmd,
+ BSONObj& info,
+ int options) {
+ if (DBClientWithCommands::runCommand(dbname, cmd, info, options))
+ return true;
- return false;
+ if (!_parentReplSetName.empty()) {
+ handleNotMasterResponse(info["errmsg"]);
}
- void DBClientConnection::_checkConnection() {
- if ( !_failed )
- return;
+ return false;
+}
- if ( !autoReconnect )
- throw SocketException( SocketException::FAILED_STATE , toString() );
+void DBClientConnection::_checkConnection() {
+ if (!_failed)
+ return;
- // Don't hammer reconnects, backoff if needed
- autoReconnectBackoff.nextSleepMillis();
+ if (!autoReconnect)
+ throw SocketException(SocketException::FAILED_STATE, toString());
- LOG(_logLevel) << "trying reconnect to " << toString() << endl;
- string errmsg;
- _failed = false;
- if ( ! _connect(errmsg) ) {
- _failed = true;
- LOG(_logLevel) << "reconnect " << toString() << " failed " << errmsg << endl;
- throw SocketException( SocketException::CONNECT_ERROR , toString() );
- }
+ // Don't hammer reconnects, backoff if needed
+ autoReconnectBackoff.nextSleepMillis();
- LOG(_logLevel) << "reconnect " << toString() << " ok" << endl;
- for( map<string, BSONObj>::const_iterator i = authCache.begin(); i != authCache.end(); i++ ) {
- try {
- DBClientConnection::_auth(i->second);
- } catch (UserException& ex) {
- if (ex.getCode() != ErrorCodes::AuthenticationFailed)
- throw;
- LOG(_logLevel) << "reconnect: auth failed " <<
- i->second[saslCommandUserDBFieldName] <<
- i->second[saslCommandUserFieldName] << ' ' <<
- ex.what() << std::endl;
- }
- }
- }
-
- void DBClientConnection::setSoTimeout(double timeout) {
- _so_timeout = timeout;
- if (p) {
- p->setSocketTimeout(timeout);
- }
- }
-
- uint64_t DBClientConnection::getSockCreationMicroSec() const {
- if (p) {
- return p->getSockCreationMicroSec();
- }
- else {
- return INVALID_SOCK_CREATION_TIME;
- }
- }
-
- const uint64_t DBClientBase::INVALID_SOCK_CREATION_TIME =
- static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFULL);
-
- unique_ptr<DBClientCursor> DBClientBase::query(const string &ns, Query query, int nToReturn,
- int nToSkip, const BSONObj *fieldsToReturn, int queryOptions , int batchSize ) {
- unique_ptr<DBClientCursor> c( new DBClientCursor( this,
- ns, query.obj, nToReturn, nToSkip,
- fieldsToReturn, queryOptions , batchSize ) );
- if ( c->init() )
- return c;
- return nullptr;
- }
-
- unique_ptr<DBClientCursor> DBClientBase::getMore( const string &ns, long long cursorId, int nToReturn, int options ) {
- unique_ptr<DBClientCursor> c( new DBClientCursor( this, ns, cursorId, nToReturn, options ) );
- if ( c->init() )
- return c;
- return nullptr;
+ LOG(_logLevel) << "trying reconnect to " << toString() << endl;
+ string errmsg;
+ _failed = false;
+ if (!_connect(errmsg)) {
+ _failed = true;
+ LOG(_logLevel) << "reconnect " << toString() << " failed " << errmsg << endl;
+ throw SocketException(SocketException::CONNECT_ERROR, toString());
}
- struct DBClientFunConvertor {
- void operator()( DBClientCursorBatchIterator &i ) {
- while( i.moreInCurrentBatch() ) {
- _f( i.nextSafe() );
+ LOG(_logLevel) << "reconnect " << toString() << " ok" << endl;
+ for (map<string, BSONObj>::const_iterator i = authCache.begin(); i != authCache.end(); i++) {
+ try {
+ DBClientConnection::_auth(i->second);
+ } catch (UserException& ex) {
+ if (ex.getCode() != ErrorCodes::AuthenticationFailed)
+ throw;
+ LOG(_logLevel) << "reconnect: auth failed " << i->second[saslCommandUserDBFieldName]
+ << i->second[saslCommandUserFieldName] << ' ' << ex.what() << std::endl;
+ }
+ }
+}
+
+void DBClientConnection::setSoTimeout(double timeout) {
+ _so_timeout = timeout;
+ if (p) {
+ p->setSocketTimeout(timeout);
+ }
+}
+
+uint64_t DBClientConnection::getSockCreationMicroSec() const {
+ if (p) {
+ return p->getSockCreationMicroSec();
+ } else {
+ return INVALID_SOCK_CREATION_TIME;
+ }
+}
+
+const uint64_t DBClientBase::INVALID_SOCK_CREATION_TIME =
+ static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFULL);
+
+unique_ptr<DBClientCursor> DBClientBase::query(const string& ns,
+ Query query,
+ int nToReturn,
+ int nToSkip,
+ const BSONObj* fieldsToReturn,
+ int queryOptions,
+ int batchSize) {
+ unique_ptr<DBClientCursor> c(new DBClientCursor(
+ this, ns, query.obj, nToReturn, nToSkip, fieldsToReturn, queryOptions, batchSize));
+ if (c->init())
+ return c;
+ return nullptr;
+}
+
+unique_ptr<DBClientCursor> DBClientBase::getMore(const string& ns,
+ long long cursorId,
+ int nToReturn,
+ int options) {
+ unique_ptr<DBClientCursor> c(new DBClientCursor(this, ns, cursorId, nToReturn, options));
+ if (c->init())
+ return c;
+ return nullptr;
+}
+
+struct DBClientFunConvertor {
+ void operator()(DBClientCursorBatchIterator& i) {
+ while (i.moreInCurrentBatch()) {
+ _f(i.nextSafe());
+ }
+ }
+ stdx::function<void(const BSONObj&)> _f;
+};
+
+unsigned long long DBClientBase::query(stdx::function<void(const BSONObj&)> f,
+ const string& ns,
+ Query query,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
+ DBClientFunConvertor fun;
+ fun._f = f;
+ stdx::function<void(DBClientCursorBatchIterator&)> ptr(fun);
+ return this->query(ptr, ns, query, fieldsToReturn, queryOptions);
+}
+
+unsigned long long DBClientBase::query(stdx::function<void(DBClientCursorBatchIterator&)> f,
+ const string& ns,
+ Query query,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
+ // mask options
+ queryOptions &= (int)(QueryOption_NoCursorTimeout | QueryOption_SlaveOk);
+
+ unique_ptr<DBClientCursor> c(this->query(ns, query, 0, 0, fieldsToReturn, queryOptions));
+ uassert(16090, "socket error for mapping query", c.get());
+
+ unsigned long long n = 0;
+
+ while (c->more()) {
+ DBClientCursorBatchIterator i(*c);
+ f(i);
+ n += i.n();
+ }
+ return n;
+}
+
+unsigned long long DBClientConnection::query(stdx::function<void(DBClientCursorBatchIterator&)> f,
+ const string& ns,
+ Query query,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
+ if (!(availableOptions() & QueryOption_Exhaust)) {
+ return DBClientBase::query(f, ns, query, fieldsToReturn, queryOptions);
+ }
+
+ // mask options
+ queryOptions &= (int)(QueryOption_NoCursorTimeout | QueryOption_SlaveOk);
+ queryOptions |= (int)QueryOption_Exhaust;
+
+ unique_ptr<DBClientCursor> c(this->query(ns, query, 0, 0, fieldsToReturn, queryOptions));
+ uassert(13386, "socket error for mapping query", c.get());
+
+ unsigned long long n = 0;
+
+ try {
+ while (1) {
+ while (c->moreInCurrentBatch()) {
+ DBClientCursorBatchIterator i(*c);
+ f(i);
+ n += i.n();
}
- }
- stdx::function<void(const BSONObj &)> _f;
- };
-
- unsigned long long DBClientBase::query( stdx::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn, int queryOptions ) {
- DBClientFunConvertor fun;
- fun._f = f;
- stdx::function<void(DBClientCursorBatchIterator &)> ptr( fun );
- return this->query( ptr, ns, query, fieldsToReturn, queryOptions );
- }
- unsigned long long DBClientBase::query(
- stdx::function<void(DBClientCursorBatchIterator &)> f,
- const string& ns,
- Query query,
- const BSONObj *fieldsToReturn,
- int queryOptions ) {
-
- // mask options
- queryOptions &= (int)( QueryOption_NoCursorTimeout | QueryOption_SlaveOk );
-
- unique_ptr<DBClientCursor> c( this->query(ns, query, 0, 0, fieldsToReturn, queryOptions) );
- uassert( 16090, "socket error for mapping query", c.get() );
-
- unsigned long long n = 0;
+ if (c->getCursorId() == 0)
+ break;
- while ( c->more() ) {
- DBClientCursorBatchIterator i( *c );
- f( i );
- n += i.n();
+ c->exhaustReceiveMore();
}
- return n;
+ } catch (std::exception&) {
+ /* connection CANNOT be used anymore as more data may be on the way from the server.
+ we have to reconnect.
+ */
+ _failed = true;
+ p->shutdown();
+ throw;
}
- unsigned long long DBClientConnection::query(
- stdx::function<void(DBClientCursorBatchIterator &)> f,
- const string& ns,
- Query query,
- const BSONObj *fieldsToReturn,
- int queryOptions ) {
-
- if ( ! ( availableOptions() & QueryOption_Exhaust ) ) {
- return DBClientBase::query( f, ns, query, fieldsToReturn, queryOptions );
- }
-
- // mask options
- queryOptions &= (int)( QueryOption_NoCursorTimeout | QueryOption_SlaveOk );
- queryOptions |= (int)QueryOption_Exhaust;
-
- unique_ptr<DBClientCursor> c( this->query(ns, query, 0, 0, fieldsToReturn, queryOptions) );
- uassert( 13386, "socket error for mapping query", c.get() );
-
- unsigned long long n = 0;
+ return n;
+}
- try {
- while( 1 ) {
- while( c->moreInCurrentBatch() ) {
- DBClientCursorBatchIterator i( *c );
- f( i );
- n += i.n();
- }
+void DBClientBase::insert(const string& ns, BSONObj obj, int flags) {
+ Message toSend;
- if( c->getCursorId() == 0 )
- break;
+ BufBuilder b;
- c->exhaustReceiveMore();
- }
- }
- catch(std::exception&) {
- /* connection CANNOT be used anymore as more data may be on the way from the server.
- we have to reconnect.
- */
- _failed = true;
- p->shutdown();
- throw;
- }
+ int reservedFlags = 0;
+ if (flags & InsertOption_ContinueOnError)
+ reservedFlags |= Reserved_InsertOption_ContinueOnError;
- return n;
- }
+ if (flags & WriteOption_FromWriteback)
+ reservedFlags |= Reserved_FromWriteback;
- void DBClientBase::insert( const string & ns , BSONObj obj , int flags) {
- Message toSend;
+ b.appendNum(reservedFlags);
+ b.appendStr(ns);
+ obj.appendSelfToBufBuilder(b);
- BufBuilder b;
+ toSend.setData(dbInsert, b.buf(), b.len());
- int reservedFlags = 0;
- if( flags & InsertOption_ContinueOnError )
- reservedFlags |= Reserved_InsertOption_ContinueOnError;
+ say(toSend);
+}
- if( flags & WriteOption_FromWriteback )
- reservedFlags |= Reserved_FromWriteback;
+// TODO: Merge with other insert implementation?
+void DBClientBase::insert(const string& ns, const vector<BSONObj>& v, int flags) {
+ Message toSend;
- b.appendNum( reservedFlags );
- b.appendStr( ns );
- obj.appendSelfToBufBuilder( b );
+ BufBuilder b;
- toSend.setData( dbInsert , b.buf() , b.len() );
+ int reservedFlags = 0;
+ if (flags & InsertOption_ContinueOnError)
+ reservedFlags |= Reserved_InsertOption_ContinueOnError;
- say( toSend );
+ if (flags & WriteOption_FromWriteback) {
+ reservedFlags |= Reserved_FromWriteback;
+ flags ^= WriteOption_FromWriteback;
}
- // TODO: Merge with other insert implementation?
- void DBClientBase::insert( const string & ns , const vector< BSONObj > &v , int flags) {
- Message toSend;
-
- BufBuilder b;
+ b.appendNum(reservedFlags);
+ b.appendStr(ns);
+ for (vector<BSONObj>::const_iterator i = v.begin(); i != v.end(); ++i)
+ i->appendSelfToBufBuilder(b);
- int reservedFlags = 0;
- if( flags & InsertOption_ContinueOnError )
- reservedFlags |= Reserved_InsertOption_ContinueOnError;
-
- if( flags & WriteOption_FromWriteback ){
- reservedFlags |= Reserved_FromWriteback;
- flags ^= WriteOption_FromWriteback;
- }
+ toSend.setData(dbInsert, b.buf(), b.len());
- b.appendNum( reservedFlags );
- b.appendStr( ns );
- for( vector< BSONObj >::const_iterator i = v.begin(); i != v.end(); ++i )
- i->appendSelfToBufBuilder( b );
+ say(toSend);
+}
- toSend.setData( dbInsert, b.buf(), b.len() );
+void DBClientBase::remove(const string& ns, Query obj, bool justOne) {
+ int flags = 0;
+ if (justOne)
+ flags |= RemoveOption_JustOne;
+ remove(ns, obj, flags);
+}
- say( toSend );
- }
+void DBClientBase::remove(const string& ns, Query obj, int flags) {
+ Message toSend;
- void DBClientBase::remove( const string & ns , Query obj , bool justOne ) {
- int flags = 0;
- if( justOne ) flags |= RemoveOption_JustOne;
- remove( ns, obj, flags );
+ BufBuilder b;
+ int reservedFlags = 0;
+ if (flags & WriteOption_FromWriteback) {
+ reservedFlags |= WriteOption_FromWriteback;
+ flags ^= WriteOption_FromWriteback;
}
- void DBClientBase::remove( const string & ns , Query obj , int flags ) {
- Message toSend;
+ b.appendNum(reservedFlags);
+ b.appendStr(ns);
+ b.appendNum(flags);
- BufBuilder b;
- int reservedFlags = 0;
- if( flags & WriteOption_FromWriteback ){
- reservedFlags |= WriteOption_FromWriteback;
- flags ^= WriteOption_FromWriteback;
- }
+ obj.obj.appendSelfToBufBuilder(b);
- b.appendNum( reservedFlags );
- b.appendStr( ns );
- b.appendNum( flags );
+ toSend.setData(dbDelete, b.buf(), b.len());
- obj.obj.appendSelfToBufBuilder( b );
+ say(toSend);
+}
- toSend.setData( dbDelete , b.buf() , b.len() );
+void DBClientBase::update(const string& ns, Query query, BSONObj obj, bool upsert, bool multi) {
+ int flags = 0;
+ if (upsert)
+ flags |= UpdateOption_Upsert;
+ if (multi)
+ flags |= UpdateOption_Multi;
+ update(ns, query, obj, flags);
+}
- say( toSend );
- }
+void DBClientBase::update(const string& ns, Query query, BSONObj obj, int flags) {
+ BufBuilder b;
- void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert, bool multi ) {
- int flags = 0;
- if ( upsert ) flags |= UpdateOption_Upsert;
- if ( multi ) flags |= UpdateOption_Multi;
- update( ns, query, obj, flags );
+ int reservedFlags = 0;
+ if (flags & WriteOption_FromWriteback) {
+ reservedFlags |= Reserved_FromWriteback;
+ flags ^= WriteOption_FromWriteback;
}
- void DBClientBase::update( const string & ns , Query query , BSONObj obj , int flags ) {
+ b.appendNum(reservedFlags); // reserved
+ b.appendStr(ns);
+ b.appendNum(flags);
- BufBuilder b;
+ query.obj.appendSelfToBufBuilder(b);
+ obj.appendSelfToBufBuilder(b);
- int reservedFlags = 0;
- if( flags & WriteOption_FromWriteback ){
- reservedFlags |= Reserved_FromWriteback;
- flags ^= WriteOption_FromWriteback;
- }
-
- b.appendNum( reservedFlags ); // reserved
- b.appendStr( ns );
- b.appendNum( flags );
+ Message toSend;
+ toSend.setData(dbUpdate, b.buf(), b.len());
- query.obj.appendSelfToBufBuilder( b );
- obj.appendSelfToBufBuilder( b );
+ say(toSend);
+}
- Message toSend;
- toSend.setData( dbUpdate , b.buf() , b.len() );
+list<BSONObj> DBClientWithCommands::getIndexSpecs(const string& ns, int options) {
+ list<BSONObj> specs;
- say( toSend );
- }
+ {
+ BSONObj cmd = BSON("listIndexes" << nsToCollectionSubstring(ns) << "cursor" << BSONObj());
- list<BSONObj> DBClientWithCommands::getIndexSpecs( const string &ns, int options ) {
- list<BSONObj> specs;
-
- {
- BSONObj cmd = BSON(
- "listIndexes" << nsToCollectionSubstring( ns ) <<
- "cursor" << BSONObj()
- );
-
- BSONObj res;
- if ( runCommand( nsToDatabase( ns ), cmd, res, options ) ) {
- BSONObj cursorObj = res["cursor"].Obj();
- BSONObjIterator i( cursorObj["firstBatch"].Obj() );
- while ( i.more() ) {
- specs.push_back( i.next().Obj().getOwned() );
- }
+ BSONObj res;
+ if (runCommand(nsToDatabase(ns), cmd, res, options)) {
+ BSONObj cursorObj = res["cursor"].Obj();
+ BSONObjIterator i(cursorObj["firstBatch"].Obj());
+ while (i.more()) {
+ specs.push_back(i.next().Obj().getOwned());
+ }
- const long long id = cursorObj["id"].Long();
+ const long long id = cursorObj["id"].Long();
- if ( id != 0 ) {
- const std::string ns = cursorObj["ns"].String();
- unique_ptr<DBClientCursor> cursor = getMore(ns, id, 0, 0);
- while ( cursor->more() ) {
- specs.push_back(cursor->nextSafe().getOwned());
- }
+ if (id != 0) {
+ const std::string ns = cursorObj["ns"].String();
+ unique_ptr<DBClientCursor> cursor = getMore(ns, id, 0, 0);
+ while (cursor->more()) {
+ specs.push_back(cursor->nextSafe().getOwned());
}
-
- return specs;
- }
- int code = res["code"].numberInt();
- string errmsg = res["errmsg"].valuestrsafe();
- if ( code == ErrorCodes::CommandNotFound ||
- errmsg.find( "no such cmd" ) != string::npos ) {
- // old version of server, ok, fall through to old code
- }
- else if ( code == ErrorCodes::NamespaceNotFound ) {
- return specs;
}
- else {
- uasserted( 18631, str::stream() << "listIndexes failed: " << res );
- }
- }
-
- // fallback to querying system.indexes
- // TODO(spencer): Remove fallback behavior after 3.0
- unique_ptr<DBClientCursor> cursor = query(NamespaceString(ns).getSystemIndexesCollection(),
- BSON("ns" << ns), 0, 0, 0, options);
- uassert(28612, str::stream() << "listIndexes failed querying " << ns, cursor.get());
- while ( cursor->more() ) {
- BSONObj spec = cursor->nextSafe();
- specs.push_back( spec.getOwned() );
+ return specs;
}
- return specs;
- }
-
-
- void DBClientWithCommands::dropIndex( const string& ns , BSONObj keys ) {
- dropIndex( ns , genIndexName( keys ) );
- }
-
-
- void DBClientWithCommands::dropIndex( const string& ns , const string& indexName ) {
- BSONObj info;
- if ( ! runCommand( nsToDatabase( ns ) ,
- BSON( "deleteIndexes" << nsToCollectionSubstring(ns) << "index" << indexName ) ,
- info ) ) {
- LOG(_logLevel) << "dropIndex failed: " << info << endl;
- uassert( 10007 , "dropIndex failed" , 0 );
+ int code = res["code"].numberInt();
+ string errmsg = res["errmsg"].valuestrsafe();
+ if (code == ErrorCodes::CommandNotFound || errmsg.find("no such cmd") != string::npos) {
+ // old version of server, ok, fall through to old code
+ } else if (code == ErrorCodes::NamespaceNotFound) {
+ return specs;
+ } else {
+ uasserted(18631, str::stream() << "listIndexes failed: " << res);
}
}
- void DBClientWithCommands::dropIndexes( const string& ns ) {
- BSONObj info;
- uassert( 10008,
- "dropIndexes failed",
- runCommand( nsToDatabase( ns ),
- BSON( "deleteIndexes" << nsToCollectionSubstring(ns) << "index" << "*"),
- info )
- );
- }
+ // fallback to querying system.indexes
+ // TODO(spencer): Remove fallback behavior after 3.0
+ unique_ptr<DBClientCursor> cursor =
+ query(NamespaceString(ns).getSystemIndexesCollection(), BSON("ns" << ns), 0, 0, 0, options);
+ uassert(28612, str::stream() << "listIndexes failed querying " << ns, cursor.get());
- void DBClientWithCommands::reIndex( const string& ns ) {
- BSONObj info;
- uassert(18908,
- str::stream() << "reIndex failed: " << info,
- runCommand(nsToDatabase(ns),
- BSON("reIndex" << nsToCollectionSubstring(ns)),
- info)
- );
+ while (cursor->more()) {
+ BSONObj spec = cursor->nextSafe();
+ specs.push_back(spec.getOwned());
}
+ return specs;
+}
- string DBClientWithCommands::genIndexName( const BSONObj& keys ) {
- stringstream ss;
+void DBClientWithCommands::dropIndex(const string& ns, BSONObj keys) {
+ dropIndex(ns, genIndexName(keys));
+}
- bool first = 1;
- for ( BSONObjIterator i(keys); i.more(); ) {
- BSONElement f = i.next();
- if ( first )
- first = 0;
- else
- ss << "_";
-
- ss << f.fieldName() << "_";
- if( f.isNumber() )
- ss << f.numberInt();
- else
- ss << f.str(); //this should match up with shell command
- }
- return ss.str();
+void DBClientWithCommands::dropIndex(const string& ns, const string& indexName) {
+ BSONObj info;
+ if (!runCommand(nsToDatabase(ns),
+ BSON("deleteIndexes" << nsToCollectionSubstring(ns) << "index" << indexName),
+ info)) {
+ LOG(_logLevel) << "dropIndex failed: " << info << endl;
+ uassert(10007, "dropIndex failed", 0);
}
+}
- void DBClientWithCommands::ensureIndex( const string &ns,
- BSONObj keys,
- bool unique,
- const string & name,
- bool background,
- int version,
- int ttl ) {
- BSONObjBuilder toSave;
- toSave.append( "ns" , ns );
- toSave.append( "key" , keys );
-
- string cacheKey(ns);
- cacheKey += "--";
-
- if ( name != "" ) {
- toSave.append( "name" , name );
- cacheKey += name;
- }
- else {
- string nn = genIndexName( keys );
- toSave.append( "name" , nn );
- cacheKey += nn;
- }
+void DBClientWithCommands::dropIndexes(const string& ns) {
+ BSONObj info;
+ uassert(10008,
+ "dropIndexes failed",
+ runCommand(nsToDatabase(ns),
+ BSON("deleteIndexes" << nsToCollectionSubstring(ns) << "index"
+ << "*"),
+ info));
+}
- if( version >= 0 )
- toSave.append("v", version);
+void DBClientWithCommands::reIndex(const string& ns) {
+ BSONObj info;
+ uassert(18908,
+ str::stream() << "reIndex failed: " << info,
+ runCommand(nsToDatabase(ns), BSON("reIndex" << nsToCollectionSubstring(ns)), info));
+}
- if ( unique )
- toSave.appendBool( "unique", unique );
- if( background )
- toSave.appendBool( "background", true );
+string DBClientWithCommands::genIndexName(const BSONObj& keys) {
+ stringstream ss;
- if ( ttl > 0 )
- toSave.append( "expireAfterSeconds", ttl );
+ bool first = 1;
+ for (BSONObjIterator i(keys); i.more();) {
+ BSONElement f = i.next();
- insert(NamespaceString(ns).getSystemIndexesCollection(), toSave.obj());
+ if (first)
+ first = 0;
+ else
+ ss << "_";
+
+ ss << f.fieldName() << "_";
+ if (f.isNumber())
+ ss << f.numberInt();
+ else
+ ss << f.str(); // this should match up with shell command
+ }
+ return ss.str();
+}
+
+void DBClientWithCommands::ensureIndex(const string& ns,
+ BSONObj keys,
+ bool unique,
+ const string& name,
+ bool background,
+ int version,
+ int ttl) {
+ BSONObjBuilder toSave;
+ toSave.append("ns", ns);
+ toSave.append("key", keys);
+
+ string cacheKey(ns);
+ cacheKey += "--";
+
+ if (name != "") {
+ toSave.append("name", name);
+ cacheKey += name;
+ } else {
+ string nn = genIndexName(keys);
+ toSave.append("name", nn);
+ cacheKey += nn;
+ }
+
+ if (version >= 0)
+ toSave.append("v", version);
+
+ if (unique)
+ toSave.appendBool("unique", unique);
+
+ if (background)
+ toSave.appendBool("background", true);
+
+ if (ttl > 0)
+ toSave.append("expireAfterSeconds", ttl);
+
+ insert(NamespaceString(ns).getSystemIndexesCollection(), toSave.obj());
+}
+
+/* -- DBClientCursor ---------------------------------------------- */
+void assembleQueryRequest(const string& ns,
+ BSONObj query,
+ int nToReturn,
+ int nToSkip,
+ const BSONObj* fieldsToReturn,
+ int queryOptions,
+ Message& toSend) {
+ if (kDebugBuild) {
+ massert(10337, (string) "object not valid assembleRequest query", query.isValid());
+ }
+
+ // see query.h for the protocol we are using here.
+ BufBuilder b;
+ int opts = queryOptions;
+ b.appendNum(opts);
+ b.appendStr(ns);
+ b.appendNum(nToSkip);
+ b.appendNum(nToReturn);
+ query.appendSelfToBufBuilder(b);
+ if (fieldsToReturn)
+ fieldsToReturn->appendSelfToBufBuilder(b);
+ toSend.setData(dbQuery, b.buf(), b.len());
+}
+
+DBClientConnection::DBClientConnection(bool _autoReconnect, double so_timeout)
+ : _failed(false),
+ autoReconnect(_autoReconnect),
+ autoReconnectBackoff(1000, 2000),
+ _so_timeout(so_timeout) {
+ _numConnections.fetchAndAdd(1);
+}
+
+void DBClientConnection::say(Message& toSend, bool isRetry, string* actualServer) {
+ checkConnection();
+ try {
+ port().say(toSend);
+ } catch (SocketException&) {
+ _failed = true;
+ throw;
}
+}
- /* -- DBClientCursor ---------------------------------------------- */
- void assembleQueryRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
- if (kDebugBuild) {
- massert( 10337 , (string)"object not valid assembleRequest query" , query.isValid() );
- }
+void DBClientConnection::sayPiggyBack(Message& toSend) {
+ port().piggyBack(toSend);
+}
- // see query.h for the protocol we are using here.
- BufBuilder b;
- int opts = queryOptions;
- b.appendNum(opts);
- b.appendStr(ns);
- b.appendNum(nToSkip);
- b.appendNum(nToReturn);
- query.appendSelfToBufBuilder(b);
- if ( fieldsToReturn )
- fieldsToReturn->appendSelfToBufBuilder(b);
- toSend.setData(dbQuery, b.buf(), b.len());
+bool DBClientConnection::recv(Message& m) {
+ if (port().recv(m)) {
+ return true;
}
- DBClientConnection::DBClientConnection(bool _autoReconnect, double so_timeout):
- _failed(false),
- autoReconnect(_autoReconnect),
- autoReconnectBackoff(1000, 2000),
- _so_timeout(so_timeout) {
- _numConnections.fetchAndAdd(1);
- }
+ _failed = true;
+ return false;
+}
- void DBClientConnection::say( Message &toSend, bool isRetry , string * actualServer ) {
- checkConnection();
- try {
- port().say( toSend );
- }
- catch( SocketException & ) {
+bool DBClientConnection::call(Message& toSend,
+ Message& response,
+ bool assertOk,
+ string* actualServer) {
+ /* todo: this is very ugly messagingport::call returns an error code AND can throw
+ an exception. we should make it return void and just throw an exception anytime
+ it fails
+ */
+ checkConnection();
+ try {
+ if (!port().call(toSend, response)) {
_failed = true;
- throw;
- }
- }
-
- void DBClientConnection::sayPiggyBack( Message &toSend ) {
- port().piggyBack( toSend );
- }
+ if (assertOk)
+ uasserted(10278,
+ str::stream() << "dbclient error communicating with server: "
+ << getServerAddress());
- bool DBClientConnection::recv( Message &m ) {
- if (port().recv(m)) {
- return true;
+ return false;
}
-
+ } catch (SocketException&) {
_failed = true;
- return false;
+ throw;
}
+ return true;
+}
- bool DBClientConnection::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
- /* todo: this is very ugly messagingport::call returns an error code AND can throw
- an exception. we should make it return void and just throw an exception anytime
- it fails
- */
- checkConnection();
- try {
- if ( !port().call(toSend, response) ) {
- _failed = true;
- if ( assertOk )
- uasserted( 10278 , str::stream() << "dbclient error communicating with server: " << getServerAddress() );
+BSONElement getErrField(const BSONObj& o) {
+ BSONElement first = o.firstElement();
+ if (strcmp(first.fieldName(), "$err") == 0)
+ return first;
- return false;
- }
- }
- catch( SocketException & ) {
- _failed = true;
- throw;
+ // temp - will be DEV only later
+ /*DEV*/
+ if (1) {
+ BSONElement e = o["$err"];
+ if (!e.eoo()) {
+ wassert(false);
}
- return true;
+ return e;
}
- BSONElement getErrField(const BSONObj& o) {
- BSONElement first = o.firstElement();
- if( strcmp(first.fieldName(), "$err") == 0 )
- return first;
-
- // temp - will be DEV only later
- /*DEV*/
- if( 1 ) {
- BSONElement e = o["$err"];
- if( !e.eoo() ) {
- wassert(false);
- }
- return e;
- }
+ return BSONElement();
+}
- return BSONElement();
- }
+bool hasErrField(const BSONObj& o) {
+ return !getErrField(o).eoo();
+}
- bool hasErrField( const BSONObj& o ){
- return ! getErrField( o ).eoo();
- }
+void DBClientConnection::checkResponse(const char* data, int nReturned, bool* retry, string* host) {
+ /* check for errors. the only one we really care about at
+ * this stage is "not master"
+ */
- void DBClientConnection::checkResponse( const char *data, int nReturned, bool* retry, string* host ) {
- /* check for errors. the only one we really care about at
- * this stage is "not master"
- */
-
- *retry = false;
- *host = _serverString;
-
- if (!_parentReplSetName.empty() && nReturned) {
- verify(data);
- BSONObj bsonView(data);
- handleNotMasterResponse(getErrField(bsonView));
- }
- }
+ *retry = false;
+ *host = _serverString;
- void DBClientConnection::killCursor( long long cursorId ) {
- StackBufBuilder b;
- b.appendNum( (int)0 ); // reserved
- b.appendNum( (int)1 ); // number
- b.appendNum( cursorId );
-
- Message m;
- m.setData( dbKillCursors , b.buf() , b.len() );
-
- if ( _lazyKillCursor )
- sayPiggyBack( m );
- else
- say(m);
+ if (!_parentReplSetName.empty() && nReturned) {
+ verify(data);
+ BSONObj bsonView(data);
+ handleNotMasterResponse(getErrField(bsonView));
}
+}
- void DBClientConnection::setParentReplSetName(const string& replSetName) {
- _parentReplSetName = replSetName;
- }
+void DBClientConnection::killCursor(long long cursorId) {
+ StackBufBuilder b;
+ b.appendNum((int)0); // reserved
+ b.appendNum((int)1); // number
+ b.appendNum(cursorId);
- void DBClientConnection::handleNotMasterResponse(const BSONElement& elemToCheck) {
- if (!isNotMasterErrorString(elemToCheck)) {
- return;
- }
+ Message m;
+ m.setData(dbKillCursors, b.buf(), b.len());
- MONGO_LOG_COMPONENT(1, logger::LogComponent::kReplication)
- << "got not master from: " << _serverString
- << " of repl set: " << _parentReplSetName;
+ if (_lazyKillCursor)
+ sayPiggyBack(m);
+ else
+ say(m);
+}
- ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(_parentReplSetName);
- if (monitor) {
- monitor->failedHost(_server);
- }
+void DBClientConnection::setParentReplSetName(const string& replSetName) {
+ _parentReplSetName = replSetName;
+}
- _failed = true;
+void DBClientConnection::handleNotMasterResponse(const BSONElement& elemToCheck) {
+ if (!isNotMasterErrorString(elemToCheck)) {
+ return;
}
- AtomicInt32 DBClientConnection::_numConnections;
- bool DBClientConnection::_lazyKillCursor = true;
+ MONGO_LOG_COMPONENT(1, logger::LogComponent::kReplication)
+ << "got not master from: " << _serverString << " of repl set: " << _parentReplSetName;
+ ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(_parentReplSetName);
+ if (monitor) {
+ monitor->failedHost(_server);
+ }
- /** @return the database name portion of an ns string */
- string nsGetDB( const string &ns ) {
- string::size_type pos = ns.find( "." );
- if ( pos == string::npos )
- return ns;
+ _failed = true;
+}
- return ns.substr( 0 , pos );
- }
+AtomicInt32 DBClientConnection::_numConnections;
+bool DBClientConnection::_lazyKillCursor = true;
- /** @return the collection name portion of an ns string */
- string nsGetCollection( const string &ns ) {
- string::size_type pos = ns.find( "." );
- if ( pos == string::npos )
- return "";
- return ns.substr( pos + 1 );
- }
+/** @return the database name portion of an ns string */
+string nsGetDB(const string& ns) {
+ string::size_type pos = ns.find(".");
+ if (pos == string::npos)
+ return ns;
+
+ return ns.substr(0, pos);
+}
+
+/** @return the collection name portion of an ns string */
+string nsGetCollection(const string& ns) {
+ string::size_type pos = ns.find(".");
+ if (pos == string::npos)
+ return "";
+
+ return ns.substr(pos + 1);
+}
-} // namespace mongo
+} // namespace mongo