Files
mongo/client/dbclient.cpp

1088 lines
34 KiB
C++
Raw Normal View History

// dbclient.cpp - connect to a Mongo database as a database, from C++
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2008-07-27 18:36:47 -04:00
2010-04-27 15:27:52 -04:00
#include "pch.h"
2008-07-27 18:36:47 -04:00
#include "dbclient.h"
2010-04-24 18:25:58 -04:00
#include "../bson/util/builder.h"
2008-10-19 17:46:53 -05:00
#include "../db/jsobj.h"
#include "../db/json.h"
#include "../db/instance.h"
#include "../util/md5.hpp"
2009-05-11 14:57:26 -04:00
#include "../db/dbmessage.h"
2009-08-25 10:24:44 -04:00
#include "../db/cmdline.h"
#include "connpool.h"
2010-04-29 21:45:58 -04:00
#include "../s/util.h"
#include "syncclusterconnection.h"
2008-10-19 11:17:25 -05:00
2009-01-14 17:09:51 -05:00
namespace mongo {
2011-02-14 14:13:06 -05:00
void ConnectionString::_fillServers( string s ) {
{
string::size_type idx = s.find( '/' );
if ( idx != string::npos ) {
_setName = s.substr( 0 , idx );
s = s.substr( idx + 1 );
_type = SET;
}
}
string::size_type idx;
while ( ( idx = s.find( ',' ) ) != string::npos ) {
_servers.push_back( s.substr( 0 , idx ) );
s = s.substr( idx + 1 );
}
_servers.push_back( s );
}
void ConnectionString::_finishInit() {
stringstream ss;
if ( _type == SET )
ss << _setName << "/";
for ( unsigned i=0; i<_servers.size(); i++ ) {
if ( i > 0 )
ss << ",";
ss << _servers[i].toString();
}
_string = ss.str();
}
DBClientBase* ConnectionString::connect( string& errmsg, double socketTimeout ) const {
2011-01-04 00:40:41 -05:00
switch ( _type ) {
case MASTER: {
DBClientConnection * c = new DBClientConnection(true);
c->setSoTimeout( socketTimeout );
log(1) << "creating new connection to:" << _servers[0] << endl;
if ( ! c->connect( _servers[0] , errmsg ) ) {
delete c;
return 0;
}
log(1) << "connected connection!" << endl;
return c;
}
2011-01-04 00:40:41 -05:00
case PAIR:
case SET: {
DBClientReplicaSet * set = new DBClientReplicaSet( _setName , _servers , socketTimeout );
2011-01-04 00:40:41 -05:00
if( ! set->connect() ) {
delete set;
errmsg = "connect failed to set ";
errmsg += toString();
return 0;
}
return set;
}
2011-01-04 00:40:41 -05:00
case SYNC: {
// TODO , don't copy
list<HostAndPort> l;
for ( unsigned i=0; i<_servers.size(); i++ )
l.push_back( _servers[i] );
SyncClusterConnection* c = new SyncClusterConnection( l, socketTimeout );
return c;
}
2011-01-04 00:40:41 -05:00
case INVALID:
throw UserException( 13421 , "trying to connect to invalid ConnectionString" );
break;
}
2011-01-04 00:40:41 -05:00
assert( 0 );
return 0;
}
2011-01-04 00:40:41 -05:00
ConnectionString ConnectionString::parse( const string& host , string& errmsg ) {
string::size_type i = host.find( '/' );
2011-01-04 00:40:41 -05:00
if ( i != string::npos && i != 0) {
// replica set
return ConnectionString( SET , host.substr( i + 1 ) , host.substr( 0 , i ) );
}
2010-12-14 02:40:37 -05:00
int numCommas = str::count( host , ',' );
2011-01-04 00:40:41 -05:00
if( numCommas == 0 )
return ConnectionString( HostAndPort( host ) );
2011-01-04 00:40:41 -05:00
if ( numCommas == 1 )
return ConnectionString( PAIR , host );
if ( numCommas == 2 )
return ConnectionString( SYNC , host );
2011-01-04 00:40:41 -05:00
errmsg = (string)"invalid hostname [" + host + "]";
return ConnectionString(); // INVALID
}
2011-01-04 00:40:41 -05:00
string ConnectionString::typeToString( ConnectionType type ) {
switch ( type ) {
2010-12-01 23:53:52 -05:00
case INVALID:
return "invalid";
case MASTER:
return "master";
case PAIR:
return "pair";
case SET:
return "set";
case SYNC:
return "sync";
}
assert(0);
return "";
}
2011-01-04 00:40:41 -05:00
Query& Query::where(const string &jscode, BSONObj scope) {
/* use where() before sort() and hint() and explain(), else this will assert. */
2010-04-17 17:22:19 -04:00
assert( ! isComplex() );
BSONObjBuilder b;
b.appendElements(obj);
b.appendWhere(jscode, scope);
2009-02-09 13:04:32 -05:00
obj = b.obj();
return *this;
}
void Query::makeComplex() {
2010-04-17 17:22:19 -04:00
if ( isComplex() )
return;
2009-01-29 18:38:35 -05:00
BSONObjBuilder b;
b.append( "query", obj );
2009-02-09 13:04:32 -05:00
obj = b.obj();
}
2011-01-04 00:40:41 -05:00
Query& Query::sort(const BSONObj& s) {
appendComplex( "orderby", s );
2011-01-04 00:40:41 -05:00
return *this;
2009-01-29 18:38:35 -05:00
}
2009-01-30 13:13:49 -05:00
Query& Query::hint(BSONObj keyPattern) {
appendComplex( "$hint", keyPattern );
2011-01-04 00:40:41 -05:00
return *this;
2009-01-30 12:46:49 -05:00
}
Query& Query::explain() {
appendComplex( "$explain", true );
2011-01-04 00:40:41 -05:00
return *this;
2009-01-30 12:46:49 -05:00
}
2011-01-04 00:40:41 -05:00
Query& Query::snapshot() {
appendComplex( "$snapshot", true );
2011-01-04 00:40:41 -05:00
return *this;
}
2011-01-04 00:40:41 -05:00
2009-05-22 15:05:13 -04:00
Query& Query::minKey( const BSONObj &val ) {
appendComplex( "$min", val );
2011-01-04 00:40:41 -05:00
return *this;
}
2009-05-22 15:05:13 -04:00
Query& Query::maxKey( const BSONObj &val ) {
appendComplex( "$max", val );
2011-01-04 00:40:41 -05:00
return *this;
}
2011-01-04 00:40:41 -05:00
bool Query::isComplex( bool * hasDollar ) const {
if ( obj.hasElement( "query" ) ) {
2010-04-17 17:22:19 -04:00
if ( hasDollar )
hasDollar[0] = false;
return true;
}
2011-01-04 00:40:41 -05:00
if ( obj.hasElement( "$query" ) ) {
2010-04-17 17:22:19 -04:00
if ( hasDollar )
hasDollar[0] = true;
return true;
}
return false;
2009-02-23 14:31:52 -05:00
}
2011-01-04 00:40:41 -05:00
2009-02-23 14:31:52 -05:00
BSONObj Query::getFilter() const {
2010-04-17 17:22:19 -04:00
bool hasDollar;
if ( ! isComplex( &hasDollar ) )
2009-02-23 14:31:52 -05:00
return obj;
2011-01-04 00:40:41 -05:00
2010-04-17 17:22:19 -04:00
return obj.getObjectField( hasDollar ? "$query" : "query" );
2009-02-23 14:31:52 -05:00
}
BSONObj Query::getSort() const {
if ( ! isComplex() )
2009-03-19 16:23:04 -04:00
return BSONObj();
2010-05-28 18:11:52 -04:00
BSONObj ret = obj.getObjectField( "orderby" );
if (ret.isEmpty())
ret = obj.getObjectField( "$orderby" );
return ret;
2009-02-23 14:31:52 -05:00
}
BSONObj Query::getHint() const {
if ( ! isComplex() )
2009-03-19 16:23:04 -04:00
return BSONObj();
2009-02-23 14:31:52 -05:00
return obj.getObjectField( "$hint" );
}
bool Query::isExplain() const {
return isComplex() && obj.getBoolField( "$explain" );
}
2011-01-04 00:40:41 -05:00
string Query::toString() const {
2009-02-11 10:37:04 -05:00
return obj.toString();
}
/* --- dbclientcommands --- */
2008-10-19 11:17:25 -05:00
2010-03-11 11:40:27 -05:00
bool DBClientWithCommands::isOk(const BSONObj& o) {
return o["ok"].trueValue();
}
2009-01-10 18:17:23 -05:00
bool DBClientWithCommands::isNotMasterErrorString( const BSONElement& e ) {
return e.type() == String && str::contains( e.valuestr() , "not master" );
}
2010-07-14 10:28:38 -07:00
enum QueryOptions DBClientWithCommands::availableOptions() {
if ( !_haveCachedAvailableOptions ) {
BSONObj ret;
if ( runCommand( "admin", BSON( "availablequeryoptions" << 1 ), ret ) ) {
_cachedAvailableOptions = ( enum QueryOptions )( ret.getIntField( "options" ) );
}
_haveCachedAvailableOptions = true;
}
return _cachedAvailableOptions;
}
2011-01-04 00:40:41 -05:00
inline bool DBClientWithCommands::runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options) {
string ns = dbname + ".$cmd";
info = findOne(ns, cmd, 0 , options);
return isOk(info);
}
2009-01-10 18:17:23 -05:00
/* 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;
2009-02-09 15:38:26 -05:00
b.append(command, 1);
return runCommand(dbname, b.done(), *info);
2009-01-14 17:17:24 -05:00
}
unsigned long long DBClientWithCommands::count(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
NamespaceString ns(myns);
BSONObj cmd = _countCmd( myns , query , options , limit , skip );
BSONObj res;
if( !runCommand(ns.db.c_str(), 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);
2010-10-28 13:47:02 -04:00
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();
2009-02-09 15:38:26 -05:00
}
BSONObj DBClientWithCommands::getLastErrorDetailed(bool fsync, bool j, int w, int wtimeout) {
2009-01-30 14:40:19 -05:00
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("admin", b.obj(), info);
2011-01-04 00:40:41 -05:00
return info;
2009-09-14 12:31:47 -04:00
}
string DBClientWithCommands::getLastError(bool fsync, bool j, int w, int wtimeout) {
BSONObj info = getLastErrorDetailed(fsync, j, w, wtimeout);
return getLastErrorString( info );
}
2011-01-04 00:40:41 -05:00
string DBClientWithCommands::getLastErrorString( const BSONObj& info ) {
2009-01-30 14:40:19 -05:00
BSONElement e = info["err"];
if( e.eoo() ) return "";
if( e.type() == Object ) return e.toString();
2011-01-04 00:40:41 -05:00
return e.str();
2009-01-30 14:40:19 -05:00
}
2011-04-14 21:22:33 -04:00
const BSONObj getpreverrorcmdobj = fromjson("{getpreverror:1}");
2009-01-30 14:40:19 -05:00
2011-01-04 00:40:41 -05:00
BSONObj DBClientWithCommands::getPrevError() {
2009-01-30 14:40:19 -05:00
BSONObj info;
runCommand("admin", getpreverrorcmdobj, info);
2009-01-30 14:40:19 -05:00
return info;
}
BSONObj getnoncecmdobj = fromjson("{getnonce:1}");
2011-01-04 00:40:41 -05:00
string DBClientWithCommands::createPasswordDigest( const string & username , const string & clearTextPassword ) {
md5digest d;
{
md5_state_t st;
md5_init(&st);
md5_append(&st, (const md5_byte_t *) username.data(), username.length());
2009-02-10 12:35:30 -05:00
md5_append(&st, (const md5_byte_t *) ":mongo:", 7 );
md5_append(&st, (const md5_byte_t *) clearTextPassword.data(), clearTextPassword.length());
md5_finish(&st, d);
}
return digestToString( d );
}
bool DBClientWithCommands::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword) {
2011-01-04 00:40:41 -05:00
string password = password_text;
if( digestPassword )
password = createPasswordDigest( username , password_text );
BSONObj info;
2009-01-20 11:37:15 -05:00
string nonce;
if( !runCommand(dbname, getnoncecmdobj, info) ) {
errmsg = "getnonce fails - connection problem?";
return false;
}
{
BSONElement e = info.getField("nonce");
2009-01-20 11:37:15 -05:00
assert( e.type() == String );
nonce = e.valuestr();
}
BSONObj authCmd;
BSONObjBuilder b;
{
2009-02-10 12:01:32 -05:00
b << "authenticate" << 1 << "nonce" << nonce << "user" << username;
2009-01-18 19:13:12 -05:00
md5digest d;
{
md5_state_t st;
md5_init(&st);
2009-01-20 11:37:15 -05:00
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() );
2009-01-18 19:13:12 -05:00
md5_finish(&st, d);
}
2009-01-20 11:37:15 -05:00
b << "key" << digestToString( d );
authCmd = b.done();
}
2011-01-04 00:40:41 -05:00
if( runCommand(dbname, authCmd, info) )
return true;
errmsg = info.toString();
return false;
}
BSONObj ismastercmdobj = fromjson("{\"ismaster\":1}");
bool DBClientWithCommands::isMaster(bool& isMaster, BSONObj *info) {
BSONObj o;
2011-01-04 00:40:41 -05:00
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) {
assert(!capped||size);
BSONObj o;
2011-01-04 00:40:41 -05:00
if ( info == 0 ) info = &o;
BSONObjBuilder b;
string db = nsToDatabase(ns.c_str());
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::setDbProfilingLevel(const string &dbname, ProfilingLevel level, BSONObj *info ) {
BSONObj o;
if ( info == 0 ) info = &o;
if ( level ) {
// Create system.profile collection. If it already exists this does nothing.
// TODO: move this into the db instead of here so that all
// drivers don't have to do this.
string ns = dbname + ".system.profile";
createCollection(ns.c_str(), 1024 * 1024, true, 0, info);
}
BSONObjBuilder b;
b.append("profile", (int) level);
return runCommand(dbname, b.done(), *info);
}
BSONObj getprofilingcmdobj = fromjson("{\"profile\":-1}");
bool DBClientWithCommands::getDbProfilingLevel(const string &dbname, ProfilingLevel& level, BSONObj *info) {
BSONObj o;
if ( info == 0 ) info = &o;
if ( runCommand(dbname, getprofilingcmdobj, *info) ) {
level = (ProfilingLevel) info->getIntField("was");
return true;
}
2008-12-28 20:28:49 -05:00
return false;
}
DBClientWithCommands::MROutput DBClientWithCommands::MRInline (BSON("inline" << 1));
BSONObj DBClientWithCommands::mapreduce(const string &ns, const string &jsmapf, const string &jsreducef, BSONObj query, MROutput output) {
BSONObjBuilder b;
b.append("mapreduce", nsGetCollection(ns));
b.appendCode("map", jsmapf);
b.appendCode("reduce", jsreducef);
if( !query.isEmpty() )
b.append("query", query);
b.append("out", output.out);
BSONObj info;
runCommand(nsGetDB(ns), b.done(), info);
return 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);
}
2011-01-04 00:40:41 -05:00
list<string> DBClientWithCommands::getDatabaseNames() {
BSONObj info;
uassert( 10005 , "listdatabases failed" , runCommand( "admin" , BSON( "listDatabases" << 1 ) , info ) );
uassert( 10006 , "listDatabases.databases not array" , info["databases"].type() == Array );
2011-01-04 00:40:41 -05:00
list<string> names;
2011-01-04 00:40:41 -05:00
BSONObjIterator i( info["databases"].embeddedObjectUserCheck() );
2011-01-04 00:40:41 -05:00
while ( i.more() ) {
names.push_back( i.next().embeddedObjectUserCheck()["name"].valuestr() );
}
return names;
}
2011-01-04 00:40:41 -05:00
list<string> DBClientWithCommands::getCollectionNames( const string& db ) {
list<string> names;
2011-01-04 00:40:41 -05:00
string ns = db + ".system.namespaces";
auto_ptr<DBClientCursor> c = query( ns.c_str() , BSONObj() );
2011-01-04 00:40:41 -05:00
while ( c->more() ) {
string name = c->next()["name"].valuestr();
if ( name.find( "$" ) != string::npos )
continue;
names.push_back( name );
}
return names;
}
2011-01-04 00:40:41 -05:00
bool DBClientWithCommands::exists( const string& ns ) {
2009-10-01 17:15:33 -04:00
list<string> names;
2011-01-04 00:40:41 -05:00
2009-10-01 17:15:33 -04:00
string db = nsGetDB( ns ) + ".system.namespaces";
BSONObj q = BSON( "name" << ns );
return count( db.c_str() , q, QueryOption_SlaveOk ) != 0;
2009-10-01 17:15:33 -04:00
}
/* --- dbclientconnection --- */
2011-01-04 00:40:41 -05:00
bool DBClientConnection::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword) {
string password = password_text;
if( digestPassword )
password = createPasswordDigest( username , password_text );
2011-01-04 00:40:41 -05:00
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.
*/
pair<string,string> p = pair<string,string>(username, password);
authCache[dbname] = p;
}
2011-01-04 00:40:41 -05:00
return DBClientBase::auth(dbname, username, password.c_str(), errmsg, false);
}
2011-05-28 16:20:18 -04:00
/** 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);
auto_ptr<DBClientCursor> c =
2011-05-28 16:20:18 -04:00
this->query(ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions);
2011-10-25 16:01:26 -04:00
uassert( 10276 , str::stream() << "DBClientBase::findN: transport error: " << getServerAddress() << " ns: " << ns << " query: " << query.toString(), c.get() );
2010-07-18 13:34:16 -04:00
if ( c->hasResultFlag( ResultFlag_ShardConfigStale ) )
throw RecvStaleConfigException( ns , "findN stale config" );
2010-04-29 21:45:58 -04:00
2011-05-28 16:20:18 -04:00
for( int i = 0; i < nToReturn; i++ ) {
if ( !c->more() )
break;
out.push_back( c->nextSafe().copy() );
}
}
2009-01-14 17:17:24 -05:00
2011-05-28 16:20:18 -04:00
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];
}
2011-01-04 00:40:41 -05:00
bool DBClientConnection::connect(const HostAndPort& server, string& errmsg) {
_server = server;
_serverString = _server.toString();
return _connect( errmsg );
}
2011-01-04 00:40:41 -05:00
bool DBClientConnection::_connect( string& errmsg ) {
_serverString = _server.toString();
2011-10-28 09:32:58 -04:00
// we keep around SockAddr for connection life -- maybe MessagingPort
// requires that?
server.reset(new SockAddr(_server.host().c_str(), _server.port()));
p.reset(new MessagingPort( _so_timeout, _logLevel ));
2011-10-28 09:32:58 -04:00
if (_server.host().empty() || server->getAddr() == "0.0.0.0") {
stringstream s;
errmsg =
str::stream() << "couldn't connect to server " << _server.toString();
return false;
}
2011-06-13 20:00:05 -04:00
// if( _so_timeout == 0 ){
// printStackTrace();
// log() << "Connecting to server " << _serverString << " timeout " << _so_timeout << endl;
// }
if ( !p->connect(*server) ) {
2011-10-28 09:32:58 -04:00
errmsg = str::stream() << "couldn't connect to server " << _server.toString();
2011-03-30 12:16:06 -04:00
_failed = true;
return false;
}
#ifdef MONGO_SSL
if ( cmdLine.sslOnNormalPorts ) {
p->secure( sslManager() );
}
#endif
return true;
}
2009-01-05 14:48:04 -05:00
inline bool DBClientConnection::runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options) {
if ( DBClientWithCommands::runCommand( dbname , cmd , info , options ) )
return true;
if ( clientSet && isNotMasterErrorString( info["errmsg"] ) ) {
clientSet->isntMaster();
}
return false;
}
2009-02-24 13:46:21 -05:00
void DBClientConnection::_checkConnection() {
2011-03-30 12:16:06 -04:00
if ( !_failed )
return;
if ( lastReconnectTry && time(0)-lastReconnectTry < 2 ) {
// we wait a little before reconnect attempt to avoid constant hammering.
// but we throw we don't want to try to use a connection in a bad state
2011-06-25 00:11:50 -04:00
throw SocketException( SocketException::FAILED_STATE , toString() );
}
if ( !autoReconnect )
2011-06-25 00:11:50 -04:00
throw SocketException( SocketException::FAILED_STATE , toString() );
lastReconnectTry = time(0);
log(_logLevel) << "trying reconnect to " << _serverString << endl;
string errmsg;
2011-03-30 12:16:06 -04:00
_failed = false;
2011-01-04 00:40:41 -05:00
if ( ! _connect(errmsg) ) {
2011-03-30 12:16:06 -04:00
_failed = true;
log(_logLevel) << "reconnect " << _serverString << " failed " << errmsg << endl;
2011-06-25 00:11:50 -04:00
throw SocketException( SocketException::CONNECT_ERROR , toString() );
2011-01-04 00:40:41 -05:00
}
2011-01-04 00:40:41 -05:00
log(_logLevel) << "reconnect " << _serverString << " ok" << endl;
for( map< string, pair<string,string> >::iterator i = authCache.begin(); i != authCache.end(); i++ ) {
const char *dbname = i->first.c_str();
const char *username = i->second.first.c_str();
const char *password = i->second.second.c_str();
if( !DBClientBase::auth(dbname, username, password, errmsg, false) )
log(_logLevel) << "reconnect: auth failed db:" << dbname << " user:" << username << ' ' << errmsg << '\n';
}
}
2009-01-05 14:48:04 -05:00
auto_ptr<DBClientCursor> DBClientBase::query(const string &ns, Query query, int nToReturn,
2011-01-04 00:40:41 -05:00
int nToSkip, const BSONObj *fieldsToReturn, int queryOptions , int batchSize ) {
auto_ptr<DBClientCursor> c( new DBClientCursor( this,
2011-01-04 00:40:41 -05:00
ns, query.obj, nToReturn, nToSkip,
fieldsToReturn, queryOptions , batchSize ) );
if ( c->init() )
return c;
return auto_ptr< DBClientCursor >( 0 );
}
2009-01-14 17:17:24 -05:00
auto_ptr<DBClientCursor> DBClientBase::getMore( const string &ns, long long cursorId, int nToReturn, int options ) {
auto_ptr<DBClientCursor> c( new DBClientCursor( this, ns, cursorId, nToReturn, options ) );
if ( c->init() )
return c;
return auto_ptr< DBClientCursor >( 0 );
}
2010-07-14 10:28:38 -07:00
struct DBClientFunConvertor {
void operator()( DBClientCursorBatchIterator &i ) {
while( i.moreInCurrentBatch() ) {
_f( i.nextSafe() );
}
}
boost::function<void(const BSONObj &)> _f;
};
2011-01-04 00:40:41 -05:00
2010-07-14 10:28:38 -07:00
unsigned long long DBClientConnection::query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn, int queryOptions ) {
DBClientFunConvertor fun;
fun._f = f;
boost::function<void(DBClientCursorBatchIterator &)> ptr( fun );
return DBClientConnection::query( ptr, ns, query, fieldsToReturn, queryOptions );
}
2011-01-04 00:40:41 -05:00
2010-07-14 10:28:38 -07:00
unsigned long long DBClientConnection::query( boost::function<void(DBClientCursorBatchIterator &)> f, const string& ns, Query query, const BSONObj *fieldsToReturn, int queryOptions ) {
// mask options
queryOptions &= (int)( QueryOption_NoCursorTimeout | QueryOption_SlaveOk );
2010-06-06 22:19:06 -04:00
unsigned long long n = 0;
2010-07-14 10:28:38 -07:00
bool doExhaust = ( availableOptions() & QueryOption_Exhaust );
if ( doExhaust ) {
2011-01-04 00:40:41 -05:00
queryOptions |= (int)QueryOption_Exhaust;
2010-07-14 10:28:38 -07:00
}
auto_ptr<DBClientCursor> c( this->query(ns, query, 0, 0, fieldsToReturn, queryOptions) );
2010-08-16 23:31:22 -04:00
uassert( 13386, "socket error for mapping query", c.get() );
2011-01-04 00:40:41 -05:00
2010-07-14 10:28:38 -07:00
if ( !doExhaust ) {
while( c->more() ) {
DBClientCursorBatchIterator i( *c );
f( i );
n += i.n();
}
return n;
}
2010-06-06 22:19:06 -04:00
2011-01-04 00:40:41 -05:00
try {
while( 1 ) {
while( c->moreInCurrentBatch() ) {
2010-07-14 10:28:38 -07:00
DBClientCursorBatchIterator i( *c );
f( i );
n += i.n();
2010-06-06 22:19:06 -04:00
}
2011-01-04 00:40:41 -05:00
if( c->getCursorId() == 0 )
2010-06-06 22:19:06 -04:00
break;
c->exhaustReceiveMore();
}
}
2011-01-04 00:40:41 -05:00
catch(std::exception&) {
2010-06-06 22:19:06 -04:00
/* connection CANNOT be used anymore as more data may be on the way from the server.
we have to reconnect.
*/
2011-03-30 12:16:06 -04:00
_failed = true;
2010-06-06 22:19:06 -04:00
p->shutdown();
throw;
}
return n;
}
void DBClientBase::insert( const string & ns , BSONObj obj , int flags) {
Message toSend;
2009-01-14 17:17:24 -05:00
BufBuilder b;
b.appendNum( flags );
b.appendStr( ns );
obj.appendSelfToBufBuilder( b );
2009-01-05 14:48:04 -05:00
toSend.setData( dbInsert , b.buf() , b.len() );
2009-01-14 17:17:24 -05:00
say( toSend );
}
2009-01-05 14:48:04 -05:00
void DBClientBase::insert( const string & ns , const vector< BSONObj > &v , int flags) {
Message toSend;
2011-01-04 00:40:41 -05:00
BufBuilder b;
b.appendNum( flags );
b.appendStr( ns );
for( vector< BSONObj >::const_iterator i = v.begin(); i != v.end(); ++i )
i->appendSelfToBufBuilder( b );
2011-01-04 00:40:41 -05:00
toSend.setData( dbInsert, b.buf(), b.len() );
2011-01-04 00:40:41 -05:00
say( toSend );
}
void DBClientBase::remove( const string & ns , Query obj , bool justOne ) {
Message toSend;
2009-01-05 14:48:04 -05:00
BufBuilder b;
int opts = 0;
b.appendNum( opts );
b.appendStr( ns );
2009-01-14 17:17:24 -05:00
int flags = 0;
if ( justOne )
2010-07-22 22:38:35 -04:00
flags |= RemoveOption_JustOne;
b.appendNum( flags );
2009-01-14 17:17:24 -05:00
2009-01-29 18:38:35 -05:00
obj.obj.appendSelfToBufBuilder( b );
2009-01-14 17:17:24 -05:00
toSend.setData( dbDelete , b.buf() , b.len() );
2009-01-13 16:08:07 -05:00
say( toSend );
}
2009-01-13 16:08:07 -05:00
void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert , bool multi ) {
2009-01-13 16:08:07 -05:00
BufBuilder b;
b.appendNum( (int)0 ); // reserved
b.appendStr( ns );
2009-01-14 17:17:24 -05:00
int flags = 0;
2010-01-03 16:37:38 -05:00
if ( upsert ) flags |= UpdateOption_Upsert;
if ( multi ) flags |= UpdateOption_Multi;
b.appendNum( flags );
2009-01-14 17:17:24 -05:00
2009-01-29 18:38:35 -05:00
query.obj.appendSelfToBufBuilder( b );
obj.appendSelfToBufBuilder( b );
Message toSend;
toSend.setData( dbUpdate , b.buf() , b.len() );
say( toSend );
2009-01-14 10:40:15 -05:00
}
2009-01-14 17:17:24 -05:00
2011-01-04 00:40:41 -05:00
auto_ptr<DBClientCursor> DBClientWithCommands::getIndexes( const string &ns ) {
return query( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , BSON( "ns" << ns ) );
}
2011-01-04 00:40:41 -05:00
void DBClientWithCommands::dropIndex( const string& ns , BSONObj keys ) {
dropIndex( ns , genIndexName( keys ) );
}
2011-01-04 00:40:41 -05:00
void DBClientWithCommands::dropIndex( const string& ns , const string& indexName ) {
BSONObj info;
2011-01-04 00:40:41 -05:00
if ( ! runCommand( nsToDatabase( ns.c_str() ) ,
BSON( "deleteIndexes" << NamespaceString( ns ).coll << "index" << indexName ) ,
info ) ) {
log(_logLevel) << "dropIndex failed: " << info << endl;
uassert( 10007 , "dropIndex failed" , 0 );
}
resetIndexCache();
}
2011-01-04 00:40:41 -05:00
void DBClientWithCommands::dropIndexes( const string& ns ) {
BSONObj info;
2011-01-04 00:40:41 -05:00
uassert( 10008 , "dropIndexes failed" , runCommand( nsToDatabase( ns.c_str() ) ,
BSON( "deleteIndexes" << NamespaceString( ns ).coll << "index" << "*") ,
info ) );
resetIndexCache();
}
2011-01-04 00:40:41 -05:00
void DBClientWithCommands::reIndex( const string& ns ) {
list<BSONObj> all;
auto_ptr<DBClientCursor> i = getIndexes( ns );
2011-01-04 00:40:41 -05:00
while ( i->more() ) {
all.push_back( i->next().getOwned() );
}
2011-01-04 00:40:41 -05:00
dropIndexes( ns );
2011-01-04 00:40:41 -05:00
for ( list<BSONObj>::iterator i=all.begin(); i!=all.end(); i++ ) {
BSONObj o = *i;
insert( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , o );
}
2011-01-04 00:40:41 -05:00
}
2011-01-04 00:40:41 -05:00
string DBClientWithCommands::genIndexName( const BSONObj& keys ) {
stringstream ss;
2011-01-04 00:40:41 -05:00
bool first = 1;
for ( BSONObjIterator i(keys); i.more(); ) {
BSONElement f = i.next();
2011-01-04 00:40:41 -05:00
if ( first )
first = 0;
else
ss << "_";
2011-01-04 00:40:41 -05:00
ss << f.fieldName() << "_";
if( f.isNumber() )
ss << f.numberInt();
}
return ss.str();
}
bool DBClientWithCommands::ensureIndex( const string &ns , BSONObj keys , bool unique, const string & name , bool cache, bool background, int version ) {
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;
2009-01-14 10:40:15 -05:00
}
2011-01-04 00:40:41 -05:00
if( version >= 0 )
toSave.append("v", version);
if ( unique )
toSave.appendBool( "unique", unique );
2009-01-14 10:40:15 -05:00
if( background )
toSave.appendBool( "background", true );
if ( _seenIndexes.count( cacheKey ) )
return 0;
if ( cache )
_seenIndexes.insert( cacheKey );
2009-02-09 13:04:32 -05:00
insert( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , toSave.obj() );
return 1;
2009-01-14 10:40:15 -05:00
}
void DBClientWithCommands::resetIndexCache() {
_seenIndexes.clear();
}
/* -- DBClientCursor ---------------------------------------------- */
2010-04-22 10:31:16 -04:00
#ifdef _DEBUG
#define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid" + (msg) , (o).isValid() )
#else
#define CHECK_OBJECT( o , msg )
#endif
void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
CHECK_OBJECT( query , "assembleRequest query" );
// see query.h for the protocol we are using here.
BufBuilder b;
int opts = queryOptions;
b.appendNum(opts);
2010-07-20 12:57:07 -04:00
b.appendStr(ns);
b.appendNum(nToSkip);
b.appendNum(nToReturn);
query.appendSelfToBufBuilder(b);
if ( fieldsToReturn )
fieldsToReturn->appendSelfToBufBuilder(b);
toSend.setData(dbQuery, b.buf(), b.len());
}
void DBClientConnection::say( Message &toSend, bool isRetry ) {
2009-02-24 13:46:21 -05:00
checkConnection();
2011-01-04 00:40:41 -05:00
try {
2009-02-24 13:46:21 -05:00
port().say( toSend );
2011-01-04 00:40:41 -05:00
}
catch( SocketException & ) {
2011-03-30 12:16:06 -04:00
_failed = true;
2009-02-24 13:46:21 -05:00
throw;
}
}
2008-12-28 20:28:49 -05:00
void DBClientConnection::sayPiggyBack( Message &toSend ) {
port().piggyBack( toSend );
}
bool DBClientConnection::recv( Message &m ) {
return port().recv(m);
2010-06-06 22:19:06 -04:00
}
bool DBClientConnection::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
2011-01-04 00:40:41 -05:00
/* 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
*/
2011-01-04 00:40:41 -05:00
try {
if ( !port().call(toSend, response) ) {
2011-03-30 12:16:06 -04:00
_failed = true;
if ( assertOk )
uasserted( 10278 , str::stream() << "dbclient error communicating with server: " << getServerAddress() );
2010-12-29 00:51:25 -05:00
return false;
}
}
2011-01-04 00:40:41 -05:00
catch( SocketException & ) {
2011-03-30 12:16:06 -04:00
_failed = true;
throw;
}
2008-12-28 20:28:49 -05:00
return true;
}
BSONElement getErrField(const BSONObj& o) {
BSONElement first = o.firstElement();
2011-05-24 11:46:53 -04:00
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();
}
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
2011-03-30 13:49:19 -04:00
* this stage is "not master"
*/
*retry = false;
*host = _serverString;
if ( clientSet && nReturned ) {
2010-06-21 17:57:56 -04:00
assert(data);
BSONObj o(data);
if ( isNotMasterErrorString( getErrField(o) ) ) {
clientSet->isntMaster();
}
}
}
2011-01-04 00:40:41 -05:00
void DBClientConnection::killCursor( long long cursorId ) {
StackBufBuilder b;
b.appendNum( (int)0 ); // reserved
b.appendNum( (int)1 ); // number
b.appendNum( cursorId );
2011-01-04 00:40:41 -05:00
2010-05-28 14:23:37 -04:00
Message m;
m.setData( dbKillCursors , b.buf() , b.len() );
if ( _lazyKillCursor )
sayPiggyBack( m );
else
say(m);
2010-05-28 14:23:37 -04:00
}
2011-07-27 09:50:17 -04:00
#ifdef MONGO_SSL
SSLManager* DBClientConnection::sslManager() {
if ( _sslManager )
return _sslManager;
SSLManager* s = new SSLManager(true);
_sslManager = s;
return s;
}
2011-07-27 09:50:17 -04:00
SSLManager* DBClientConnection::_sslManager = 0;
#endif
AtomicUInt DBClientConnection::_numConnections;
bool DBClientConnection::_lazyKillCursor = true;
2011-07-27 09:50:17 -04:00
2010-04-08 12:14:20 -07:00
bool serverAlive( const string &uri ) {
DBClientConnection c( false, 0, 20 ); // potentially the connection to server could fail while we're checking if it's alive - so use timeouts
string err;
if ( !c.connect( uri, err ) )
return false;
if ( !c.simpleCommand( "admin", 0, "ping" ) )
return false;
return true;
}
2011-01-04 00:40:41 -05:00
/** @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 );
}
2009-01-14 17:09:51 -05:00
} // namespace mongo