Files
mongo/db/dbcommands.cpp

1874 lines
70 KiB
C++
Raw Normal View History

2008-12-28 20:28:49 -05:00
// dbcommands.cpp
2008-10-22 16:56:39 -04:00
/**
2008-12-28 20:28:49 -05:00
*
2008-10-22 16:56:39 -04:00
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
2008-12-28 20:28:49 -05:00
*
2008-10-22 16:56:39 -04:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2008-12-28 20:28:49 -05:00
*
2008-10-22 16:56:39 -04:00
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2010-04-27 15:27:52 -04:00
#include "pch.h"
2008-10-22 16:56:39 -04:00
#include "query.h"
#include "pdfile.h"
#include "jsobj.h"
2010-04-24 18:25:58 -04:00
#include "../bson/util/builder.h"
2008-10-22 16:56:39 -04:00
#include <time.h>
#include "introspect.h"
#include "btree.h"
#include "../util/lruishmap.h"
2009-02-11 10:37:27 -05:00
#include "../util/md5.hpp"
#include "../util/processinfo.h"
2008-10-22 16:56:39 -04:00
#include "json.h"
#include "repl.h"
#include "repl_block.h"
2010-04-21 18:46:31 -04:00
#include "replpair.h"
2008-10-22 16:56:39 -04:00
#include "commands.h"
2008-12-05 16:45:10 -05:00
#include "db.h"
2008-12-29 14:07:21 -05:00
#include "instance.h"
2009-01-05 15:30:07 -05:00
#include "lasterror.h"
2009-01-18 20:31:33 -05:00
#include "security.h"
#include "queryoptimizer.h"
#include "../scripting/engine.h"
2010-02-01 10:38:00 -05:00
#include "stats/counters.h"
#include "background.h"
#include "../util/version.h"
#include "../s/d_writeback.h"
2010-12-27 11:29:16 -05:00
#include "dur_stats.h"
2008-10-22 16:56:39 -04:00
2009-01-14 17:09:51 -05:00
namespace mongo {
extern int otherTraceLevel;
void flushDiagLog();
/* reset any errors so that getlasterror comes back clean.
useful before performing a long series of operations where we want to
see if any of the operations triggered an error, but don't want to check
after each op as that woudl be a client/server turnaround.
*/
class CmdResetError : public Command {
public:
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
2009-01-18 20:31:33 -05:00
virtual bool requiresAuth() { return false; }
virtual bool logTheOp() {
return false;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2009-01-05 15:30:07 -05:00
return true;
}
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "reset error state (used with getpreverror)";
}
2010-04-23 16:41:56 -04:00
CmdResetError() : Command("resetError", false, "reseterror") {}
bool run(const string& db, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
LastError *le = lastError.get();
assert( le );
le->reset();
return true;
}
} cmdResetError;
2009-01-05 15:30:07 -05:00
2011-01-04 00:40:41 -05:00
/* set by replica sets if specified in the configuration.
a pointer is used to avoid any possible locking issues with lockless reading (see below locktype() is NONE
2010-08-09 16:15:41 -04:00
and would like to keep that)
(for now, it simply orphans any old copy as config changes should be extremely rare).
note: once non-null, never goes to null again.
*/
BSONObj *getLastErrorDefault = 0;
class CmdGetLastError : public Command {
public:
CmdGetLastError() : Command("getLastError", false, "getlasterror") { }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
virtual bool requiresAuth() { return false; }
virtual bool logTheOp() { return false; }
virtual bool slaveOk() const { return true; }
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "return error status of the last operation on this connection\n"
2011-01-04 00:40:41 -05:00
<< "options:\n"
<< " fsync - fsync before returning, or wait for journal commit if running with --dur\n"
<< " w - await replication to w servers (including self) before returning\n"
<< " wtimeout - timeout for w in milliseconds";
2009-09-01 16:23:56 -04:00
}
bool run(const string& dbname, BSONObj& _cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
LastError *le = lastError.disableForCommand();
2011-01-04 00:40:41 -05:00
bool err = false;
if ( le->nPrev != 1 )
err = LastError::noError.appendSelf( result , false );
else
err = le->appendSelf( result , false );
2011-01-04 00:40:41 -05:00
Client& c = cc();
c.appendLastOp( result );
2011-01-04 00:40:41 -05:00
result.appendNumber( "connectionId" , c.getConnectionId() );
2010-08-09 16:15:41 -04:00
BSONObj cmdObj = _cmdObj;
2011-01-04 00:40:41 -05:00
{
2010-08-09 16:15:41 -04:00
BSONObj::iterator i(_cmdObj);
i.next();
2011-01-04 00:40:41 -05:00
if( !i.more() ) {
2010-08-09 16:15:41 -04:00
/* empty, use default */
BSONObj *def = getLastErrorDefault;
if( def )
cmdObj = *def;
}
}
if ( cmdObj["fsync"].trueValue() ) {
Timer t;
if( !getDur().awaitCommit() ) {
// if get here, not running with --dur
log() << "fsync from getlasterror" << endl;
result.append( "fsyncFiles" , MemoryMappedFile::flushAll( true ) );
}
2011-01-04 00:40:41 -05:00
else {
2011-01-03 14:05:56 -05:00
// this perhaps is temp. how long we wait for the group commit to occur.
result.append( "waited", t.millis() );
}
2009-12-17 16:15:36 -05:00
}
if ( err ) {
// doesn't make sense to wait for replication
// if there was an error
return true;
}
BSONElement e = cmdObj["w"];
2011-01-04 00:40:41 -05:00
if ( e.isNumber() ) {
int timeout = cmdObj["wtimeout"].numberInt();
Timer t;
int w = e.numberInt();
2011-01-04 00:40:41 -05:00
long long passes = 0;
char buf[32];
2011-01-04 00:40:41 -05:00
while ( 1 ) {
OpTime op(c.getLastOp());
2011-01-17 23:31:31 -05:00
if ( op.isNull() ) {
if ( anyReplEnabled() ) {
result.append( "wnote" , "no write has been done on this connection" );
}
else if ( w <= 1 ) {
// don't do anything
// w=1 and no repl, so this is fine
}
else {
// w=2 and no repl
result.append( "wnote" , "no replication has been enabled, so w=2+ won't work" );
result.append( "err", "norepl" );
return true;
}
break;
}
2011-01-17 23:31:31 -05:00
// check this first for w=0 or w=1
if ( opReplicatedEnough( op, w ) )
break;
// if replication isn't enabled (e.g., config servers)
2011-01-04 00:40:41 -05:00
if ( ! anyReplEnabled() ) {
result.append( "err", "norepl" );
return true;
}
2011-01-04 00:40:41 -05:00
2011-01-04 00:40:41 -05:00
if ( timeout > 0 && t.millis() >= timeout ) {
result.append( "wtimeout" , true );
errmsg = "timed out waiting for slaves";
result.append( "waited" , t.millis() );
result.append( "err" , "timeout" );
return true;
}
assert( sprintf( buf , "w block pass: %lld" , ++passes ) < 30 );
c.curop()->setMessage( buf );
sleepmillis(1);
killCurrentOp.checkForInterrupt();
}
result.appendNumber( "wtime" , t.millis() );
}
2011-01-04 00:40:41 -05:00
result.appendNull( "err" );
return true;
}
} cmdGetLastError;
2009-01-05 15:30:07 -05:00
class CmdGetPrevError : public Command {
public:
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
2009-01-18 20:31:33 -05:00
virtual bool requiresAuth() { return false; }
virtual bool logTheOp() {
return false;
}
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "check for errors since last reseterror commandcal";
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2010-04-23 16:41:56 -04:00
CmdGetPrevError() : Command("getPrevError", false, "getpreverror") {}
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
LastError *le = lastError.disableForCommand();
le->appendSelf( result );
if ( le->valid )
result.append( "nPrev", le->nPrev );
else
result.append( "nPrev", -1 );
return true;
}
} cmdGetPrevError;
class CmdDropDatabase : public Command {
public:
virtual bool logTheOp() {
return true;
}
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "drop (delete) this database";
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2008-12-16 10:20:24 -05:00
return false;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
CmdDropDatabase() : Command("dropDatabase") {}
2010-12-12 19:46:12 -05:00
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
2010-04-23 17:37:05 -04:00
BSONElement e = cmdObj.firstElement();
2010-12-12 19:46:12 -05:00
log() << "dropDatabase " << dbname << endl;
int p = (int) e.number();
if ( p != 1 )
return false;
2010-12-12 19:46:12 -05:00
dropDatabase(dbname);
result.append( "dropped" , dbname );
return true;
}
} cmdDropDatabase;
2008-12-16 10:20:24 -05:00
class CmdRepairDatabase : public Command {
public:
virtual bool logTheOp() {
return false;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "repair database. also compacts. note: slow.";
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
CmdRepairDatabase() : Command("repairDatabase") {}
bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
2010-04-23 17:37:05 -04:00
BSONElement e = cmdObj.firstElement();
log() << "repairDatabase " << dbname << endl;
int p = (int) e.number();
if ( p != 1 )
return false;
e = cmdObj.getField( "preserveClonedFilesOnFailure" );
bool preserveClonedFilesOnFailure = e.isBoolean() && e.boolean();
e = cmdObj.getField( "backupOriginalFiles" );
bool backupOriginalFiles = e.isBoolean() && e.boolean();
return repairDatabase( dbname, errmsg, preserveClonedFilesOnFailure, backupOriginalFiles );
}
} cmdRepairDatabase;
2011-01-04 00:40:41 -05:00
/* set db profiling level
todo: how do we handle profiling information put in the db with replication?
sensibly or not?
*/
class CmdProfile : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
2010-04-23 15:50:49 -04:00
help << "enable or disable performance profiling\n";
help << "{ profile : <n> }\n";
help << "0=off 1=log slow ops 2=log all\n";
help << "-1 to get current values\n";
2010-04-23 15:50:49 -04:00
help << "http://www.mongodb.org/display/DOCS/Database+Profiler";
2009-09-01 16:23:56 -04:00
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
CmdProfile() : Command("profile") {}
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
2010-04-23 17:37:05 -04:00
BSONElement e = cmdObj.firstElement();
2010-07-24 12:02:43 -04:00
result.append("was", cc().database()->profile);
result.append("slowms", cmdLine.slowMS );
int p = (int) e.number();
bool ok = false;
2011-01-04 00:40:41 -05:00
if ( p == -1 )
ok = true;
else if ( p >= 0 && p <= 2 ) {
ok = cc().database()->setProfilingLevel( p , errmsg );
}
BSONElement slow = cmdObj["slowms"];
if ( slow.isNumber() )
cmdLine.slowMS = slow.numberInt();
2011-01-04 00:40:41 -05:00
return ok;
}
} cmdProfile;
class CmdServerStatus : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2010-04-19 21:05:46 -04:00
CmdServerStatus() : Command("serverStatus", true) {
2009-01-26 15:44:48 -05:00
started = time(0);
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const {
help << "returns lots of administrative server statistics";
}
2010-02-12 13:36:10 -05:00
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
2010-06-30 12:57:36 -04:00
long long start = Listener::getElapsedTimeMillis();
BSONObjBuilder timeBuilder(128);
2011-01-04 00:40:41 -05:00
bool authed = cc().getAuthenticationInfo()->isAuthorizedReads("admin");
2008-12-05 16:45:10 -05:00
result.append( "host" , prettyHostName() );
result.append("version", versionString);
result.append("process","mongod");
result.append("uptime",(double) (time(0)-started));
2010-06-30 12:57:36 -04:00
result.append("uptimeEstimate",(double) (start/1000));
result.appendDate( "localTime" , jsTime() );
{
BSONObjBuilder t;
unsigned long long last, start, timeLocked;
2009-12-03 13:12:51 -05:00
dbMutex.info().getTimingInfo(start, timeLocked);
last = curTimeMicros64();
double tt = (double) last-start;
double tl = (double) timeLocked;
t.append("totalTime", tt);
t.append("lockTime", tl);
t.append("ratio", (tt ? tl/tt : 0));
2011-01-04 00:40:41 -05:00
{
BSONObjBuilder ttt( t.subobjStart( "currentQueue" ) );
int w=0, r=0;
Client::recommendedYieldMicros( &w , &r );
ttt.append( "total" , w + r );
ttt.append( "readers" , r );
ttt.append( "writers" , w );
ttt.done();
}
2011-01-04 00:40:41 -05:00
{
BSONObjBuilder ttt( t.subobjStart( "activeClients" ) );
int w=0, r=0;
Client::getActiveClientCount( w , r );
ttt.append( "total" , w + r );
ttt.append( "readers" , r );
ttt.append( "writers" , w );
ttt.done();
}
2011-01-04 00:40:41 -05:00
2010-07-12 13:06:16 -04:00
result.append( "globalLock" , t.obj() );
}
2010-06-30 12:57:36 -04:00
timeBuilder.appendNumber( "after basic" , Listener::getElapsedTimeMillis() - start );
{
2011-01-04 00:40:41 -05:00
2010-01-10 20:22:17 -05:00
BSONObjBuilder t( result.subobjStart( "mem" ) );
2011-01-04 00:40:41 -05:00
t.append("bits", ( sizeof(int*) == 4 ? 32 : 64 ) );
ProcessInfo p;
2011-03-01 01:55:30 -05:00
int v = 0;
2011-01-04 00:40:41 -05:00
if ( p.supported() ) {
t.appendNumber( "resident" , p.getResidentSize() );
2011-03-01 01:55:30 -05:00
v = p.getVirtualMemorySize();
t.appendNumber( "virtual" , v );
2010-01-10 20:22:17 -05:00
t.appendBool( "supported" , true );
}
else {
2010-01-10 20:22:17 -05:00
result.append( "note" , "not all mem info support on this platform" );
t.appendBool( "supported" , false );
}
2011-01-04 00:40:41 -05:00
2011-02-28 18:40:02 -05:00
timeBuilder.appendNumber( "middle of mem" , Listener::getElapsedTimeMillis() - start );
2011-03-01 01:55:30 -05:00
int m = (int) (MemoryMappedFile::totalMappedLength() / ( 1024 * 1024 ));
t.appendNumber( "mapped" , m );
if( v - m > 5000 ) {
t.append("note", "virtual minus mapped is large. could indicate a memory leak");
log() << "warning: virtual size (" << v << "MB) - mapped size (" << m << "MB) is large. could indicate a memory leak" << endl;
}
2010-01-10 20:22:17 -05:00
t.done();
2011-01-04 00:40:41 -05:00
}
timeBuilder.appendNumber( "after mem" , Listener::getElapsedTimeMillis() - start );
2011-01-04 00:40:41 -05:00
{
BSONObjBuilder bb( result.subobjStart( "connections" ) );
bb.append( "current" , connTicketHolder.used() );
bb.append( "available" , connTicketHolder.available() );
bb.done();
}
2010-06-30 12:57:36 -04:00
timeBuilder.appendNumber( "after connections" , Listener::getElapsedTimeMillis() - start );
2011-01-04 00:40:41 -05:00
{
BSONObjBuilder bb( result.subobjStart( "extra_info" ) );
bb.append("note", "fields vary by platform");
ProcessInfo p;
p.getExtraInfo(bb);
bb.done();
2010-06-30 12:57:36 -04:00
timeBuilder.appendNumber( "after extra info" , Listener::getElapsedTimeMillis() - start );
2011-01-04 00:40:41 -05:00
}
{
BSONObjBuilder bb( result.subobjStart( "indexCounters" ) );
globalIndexCounters.append( bb );
bb.done();
}
2011-01-04 00:40:41 -05:00
2010-03-15 16:02:24 -04:00
{
BSONObjBuilder bb( result.subobjStart( "backgroundFlushing" ) );
globalFlushCounters.append( bb );
bb.done();
}
2011-01-04 00:40:41 -05:00
{
2010-08-19 17:04:49 -04:00
BSONObjBuilder bb( result.subobjStart( "cursors" ) );
ClientCursor::appendStats( bb );
bb.done();
}
2010-11-09 00:14:52 -05:00
{
BSONObjBuilder bb( result.subobjStart( "network" ) );
networkCounter.append( bb );
bb.done();
}
2010-06-30 12:57:36 -04:00
2011-01-04 00:40:41 -05:00
timeBuilder.appendNumber( "after counters" , Listener::getElapsedTimeMillis() - start );
if ( anyReplEnabled() ) {
BSONObjBuilder bb( result.subobjStart( "repl" ) );
2010-02-10 14:18:57 -05:00
appendReplicationInfo( bb , authed , cmdObj["repl"].numberInt() );
bb.done();
2010-11-09 17:01:58 -05:00
2011-01-04 00:40:41 -05:00
if ( ! _isMaster() ) {
2010-11-09 17:01:58 -05:00
result.append( "opcountersRepl" , replOpCounters.getObj() );
}
2011-01-04 00:40:41 -05:00
}
2010-06-30 12:57:36 -04:00
2011-01-04 00:40:41 -05:00
timeBuilder.appendNumber( "after repl" , Listener::getElapsedTimeMillis() - start );
2010-01-16 00:50:02 -05:00
result.append( "opcounters" , globalOpCounters.getObj() );
2011-01-04 00:40:41 -05:00
2010-02-23 07:51:36 -05:00
{
BSONObjBuilder asserts( result.subobjStart( "asserts" ) );
asserts.append( "regular" , assertionCount.regular );
asserts.append( "warning" , assertionCount.warning );
asserts.append( "msg" , assertionCount.msg );
asserts.append( "user" , assertionCount.user );
asserts.append( "rollovers" , assertionCount.rollovers );
asserts.done();
}
2011-01-04 00:40:41 -05:00
timeBuilder.appendNumber( "after asserts" , Listener::getElapsedTimeMillis() - start );
2010-06-30 12:57:36 -04:00
result.append( "writeBacksQueued" , ! writeBackManager.queuesEmpty() );
2010-12-27 11:29:16 -05:00
if( cmdLine.dur ) {
result.append("dur", dur::stats.asObj());
}
if ( ! authed )
result.append( "note" , "run against admin for more info" );
2011-01-04 00:40:41 -05:00
if ( Listener::getElapsedTimeMillis() - start > 1000 ) {
2010-09-18 23:41:47 -04:00
BSONObj t = timeBuilder.obj();
log() << "serverStatus was very slow: " << t << endl;
result.append( "timing" , t );
}
return true;
}
time_t started;
} cmdServerStatus;
class CmdGetOpTime : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const { help << "internal"; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
CmdGetOpTime() : Command("getoptime") { }
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
writelock l( "" );
result.appendDate("optime", OpTime::now().asDate());
return true;
}
} cmdgetoptime;
/*
class Cmd : public Command {
public:
Cmd() : Command("") { }
2010-04-23 16:41:56 -04:00
bool adminOnly() const { return true; }
bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result) {
return true;
}
} cmd;
*/
2008-10-22 16:56:39 -04:00
2009-11-11 13:54:05 -05:00
class CmdDiagLogging : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2009-11-11 13:54:05 -05:00
CmdDiagLogging() : Command("diagLogging") { }
2010-04-23 16:41:56 -04:00
bool adminOnly() const {
return true;
}
2010-04-23 16:41:56 -04:00
void help(stringstream& h) const { h << "http://www.mongodb.org/display/DOCS/Monitoring+and+Diagnostics#MonitoringandDiagnostics-DatabaseRecord%2FReplay"; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
int was = _diaglog.setLevel( cmdObj.firstElement().numberInt() );
flushDiagLog();
2009-08-25 14:35:22 -04:00
if ( !cmdLine.quiet )
2010-05-19 12:11:17 -04:00
tlog() << "CMD: diagLogging set to " << _diaglog.level << " from: " << was << endl;
2009-11-11 13:54:05 -05:00
result.append( "was" , was );
return true;
}
2009-11-11 13:54:05 -05:00
} cmddiaglogging;
2008-10-22 16:56:39 -04:00
2009-10-21 18:29:27 -04:00
/* remove bit from a bit array - actually remove its slot, not a clear
note: this function does not work with x == 63 -- that is ok
2011-01-04 00:40:41 -05:00
but keep in mind in the future if max indexes were extended to
2009-10-21 18:29:27 -04:00
exactly 64 it would be a problem
*/
unsigned long long removeBit(unsigned long long b, int x) {
unsigned long long tmp = b;
2009-10-06 14:31:02 -04:00
return
(tmp & ((((unsigned long long) 1) << x)-1)) |
((tmp >> (x+1)) << x);
}
struct DBCommandsUnitTest {
DBCommandsUnitTest() {
assert( removeBit(1, 0) == 0 );
assert( removeBit(2, 0) == 1 );
assert( removeBit(2, 1) == 0 );
assert( removeBit(255, 1) == 127 );
assert( removeBit(21, 2) == 9 );
2009-10-21 18:29:27 -04:00
assert( removeBit(0x4000000000000001ULL, 62) == 1 );
}
} dbc_unittest;
void assureSysIndexesEmptied(const char *ns, IndexDetails *exceptForIdIndex);
int removeFromSysIndexes(const char *ns, const char *idxName);
2010-01-22 15:58:49 -05:00
bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name, string &errmsg, BSONObjBuilder &anObjBuilder, bool mayDeleteIdIndex ) {
BackgroundOperation::assertNoBgOpInProgForNs(ns);
d = d->writingWithExtra();
2009-04-07 14:24:16 -04:00
d->aboutToDeleteAnIndex();
2009-04-07 14:24:16 -04:00
/* there may be pointers pointing at keys in the btree(s). kill them. */
ClientCursor::invalidate(ns);
2009-10-06 14:31:02 -04:00
2009-04-07 14:24:16 -04:00
// delete a specific index or all?
if ( *name == '*' && name[1] == 0 ) {
2009-09-27 14:46:51 -04:00
log(4) << " d->nIndexes was " << d->nIndexes << '\n';
2009-04-07 14:24:16 -04:00
anObjBuilder.append("nIndexesWas", (double)d->nIndexes);
IndexDetails *idIndex = 0;
2009-10-06 14:31:02 -04:00
if( d->nIndexes ) {
for ( int i = 0; i < d->nIndexes; i++ ) {
if ( !mayDeleteIdIndex && d->idx(i).isIdIndex() ) {
idIndex = &d->idx(i);
2011-01-04 00:40:41 -05:00
}
else {
d->idx(i).kill_idx();
}
}
2009-04-07 14:24:16 -04:00
d->nIndexes = 0;
}
if ( idIndex ) {
d->addIndex(ns) = *idIndex;
wassert( d->nIndexes == 1 );
}
/* assuming here that id index is not multikey: */
d->multiKeyIndexBits = 0;
assureSysIndexesEmptied(ns, idIndex);
2011-01-04 00:40:41 -05:00
anObjBuilder.append("msg", mayDeleteIdIndex ?
"indexes dropped for collection" :
"non-_id indexes dropped for collection");
2009-04-07 14:24:16 -04:00
}
else {
// delete just one index
int x = d->findIndexByName(name);
if ( x >= 0 ) {
log(4) << " d->nIndexes was " << d->nIndexes << endl;
2009-04-07 14:24:16 -04:00
anObjBuilder.append("nIndexesWas", (double)d->nIndexes);
2009-10-06 14:31:02 -04:00
2009-04-07 14:24:16 -04:00
/* note it is important we remove the IndexDetails with this
call, otherwise, on recreate, the old one would be reused, and its
IndexDetails::info ptr would be bad info.
*/
IndexDetails *id = &d->idx(x);
if ( !mayDeleteIdIndex && id->isIdIndex() ) {
errmsg = "may not delete _id index";
return false;
}
id->kill_idx();
d->multiKeyIndexBits = removeBit(d->multiKeyIndexBits, x);
2009-04-07 14:24:16 -04:00
d->nIndexes--;
for ( int i = x; i < d->nIndexes; i++ )
d->idx(i) = d->idx(i+1);
2011-01-04 00:40:41 -05:00
}
else {
int n = removeFromSysIndexes(ns, name); // just in case an orphaned listing there - i.e. should have been repaired but wasn't
2011-01-04 00:40:41 -05:00
if( n ) {
log() << "info: removeFromSysIndexes cleaned up " << n << " entries" << endl;
}
2010-01-22 15:58:49 -05:00
log() << "dropIndexes: " << name << " not found" << endl;
2009-04-07 14:24:16 -04:00
errmsg = "index not found";
return false;
}
}
return true;
}
2009-10-06 14:31:02 -04:00
/* drop collection */
class CmdDrop : public Command {
public:
CmdDrop() : Command("drop") { }
virtual bool logTheOp() {
return true;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2008-12-11 18:51:49 -05:00
return false;
}
2010-04-23 16:41:56 -04:00
virtual bool adminOnly() const {
2008-12-11 18:51:49 -05:00
return false;
}
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const { help << "drop a collection\n{drop : <collectionName>}"; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
string nsToDrop = dbname + '.' + cmdObj.firstElement().valuestr();
NamespaceDetails *d = nsdetails(nsToDrop.c_str());
2009-08-25 14:35:22 -04:00
if ( !cmdLine.quiet )
2010-05-19 12:11:17 -04:00
tlog() << "CMD: drop " << nsToDrop << endl;
if ( d == 0 ) {
errmsg = "ns not found";
return false;
}
uassert( 10039 , "can't drop collection with reserved $ character in name", strchr(nsToDrop.c_str(), '$') == 0 );
2009-05-11 10:45:10 -04:00
dropCollection( nsToDrop, errmsg, result );
return true;
}
} cmdDrop;
2008-12-11 18:51:49 -05:00
/* select count(*) */
class CmdCount : public Command {
public:
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
CmdCount() : Command("count") { }
virtual bool logTheOp() {
return false;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2009-10-06 14:31:02 -04:00
// ok on --slave setups, not ok for nonmaster of a repl pair (unless override)
return replSettings.slave == SimpleSlave;
}
2009-02-04 17:24:15 -05:00
virtual bool slaveOverrideOk() {
return true;
}
2010-04-23 16:41:56 -04:00
virtual bool adminOnly() const {
return false;
}
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const { help << "count objects in collection"; }
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
string err;
2009-03-24 12:12:04 -04:00
long long n = runCount(ns.c_str(), cmdObj, err);
long long nn = n;
bool ok = true;
2011-01-04 00:40:41 -05:00
if ( n == -1 ) {
2009-09-01 17:28:15 -04:00
nn = 0;
result.appendBool( "missing" , true );
}
else if ( n < 0 ) {
nn = 0;
2009-09-01 17:28:15 -04:00
ok = false;
if ( !err.empty() )
errmsg = err;
}
result.append("n", (double) nn);
return ok;
}
} cmdCount;
/* create collection */
class CmdCreate : public Command {
public:
CmdCreate() : Command("create") { }
virtual bool logTheOp() {
return false;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return false;
}
2010-04-23 16:41:56 -04:00
virtual bool adminOnly() const {
return false;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
help << "create a collection";
}
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
string err;
bool ok = userCreateNS(ns.c_str(), cmdObj, err, ! fromRepl );
if ( !ok && !err.empty() )
2008-12-11 18:51:49 -05:00
errmsg = err;
return ok;
2008-12-11 18:51:49 -05:00
}
} cmdCreate;
2008-12-11 18:51:49 -05:00
2010-01-22 15:58:49 -05:00
/* "dropIndexes" is now the preferred form - "deleteIndexes" deprecated */
class CmdDropIndexes : public Command {
public:
virtual bool logTheOp() {
return true;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return false;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
2009-09-01 16:23:56 -04:00
virtual void help( stringstream& help ) const {
2010-01-22 15:58:49 -05:00
help << "drop indexes for a collection";
2009-09-01 16:23:56 -04:00
}
2010-04-23 17:35:05 -04:00
CmdDropIndexes() : Command("dropIndexes", false, "deleteIndexes") { }
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& anObjBuilder, bool /*fromRepl*/) {
2010-04-23 17:35:05 -04:00
BSONElement e = jsobj.firstElement();
string toDeleteNs = dbname + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(toDeleteNs.c_str());
2009-08-25 14:35:22 -04:00
if ( !cmdLine.quiet )
2010-05-19 12:11:17 -04:00
tlog() << "CMD: dropIndexes " << toDeleteNs << endl;
if ( d ) {
BSONElement f = jsobj.getField("index");
2009-04-07 14:21:21 -04:00
if ( f.type() == String ) {
2010-01-22 15:58:49 -05:00
return dropIndexes( d, toDeleteNs.c_str(), f.valuestr(), errmsg, anObjBuilder, false );
2009-04-07 14:21:21 -04:00
}
2011-01-04 00:40:41 -05:00
else if ( f.type() == Object ) {
int idxId = d->findIndexByKeyPattern( f.embeddedObject() );
2011-01-04 00:40:41 -05:00
if ( idxId < 0 ) {
errmsg = "can't find index with key:";
errmsg += f.embeddedObject().toString();
return false;
}
else {
IndexDetails& ii = d->idx( idxId );
string iName = ii.indexName();
return dropIndexes( d, toDeleteNs.c_str(), iName.c_str() , errmsg, anObjBuilder, false );
}
}
2009-04-07 14:21:21 -04:00
else {
errmsg = "invalid index name spec";
return false;
}
}
else {
errmsg = "ns not found";
return false;
}
}
2010-01-22 15:58:49 -05:00
} cmdDropIndexes;
2009-10-06 14:31:02 -04:00
class CmdReIndex : public Command {
public:
virtual bool logTheOp() { return false; } // only reindexes on the one node
virtual bool slaveOk() const { return true; } // can reindex on a secondary
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
virtual void help( stringstream& help ) const {
help << "re-index a collection";
}
CmdReIndex() : Command("reIndex") { }
bool run(const string& dbname , BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) {
static DBDirectClient db;
2009-10-06 14:31:02 -04:00
2010-04-23 17:35:05 -04:00
BSONElement e = jsobj.firstElement();
string toDeleteNs = dbname + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(toDeleteNs.c_str());
2010-05-23 15:07:32 -04:00
tlog() << "CMD: reIndex " << toDeleteNs << endl;
BackgroundOperation::assertNoBgOpInProgForNs(toDeleteNs.c_str());
2009-10-06 14:31:02 -04:00
2011-01-04 00:40:41 -05:00
if ( ! d ) {
errmsg = "ns not found";
return false;
}
2009-10-06 14:31:02 -04:00
list<BSONObj> all;
auto_ptr<DBClientCursor> i = db.getIndexes( toDeleteNs );
BSONObjBuilder b;
2011-01-04 00:40:41 -05:00
while ( i->more() ) {
BSONObj o = i->next().getOwned();
b.append( BSONObjBuilder::numStr( all.size() ) , o );
all.push_back( o );
}
2009-10-06 14:31:02 -04:00
2010-01-22 15:58:49 -05:00
bool ok = dropIndexes( d, toDeleteNs.c_str(), "*" , errmsg, result, true );
2011-01-04 00:40:41 -05:00
if ( ! ok ) {
2010-01-22 15:58:49 -05:00
errmsg = "dropIndexes failed";
return false;
}
2009-10-06 14:31:02 -04:00
2011-01-04 00:40:41 -05:00
for ( list<BSONObj>::iterator i=all.begin(); i!=all.end(); i++ ) {
BSONObj o = *i;
theDataFileMgr.insertWithObjMod( Namespace( toDeleteNs.c_str() ).getSisterNS( "system.indexes" ).c_str() , o , true );
}
2009-10-06 14:31:02 -04:00
result.append( "ok" , 1 );
result.append( "nIndexes" , (int)all.size() );
result.appendArray( "indexes" , b.obj() );
return true;
}
} cmdReIndex;
2009-10-06 14:31:02 -04:00
class CmdListDatabases : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2009-02-09 17:22:26 -05:00
virtual bool slaveOverrideOk() {
return true;
}
2010-04-23 16:41:56 -04:00
virtual bool adminOnly() const {
return true;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const { help << "list databases on this server"; }
CmdListDatabases() : Command("listDatabases" , true ) {}
bool run(const string& dbname , BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) {
vector< string > dbNames;
getDatabaseNames( dbNames );
vector< BSONObj > dbInfos;
2009-10-06 14:31:02 -04:00
set<string> seen;
2009-04-17 13:33:44 -04:00
boost::intmax_t totalSize = 0;
for ( vector< string >::iterator i = dbNames.begin(); i != dbNames.end(); ++i ) {
BSONObjBuilder b;
b.append( "name", i->c_str() );
2009-04-17 13:33:44 -04:00
boost::intmax_t size = dbSize( i->c_str() );
b.append( "sizeOnDisk", (double) size );
Client::Context ctx( *i );
b.appendBool( "empty", ctx.db()->isEmpty() );
2009-04-17 13:33:44 -04:00
totalSize += size;
2009-02-09 13:04:32 -05:00
dbInfos.push_back( b.obj() );
seen.insert( i->c_str() );
}
2011-01-04 00:40:41 -05:00
2010-01-02 01:28:08 -05:00
// TODO: erh 1/1/2010 I think this is broken where path != dbpath ??
set<string> allShortNames;
dbHolder.getAllShortNames( allShortNames );
2011-01-04 00:40:41 -05:00
for ( set<string>::iterator i = allShortNames.begin(); i != allShortNames.end(); i++ ) {
string name = *i;
if ( seen.count( name ) )
continue;
2009-10-06 14:31:02 -04:00
2009-05-01 12:18:17 -04:00
BSONObjBuilder b;
b << "name" << name << "sizeOnDisk" << double( 1 );
Client::Context ctx( name );
b.appendBool( "empty", ctx.db()->isEmpty() );
2009-10-06 14:31:02 -04:00
2009-05-01 12:18:17 -04:00
dbInfos.push_back( b.obj() );
}
result.append( "databases", dbInfos );
2009-04-17 13:33:44 -04:00
result.append( "totalSize", double( totalSize ) );
return true;
}
} cmdListDatabases;
2008-12-29 13:24:24 -05:00
2011-01-04 00:40:41 -05:00
/* note an access to a database right after this will open it back up - so this is mainly
for diagnostic purposes.
*/
2009-02-17 11:11:13 -05:00
class CmdCloseAllDatabases : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual void help( stringstream& help ) const { help << "Close all database files.\nA new request will cause an immediate reopening; thus, this is mostly for testing purposes."; }
2010-04-23 16:41:56 -04:00
virtual bool adminOnly() const { return true; }
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return false; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
2010-04-23 15:50:49 -04:00
2009-02-17 11:11:13 -05:00
CmdCloseAllDatabases() : Command( "closeAllDatabases" ) {}
bool run(const string& dbname , BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) {
2011-01-19 17:31:52 -05:00
bool ok;
try {
ok = dbHolder.closeAll( dbpath , result, false );
}
catch(DBException&) {
throw;
}
catch(...) {
log() << "ERROR uncaught exception in command closeAllDatabases" << endl;
errmsg = "unexpected uncaught exception";
return false;
}
return ok;
2009-02-17 11:11:13 -05:00
}
} cmdCloseAllDatabases;
2009-02-17 11:16:13 -05:00
2009-02-11 10:37:27 -05:00
class CmdFileMD5 : public Command {
public:
2011-01-04 00:40:41 -05:00
CmdFileMD5() : Command( "filemd5" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2009-02-11 10:37:27 -05:00
return true;
}
2009-02-18 11:29:04 -05:00
virtual void help( stringstream& help ) const {
2010-04-13 20:53:48 -04:00
help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }";
2009-02-18 11:29:04 -05:00
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
string ns = dbname;
2009-02-18 13:11:22 -05:00
ns += ".";
{
string root = jsobj.getStringField( "root" );
if ( root.size() == 0 )
root = "fs";
ns += root;
}
ns += ".chunks"; // make this an option in jsobj
2009-10-06 14:31:02 -04:00
2009-02-11 10:37:27 -05:00
md5digest d;
md5_state_t st;
md5_init(&st);
BSONObj query = BSON( "files_id" << jsobj["filemd5"] );
BSONObj sort = BSON( "files_id" << 1 << "n" << 1 );
shared_ptr<Cursor> cursor = bestGuessCursor(ns.c_str(), query, sort);
scoped_ptr<ClientCursor> cc (new ClientCursor(QueryOption_NoCursorTimeout, cursor, ns.c_str()));
2009-10-06 14:31:02 -04:00
2009-02-11 10:37:27 -05:00
int n = 0;
2011-01-04 00:40:41 -05:00
while ( cursor->ok() ) {
if ( ! cursor->matcher()->matchesCurrent( cursor.get() ) ) {
log() << "**** NOT MATCHING ****" << endl;
PRINT(cursor->current());
cursor->advance();
continue;
}
BSONObj obj = cursor->current();
cursor->advance();
2010-05-19 19:27:04 -04:00
ClientCursor::YieldLock yield (cc);
try {
BSONElement ne = obj["n"];
assert(ne.isNumber());
int myn = ne.numberInt();
2011-01-04 00:40:41 -05:00
if ( n != myn ) {
log() << "should have chunk: " << n << " have:" << myn << endl;
2009-10-06 14:31:02 -04:00
DBDirectClient client;
Query q(query);
q.sort(sort);
auto_ptr<DBClientCursor> c = client.query(ns, q);
while(c->more())
PRINT(c->nextSafe());
uassert( 10040 , "chunks out of order" , n == myn );
}
2009-02-11 10:37:27 -05:00
int len;
const char * data = obj["data"].binDataClean( len );
md5_append( &st , (const md5_byte_t*)(data) , len );
n++;
2011-01-04 00:40:41 -05:00
}
catch (...) {
yield.relock(); // needed before yield goes out of scope
throw;
}
2011-01-04 00:40:41 -05:00
if ( ! yield.stillOk() ) {
uasserted(13281, "File deleted during filemd5 command");
}
2009-02-11 10:37:27 -05:00
}
2009-02-11 10:37:27 -05:00
md5_finish(&st, d);
2009-10-06 14:31:02 -04:00
2010-07-14 14:18:08 -04:00
result.append( "numChunks" , n );
2009-10-06 14:31:02 -04:00
result.append( "md5" , digestToString( d ) );
2009-02-11 10:37:27 -05:00
return true;
}
} cmdFileMD5;
2009-10-06 14:31:02 -04:00
static IndexDetails *cmdIndexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) {
if ( ns[ 0 ] == '\0' || min.isEmpty() || max.isEmpty() ) {
errmsg = "invalid command syntax (note: min and max are required)";
return 0;
}
return indexDetailsForRange( ns, errmsg, min, max, keyPattern );
}
2009-10-06 14:31:02 -04:00
2009-04-02 17:29:51 -04:00
class CmdDatasize : public Command {
public:
2010-04-23 15:50:49 -04:00
CmdDatasize() : Command( "dataSize", false, "datasize" ) {}
virtual bool slaveOk() const { return true; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
2009-04-02 17:29:51 -04:00
virtual void help( stringstream &help ) const {
2009-10-06 14:31:02 -04:00
help <<
2011-01-04 00:40:41 -05:00
"determine data size for a set of data in a certain range"
"\nexample: { dataSize:\"blog.posts\", keyPattern:{x:1}, min:{x:10}, max:{x:55} }"
"\nkeyPattern, min, and max parameters are optional."
"\nnote: This command may take a while to run";
2009-04-02 17:29:51 -04:00
}
2011-01-04 00:40:41 -05:00
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
Timer timer;
2010-04-28 12:19:46 -04:00
string ns = jsobj.firstElement().String();
2009-04-02 17:29:51 -04:00
BSONObj min = jsobj.getObjectField( "min" );
BSONObj max = jsobj.getObjectField( "max" );
BSONObj keyPattern = jsobj.getObjectField( "keyPattern" );
bool estimate = jsobj["estimate"].trueValue();
2009-04-02 17:29:51 -04:00
Client::Context ctx( ns );
NamespaceDetails *d = nsdetails(ns.c_str());
2011-01-04 00:40:41 -05:00
if ( ! d || d->stats.nrecords == 0 ) {
2010-08-24 01:39:38 -04:00
result.appendNumber( "size" , 0 );
result.appendNumber( "numObjects" , 0 );
result.append( "millis" , timer.millis() );
return true;
}
2011-01-04 00:40:41 -05:00
result.appendBool( "estimate" , estimate );
shared_ptr<Cursor> c;
2009-04-02 17:29:51 -04:00
if ( min.isEmpty() && max.isEmpty() ) {
2011-01-04 00:40:41 -05:00
if ( estimate ) {
2010-09-27 12:35:22 -04:00
result.appendNumber( "size" , d->stats.datasize );
result.appendNumber( "numObjects" , d->stats.nrecords );
result.append( "millis" , timer.millis() );
return 1;
}
2010-04-28 12:19:46 -04:00
c = theDataFileMgr.findAll( ns.c_str() );
2011-01-04 00:40:41 -05:00
}
2010-04-28 12:19:46 -04:00
else if ( min.isEmpty() || max.isEmpty() ) {
2009-04-02 17:29:51 -04:00
errmsg = "only one of min or max specified";
return false;
2011-01-04 00:40:41 -05:00
}
2010-04-28 12:19:46 -04:00
else {
IndexDetails *idx = cmdIndexDetailsForRange( ns.c_str(), errmsg, min, max, keyPattern );
if ( idx == 0 )
2009-04-02 17:29:51 -04:00
return false;
2011-01-04 00:40:41 -05:00
2010-07-16 01:49:15 -07:00
c.reset( new BtreeCursor( d, d->idxNo(*idx), *idx, min, max, false, 1 ) );
2009-04-02 17:29:51 -04:00
}
2011-01-04 00:40:41 -05:00
2010-09-27 12:35:22 -04:00
long long avgObjSize = d->stats.datasize / d->stats.nrecords;
2010-04-28 12:19:46 -04:00
long long maxSize = jsobj["maxSize"].numberLong();
long long maxObjects = jsobj["maxObjects"].numberLong();
2009-10-06 14:31:02 -04:00
2009-04-02 17:29:51 -04:00
long long size = 0;
2009-04-20 14:45:45 -04:00
long long numObjects = 0;
2009-04-02 17:29:51 -04:00
while( c->ok() ) {
if ( estimate )
size += avgObjSize;
else
size += c->currLoc().rec()->netLength();
2011-01-04 00:40:41 -05:00
2009-04-20 14:45:45 -04:00
numObjects++;
2011-01-04 00:40:41 -05:00
if ( ( maxSize && size > maxSize ) ||
( maxObjects && numObjects > maxObjects ) ) {
2010-04-28 12:19:46 -04:00
result.appendBool( "maxReached" , true );
break;
}
c->advance();
2009-04-02 17:29:51 -04:00
}
2010-07-02 11:43:26 -04:00
ostringstream os;
os << "Finding size for ns: " << ns;
2011-01-04 00:40:41 -05:00
if ( ! min.isEmpty() ) {
2010-07-02 11:43:26 -04:00
os << " between " << min << " and " << max;
2009-04-02 17:29:51 -04:00
}
logIfSlow( timer , os.str() );
2009-10-06 14:31:02 -04:00
result.appendNumber( "size", size );
result.appendNumber( "numObjects" , numObjects );
result.append( "millis" , timer.millis() );
2009-04-02 17:29:51 -04:00
return true;
}
} cmdDatasize;
namespace {
2011-01-04 00:40:41 -05:00
long long getIndexSizeForCollection(string db, string ns, BSONObjBuilder* details=NULL, int scale = 1 ) {
dbMutex.assertAtLeastReadLocked();
NamespaceDetails * nsd = nsdetails( ns.c_str() );
if ( ! nsd )
return 0;
2011-01-04 00:40:41 -05:00
long long totalSize = 0;
NamespaceDetails::IndexIterator ii = nsd->ii();
2011-01-04 00:40:41 -05:00
while ( ii.more() ) {
IndexDetails& d = ii.next();
string collNS = d.indexNamespace();
NamespaceDetails * mine = nsdetails( collNS.c_str() );
2011-01-04 00:40:41 -05:00
if ( ! mine ) {
log() << "error: have index [" << collNS << "] but no NamespaceDetails" << endl;
continue;
}
2010-09-27 12:35:22 -04:00
totalSize += mine->stats.datasize;
if ( details )
2010-09-27 12:35:22 -04:00
details->appendNumber( d.indexName() , mine->stats.datasize / scale );
}
return totalSize;
}
}
2009-08-12 13:53:52 -04:00
class CollectionStats : public Command {
public:
2010-04-23 16:41:56 -04:00
CollectionStats() : Command( "collStats", false, "collstats" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return true; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
2009-08-12 13:53:52 -04:00
virtual void help( stringstream &help ) const {
help << "{ collStats:\"blog.posts\" , scale : 1 } scale divides sizes e.g. for KB use 1024";
2009-08-12 13:53:52 -04:00
}
2011-01-04 00:40:41 -05:00
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
string ns = dbname + "." + jsobj.firstElement().valuestr();
Client::Context cx( ns );
2011-01-04 00:40:41 -05:00
2009-08-12 13:53:52 -04:00
NamespaceDetails * nsd = nsdetails( ns.c_str() );
2011-01-04 00:40:41 -05:00
if ( ! nsd ) {
2009-08-12 13:53:52 -04:00
errmsg = "ns not found";
return false;
}
2009-10-06 14:31:02 -04:00
2009-08-12 13:53:52 -04:00
result.append( "ns" , ns.c_str() );
2011-01-04 00:40:41 -05:00
2010-02-27 12:11:41 -05:00
int scale = 1;
2011-01-04 00:40:41 -05:00
if ( jsobj["scale"].isNumber() ) {
2010-02-27 12:11:41 -05:00
scale = jsobj["scale"].numberInt();
2011-01-04 00:40:41 -05:00
if ( scale <= 0 ) {
errmsg = "scale has to be > 0";
return false;
}
2011-01-04 00:40:41 -05:00
}
2011-01-04 00:40:41 -05:00
else if ( jsobj["scale"].trueValue() ) {
errmsg = "scale has to be a number > 0";
return false;
}
2009-10-06 14:31:02 -04:00
bool verbose = jsobj["verbose"].trueValue();
2010-09-27 12:35:22 -04:00
long long size = nsd->stats.datasize / scale;
result.appendNumber( "count" , nsd->stats.nrecords );
result.appendNumber( "size" , size );
2010-10-17 19:22:46 -04:00
if( nsd->stats.nrecords )
result.append ( "avgObjSize" , double(size) / double(nsd->stats.nrecords) );
2010-02-07 10:38:23 -05:00
int numExtents;
BSONArrayBuilder extents;
2011-01-04 00:40:41 -05:00
result.appendNumber( "storageSize" , nsd->storageSize( &numExtents , verbose ? &extents : 0 ) / scale );
2010-02-07 10:38:23 -05:00
result.append( "numExtents" , numExtents );
2009-08-12 13:53:52 -04:00
result.append( "nindexes" , nsd->nIndexes );
2010-02-27 12:11:41 -05:00
result.append( "lastExtentSize" , nsd->lastExtentSize / scale );
2010-02-07 10:38:23 -05:00
result.append( "paddingFactor" , nsd->paddingFactor );
result.append( "flags" , nsd->flags );
BSONObjBuilder indexSizes;
result.appendNumber( "totalIndexSize" , getIndexSizeForCollection(dbname, ns, &indexSizes, scale) / scale );
result.append("indexSizes", indexSizes.obj());
2011-01-04 00:40:41 -05:00
if ( nsd->capped ) {
2009-08-21 17:05:19 -04:00
result.append( "capped" , nsd->capped );
result.append( "max" , nsd->max );
}
2011-01-04 00:40:41 -05:00
if ( verbose )
result.appendArray( "extents" , extents.arr() );
2009-08-21 17:05:19 -04:00
2009-08-12 13:53:52 -04:00
return true;
}
} cmdCollectionStatis;
class DBStats : public Command {
public:
2010-04-23 16:41:56 -04:00
DBStats() : Command( "dbStats", false, "dbstats" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return true; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return READ; }
virtual void help( stringstream &help ) const {
2011-03-01 15:46:27 -05:00
help <<
"Get stats on a database. Not instantaneous. Slower for databases with large .ns files.\n" <<
"Example: { dbStats:1 }";
}
2011-01-04 00:40:41 -05:00
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
list<string> collections;
Database* d = cc().database();
if ( d )
d->namespaceIndex.getNamespaces( collections );
long long ncollections = 0;
long long objects = 0;
long long size = 0;
long long storageSize = 0;
long long numExtents = 0;
long long indexes = 0;
long long indexSize = 0;
2011-01-04 00:40:41 -05:00
for (list<string>::const_iterator it = collections.begin(); it != collections.end(); ++it) {
const string ns = *it;
NamespaceDetails * nsd = nsdetails( ns.c_str() );
2011-01-04 00:40:41 -05:00
if ( ! nsd ) {
errmsg = "missing ns: ";
errmsg += ns;
return false;
}
ncollections += 1;
2010-09-27 12:35:22 -04:00
objects += nsd->stats.nrecords;
size += nsd->stats.datasize;
int temp;
storageSize += nsd->storageSize( &temp );
numExtents += temp;
indexes += nsd->nIndexes;
indexSize += getIndexSizeForCollection(dbname, ns);
}
2011-01-27 17:00:51 -05:00
result.append ( "db" , dbname );
result.appendNumber( "collections" , ncollections );
result.appendNumber( "objects" , objects );
result.append ( "avgObjSize" , objects == 0 ? 0 : double(size) / double(objects) );
result.appendNumber( "dataSize" , size );
result.appendNumber( "storageSize" , storageSize);
result.appendNumber( "numExtents" , numExtents );
result.appendNumber( "indexes" , indexes );
result.appendNumber( "indexSize" , indexSize );
2010-06-03 19:46:53 -04:00
result.appendNumber( "fileSize" , d->fileSize() );
2011-03-01 15:46:27 -05:00
if( d )
result.appendNumber( "nsSizeMB", (int) d->namespaceIndex.fileLength() / 1024 / 1024 );
2010-10-13 16:11:32 -04:00
return true;
}
} cmdDBStats;
/* convertToCapped seems to use this */
2009-05-18 13:25:30 -04:00
class CmdCloneCollectionAsCapped : public Command {
public:
CmdCloneCollectionAsCapped() : Command( "cloneCollectionAsCapped" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return false; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
2009-05-18 13:25:30 -04:00
virtual void help( stringstream &help ) const {
2010-04-23 15:50:49 -04:00
help << "{ cloneCollectionAsCapped:<fromName>, toCollection:<toName>, size:<sizeInBytes> }";
2009-05-18 13:25:30 -04:00
}
2011-01-04 00:40:41 -05:00
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
2009-05-18 13:25:30 -04:00
string from = jsobj.getStringField( "cloneCollectionAsCapped" );
string to = jsobj.getStringField( "toCollection" );
long long size = (long long)jsobj.getField( "size" ).number();
2009-10-06 14:31:02 -04:00
2009-05-18 13:25:30 -04:00
if ( from.empty() || to.empty() || size == 0 ) {
errmsg = "invalid command spec";
return false;
}
string fromNs = dbname + "." + from;
string toNs = dbname + "." + to;
2009-05-18 13:25:30 -04:00
NamespaceDetails *nsd = nsdetails( fromNs.c_str() );
massert( 10301 , "source collection " + fromNs + " does not exist", nsd );
2010-09-27 12:35:22 -04:00
long long excessSize = nsd->stats.datasize - size * 2; // datasize and extentSize can't be compared exactly, so add some padding to 'size'
2009-05-18 13:25:30 -04:00
DiskLoc extent = nsd->firstExtent;
for( ; excessSize > extent.ext()->length && extent != nsd->lastExtent; extent = extent.ext()->xnext ) {
2009-05-18 13:25:30 -04:00
excessSize -= extent.ext()->length;
log( 2 ) << "cloneCollectionAsCapped skipping extent of size " << extent.ext()->length << endl;
2009-05-18 13:25:30 -04:00
log( 6 ) << "excessSize: " << excessSize << endl;
}
DiskLoc startLoc = extent.ext()->firstRecord;
2009-10-06 14:31:02 -04:00
2009-05-18 13:25:30 -04:00
CursorId id;
{
shared_ptr<Cursor> c = theDataFileMgr.findAll( fromNs.c_str(), startLoc );
2010-03-25 16:16:59 -04:00
ClientCursor *cc = new ClientCursor(0, c, fromNs.c_str());
id = cc->cursorid();
2009-05-18 13:25:30 -04:00
}
2009-10-06 14:31:02 -04:00
2009-05-18 13:25:30 -04:00
DBDirectClient client;
Client::Context ctx( toNs );
2009-05-18 13:25:30 -04:00
BSONObjBuilder spec;
spec.appendBool( "capped", true );
spec.append( "size", double( size ) );
if ( !userCreateNS( toNs.c_str(), spec.done(), errmsg, true ) )
return false;
2009-10-06 14:31:02 -04:00
2009-05-18 13:25:30 -04:00
auto_ptr< DBClientCursor > c = client.getMore( fromNs, id );
while( c->more() ) {
BSONObj obj = c->next();
theDataFileMgr.insertAndLog( toNs.c_str(), obj, true );
2009-05-18 13:25:30 -04:00
}
2009-10-06 14:31:02 -04:00
2009-05-18 13:25:30 -04:00
return true;
2009-10-06 14:31:02 -04:00
}
2009-05-18 13:25:30 -04:00
} cmdCloneCollectionAsCapped;
2009-10-06 14:31:02 -04:00
2011-01-04 00:40:41 -05:00
/* jan2010:
Converts the given collection to a capped collection w/ the specified size.
This command is not highly used, and is not currently supported with sharded
environments.
2010-01-21 13:40:18 -05:00
*/
2009-05-18 16:25:17 -04:00
class CmdConvertToCapped : public Command {
public:
CmdConvertToCapped() : Command( "convertToCapped" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return false; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
2009-05-18 16:25:17 -04:00
virtual void help( stringstream &help ) const {
2010-04-23 15:50:49 -04:00
help << "{ convertToCapped:<fromCollectionName>, size:<sizeInBytes> }";
2009-05-18 16:25:17 -04:00
}
2011-01-04 00:40:41 -05:00
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
BackgroundOperation::assertNoBgOpInProgForDb(dbname.c_str());
2009-05-18 16:25:17 -04:00
string from = jsobj.getStringField( "convertToCapped" );
long long size = (long long)jsobj.getField( "size" ).number();
2009-10-06 14:31:02 -04:00
2009-05-18 16:25:17 -04:00
if ( from.empty() || size == 0 ) {
errmsg = "invalid command spec";
return false;
}
2009-10-06 14:31:02 -04:00
2010-10-17 22:59:27 -04:00
string shortTmpName = str::stream() << ".tmp.convertToCapped." << from;
string longTmpName = str::stream() << dbname << "." << shortTmpName;
2009-05-18 16:25:17 -04:00
DBDirectClient client;
2010-10-17 22:59:27 -04:00
client.dropCollection( longTmpName );
2009-05-18 16:25:17 -04:00
BSONObj info;
if ( !client.runCommand( dbname ,
2011-01-04 00:40:41 -05:00
BSON( "cloneCollectionAsCapped" << from << "toCollection" << shortTmpName << "size" << double( size ) ),
info ) ) {
errmsg = "cloneCollectionAsCapped failed: " + info.toString();
2009-05-18 16:25:17 -04:00
return false;
}
2009-10-06 14:31:02 -04:00
if ( !client.dropCollection( dbname + "." + from ) ) {
2009-05-18 16:25:17 -04:00
errmsg = "failed to drop original collection";
return false;
}
2009-10-06 14:31:02 -04:00
2009-05-18 16:25:17 -04:00
if ( !client.runCommand( "admin",
2010-10-17 22:59:27 -04:00
BSON( "renameCollection" << longTmpName <<
"to" << ( dbname + "." + from ) ),
2011-01-04 00:40:41 -05:00
info ) ) {
errmsg = "renameCollection failed: " + info.toString();
2009-05-18 16:25:17 -04:00
return false;
}
return true;
}
} cmdConvertToCapped;
2009-12-30 20:08:43 -05:00
/* Find and Modify an object returning either the old (default) or new value*/
class CmdFindAndModify : public Command {
public:
2010-04-23 15:50:49 -04:00
virtual void help( stringstream &help ) const {
2011-01-04 00:40:41 -05:00
help <<
"{ findAndModify: \"collection\", query: {processed:false}, update: {$set: {processed:true}}, new: true}\n"
"{ findAndModify: \"collection\", query: {processed:false}, remove: true, sort: {priority:-1}}\n"
"Either update or remove is required, all other fields have default values.\n"
"Output is in the \"value\" field\n";
2010-04-23 15:50:49 -04:00
}
2010-04-23 16:41:56 -04:00
CmdFindAndModify() : Command("findAndModify", false, "findandmodify") { }
2009-12-30 20:08:43 -05:00
virtual bool logTheOp() {
return false; // the modification will be logged directly
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
2009-12-30 20:08:43 -05:00
return false;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return WRITE; }
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
2009-12-30 20:08:43 -05:00
static DBDirectClient db;
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
2009-12-30 20:08:43 -05:00
BSONObj origQuery = cmdObj.getObjectField("query"); // defaults to {}
Query q (origQuery);
2009-12-30 20:08:43 -05:00
BSONElement sort = cmdObj["sort"];
if (!sort.eoo())
q.sort(sort.embeddedObjectUserCheck());
bool upsert = cmdObj["upsert"].trueValue();
BSONObj fieldsHolder (cmdObj.getObjectField("fields"));
const BSONObj* fields = (fieldsHolder.isEmpty() ? NULL : &fieldsHolder);
BSONObj out = db.findOne(ns, q, fields);
2011-01-04 00:40:41 -05:00
if (out.isEmpty()) {
if (!upsert) {
errmsg = "No matching object found";
return false;
}
2009-12-30 20:08:43 -05:00
BSONElement update = cmdObj["update"];
uassert(13329, "upsert mode requires update field", !update.eoo());
uassert(13330, "upsert mode requires query field", !origQuery.isEmpty());
db.update(ns, origQuery, update.embeddedObjectUserCheck(), true);
BSONObj gle = db.getLastErrorDetailed();
2011-01-04 00:40:41 -05:00
if (gle["err"].type() == String) {
errmsg = gle["err"].String();
return false;
}
2010-07-02 14:56:45 -04:00
2011-01-04 00:40:41 -05:00
if (cmdObj["new"].trueValue()) {
2010-07-02 14:56:45 -04:00
BSONElement _id = gle["upserted"];
if (_id.eoo())
2010-07-02 14:56:45 -04:00
_id = origQuery["_id"];
out = db.findOne(ns, QUERY("_id" << _id), fields);
2010-05-11 15:10:07 -04:00
}
2011-01-04 00:40:41 -05:00
}
else {
2011-01-04 00:40:41 -05:00
if (cmdObj["remove"].trueValue()) {
uassert(12515, "can't remove and update", cmdObj["update"].eoo());
db.remove(ns, QUERY("_id" << out["_id"]), 1);
2011-01-04 00:40:41 -05:00
}
else { // update
2009-12-30 20:08:43 -05:00
2010-10-28 16:16:38 -04:00
BSONElement queryId = origQuery["_id"];
2011-01-04 00:40:41 -05:00
if (queryId.eoo() || getGtLtOp(queryId) != BSONObj::Equality) {
// need to include original query for $ positional operator
BSONObjBuilder b;
b.append(out["_id"]);
BSONObjIterator it(origQuery);
2011-01-04 00:40:41 -05:00
while (it.more()) {
BSONElement e = it.next();
if (strcmp(e.fieldName(), "_id"))
b.append(e);
}
q = Query(b.obj());
}
if (q.isComplex()) // update doesn't work with complex queries
q = Query(q.getFilter().getOwned());
BSONElement update = cmdObj["update"];
uassert(12516, "must specify remove or update", !update.eoo());
db.update(ns, q, update.embeddedObjectUserCheck());
BSONObj gle = db.getLastErrorDetailed();
2011-01-04 00:40:41 -05:00
if (gle["err"].type() == String) {
errmsg = gle["err"].String();
return false;
}
if (cmdObj["new"].trueValue())
out = db.findOne(ns, QUERY("_id" << out["_id"]), fields);
}
2009-12-30 20:08:43 -05:00
}
result.append("value", out);
return true;
}
} cmdFindAndModify;
2011-01-04 00:40:41 -05:00
/* Returns client's uri */
class CmdWhatsMyUri : public Command {
public:
CmdWhatsMyUri() : Command("whatsmyuri") { }
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return true;
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
virtual bool requiresAuth() {
return false;
}
virtual void help( stringstream &help ) const {
help << "{whatsmyuri:1}";
2011-01-04 00:40:41 -05:00
}
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
BSONObj info = cc().curop()->infoNoauth();
result << "you" << info[ "client" ];
return true;
}
} cmdWhatsMyUri;
2011-01-04 00:40:41 -05:00
/* For testing only, not for general use */
class GodInsert : public Command {
public:
GodInsert() : Command( "godinsert" ) { }
virtual bool logTheOp() {
return true;
}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const {
return false;
}
2010-04-23 15:50:49 -04:00
virtual LockType locktype() const { return WRITE; }
virtual bool requiresAuth() {
return true;
}
virtual void help( stringstream &help ) const {
2010-04-23 15:50:49 -04:00
help << "internal. for testing only.";
2011-01-04 00:40:41 -05:00
}
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
string coll = cmdObj[ "godinsert" ].valuestrsafe();
uassert( 13049, "godinsert must specify a collection", !coll.empty() );
string ns = dbname + "." + coll;
BSONObj obj = cmdObj[ "obj" ].embeddedObjectUserCheck();
2010-04-28 08:25:56 -04:00
DiskLoc loc = theDataFileMgr.insertWithObjMod( ns.c_str(), obj, true );
return true;
}
} cmdGodInsert;
2010-03-11 08:57:00 -05:00
class DBHashCmd : public Command {
public:
2011-01-04 00:40:41 -05:00
DBHashCmd() : Command( "dbHash", false, "dbhash" ) {}
2010-04-23 15:50:49 -04:00
virtual bool slaveOk() const { return true; }
virtual LockType locktype() const { return READ; }
2011-01-04 00:40:41 -05:00
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
list<string> colls;
Database* db = cc().database();
if ( db )
db->namespaceIndex.getNamespaces( colls );
2010-03-11 08:57:00 -05:00
colls.sort();
2011-01-04 00:40:41 -05:00
2010-03-11 08:57:00 -05:00
result.appendNumber( "numCollections" , (long long)colls.size() );
result.append( "host" , prettyHostName() );
2011-01-04 00:40:41 -05:00
2010-03-11 08:57:00 -05:00
md5_state_t globalState;
md5_init(&globalState);
BSONObjBuilder bb( result.subobjStart( "collections" ) );
2011-01-04 00:40:41 -05:00
for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) {
2010-03-11 08:57:00 -05:00
string c = *i;
if ( c.find( ".system.profil" ) != string::npos )
continue;
2011-01-04 00:40:41 -05:00
shared_ptr<Cursor> cursor;
2010-03-11 08:57:00 -05:00
NamespaceDetails * nsd = nsdetails( c.c_str() );
2011-01-04 00:40:41 -05:00
2010-03-30 11:58:21 -07:00
// debug SERVER-761
NamespaceDetails::IndexIterator ii = nsd->ii();
while( ii.more() ) {
const IndexDetails &idx = ii.next();
if ( !idx.head.isValid() || !idx.info.isValid() ) {
log() << "invalid index for ns: " << c << " " << idx.head << " " << idx.info;
if ( idx.info.isValid() )
log() << " " << idx.info.obj();
2010-03-30 11:58:21 -07:00
log() << endl;
}
}
2011-01-04 00:40:41 -05:00
2010-03-11 08:57:00 -05:00
int idNum = nsd->findIdIndex();
2011-01-04 00:40:41 -05:00
if ( idNum >= 0 ) {
2010-03-11 08:57:00 -05:00
cursor.reset( new BtreeCursor( nsd , idNum , nsd->idx( idNum ) , BSONObj() , BSONObj() , false , 1 ) );
}
2011-01-04 00:40:41 -05:00
else if ( c.find( ".system." ) != string::npos ) {
2010-03-11 08:57:00 -05:00
continue;
}
2011-01-04 00:40:41 -05:00
else if ( nsd->capped ) {
2010-03-11 08:57:00 -05:00
cursor = findTableScan( c.c_str() , BSONObj() );
}
else {
bb.done();
errmsg = (string)"can't find _id index for: " + c;
return 0;
}
md5_state_t st;
md5_init(&st);
2011-01-04 00:40:41 -05:00
2010-03-11 08:57:00 -05:00
long long n = 0;
2011-01-04 00:40:41 -05:00
while ( cursor->ok() ) {
2010-03-11 08:57:00 -05:00
BSONObj c = cursor->current();
md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
n++;
cursor->advance();
}
md5digest d;
md5_finish(&st, d);
string hash = digestToString( d );
2011-01-04 00:40:41 -05:00
2010-03-11 08:57:00 -05:00
bb.append( c.c_str() + ( dbname.size() + 1 ) , hash );
md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
}
bb.done();
md5digest d;
md5_finish(&globalState, d);
string hash = digestToString( d );
result.append( "md5" , hash );
return 1;
}
} dbhashCmd;
2010-07-14 10:28:38 -07:00
2010-08-02 15:00:47 -04:00
/* for diagnostic / testing purposes. */
2011-01-04 00:40:41 -05:00
class CmdSleep : public Command {
2010-08-02 15:00:47 -04:00
public:
2011-01-04 00:40:41 -05:00
virtual LockType locktype() const { return NONE; }
2010-08-02 15:00:47 -04:00
virtual bool adminOnly() const { return true; }
2010-08-16 09:00:58 -04:00
virtual bool logTheOp() { return false; }
virtual bool slaveOk() const { return true; }
2010-08-02 15:00:47 -04:00
virtual void help( stringstream& help ) const {
help << "internal testing command. Makes db block (in a read lock) for 100 seconds\n";
help << "w:true write lock";
}
CmdSleep() : Command("sleep") { }
bool run(const string& ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
2011-01-04 00:40:41 -05:00
int secs = 100;
if ( cmdObj["secs"].isNumber() )
secs = cmdObj["secs"].numberInt();
2011-01-04 00:40:41 -05:00
if( cmdObj.getBoolField("w") ) {
2010-08-02 15:00:47 -04:00
writelock lk("");
sleepsecs(secs);
2010-08-02 15:00:47 -04:00
}
else {
readlock lk("");
sleepsecs(secs);
2010-08-02 15:00:47 -04:00
}
2010-08-02 15:00:47 -04:00
return true;
}
} cmdSleep;
2010-07-26 15:34:16 -07:00
// just for testing
class CapTrunc : public Command {
public:
2011-01-04 00:40:41 -05:00
CapTrunc() : Command( "captrunc" ) {}
2010-07-26 15:34:16 -07:00
virtual bool slaveOk() const { return false; }
virtual LockType locktype() const { return WRITE; }
virtual bool requiresAuth() { return true; }
2011-01-04 00:40:41 -05:00
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
2010-07-26 15:34:16 -07:00
string coll = cmdObj[ "captrunc" ].valuestrsafe();
2010-07-26 15:45:40 -07:00
uassert( 13416, "captrunc must specify a collection", !coll.empty() );
2010-07-26 15:34:16 -07:00
string ns = dbname + "." + coll;
int n = cmdObj.getIntField( "n" );
2010-12-22 12:29:30 -05:00
// inclusive range?
2010-07-26 15:34:16 -07:00
bool inc = cmdObj.getBoolField( "inc" );
NamespaceDetails *nsd = nsdetails( ns.c_str() );
ReverseCappedCursor c( nsd );
2010-12-22 12:29:30 -05:00
massert( 13417, "captrunc collection not found or empty", c.ok() );
2010-07-26 15:34:16 -07:00
for( int i = 0; i < n; ++i ) {
2010-07-26 15:45:40 -07:00
massert( 13418, "captrunc invalid n", c.advance() );
2010-07-26 15:34:16 -07:00
}
DiskLoc end = c.currLoc();
nsd->cappedTruncateAfter( ns.c_str(), end, inc );
return true;
}
2011-01-04 00:40:41 -05:00
} capTruncCmd;
2010-08-02 14:26:14 -07:00
// just for testing
class EmptyCapped : public Command {
public:
2011-01-04 00:40:41 -05:00
EmptyCapped() : Command( "emptycapped" ) {}
2010-08-02 14:26:14 -07:00
virtual bool slaveOk() const { return false; }
virtual LockType locktype() const { return WRITE; }
virtual bool requiresAuth() { return true; }
2011-01-04 00:40:41 -05:00
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
2010-08-02 14:26:14 -07:00
string coll = cmdObj[ "emptycapped" ].valuestrsafe();
uassert( 13428, "emptycapped must specify a collection", !coll.empty() );
string ns = dbname + "." + coll;
NamespaceDetails *nsd = nsdetails( ns.c_str() );
massert( 13429, "emptycapped no such collection", nsd );
nsd->emptyCappedCollection( ns.c_str() );
return true;
}
2011-01-04 00:40:41 -05:00
} emptyCappedCmd;
/**
2010-02-12 13:21:33 -05:00
* this handles
- auth
- locking
- context
then calls run()
*/
bool execCommand( Command * c ,
2011-01-04 00:40:41 -05:00
Client& client , int queryOptions ,
const char *cmdns, BSONObj& cmdObj ,
BSONObjBuilder& result,
bool fromRepl ) {
string dbname = nsToDatabase( cmdns );
2010-02-12 13:21:33 -05:00
2011-01-04 00:40:41 -05:00
AuthenticationInfo *ai = client.getAuthenticationInfo();
if( c->adminOnly() && c->localHostOnlyIfNoAuth( cmdObj ) && noauth && !ai->isLocalHost ) {
result.append( "errmsg" ,
2010-02-12 13:21:33 -05:00
"unauthorized: this command must run from localhost when running db without auth" );
log() << "command denied: " << cmdObj.toString() << endl;
return false;
}
if ( c->adminOnly() && ! fromRepl && dbname != "admin" ) {
2010-08-18 11:50:52 -04:00
result.append( "errmsg" , "access denied; use admin db" );
2010-02-12 13:21:33 -05:00
log() << "command denied: " << cmdObj.toString() << endl;
return false;
2011-01-04 00:40:41 -05:00
}
2010-02-12 13:21:33 -05:00
2011-01-04 00:40:41 -05:00
if ( cmdObj["help"].trueValue() ) {
2010-02-12 13:21:33 -05:00
stringstream ss;
ss << "help for: " << c->name << " ";
c->help( ss );
result.append( "help" , ss.str() );
2010-03-11 11:20:10 -05:00
result.append( "lockType" , c->locktype() );
2010-02-12 13:21:33 -05:00
return true;
2011-01-04 00:40:41 -05:00
}
2010-02-12 13:21:33 -05:00
2011-01-04 00:40:41 -05:00
bool canRunHere =
2010-02-12 13:36:10 -05:00
isMaster( dbname.c_str() ) ||
2010-02-12 13:21:33 -05:00
c->slaveOk() ||
( c->slaveOverrideOk() && ( queryOptions & QueryOption_SlaveOk ) ) ||
fromRepl;
2011-01-04 00:40:41 -05:00
if ( ! canRunHere ) {
2010-02-12 13:21:33 -05:00
result.append( "errmsg" , "not master" );
return false;
}
if ( c->adminOnly() )
log( 2 ) << "command: " << cmdObj << endl;
2011-01-04 00:40:41 -05:00
if ( c->locktype() == Command::NONE ) {
2010-02-12 13:21:33 -05:00
// we also trust that this won't crash
string errmsg;
int ok = c->run( dbname , cmdObj , errmsg , result , fromRepl );
2010-02-12 13:21:33 -05:00
if ( ! ok )
result.append( "errmsg" , errmsg );
return ok;
}
2011-01-04 00:40:41 -05:00
2010-02-26 14:38:51 -05:00
bool needWriteLock = c->locktype() == Command::WRITE;
2011-01-04 00:40:41 -05:00
if ( ! needWriteLock ) {
2010-02-12 13:21:33 -05:00
assert( ! c->logTheOp() );
}
mongolock lk( needWriteLock );
Client::Context ctx( dbname , dbpath , &lk , c->requiresAuth() );
2011-01-04 00:40:41 -05:00
2010-02-12 13:21:33 -05:00
try {
string errmsg;
2011-01-04 00:40:41 -05:00
if ( ! c->run(dbname, cmdObj, errmsg, result, fromRepl ) ) {
2010-02-12 13:21:33 -05:00
result.append( "errmsg" , errmsg );
return false;
}
}
2011-01-04 00:40:41 -05:00
catch ( DBException& e ) {
2010-02-12 13:21:33 -05:00
stringstream ss;
2010-05-09 17:29:35 -04:00
ss << "exception: " << e.what();
2010-02-12 13:21:33 -05:00
result.append( "errmsg" , ss.str() );
2010-04-16 01:14:45 -04:00
result.append( "code" , e.getCode() );
2010-02-12 13:21:33 -05:00
return false;
}
2011-01-04 00:40:41 -05:00
if ( c->logTheOp() && ! fromRepl ) {
logOp("c", cmdns, cmdObj);
2010-02-12 13:21:33 -05:00
}
2011-01-04 00:40:41 -05:00
2010-02-12 13:21:33 -05:00
return true;
}
/* TODO make these all command objects -- legacy stuff here
usage:
abc.$cmd.findOne( { ismaster:1 } );
returns true if ran a cmd
*/
2009-12-29 23:30:29 -05:00
bool _runCommands(const char *ns, BSONObj& _cmdobj, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) {
2010-05-26 11:11:10 -04:00
cc().curop()->ensureStarted();
string dbname = nsToDatabase( ns );
2011-01-04 00:40:41 -05:00
if( logLevel >= 1 )
log() << "run command " << ns << ' ' << _cmdobj << endl;
2011-01-04 00:40:41 -05:00
const char *p = strchr(ns, '.');
if ( !p ) return false;
if ( strcmp(p, ".$cmd") != 0 ) return false;
2008-10-22 16:56:39 -04:00
BSONObj jsobj;
{
BSONElement e = _cmdobj.firstElement();
if ( e.type() == Object && string("query") == e.fieldName() ) {
jsobj = e.embeddedObject();
}
else {
jsobj = _cmdobj;
}
}
Client& client = cc();
bool ok = false;
2009-11-11 13:30:11 -05:00
BSONElement e = jsobj.firstElement();
2011-01-04 00:40:41 -05:00
2010-01-20 14:02:14 -05:00
Command * c = e.type() ? Command::findCommand( e.fieldName() ) : 0;
2010-05-26 11:11:10 -04:00
2011-01-04 00:40:41 -05:00
if ( c ) {
2010-02-12 13:21:33 -05:00
ok = execCommand( c , client , queryOptions , ns , jsobj , anObjBuilder , fromRepl );
2008-12-28 20:28:49 -05:00
}
2009-11-11 13:30:11 -05:00
else {
2011-01-21 12:44:13 -05:00
anObjBuilder.append("errmsg", str::stream() << "no such cmd: " << e.fieldName() );
2009-09-01 12:17:41 -04:00
anObjBuilder.append("bad cmd" , _cmdobj );
}
2010-05-26 11:11:10 -04:00
// switch to bool, but wait a bit longer before switching?
// anObjBuilder.append("ok", ok);
2010-07-16 01:49:15 -07:00
anObjBuilder.append("ok", ok?1.0:0.0);
BSONObj x = anObjBuilder.done();
b.appendBuf((void*) x.objdata(), x.objsize());
2010-05-26 11:11:10 -04:00
return true;
}
2011-01-04 00:40:41 -05:00
2009-01-14 17:09:51 -05:00
} // namespace mongo