summaryrefslogtreecommitdiff
path: root/db/commands.cpp
diff options
context:
space:
mode:
authordwight <dwight@Dwights-MacBook.local>2008-09-08 20:37:59 -0400
committerdwight <dwight@Dwights-MacBook.local>2008-09-08 20:37:59 -0400
commit4af0da7918b1794e85751ae3a67f1759f39182fd (patch)
treea4d6f5a8a08880a70931812563921370136e0e61 /db/commands.cpp
parent8dd8937576635dbe8da2ea4cce90854ab79aeb19 (diff)
downloadmongo-4af0da7918b1794e85751ae3a67f1759f39182fd.tar.gz
break out code to serparate file
Diffstat (limited to 'db/commands.cpp')
-rw-r--r--db/commands.cpp464
1 files changed, 464 insertions, 0 deletions
diff --git a/db/commands.cpp b/db/commands.cpp
new file mode 100644
index 00000000000..78c812e8f7d
--- /dev/null
+++ b/db/commands.cpp
@@ -0,0 +1,464 @@
+/* commands.cpp
+ db "commands" (sent via db.$cmd.findOne(...))
+ */
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* 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.
+*
+* 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.
+*
+* 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/>.
+*/
+
+#include "stdafx.h"
+#include "query.h"
+#include "pdfile.h"
+#include "jsobj.h"
+#include "../util/builder.h"
+#include <time.h>
+#include "introspect.h"
+#include "btree.h"
+#include "../util/lruishmap.h"
+#include "javajs.h"
+#include "json.h"
+#include "repl.h"
+
+extern int queryTraceLevel;
+extern int otherTraceLevel;
+extern int opLogging;
+bool userCreateNS(const char *ns, JSObj& j, string& err);
+void flushOpLog();
+int runCount(const char *ns, JSObj& cmd, string& err);
+
+const int edebug=0;
+
+bool dbEval(JSObj& cmd, JSObjBuilder& result) {
+ Element e = cmd.firstElement();
+ assert( e.type() == Code );
+ const char *code = e.valuestr();
+ if ( ! JavaJS ) {
+ result.append("errmsg", "db side execution is disabled");
+ return false;
+ }
+
+ jlong f = JavaJS->functionCreate(code);
+ if( f == 0 ) {
+ result.append("errmsg", "compile failed");
+ return false;
+ }
+
+ Scope s;
+ s.setString("$client", client->name.c_str());
+ Element args = cmd.findElement("args");
+ if( args.type() == Array ) {
+ JSObj eo = args.embeddedObject();
+ if( edebug ) {
+ cout << "args:" << eo.toString() << endl;
+ cout << "code:\n" << code << endl;
+ }
+ s.setObject("args", eo);
+ }
+
+ int res = s.invoke(f);
+ if( res ) {
+ result.append("errno", (double) res);
+ result.append("errmsg", "invoke failed");
+ return false;
+ }
+
+ int type = s.type("return");
+ if( type == Object || type == Array )
+ result.append("retval", s.getObject("return"));
+ else if( type == Number )
+ result.append("retval", s.getNumber("return"));
+ else if( type == String )
+ result.append("retval", s.getString("return").c_str());
+ else if( type == Bool ) {
+ result.appendBool("retval", s.getBoolean("return"));
+ }
+
+ return true;
+}
+
+void clean(const char *ns, NamespaceDetails *d) {
+ for( int i = 0; i < Buckets; i++ )
+ d->deletedList[i].Null();
+}
+
+string validateNS(const char *ns, NamespaceDetails *d) {
+ bool valid = true;
+ stringstream ss;
+ ss << "\nvalidate\n";
+ ss << " details: " << hex << d << " ofs:" << nsindex(ns)->detailsOffset(d) << dec << endl;
+ if( d->capped )
+ ss << " capped:" << d->capped << " max:" << d->max << '\n';
+
+ ss << " firstExtent:" << d->firstExtent.toString() << " ns:" << d->firstExtent.ext()->ns.buf << '\n';
+ ss << " lastExtent:" << d->lastExtent.toString() << " ns:" << d->lastExtent.ext()->ns.buf << '\n';
+ try {
+ d->firstExtent.ext()->assertOk();
+ d->lastExtent.ext()->assertOk();
+ } catch(...) { valid=false; ss << " extent asserted "; }
+
+ ss << " datasize?:" << d->datasize << " nrecords?:" << d->nrecords << " lastExtentSize:" << d->lastExtentSize << '\n';
+ ss << " padding:" << d->paddingFactor << '\n';
+ try {
+
+ try {
+ ss << " first extent:\n";
+ d->firstExtent.ext()->dump(ss);
+ valid = valid && d->firstExtent.ext()->validates();
+ }
+ catch(...) {
+ ss << "\n exception firstextent\n" << endl;
+ }
+
+ auto_ptr<Cursor> c = theDataFileMgr.findAll(ns);
+ int n = 0;
+ long long len = 0;
+ long long nlen = 0;
+ set<DiskLoc> recs;
+ int outOfOrder = 0;
+ DiskLoc cl_last;
+ while( c->ok() ) {
+ n++;
+
+ DiskLoc cl = c->currLoc();
+ if( n < 1000000 )
+ recs.insert(cl);
+ if( d->capped ) {
+ if( cl < cl_last )
+ outOfOrder++;
+ cl_last = cl;
+ }
+
+ Record *r = c->_current();
+ len += r->lengthWithHeaders;
+ nlen += r->netLength();
+ c->advance();
+ }
+ if( d->capped ) {
+ ss << " capped outOfOrder:" << outOfOrder;
+ if( outOfOrder > 1 ) {
+ valid = false;
+ ss << " ???";
+ }
+ else ss << " (OK)";
+ ss << '\n';
+ }
+ ss << " " << n << " objects found, nobj:" << d->nrecords << "\n";
+ ss << " " << len << " bytes data w/headers\n";
+ ss << " " << nlen << " bytes data wout/headers\n";
+
+ ss << " deletedList: ";
+ for( int i = 0; i < Buckets; i++ ) {
+ ss << (d->deletedList[i].isNull() ? '0' : '1');
+ }
+ ss << endl;
+ int ndel = 0;
+ long long delSize = 0;
+ int incorrect = 0;
+ for( int i = 0; i < Buckets; i++ ) {
+ DiskLoc loc = d->deletedList[i];
+ try {
+ int k = 0;
+ while( !loc.isNull() ) {
+ if( recs.count(loc) )
+ incorrect++;
+ ndel++;
+
+ if( loc.questionable() ) {
+ if( loc.a() <= 0 || strstr(ns, "hudsonSmall") == 0 ) {
+ ss << " ?bad deleted loc: " << loc.toString() << " bucket:" << i << " k:" << k << endl;
+ valid = false;
+ break;
+ }
+ }
+
+ DeletedRecord *d = loc.drec();
+ delSize += d->lengthWithHeaders;
+ loc = d->nextDeleted;
+ k++;
+ }
+ } catch(...) { ss <<" ?exception in deleted chain for bucket " << i << endl; valid = false; }
+ }
+ ss << " deleted: n: " << ndel << " size: " << delSize << '\n';
+ if( incorrect ) {
+ ss << " ?corrupt: " << incorrect << " records from datafile are in deleted list\n";
+ valid = false;
+ }
+
+ int idxn = 0;
+ try {
+ ss << " nIndexes:" << d->nIndexes << endl;
+ for( ; idxn < d->nIndexes; idxn++ ) {
+ ss << " " << d->indexes[idxn].indexNamespace() << " keys:" <<
+ d->indexes[idxn].head.btree()->fullValidate(d->indexes[idxn].head) << endl;
+ }
+ }
+ catch(...) {
+ ss << "\n exception during index validate idxn:" << idxn << endl; valid=false;
+ }
+
+ }
+ catch(AssertionException) {
+ ss << "\n exception during validate\n" << endl;
+ valid = false;
+ }
+
+ if( !valid )
+ ss << " ns corrupt, requires dbchk\n";
+
+ return ss.str();
+}
+
+// e.g.
+// system.cmd$.find( { queryTraceLevel: 2 } );
+//
+// returns true if ran a cmd
+//
+bool _runCommands(const char *ns, JSObj& jsobj, stringstream& ss, BufBuilder &b, JSObjBuilder& anObjBuilder) {
+
+ const char *p = strchr(ns, '.');
+ if( !p ) return false;
+ if( strcmp(p, ".$cmd") != 0 ) return false;
+
+ bool ok = false;
+ bool valid = false;
+
+ //cout << jsobj.toString() << endl;
+
+ Element e;
+ e = jsobj.firstElement();
+
+ if( e.eoo() ) goto done;
+ if( e.type() == Code ) {
+ valid = true;
+ ok = dbEval(jsobj, anObjBuilder);
+ }
+ else if( e.type() == Number ) {
+ if( strcmp(e.fieldName(), "getoptime") == 0 ) {
+ valid = true;
+ ok = true;
+ anObjBuilder.appendDate("optime", OpTime::now().asDate());
+ }
+ else if( strcmp(e.fieldName(), "dropDatabase") == 0 ) {
+ if( 1 ) {
+ cout << "dropDatabase " << ns << endl;
+ valid = true;
+ int p = (int) e.number();
+ if( p != 1 ) {
+ ok = false;
+ } else {
+ dropDatabase(ns);
+ logOp("c", ns, jsobj);
+ ok = true;
+ }
+ }
+ else {
+ cout << "TEMP CODE: dropdatabase commented out " << endl;
+ }
+ }
+ else if( strcmp(e.fieldName(), "profile") == 0 ) {
+ anObjBuilder.append("was", (double) client->profile);
+ int p = (int) e.number();
+ valid = true;
+ if( p == -1 )
+ ok = true;
+ else if( p >= 0 && p <= 2 ) {
+ ok = true;
+ client->profile = p;
+ }
+ else {
+ ok = false;
+ }
+ }
+ else {
+ // admin only commands.
+ if( strncmp(ns, "admin", p-ns) != 0 )
+ return false;
+ if( strcmp(e.fieldName(),"opLogging") == 0 ) {
+ valid = ok = true;
+ opLogging = (int) e.number();
+ flushOpLog();
+ log() << "CMD: opLogging set to " << opLogging << endl;
+ } else if( strcmp(e.fieldName(),"queryTraceLevel") == 0 ) {
+ valid = ok = true;
+ queryTraceLevel = (int) e.number();
+ } else if( strcmp(e.fieldName(),"traceAll") == 0 ) {
+ valid = ok = true;
+ queryTraceLevel = (int) e.number();
+ otherTraceLevel = (int) e.number();
+ }
+ }
+ }
+ else if( e.type() == String ) {
+ /* { count: "collectionname"[, query: <query>] } */
+ string us(ns, p-ns);
+
+ if( strcmp( e.fieldName(), "count" ) == 0 ) {
+ valid = true;
+ string ns = us + '.' + e.valuestr();
+ string err;
+ int n = runCount(ns.c_str(), jsobj, err);
+ int nn = n;
+ ok = true;
+ if( n < 0 ) {
+ ok = false;
+ nn = 0;
+ if( !err.empty() )
+ anObjBuilder.append("errmsg", err.c_str());
+ }
+ anObjBuilder.append("n", (double) nn);
+ }
+ else if( strcmp( e.fieldName(), "clone") == 0 ) {
+ valid = true;
+ string err;
+ ok = cloneFrom(e.valuestr(), err);
+ if( !err.empty() )
+ anObjBuilder.append("errmsg", err.c_str());
+ }
+ else if( strcmp( e.fieldName(), "create") == 0 ) {
+ valid = true;
+ string ns = us + '.' + e.valuestr();
+ string err;
+ ok = userCreateNS(ns.c_str(), jsobj, err);
+ if( ok )
+ logOp("c", ns.c_str(), jsobj);
+ if( !ok && !err.empty() )
+ anObjBuilder.append("errmsg", err.c_str());
+ }
+ else if( strcmp( e.fieldName(), "clean") == 0 ) {
+ valid = true;
+ string dropNs = us + '.' + e.valuestr();
+ NamespaceDetails *d = nsdetails(dropNs.c_str());
+ log() << "CMD: clean " << dropNs << endl;
+ if( d ) {
+ ok = true;
+ anObjBuilder.append("ns", dropNs.c_str());
+ clean(dropNs.c_str(), d);
+ }
+ else {
+ anObjBuilder.append("errmsg", "ns not found");
+ }
+ }
+ else if( strcmp( e.fieldName(), "drop") == 0 ) {
+ valid = true;
+ string nsToDrop = us + '.' + e.valuestr();
+ NamespaceDetails *d = nsdetails(nsToDrop.c_str());
+ log() << "CMD: drop " << nsToDrop << endl;
+ if( d == 0 ) {
+ anObjBuilder.append("errmsg", "ns not found");
+ }
+ else if( d->nIndexes != 0 ) {
+ // client is supposed to drop the indexes first
+ anObjBuilder.append("errmsg", "ns has indexes (not permitted on drop)");
+ }
+ else {
+ ok = true;
+ anObjBuilder.append("ns", nsToDrop.c_str());
+ ClientCursor::invalidate(nsToDrop.c_str());
+ dropNS(nsToDrop);
+ logOp("c", ns, jsobj);
+ /*
+ {
+ JSObjBuilder b;
+ b.append("name", dropNs.c_str());
+ JSObj cond = b.done(); // { name: "colltodropname" }
+ deleteObjects("system.namespaces", cond, false, true);
+ }
+ client->namespaceIndex.kill(dropNs.c_str());
+ */
+ }
+ }
+ else if( strcmp( e.fieldName(), "validate") == 0 ) {
+ valid = true;
+ string toValidateNs = us + '.' + e.valuestr();
+ NamespaceDetails *d = nsdetails(toValidateNs.c_str());
+ log() << "CMD: validate " << toValidateNs << endl;
+ if( d ) {
+ ok = true;
+ anObjBuilder.append("ns", toValidateNs.c_str());
+ string s = validateNS(toValidateNs.c_str(), d);
+ anObjBuilder.append("result", s.c_str());
+ }
+ else {
+ anObjBuilder.append("errmsg", "ns not found");
+ }
+ }
+ else if( strcmp(e.fieldName(),"deleteIndexes") == 0 ) {
+ valid = true;
+ /* note: temp implementation. space not reclaimed! */
+ string toDeleteNs = us + '.' + e.valuestr();
+ NamespaceDetails *d = nsdetails(toDeleteNs.c_str());
+ log() << "CMD: deleteIndexes " << toDeleteNs << endl;
+ if( d ) {
+ Element f = jsobj.findElement("index");
+ if( !f.eoo() ) {
+
+ d->aboutToDeleteAnIndex();
+
+ ClientCursor::invalidate(toDeleteNs.c_str());
+ logOp("c", ns, jsobj);
+
+ // delete a specific index or all?
+ if( f.type() == String ) {
+ const char *idxName = f.valuestr();
+ if( *idxName == '*' && idxName[1] == 0 ) {
+ ok = true;
+ log() << " d->nIndexes was " << d->nIndexes << '\n';
+ anObjBuilder.append("nIndexesWas", (double)d->nIndexes);
+ anObjBuilder.append("msg", "all indexes deleted for collection");
+ for( int i = 0; i < d->nIndexes; i++ )
+ d->indexes[i].kill();
+ d->nIndexes = 0;
+ log() << " alpha implementation, space not reclaimed" << endl;
+ }
+ else {
+ // delete just one index
+ int x = d->findIndexByName(idxName);
+ if( x >= 0 ) {
+ cout << " d->nIndexes was " << d->nIndexes << endl;
+ anObjBuilder.append("nIndexesWas", (double)d->nIndexes);
+
+ /* 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.
+ */
+ d->indexes[x].kill();
+
+ d->nIndexes--;
+ for( int i = x; i < d->nIndexes; i++ )
+ d->indexes[i] = d->indexes[i+1];
+ ok=true;
+ cout << " alpha implementation, space not reclaimed\n";
+ } else {
+ cout << "deleteIndexes: " << idxName << " not found" << endl;
+ }
+ }
+ }
+ }
+ }
+ else {
+ anObjBuilder.append("errmsg", "ns not found");
+ }
+ }
+ }
+
+done:
+ if( !valid )
+ anObjBuilder.append("errmsg", "no such cmd");
+ anObjBuilder.append("ok", ok?1.0:0.0);
+ JSObj x = anObjBuilder.done();
+ b.append((void*) x.objdata(), x.objsize());
+ return true;
+}
+