summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2009-01-26 22:19:15 -0500
committerEliot Horowitz <eliot@10gen.com>2009-01-26 22:19:15 -0500
commit4ffda5ea25020b5b6e18392900ed52e20e7cb6e3 (patch)
tree8741dc7f1bac56cc194deb76e30757b36de30845
parentb29806e4a1821c32fc567c4208059cf1bf4a3552 (diff)
downloadmongo-4ffda5ea25020b5b6e18392900ed52e20e7cb6e3.tar.gz
dbshell
-rw-r--r--.gitignore13
-rw-r--r--SConstruct97
-rw-r--r--shell/MongoJS.cpp522
-rw-r--r--shell/MongoJS.h49
-rw-r--r--shell/ShellUtils.cpp188
-rw-r--r--shell/ShellUtils.h37
-rw-r--r--shell/collection.js269
-rw-r--r--shell/db.js327
-rw-r--r--shell/dbshell.cpp216
-rw-r--r--shell/md5.js279
-rw-r--r--shell/mongo.js60
-rw-r--r--shell/query.js159
-rw-r--r--shell/utils.js187
13 files changed, 2400 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 716ed63065d..34ea4c795e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
.jsdbshell
+.dbshell
.sconsign.dblite
+.sconf_temp
+
*~
*.o
*.aps
@@ -10,21 +13,31 @@
*.obj
*.opt
*.pch
+*.jsh
+*.jsall
+
db/Debug
db/db
db/dbgrid
db/dbtests
db/oplog*
db/.gdb*
+config.log
#temp dirs
dump
log
logs
+docs
# binaryies
mongodump
mongoimport
firstExample
+secondExample
libmongoclient.*
+libmongotestfiles.*
test
+authTest
+clientTest
+dbshell
diff --git a/SConstruct b/SConstruct
index f22d0cfcde0..c6268755e87 100644
--- a/SConstruct
+++ b/SConstruct
@@ -30,6 +30,12 @@ AddOption( "--32",
action="store",
help="whether to force 32 bit" )
+AddOption( "--release",
+ dest="release",
+ type="string",
+ nargs=0,
+ action="store",
+ help="relase build")
AddOption('--java',
dest='javaHome',
@@ -40,6 +46,13 @@ AddOption('--java',
metavar='DIR',
help='java home')
+AddOption( "--v8" ,
+ dest="v8home",
+ type="string",
+ nargs=1,
+ action="store",
+ metavar="dir",
+ help="v8 location")
# --- environment setup ---
@@ -111,6 +124,9 @@ elif "linux2" == os.sys.platform:
env.Append( LINKFLAGS="-Xlinker -rpath -Xlinker " + javaHome + "jre/lib/" + javaVersion + "/server" )
env.Append( LINKFLAGS="-Xlinker -rpath -Xlinker " + javaHome + "jre/lib/" + javaVersion )
+ if force32:
+ env.Append( LIBPATH["/usr/lib32"] )
+
nix = True
elif "win32" == os.sys.platform:
@@ -168,6 +184,8 @@ if nix:
env.Append( CXXFLAGS="-m32" )
env.Append( LINKFLAGS="-m32" )
+ if not GetOption( "release" ) is None:
+ env.Append( LINKFLAGS=" -static " )
# --- check system ---
@@ -193,6 +211,73 @@ conf.CheckLib( "boost_system-mt" )
env = conf.Finish()
+# --- v8 ---
+
+v8Home = GetOption( "v8home" )
+
+if not v8Home:
+ for poss in [ "../v8" , "../open-source/v8" ]:
+ if os.path.exists( poss ):
+ v8Home = poss
+ break
+
+# --- js concat ---
+
+def concatjs(target, source, env):
+
+ outFile = str( target[0] )
+
+ fullSource = ""
+
+ for s in source:
+ f = open( str(s) , 'r' )
+ for l in f:
+ fullSource += l
+
+ out = open( outFile , 'w' )
+ out.write( fullSource )
+
+ return None
+
+jsBuilder = Builder(action = concatjs,
+ suffix = '.jsall',
+ src_suffix = '.js')
+
+env.Append( BUILDERS={'JSConcat' : jsBuilder})
+
+# --- jsh ---
+
+def jsToH(target, source, env):
+
+ outFile = str( target[0] )
+ if len( source ) != 1:
+ raise Exception( "wrong" )
+
+ h = "const char * jsconcatcode = \n"
+
+ for l in open( str(source[0]) , 'r' ):
+ l = l.strip()
+ l = l.partition( "//" )[0]
+ l = l.replace( '\\' , "\\\\" )
+ l = l.replace( '"' , "\\\"" )
+
+
+ h += '"' + l + "\\n\"\n "
+
+ h += ";\n\n"
+
+ out = open( outFile , 'w' )
+ out.write( h )
+
+ return None
+
+jshBuilder = Builder(action = jsToH,
+ suffix = '.jsh',
+ src_suffix = '.jsall')
+
+env.Append( BUILDERS={'JSHeader' : jshBuilder})
+
+
# --- targets ----
clientEnv = env.Clone();
@@ -205,9 +290,10 @@ testEnv.Append( CPPPATH=["../"] )
testEnv.Append( LIBS=[ "unittest" , "libmongotestfiles.a" ] )
testEnv.Append( LIBPATH=["."] )
-# SYSTEM CHECKS
-configure = env.Configure()
-
+shellEnv = env.Clone();
+shellEnv.Append( CPPPATH=[ "../" , v8Home + "/include/" ] )
+shellEnv.Append( LIBS=[ "libmongoclient.a" , "v8" , "readline" , "history" ] )
+shellEnv.Append( LIBPATH=[ "." , v8Home] )
# ----- TARGETS ------
@@ -236,6 +322,11 @@ clientEnv.Program( "authTest" , [ "client/examples/authTest.cpp" ] )
test = testEnv.Program( "test" , Glob( "dbtests/*.cpp" ) )
clientEnv.Program( "clientTest" , [ "client/examples/clientTest.cpp" ] )
+# shell
+shellEnv.JSConcat( "shell/mongo.jsall" , Glob( "shell/*.js" ) )
+shellEnv.JSHeader( "shell/mongo.jsall" )
+dbshell = shellEnv.Program( "dbshell" , Glob( "shell/*.cpp" ) );
+
# ---- RUNNING TESTS ----
testEnv.Alias( "smoke", "test", test[ 0 ].abspath )
diff --git a/shell/MongoJS.cpp b/shell/MongoJS.cpp
new file mode 100644
index 00000000000..8ad69c61758
--- /dev/null
+++ b/shell/MongoJS.cpp
@@ -0,0 +1,522 @@
+// MongoJS.cpp
+
+#include "MongoJS.h"
+#include "ShellUtils.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace mongo;
+using namespace v8;
+
+#define CONN_STRING (String::New( "_conn" ))
+
+#define DDD(x)
+//#define DDD(x) ( cout << x << endl );
+
+void installMongoGlobals( Handle<ObjectTemplate>& global ){
+ global->Set(String::New("mongoInject"), FunctionTemplate::New(mongoInject));
+
+ v8::Local<v8::FunctionTemplate> mongo = FunctionTemplate::New( mongoInit );
+ global->Set(String::New("Mongo") , mongo );
+
+ v8::Local<v8::FunctionTemplate> db = FunctionTemplate::New( dbInit );
+ global->Set(String::New("DB") , db );
+ db->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+
+ v8::Local<v8::FunctionTemplate> dbCollection = FunctionTemplate::New( collectionInit );
+ global->Set(String::New("DBCollection") , dbCollection );
+ dbCollection->InstanceTemplate()->SetNamedPropertyHandler( collectionFallback );
+
+ v8::Local<v8::FunctionTemplate> dbQuery = FunctionTemplate::New( dbQueryInit );
+ global->Set(String::New("DBQuery") , dbQuery );
+ dbQuery->InstanceTemplate()->SetIndexedPropertyHandler( dbQueryIndexAccess );
+
+ v8::Local<v8::FunctionTemplate> objectId = FunctionTemplate::New( objectIdInit );
+ global->Set(String::New("ObjectId") , objectId );
+}
+
+Handle<Value> mongoInject(const Arguments& args){
+ jsassert( args.Length() == 1 , "mongoInject takes exactly 1 argument" );
+ jsassert( args[0]->IsObject() , "mongoInject needs to be massed a prototype" );
+
+ Local<v8::Object> o = args[0]->ToObject();
+
+ o->Set( String::New( "init" ) , FunctionTemplate::New( mongoInit )->GetFunction() );
+ o->Set( String::New( "find" ) , FunctionTemplate::New( mongoFind )->GetFunction() );
+ o->Set( String::New( "insert" ) , FunctionTemplate::New( mongoInsert )->GetFunction() );
+ o->Set( String::New( "remove" ) , FunctionTemplate::New( mongoRemove )->GetFunction() );
+ o->Set( String::New( "update" ) , FunctionTemplate::New( mongoUpdate )->GetFunction() );
+
+ Local<FunctionTemplate> t = FunctionTemplate::New( internalCursorCons );
+ t->PrototypeTemplate()->Set( String::New("next") , FunctionTemplate::New( internalCursorNext ) );
+ t->PrototypeTemplate()->Set( String::New("hasNext") , FunctionTemplate::New( internalCursorHasNext ) );
+ o->Set( String::New( "internalCursor" ) , t->GetFunction() );
+
+ return v8::Undefined();
+}
+
+Handle<Value> mongoInit(const Arguments& args){
+
+ char host[255];
+
+ if ( args.Length() > 0 && args[0]->IsString() ){
+ assert( args[0]->ToString()->Utf8Length() < 250 );
+ args[0]->ToString()->WriteAscii( host );
+ }
+ else {
+ strcpy( host , "127.0.0.1" );
+ }
+
+ DBClientConnection * conn = new DBClientConnection( true );
+
+ string errmsg;
+ if ( ! conn->connect( host , errmsg ) ){
+ cout << "couldn't connect: " << errmsg << endl;
+ jsassert( 0 , errmsg );
+ }
+
+ args.This()->Set( CONN_STRING , External::New( conn ) );
+
+
+ return v8::Undefined();
+}
+
+
+// ---
+
+Local<v8::Object> mongoToV8( BSONObj & m , bool array ){
+ Local<v8::Object> o;
+ if ( array )
+ o = v8::Array::New();
+ else
+ o = v8::Object::New();
+
+ mongo::BSONObj sub;
+
+ for ( BSONObjIterator i(m); i.more(); ) {
+ BSONElement f = i.next();
+ if ( f.eoo() )
+ break;
+
+ Local<Value> v;
+
+ switch ( f.type() ){
+
+ case mongo::Code:
+ cout << "warning, code saved in database just turned into string right now" << endl;
+ case mongo::String:
+ o->Set( v8::String::New( f.fieldName() ) , v8::String::New( f.valuestr() ) );
+ break;
+
+ case mongo::jstOID: {
+ v8::Function * idCons = getObjectIdCons();
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::String::New( f.__oid().str().c_str() );
+ o->Set( v8::String::New( f.fieldName() ) ,
+ idCons->NewInstance( 1 , argv ) );
+ break;
+ }
+
+ case mongo::NumberDouble:
+ case mongo::NumberInt:
+ o->Set( v8::String::New( f.fieldName() ) , v8::Number::New( f.number() ) );
+ break;
+
+ case mongo::Array:
+ case mongo::Object:
+ sub = f.embeddedObject();
+ o->Set( v8::String::New( f.fieldName() ) , mongoToV8( sub , f.type() == mongo::Array ) );
+ break;
+
+ case mongo::Date:
+ o->Set( v8::String::New( f.fieldName() ) , v8::Date::New( f.date() ) );
+ break;
+
+ case mongo::Bool:
+ o->Set( v8::String::New( f.fieldName() ) , v8::Boolean::New( f.boolean() ) );
+ break;
+
+ case mongo::jstNULL:
+ o->Set( v8::String::New( f.fieldName() ) , v8::Null() );
+ break;
+
+ case mongo::RegEx: {
+ v8::Function * regex = getNamedCons( "RegExp" );
+
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = v8::String::New( f.regex() );
+ argv[1] = v8::String::New( f.regexFlags() );
+
+ o->Set( v8::String::New( f.fieldName() ) , regex->NewInstance( 2 , argv ) );
+ break;
+ }
+
+ default:
+ cout << "can't handle type: ";
+ cout << f.type() << " ";
+ cout << f.toString();
+ cout << endl;
+ break;
+ }
+
+ }
+
+ return o;
+}
+
+void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value ){
+
+ if ( value->IsString() ){
+ b.append( sname.c_str() , toSTLString( value ).c_str() );
+ return;
+ }
+
+ if ( value->IsFunction() ){
+ b.appendCode( sname.c_str() , toSTLString( value ).c_str() );
+ return;
+ }
+
+ if ( value->IsNumber() ){
+ b.append( sname.c_str() , value->ToNumber()->Value() );
+ return;
+ }
+
+ if ( value->IsArray() ){
+ BSONObj sub = v8ToMongo( value->ToObject() );
+ b.appendArray( sname.c_str() , sub );
+ return;
+ }
+
+ if ( value->IsDate() ){
+ b.appendDate( sname.c_str() , v8::Date::Cast( *value )->NumberValue() );
+ return;
+ }
+
+ if ( value->IsObject() ){
+ string s = toSTLString( value );
+ if ( s.size() && s[0] == '/' ){
+ s = s.substr( 1 );
+ string r = s.substr( 0 , s.find( "/" ) );
+ string o = s.substr( s.find( "/" ) + 1 );
+ b.appendRegex( sname.c_str() , r.c_str() , o.c_str() );
+ }
+ else if ( value->ToObject()->GetPrototype()->IsObject() &&
+ value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( String::New( "isObjectId" ) ) ){
+ OID oid;
+ oid.init( toSTLString( value ) );
+ b.appendOID( sname.c_str() , &oid );
+ }
+ else {
+ BSONObj sub = v8ToMongo( value->ToObject() );
+ b.append( sname.c_str() , sub );
+ }
+ return;
+ }
+
+ if ( value->IsBoolean() ){
+ b.appendBool( sname.c_str() , value->ToBoolean()->Value() );
+ return;
+ }
+
+ else if ( value->IsUndefined() ){
+ return;
+ }
+
+ else if ( value->IsNull() ){
+ b.appendNull( sname.c_str() );
+ return;
+ }
+
+ cout << "don't know how to covert to mongo field [" << name << "]\t" << value << endl;
+}
+
+BSONObj v8ToMongo( v8::Handle<v8::Object> o ){
+ BSONObjBuilder b;
+
+ v8::Handle<v8::String> idName = String::New( "_id" );
+ if ( o->HasRealNamedProperty( idName ) ){
+ v8ToMongoElement( b , idName , "_id" , o->Get( idName ) );
+ }
+
+ Local<v8::Array> names = o->GetPropertyNames();
+ for ( unsigned int i=0; i<names->Length(); i++ ){
+ v8::Local<v8::String> name = names->Get(v8::Integer::New(i) )->ToString();
+
+ if ( o->GetPrototype()->IsObject() &&
+ o->GetPrototype()->ToObject()->HasRealNamedProperty( name ) )
+ continue;
+
+ v8::Local<v8::Value> value = o->Get( name );
+
+ const string sname = toSTLString( name );
+ if ( sname == "_id" )
+ continue;
+
+ v8ToMongoElement( b , name , sname , value );
+ }
+ return b.doneAndDecouple();
+}
+
+#define GETNS char ns[args[0]->ToString()->Utf8Length()]; args[0]->ToString()->WriteUtf8( ns );
+
+DBClientConnection * getConnection( const Arguments& args ){
+ Local<External> c = External::Cast( *(args.This()->Get( CONN_STRING )) );
+ DBClientConnection * conn = (DBClientConnection*)(c->Value());
+ assert( conn );
+ return conn;
+}
+
+// ---- real methods
+
+/**
+ 0 - namespace
+ 1 - query
+ 2 - fields
+ 3 - limit
+ 4 - skip
+ */
+Handle<Value> mongoFind(const Arguments& args){
+ jsassert( args.Length() == 5 , "find needs 5 args" );
+ jsassert( args[1]->IsObject() , "needs to be an object" );
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ BSONObj q = v8ToMongo( args[1]->ToObject() );
+ DDD( "query:" << q );
+
+ BSONObj fields;
+ bool haveFields = args[2]->IsObject() && args[2]->ToObject()->GetPropertyNames()->Length() > 0;
+ if ( haveFields )
+ fields = v8ToMongo( args[2]->ToObject() );
+
+ auto_ptr<mongo::DBClientCursor> cursor = conn->query( ns, q , args[3]->ToNumber()->Value() , args[4]->ToNumber()->Value() , haveFields ? &fields : 0 );
+
+ Local<v8::Object> mongo = args.This();
+
+ v8::Function * cons = (v8::Function*)( *( mongo->Get( String::New( "internalCursor" ) ) ) );
+ Local<v8::Object> c = cons->NewInstance();
+
+ c->Set( v8::String::New( "cursor" ) , External::New( cursor.release() ) );
+ return c;
+}
+
+v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args){
+ jsassert( args.Length() == 2 , "insert needs 2 args" );
+ jsassert( args[1]->IsObject() , "have to insert an object" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> in = args[1]->ToObject();
+
+ if ( ! in->Has( String::New( "_id" ) ) ){
+ v8::Handle<v8::Value> argv[0];
+ in->Set( String::New( "_id" ) , getObjectIdCons()->NewInstance( 0 , argv ) );
+ }
+
+ BSONObj o = v8ToMongo( in );
+
+ DDD( "want to save : " << o.jsonString() );
+ conn->insert( ns , o );
+
+ return args[1];
+}
+
+v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args){
+ jsassert( args.Length() == 2 , "remove needs 2 args" );
+ jsassert( args[1]->IsObject() , "have to remove an object template" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> in = args[1]->ToObject();
+ BSONObj o = v8ToMongo( in );
+
+ DDD( "want to remove : " << o.jsonString() );
+ conn->remove( ns , o );
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args){
+ jsassert( args.Length() >= 3 , "update needs at least 3 args" );
+ jsassert( args[1]->IsObject() , "1st param to update has to be an object" );
+ jsassert( args[2]->IsObject() , "2nd param to update has to be an object" );
+
+ DBClientConnection * conn = getConnection( args );
+ GETNS;
+
+ v8::Handle<v8::Object> q = args[1]->ToObject();
+ v8::Handle<v8::Object> o = args[2]->ToObject();
+
+ bool upsert = args.Length() > 3 && args[3]->IsBoolean() && args[3]->ToBoolean()->Value();
+
+ conn->update( ns , v8ToMongo( q ) , v8ToMongo( o ) , upsert );
+
+ return v8::Undefined();
+}
+
+
+
+
+// --- cursor ---
+
+mongo::DBClientCursor * getCursor( const Arguments& args ){
+ Local<External> c = External::Cast( *(args.This()->Get( String::New( "cursor" ) ) ) );
+ mongo::DBClientCursor * cursor = (mongo::DBClientCursor*)(c->Value());
+ return cursor;
+}
+
+v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args){
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args){
+ mongo::DBClientCursor * cursor = getCursor( args );
+ if ( ! cursor )
+ return v8::Undefined();
+ BSONObj o = cursor->next();
+ return mongoToV8( o );
+}
+
+v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args){
+ mongo::DBClientCursor * cursor = getCursor( args );
+ if ( ! cursor )
+ return Boolean::New( false );
+ return Boolean::New( cursor->more() );
+}
+
+
+// --- DB ----
+
+v8::Handle<v8::Value> dbInit(const v8::Arguments& args){
+ assert( args.Length() == 2 );
+
+ args.This()->Set( String::New( "_mongo" ) , args[0] );
+ args.This()->Set( String::New( "_name" ) , args[1] );
+
+ for ( int i=0; i<args.Length(); i++ )
+ assert( ! args[i]->IsUndefined() );
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ){
+ assert( args.Length() == 4 );
+
+ args.This()->Set( String::New( "_mongo" ) , args[0] );
+ args.This()->Set( String::New( "_db" ) , args[1] );
+ args.This()->Set( String::New( "_shortName" ) , args[2] );
+ args.This()->Set( String::New( "_fullName" ) , args[3] );
+
+ for ( int i=0; i<args.Length(); i++ )
+ assert( ! args[i]->IsUndefined() );
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ){
+
+ v8::Handle<v8::Object> t = args.This();
+
+ assert( args.Length() >= 4 );
+
+ t->Set( String::New( "_mongo" ) , args[0] );
+ t->Set( String::New( "_db" ) , args[1] );
+ t->Set( String::New( "_collection" ) , args[2] );
+ t->Set( String::New( "_ns" ) , args[3] );
+
+ if ( args.Length() > 4 && args[4]->IsObject() )
+ t->Set( String::New( "_query" ) , args[4] );
+ else
+ t->Set( String::New( "_query" ) , v8::Object::New() );
+
+ if ( args.Length() > 5 && args[5]->IsObject() )
+ t->Set( String::New( "_fields" ) , args[5] );
+ else
+ t->Set( String::New( "_fields" ) , v8::Null() );
+
+
+ if ( args.Length() > 6 && args[6]->IsNumber() )
+ t->Set( String::New( "_limit" ) , args[6] );
+ else
+ t->Set( String::New( "_limit" ) , Number::New( 0 ) );
+
+ if ( args.Length() > 7 && args[7]->IsNumber() )
+ t->Set( String::New( "_skip" ) , args[7] );
+ else
+ t->Set( String::New( "_skip" ) , Number::New( 0 ) );
+
+ t->Set( String::New( "_cursor" ) , v8::Null() );
+ t->Set( String::New( "_numReturned" ) , v8::Number::New(0) );
+ t->Set( String::New( "_special" ) , Boolean::New(false) );
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info) {
+ DDD( "collectionFallback [" << name << "]" );
+
+ v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get( name );
+ if ( ! real->IsUndefined() )
+ return real;
+
+ string sname = toSTLString( name );
+ if ( sname[0] == '_' ){
+ if ( ! ( info.This()->HasRealNamedProperty( name ) ) )
+ return v8::Undefined();
+ return info.This()->GetRealNamedPropertyInPrototypeChain( name );
+ }
+
+ v8::Handle<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get( String::New( "getCollection" ) );
+ assert( getCollection->IsFunction() );
+
+ v8::Function * f = (v8::Function*)(*getCollection);
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = name;
+
+ return f->Call( info.This() , 1 , argv );
+}
+
+v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::AccessorInfo& info ){
+ v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( String::New( "arrayAccess" ) );
+ assert( arrayAccess->IsFunction() );
+
+ v8::Function * f = (v8::Function*)(*arrayAccess);
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = v8::Number::New( index );
+
+ return f->Call( info.This() , 1 , argv );
+}
+
+v8::Function * getNamedCons( const char * name ){
+ return v8::Function::Cast( *(v8::Context::GetCurrent()->Global()->Get( String::New( name ) ) ) );
+}
+
+v8::Function * getObjectIdCons(){
+ return getNamedCons( "ObjectId" );
+}
+
+v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ){
+ v8::Handle<v8::Object> it = args.This();
+
+ if ( it->IsUndefined() || it == v8::Context::GetCurrent()->Global() ){
+ v8::Function * f = getObjectIdCons();
+ it = f->NewInstance();
+ }
+
+ OID oid;
+
+ if ( args.Length() == 0 ){
+ oid.init();
+ }
+ else {
+ string s = toSTLString( args[0] );
+ oid.init( s );
+ }
+
+ it->Set( String::New( "str" ) , String::New( oid.str().c_str() ) );
+
+ return it;
+}
diff --git a/shell/MongoJS.h b/shell/MongoJS.h
new file mode 100644
index 00000000000..483d342c866
--- /dev/null
+++ b/shell/MongoJS.h
@@ -0,0 +1,49 @@
+// MongoJS.h
+
+#pragma once
+
+#include <v8.h>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+
+#include "mongo/client/dbclient.h"
+
+void installMongoGlobals( v8::Handle<v8::ObjectTemplate>& global );
+
+// the actual globals
+v8::Handle<v8::Value> mongoInject(const v8::Arguments& args);
+
+// utils
+v8::Local<v8::Object> mongoToV8( mongo::BSONObj & m , bool array = 0 );
+mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o );
+
+mongo::DBClientConnection * getConnection( const v8::Arguments& args );
+
+
+
+// Mongo members
+v8::Handle<v8::Value> mongoInit(const v8::Arguments& args);
+v8::Handle<v8::Value> mongoFind(const v8::Arguments& args);
+v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args);
+v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args);
+v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args);
+
+
+v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args);
+v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args);
+v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args);
+
+// DB members
+
+v8::Handle<v8::Value> dbInit(const v8::Arguments& args);
+v8::Handle<v8::Value> collectionInit( const v8::Arguments& args );
+v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args );
+
+v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args );
+v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::AccessorInfo& info );
+
+v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, const v8::AccessorInfo &info);
+
+v8::Function * getNamedCons( const char * name );
+v8::Function * getObjectIdCons();
diff --git a/shell/ShellUtils.cpp b/shell/ShellUtils.cpp
new file mode 100644
index 00000000000..80ddb341bb3
--- /dev/null
+++ b/shell/ShellUtils.cpp
@@ -0,0 +1,188 @@
+// ShellUtils.cpp
+
+#include "ShellUtils.h"
+#include <boost/thread/thread.hpp>
+#include <boost/thread/xtime.hpp>
+#include <iostream>
+
+using namespace std;
+using namespace v8;
+
+v8::Handle<v8::Value> Print(const v8::Arguments& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ v8::HandleScope handle_scope;
+ if (first) {
+ first = false;
+ } else {
+ printf(" ");
+ }
+ v8::String::Utf8Value str(args[i]);
+ printf("%s", *str);
+ }
+ printf("\n");
+ return v8::Undefined();
+}
+
+std::string toSTLString( const v8::Handle<v8::Value> & o ){
+ v8::String::Utf8Value str(o);
+ const char * foo = *str;
+ std::string s(foo);
+ return s;
+}
+
+std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o ){
+ v8::String::Utf8Value str(o);
+ s << *str;
+ return s;
+}
+
+std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ){
+ v8::HandleScope handle_scope;
+ v8::String::Utf8Value exception(try_catch->Exception());
+ v8::Handle<v8::Message> message = try_catch->Message();
+
+ if (message.IsEmpty()) {
+ s << *exception << endl;
+ }
+ else {
+
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ int linenum = message->GetLineNumber();
+ cout << *filename << ":" << linenum << " " << *exception << endl;
+
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ cout << *sourceline << endl;
+
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++)
+ cout << " ";
+
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++)
+ cout << "^";
+
+ cout << endl;
+ }
+
+ if ( try_catch->next_ )
+ s << try_catch->next_;
+
+ return s;
+}
+
+v8::Handle<v8::Value> Load(const v8::Arguments& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ v8::HandleScope handle_scope;
+ v8::String::Utf8Value file(args[i]);
+ v8::Handle<v8::String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ return v8::ThrowException(v8::String::New("Error loading file"));
+ }
+ if (!ExecuteString(source, v8::String::New(*file), false, false)) {
+ return v8::ThrowException(v8::String::New("Error executing file"));
+ }
+ }
+ return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> Quit(const v8::Arguments& args) {
+ // If not arguments are given args[0] will yield undefined which
+ // converts to the integer value 0.
+ int exit_code = args[0]->Int32Value();
+ exit(exit_code);
+ return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> Version(const v8::Arguments& args) {
+ return v8::String::New(v8::V8::GetVersion());
+}
+
+v8::Handle<v8::String> ReadFile(const char* name) {
+ FILE* file = fopen(name, "rb");
+ if (file == NULL) return v8::Handle<v8::String>();
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = fread(&chars[i], 1, size - i, file);
+ i += read;
+ }
+ fclose(file);
+ v8::Handle<v8::String> result = v8::String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+bool ExecuteString(v8::Handle<v8::String> source, v8::Handle<v8::Value> name,
+ bool print_result, bool report_exceptions ){
+
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+
+ v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
+ if (script.IsEmpty()) {
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ }
+
+ v8::Handle<v8::Value> result = script->Run();
+ if ( result.IsEmpty() ){
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ }
+
+ if ( print_result ){
+
+ Local<Context> current = Context::GetCurrent();
+ Local<Object> global = current->Global();
+
+ Local<Value> shellPrint = global->Get( String::New( "shellPrint" ) );
+
+ if ( shellPrint->IsFunction() ){
+ v8::Function * f = (v8::Function*)(*shellPrint);
+ v8::Handle<v8::Value> argv[1];
+ argv[0] = result;
+ f->Call( global , 1 , argv );
+ }
+ else if ( ! result->IsUndefined() ){
+ cout << result << endl;
+ }
+ }
+
+ return true;
+}
+
+v8::Handle<v8::Value> JSSleep(const v8::Arguments& args){
+ assert( args.Length() == 1 );
+ assert( args[0]->IsNumber() );
+
+
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.nsec += args[0]->ToNumber()->Value() * 1000000;
+ boost::thread::sleep(xt);
+
+ return v8::Undefined();
+}
+
+void ReportException(v8::TryCatch* try_catch) {
+ cout << try_catch << endl;
+}
+
+void installShellUtils( v8::Handle<v8::ObjectTemplate>& global ){
+ global->Set(v8::String::New("sleep"), v8::FunctionTemplate::New(JSSleep));
+ global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
+ global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
+ global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
+ global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
+}
diff --git a/shell/ShellUtils.h b/shell/ShellUtils.h
new file mode 100644
index 00000000000..83b2790bf26
--- /dev/null
+++ b/shell/ShellUtils.h
@@ -0,0 +1,37 @@
+// ShellUtils.h
+
+#pragma once
+
+#include <v8.h>
+
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <assert.h>
+#include <iostream>
+
+// Executes a string within the current v8 context.
+bool ExecuteString(v8::Handle<v8::String> source,
+ v8::Handle<v8::Value> name,
+ bool print_result,
+ bool report_exceptions);
+
+v8::Handle<v8::Value> Print(const v8::Arguments& args);
+v8::Handle<v8::Value> Load(const v8::Arguments& args);
+v8::Handle<v8::Value> Quit(const v8::Arguments& args);
+v8::Handle<v8::Value> Version(const v8::Arguments& args);
+v8::Handle<v8::Value> JSSleep(const v8::Arguments& args);
+
+v8::Handle<v8::String> ReadFile(const char* name);
+
+
+void ReportException(v8::TryCatch* handler);
+
+
+void installShellUtils( v8::Handle<v8::ObjectTemplate>& global );
+
+#define jsassert(x,msg) assert(x)
+
+std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o );
+std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatch> * try_catch );
+std::string toSTLString( const v8::Handle<v8::Value> & o );
diff --git a/shell/collection.js b/shell/collection.js
new file mode 100644
index 00000000000..e679bad4d47
--- /dev/null
+++ b/shell/collection.js
@@ -0,0 +1,269 @@
+// collection.js
+
+
+if ( ( typeof DBCollection ) == "undefined" ){
+ print( "defined DBCollection" );
+
+ DBCollection = function( mongo , db , shortName , fullName ){
+ this._mongo = mongo;
+ this._db = db;
+ this._shortName = shortName;
+ this._fullName = fullName;
+
+ assert( this._mongo , "no mongo" );
+ assert( this._db , "no db" );
+ assert( this._shortName , "no shortName" );
+ assert( this._fullName , "no fullName" );
+ }
+}
+
+DBCollection.prototype.getName = function(){
+ return this._shortName;
+}
+
+DBCollection.prototype.help = function(){
+ print("DBCollection help");
+ print("\tdb.foo.getDB() get DB object associated with collection");
+ print("\tdb.foo.findOne(...)");
+ print("\tdb.foo.find(...)");
+ print("\tdb.foo.find(...).sort(...)");
+ print("\tdb.foo.find(...).limit(n)");
+ print("\tdb.foo.find(...).skip(n)");
+ print("\tdb.foo.find(...).count()");
+ print("\tdb.foo.count()");
+ print("\tdb.foo.save(obj)");
+ print("\tdb.foo.update(query, object[, upsert_bool])");
+ print("\tdb.foo.ensureIndex(keypattern)");
+ print("\tdb.foo.dropIndexes()");
+ print("\tdb.foo.dropIndex(name)");
+ print("\tdb.foo.getIndexes()");
+ print("\tdb.foo.drop() drop the collection");
+ print("\tdb.foo.validate()");
+}
+
+DBCollection.prototype.getFullName = function(){
+ return this._fullName;
+}
+DBCollection.prototype.getDB = function(){
+ return this._db;
+}
+
+DBCollection.prototype._dbCommand = function( cmd ){
+ return this._db._dbCommand( cmd );
+}
+
+DBCollection.prototype._massageObject = function( q ){
+ if ( ! q )
+ return {};
+
+ var type = typeof q;
+
+ if ( type == "function" )
+ return { $where : q };
+
+ if ( q.isObjectId )
+ return { _id : q };
+
+ if ( type == "object" )
+ return q;
+
+ if ( type == "string" ){
+ if ( q.length == 24 )
+ return { _id : q };
+
+ throw "don't know how to handle string [" + q + "]";
+ }
+
+ throw "don't know how to massage : " + type;
+
+}
+
+DBCollection.prototype._validateForStorage = function( o ){
+ for ( k in o ){
+ if ( k.indexOf( "." ) >= 0 )
+ throw "can't have . in field names [" + k + "]" ;
+ }
+}
+
+DBCollection.prototype.find = function( query , fields , limit , skip ){
+ return new DBQuery( this._mongo , this._db , this ,
+ this._fullName , this._massageObject( query ) , fields , limit , skip );
+}
+
+DBCollection.prototype.findOne = function( query , fields ){
+ var cursor = this._mongo.find( this._fullName , this._massageObject( query ) || {} , fields , 1 , 0 );
+ if ( ! cursor.hasNext() )
+ return null;
+ var ret = cursor.next();
+ if ( cursor.hasNext() ) throw "something is wrong";
+ if ( ret.$err )
+ throw "error " + tojson( ret );
+ return ret;
+}
+
+DBCollection.prototype.insert = function( obj ){
+ if ( ! obj )
+ throw "no object!";
+ this._validateForStorage( obj );
+ this._mongo.insert( this._fullName , obj );
+}
+
+DBCollection.prototype.remove = function( t ){
+ this._mongo.remove( this._fullName , this._massageObject( t ) );
+}
+
+DBCollection.prototype.update = function( query , obj , upsert ){
+ assert( query , "need a query" );
+ assert( obj , "need an object" );
+ return this._mongo.update( this._fullName , query , obj , upsert ? true : false );
+}
+
+DBCollection.prototype.save = function( obj ){
+ if ( ! obj._id ){
+ this.insert( obj );
+ }
+ else {
+ this.update( { _id : obj._id } , obj , true );
+ }
+}
+
+DBCollection.prototype._genIndexName = function( keys ){
+ var name = "";
+ for ( k in keys ){
+ if ( name.length > 0 )
+ name += "_";
+ name += k + "_";
+
+ var v = keys[k];
+ if ( typeof v == "number" )
+ name += v;
+ }
+ return name;
+}
+
+DBCollection.prototype.createIndex = function( keys , name ){
+ name = name || this._genIndexName( keys );
+ var o = { ns : this._fullName , key : keys , name : name };
+ this._db.getCollection( "system.indexes" ).insert( o );
+}
+
+DBCollection.prototype.ensureIndex = function( keys , name ){
+ name = name || this._genIndexName( keys );
+ this._indexCache = this._indexCache || {};
+ if ( this._indexCache[ name ] )
+ return false;
+
+ this.createIndex( keys , name );
+ this._indexCache[name] = true;
+ return true;
+}
+
+DBCollection.prototype.resetIndexCache = function(){
+ this._indexCache = {};
+}
+
+DBCollection.prototype.dropIndexes = function() {
+ this.resetIndexCache();
+
+ var res = this._db.runCommand( { deleteIndexes: this.getName(), index: "*" } );
+ assert( res , "no result from dropIndex result" );
+ if ( res.ok )
+ return res;
+
+ if ( res.errmsg.match( /not found/ ) )
+ return res;
+
+ throw "error dropping indexes : " + tojson( res );
+}
+
+
+DBCollection.prototype.drop = function(){
+ var res = this.dropIndexes();
+ if( ! res )
+ return res;
+
+ if( ! res.ok ) {
+ res.errmsg = "dropping indexes..." + res.errmsg;
+ return res;
+ }
+
+ res = this._db.runCommand( { drop: this.getName() } );
+ return res;
+}
+
+DBCollection.prototype.validate = function() {
+ var res = this._db.runCommand( { validate: this.getName() } );
+
+ res.valid = false;
+
+ if ( res.result ){
+ var str = "-" + tojson( res.result );
+ res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) );
+
+ var p = /lastExtentSize:(\d+)/;
+ var r = p.exec( str );
+ if ( r ){
+ res.lastExtentSize = Number( r[1] );
+ }
+ }
+
+ return res;
+}
+
+DBCollection.prototype.getIndexes = function(){
+ return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } );
+}
+
+DBCollection.prototype.getIndexKeys = function(){
+ return this.getIndexes().toArray().map(
+ function(i){
+ return i.key;
+ }
+ );
+}
+
+
+DBCollection.prototype.count = function(){
+ return this.find().count();
+}
+
+/**
+ * Drop free lists. Normally not used.
+ * Note this only does the collection itself, not the namespaces of its indexes (see cleanAll).
+ */
+DBCollection.prototype.clean = function() {
+ return this._dbCommand( { clean: this.getName() } );
+}
+
+
+
+/**
+ * <p>Drop a specified index.</p>
+ *
+ * <p>
+ * Name is the name of the index in the system.indexes name field. (Run db.system.indexes.find() to
+ * see example data.)
+ * </p>
+ *
+ * <p>Note : alpha: space is not reclaimed </p>
+ * @param {String} name of index to delete.
+ * @return A result object. result.ok will be true if successful.
+ */
+DBCollection.prototype.dropIndex = function(index) {
+ assert(index , "need to specify index to dropIndex" );
+
+ if ( ! isString( index ) && isObject( index ) )
+ index = this._genIndexName( index );
+
+ var res = this._dbCommand( { deleteIndexes: this.getName(), index: index } );
+ this.resetIndexCache();
+ return res;
+}
+
+DBCollection.prototype.getCollection = function( subName ){
+ return this._db.getCollection( this._shortName + "." + subName );
+}
+
+DBCollection.prototype.toString = function(){
+ return this.getFullName();
+}
diff --git a/shell/db.js b/shell/db.js
new file mode 100644
index 00000000000..ef2d6bf274c
--- /dev/null
+++ b/shell/db.js
@@ -0,0 +1,327 @@
+// db.js
+
+if ( typeof DB == "undefined" ){
+ DB = function( mongo , name ){
+ this._mongo = mongo;
+ this._name = name;
+ }
+}
+
+DB.prototype.getMongo = function(){
+ assert( this._mongo , "why no mongo!" );
+ return this._mongo;
+}
+
+DB.prototype.getName = function(){
+ return this._name;
+}
+
+DB.prototype.getCollection = function( name ){
+ return new DBCollection( this._mongo , this , name , this._name + "." + name );
+}
+
+DB.prototype.runCommand = function( obj ){
+ if ( typeof( obj ) == "string" ){
+ var n = {};
+ n[obj] = 1;
+ obj = n;
+ }
+ return this.getCollection( "$cmd" ).findOne( obj );
+}
+
+DB.prototype._dbCommand = DB.prototype.runCommand;
+
+DB.prototype.addUser = function( username , pass ){
+ var c = this.getCollection( "system.users" );
+
+ var u = c.findOne( { user : username } ) || { user : username };
+ u.pwd = hex_md5( "mongo" + pass );
+ print( tojson( u ) );
+
+ c.save( u );
+}
+
+DB.prototype.auth = function( username , pass ){
+ var n = this.runCommand( { getnonce : 1 } );
+
+ var a = this.runCommand(
+ {
+ authenticate : 1 ,
+ user : username ,
+ nonce : n.nonce ,
+ key : hex_md5( n.nonce + username + hex_md5( "mongo" + pass ) )
+ }
+ );
+
+ return a.ok;
+}
+
+/**
+ Create a new collection in the database. Normally, collection creation is automatic. You would
+ use this function if you wish to specify special options on creation.
+
+ If the collection already exists, no action occurs.
+
+ <p>Options:</p>
+ <ul>
+ <li>
+ size: desired initial extent size for the collection. Must be <= 1000000000.
+ for fixed size (capped) collections, this size is the total/max size of the
+ collection.
+ </li>
+ <li>
+ capped: if true, this is a capped collection (where old data rolls out).
+ </li>
+ <li> max: maximum number of objects if capped (optional).</li>
+ </ul>
+
+ <p>Example: </p>
+
+ <code>db.createCollection("movies", { size: 10 * 1024 * 1024, capped:true } );</code>
+
+ * @param {String} name Name of new collection to create
+ * @param {Object} options Object with options for call. Options are listed above.
+ * @return SOMETHING_FIXME
+*/
+DB.prototype.createCollection = function(name, opt) {
+ var options = opt || {};
+ var cmd = { create: name, capped: options.capped, size: options.size, max: options.max };
+ var res = this._dbCommand(cmd);
+ return res;
+}
+
+/**
+ * Returns the current profiling level of this database
+ * @return SOMETHING_FIXME or null on error
+ */
+ DB.prototype.getProfilingLevel = function() {
+ var res = this._dbCommand( { profile: -1 } );
+ return res ? res.was : null;
+}
+
+
+/**
+ Erase the entire database. (!)
+
+ * @return Object returned has member ok set to true if operation succeeds, false otherwise.
+*/
+DB.prototype.dropDatabase = function() {
+ return this._dbCommand( { dropDatabase: 1 } );
+}
+
+
+DB.prototype.help = function() {
+ print("DB methods:");
+ print("\tdb.auth(username, password)");
+ print("\tdb.getMongo() get the server connection object");
+ print("\tdb.getName()");
+ print("\tdb.getCollection(cname) same as db['cname'] or db.cname");
+ print("\tdb.runCommand(cmdObj) run a database command. if cmdObj is a string, turns it into { cmdObj : 1 }");
+ print("\tdb.addUser(username, password)");
+ print("\tdb.createCollection(name, { size : ..., capped : ..., max : ... } )");
+ print("\tdb.getProfilingLevel()");
+ print("\tdb.setProfilingLevel(level) 0=off 1=slow 2=all");
+ print("\tdb.eval(func, args) run code server-side");
+ print("\tdb.getLastError()");
+ print("\tdb.getPrevError()");
+ print("\tdb.resetError()");
+ print("\tdb.getCollectionNames()");
+ print("\tdb.group(ns, key[, keyf], cond, reduce, initial)");
+}
+
+/**
+ * <p> Set profiling level for your db. Profiling gathers stats on query performance. </p>
+ *
+ * <p>Default is off, and resets to off on a database restart -- so if you want it on,
+ * turn it on periodically. </p>
+ *
+ * <p>Levels :</p>
+ * <ul>
+ * <li>0=off</li>
+ * <li>1=log very slow (>100ms) operations</li>
+ * <li>2=log all</li>
+ * @param {String} level Desired level of profiling
+ * @return SOMETHING_FIXME or null on error
+ */
+DB.prototype.setProfilingLevel = function(level) {
+
+ if (level < 0 || level > 2) {
+ throw { dbSetProfilingException : "input level " + level + " is out of range [0..2]" };
+ }
+
+ if (level) {
+ // if already exists does nothing
+ this.createCollection("system.profile", { capped: true, size: 128 * 1024 } );
+ }
+ return this._dbCommand( { profile: level } );
+}
+
+
+/**
+ * <p> Evaluate a js expression at the database server.</p>
+ *
+ * <p>Useful if you need to touch a lot of data lightly; in such a scenario
+ * the network transfer of the data could be a bottleneck. A good example
+ * is "select count(*)" -- can be done server side via this mechanism.
+ * </p>
+ *
+ * <p>
+ * If the eval fails, an exception is thrown of the form:
+ * </p>
+ * <code>{ dbEvalException: { retval: functionReturnValue, ok: num [, errno: num] [, errmsg: str] } }</code>
+ *
+ * <p>Example: </p>
+ * <code>print( "mycount: " + db.eval( function(){db.mycoll.find({},{_id:ObjId()}).length();} );</code>
+ *
+ * @param {Function} jsfunction Javascript function to run on server. Note this it not a closure, but rather just "code".
+ * @return result of your function, or null if error
+ *
+ */
+DB.prototype.eval = function(jsfunction) {
+ var cmd = { $eval : jsfunction };
+ if ( arguments.length > 1 ) {
+ cmd.args = argumentsToArray( arguments ).slice(1);
+ }
+
+ var res = this._dbCommand( cmd );
+
+ if (!res.ok)
+ throw tojson( res );
+
+ return res.retval;
+}
+
+DB.prototype.dbEval = DB.prototype.eval;
+
+
+/**
+ *
+ * <p>
+ * Similar to SQL group by. For example: </p>
+ *
+ * <code>select a,b,sum(c) csum from coll where active=1 group by a,b</code>
+ *
+ * <p>
+ * corresponds to the following in 10gen:
+ * </p>
+ *
+ * <code>
+ db.group(
+ {
+ ns: "coll",
+ key: { a:true, b:true },
+ // keyf: ...,
+ cond: { active:1 },
+ reduce: function(obj,prev) { prev.csum += obj.c; } ,
+ initial: { csum: 0 }
+ });
+ </code>
+ *
+ *
+ * <p>
+ * An array of grouped items is returned. The array must fit in RAM, thus this function is not
+ * suitable when the return set is extremely large.
+ * </p>
+ * <p>
+ * To order the grouped data, simply sort it client side upon return.
+ * <p>
+ Defaults
+ cond may be null if you want to run against all rows in the collection
+ keyf is a function which takes an object and returns the desired key. set either key or keyf (not both).
+ * </p>
+*/
+DB.prototype.group = function(parmsObj) {
+
+ var groupFunction = function() {
+ var parms = args[0];
+ var c = db[parms.ns].find(parms.cond||{});
+ var map = new Map();
+
+ while( c.hasNext() ) {
+ var obj = c.next();
+
+ var key = {};
+ if( parms.key ) {
+ for( var i in parms.key )
+ key[i] = obj[i];
+ }
+ else {
+ key = parms.$keyf(obj);
+ }
+
+ var aggObj = map[key];
+ if( aggObj == null ) {
+ var newObj = Object.extend({}, key); // clone
+ aggObj = map[key] = Object.extend(newObj, parms.initial)
+ }
+ parms.$reduce(obj, aggObj);
+ }
+
+ var ret = map.values();
+ return ret;
+ }
+
+ var parms = Object.extend({}, parmsObj);
+
+ if( parms.reduce ) {
+ parms.$reduce = parms.reduce; // must have $ to pass to db
+ delete parms.reduce;
+ }
+
+ if( parms.keyf ) {
+ parms.$keyf = parms.keyf;
+ delete parms.keyf;
+ }
+
+ return this.eval(groupFunction, parms);
+}
+
+DB.prototype.resetError = function(){
+ return this.runCommand( { reseterror : 1 } );
+}
+
+DB.prototype.forceError = function(){
+ return this.runCommand( { forceerror : 1 } );
+}
+
+DB.prototype.getLastError = function(){
+ return this.runCommand( { getlasterror : 1 } ).err;
+}
+
+/* Return the last error which has occurred, even if not the very last error.
+
+ Returns:
+ { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 }
+
+ result.err will be null if no error has occurred.
+ */
+DB.prototype.getPrevError = function(){
+ return this.runCommand( { getpreverror : 1 } );
+}
+
+DB.prototype.getCollectionNames = function(){
+ var all = [];
+
+ var nsLength = this._name.length + 1;
+
+ this.getCollection( "system.namespaces" ).find().forEach(
+ function(z){
+ var name = z.name;
+
+ if ( name.indexOf( "$" ) >= 0 )
+ return;
+
+ all.push( name.substring( nsLength ) );
+ }
+ );
+ return all;
+}
+
+DB.prototype.tojson = function(){
+ return this.toString();
+}
+
+DB.prototype.toString = function(){
+ return this._name;
+}
+
diff --git a/shell/dbshell.cpp b/shell/dbshell.cpp
new file mode 100644
index 00000000000..4685056b2ae
--- /dev/null
+++ b/shell/dbshell.cpp
@@ -0,0 +1,216 @@
+// dbshell.cpp
+
+#include <v8.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "ShellUtils.h"
+#include "MongoJS.h"
+
+#include "mongo.jsh"
+
+
+void quitNicely( int sig ){
+ write_history( ".dbshell" );
+ exit(0);
+}
+
+int main(int argc, char* argv[]) {
+ signal( SIGINT , quitNicely );
+
+ //v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+
+ v8::HandleScope handle_scope;
+
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+
+ installShellUtils( global );
+ installMongoGlobals( global );
+
+
+ v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
+ v8::Context::Scope context_scope(context);
+
+ { // init mongo code
+ v8::HandleScope handle_scope;
+ if ( ! ExecuteString( v8::String::New( jsconcatcode ) , v8::String::New( "(mongo init)" ) , false , true ) )
+ return -1;
+ }
+
+ const char * host = "test";
+
+ string username;
+ string password;
+
+ bool runShell = false;
+
+ int argNumber = 1;
+ for ( ; argNumber < argc; argNumber++) {
+ const char* str = argv[argNumber];
+
+ if (strcmp(str, "--shell") == 0) {
+ runShell = true;
+ continue;
+ }
+
+ if ( strcmp( str , "-u" ) == 0 ){
+ username = argv[argNumber+1];
+ argNumber++;
+ continue;
+ }
+
+ if ( strcmp( str , "-p" ) == 0 ){
+ password = argv[argNumber+1];
+ argNumber++;
+ continue;
+ }
+
+ if ( strstr( str , "-p" ) == str ){
+ password = str;
+ password = password.substr( 2 );
+ continue;
+ }
+
+ if ( strcmp(str, "--help") == 0 ||
+ strcmp(str, "-h" ) == 0 ) {
+
+ cout
+ << "usage: ./dbshell [options] <db address> [file names]\n"
+ << "db address can be:\n"
+ << " foo = foo database on local machine\n"
+ << " 192.169.0.5/foo = foo database on 192.168.0.5 machine\n"
+ << " 192.169.0.5:9999/foo = foo database on 192.168.0.5 machine on port 9999\n"
+ << "options\n"
+ << " --shell run the shell after executing files\n"
+ << " -u <username>\n"
+ << " -p<password> - notice no space\n"
+ << "file names: a list of files to run. will exit after unless --shell is specified\n"
+ ;
+
+ return 0;
+ }
+
+ if (strcmp(str, "-f") == 0) {
+ continue;
+ }
+
+ if (strncmp(str, "--", 2) == 0) {
+ printf("Warning: unknown flag %s.\nTry --help for options\n", str);
+ continue;
+ }
+
+ {
+ const char * last = strstr( str , "/" );
+ if ( last )
+ last++;
+ else
+ last = str;
+
+ if ( ! strstr( last , "." ) ){
+ host = str;
+ continue;
+ }
+
+ }
+
+ break;
+ }
+
+ { // init mongo code
+ v8::HandleScope handle_scope;
+ string setup = (string)"db = connect( \"" + host + "\")";
+ if ( ! ExecuteString( v8::String::New( setup.c_str() ) , v8::String::New( "(connect)" ) , false , true ) ){
+ cout << "error connecting!" << endl;
+ return -1;
+ }
+
+ if ( username.size() && password.size() ){
+ stringstream ss;
+ ss << "if ( ! db.auth( \"" << username << "\" , \"" << password << "\" ) ){ throw 'login failed'; }";
+
+ if ( ! ExecuteString( v8::String::New( ss.str().c_str() ) , v8::String::New( "(auth)" ) , true , true ) ){
+ cout << "login failed" << endl;
+ return -1;
+ }
+
+
+ }
+
+ }
+
+ int numFiles = 0;
+
+ for ( ; argNumber < argc; argNumber++) {
+ const char* str = argv[argNumber];
+
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::String> file_name = v8::String::New(str);
+ v8::Handle<v8::String> source = ReadFile(str);
+ if (source.IsEmpty()) {
+ printf("Error reading '%s'\n", str);
+ return 1;
+ }
+
+ if (!ExecuteString(source, file_name, false, true)){
+ cout << "error processing: " << file_name << endl;
+ return 1;
+ }
+
+ numFiles++;
+ }
+
+ if ( numFiles == 0 )
+ runShell = true;
+
+ if ( runShell ){
+
+ using_history();
+ read_history( ".dbshell" );
+
+ cout << "type \"help\" for help" << endl;
+
+ v8::Handle<v8::Object> shellHelper = context->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
+
+ while ( 1 ){
+
+ char * line = readline( "> " );
+
+ if ( ! line || ( strlen(line) == 4 && strstr( line , "exit" ) ) ){
+ cout << "bye" << endl;
+ break;
+ }
+
+ string code = line;
+
+ {
+ string cmd = line;
+ if ( cmd.find( " " ) > 0 )
+ cmd = cmd.substr( 0 , cmd.find( " " ) );
+
+ if ( shellHelper->HasRealNamedProperty( v8::String::New( cmd.c_str() ) ) ){
+ stringstream ss;
+ ss << "shellHelper( \"" << cmd << "\" , \"" << code.substr( cmd.size() ) << "\" )";
+ code = ss.str();
+ }
+
+ }
+
+ v8::HandleScope handle_scope;
+ ExecuteString(v8::String::New( code.c_str() ),
+ v8::String::New("(shell)"),
+ true,
+ true);
+
+
+ if ( strlen( line ) )
+ add_history( line );
+ }
+
+ write_history( ".dbshell" );
+ }
+
+ return 0;
+}
+
+
diff --git a/shell/md5.js b/shell/md5.js
new file mode 100644
index 00000000000..9610e726432
--- /dev/null
+++ b/shell/md5.js
@@ -0,0 +1,279 @@
+/**
+* Copyright (C) 2008 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.
+*/
+
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
+var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){
+ var theCore = core_md5(str2binl(s), s.length * chrsz);
+ return binl2hex( theCore );
+}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+ var actual = hex_md5("abc");
+ return actual == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+ /* append padding */
+ x[len >> 5] |= 0x80 << ((len) % 32);
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for(var i = 0; i < x.length; i += 16)
+ {
+ var olda = a;
+ var oldb = b;
+ var oldc = c;
+ var oldd = d;
+
+ a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+ d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+ c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
+ b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+ a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+ d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
+
+ c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+ b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+ a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
+ d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+ c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+ b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+ a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
+ d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+ c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+ b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
+
+ a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+ d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+ c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
+ b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+ a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+ d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
+ c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+ b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+ a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
+ d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+ c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+ b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
+ a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+ d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+ c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
+ b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+
+ a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+ d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+ c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
+ b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+ a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+ d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
+ c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+ b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+ a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
+ d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+ c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+ b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
+ a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+ d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+ c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
+ b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+ a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+ d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
+ c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+ b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+ a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
+ d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+ c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+ b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+ a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
+ d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+ c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+ b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
+ a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+ d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+ c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
+ b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ }
+
+ return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+ var bkey = str2binl(key);
+ if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+ var ipad = Array(16), opad = Array(16);
+ for(var i = 0; i < 16; i = i + 1)
+ {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+
+ var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+ return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+ return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+ var bin = Array();
+ var mask = (1 << chrsz) - 1;
+ for(var i = 0; i < str.length * chrsz; i += chrsz)
+ bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+ return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+ var str = "";
+ var mask = (1 << chrsz) - 1;
+ for(var i = 0; i < bin.length * 32; i += chrsz)
+ str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+ var str = "";
+ for(var i = 0; i < binarray.length * 4; i = 1 + i)
+ {
+ str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+ hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
+ }
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var str = "";
+ for(var i = 0; i < binarray.length * 4; i += 3)
+ {
+ var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
+ | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+ | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+ for(var j = 0; j < 4; j = j + 1)
+ {
+ if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+ else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+ }
+ }
+ return str;
+}
diff --git a/shell/mongo.js b/shell/mongo.js
new file mode 100644
index 00000000000..001b6ff66be
--- /dev/null
+++ b/shell/mongo.js
@@ -0,0 +1,60 @@
+// mongo.js
+
+if ( typeof Mongo == "undefined" ){
+ Mongo = function( host ){
+ this.init( host );
+ }
+}
+
+Mongo.prototype.find = function( ns , query , fields , limit , skip ){ throw "find not implemented"; }
+Mongo.prototype.insert = function( ns , obj ){ throw "insert not implemented"; }
+Mongo.prototype.remove = function( ns , pattern ){ throw "remove not implemented;" }
+Mongo.prototype.update = function( ns , query , obj ){ throw "update not implemented;" }
+
+mongoInject( Mongo.prototype );
+
+Mongo.prototype.getDB = function( name ){
+ return new DB( this , name );
+}
+
+Mongo.prototype.getDBs = function(){
+ var res = this.getDB( "admin" ).runCommand( { "listDatabases" : 1 } );
+ assert( res.ok == 1 , "listDatabases failed" );
+ return res;
+}
+
+Mongo.prototype.getDBNames = function(){
+ return this.getDBs().databases.map(
+ function(z){
+ return z.name;
+ }
+ );
+}
+
+Mongo.prototype.toString = function(){
+ return "mongo connection";
+}
+
+connect = function( url , user , pass ){
+ if ( user && ! pass )
+ throw "you specified a user and not a password. either you need a password, or you're using the old connect api";
+
+ var idx = url.indexOf( "/" );
+
+ var db;
+
+ if ( idx < 0 )
+ db = new Mongo().getDB( url );
+ else
+ db = new Mongo( url.substring( 0 , idx ) ).getDB( url.substring( idx + 1 ) );
+
+ if ( user && pass ){
+ if ( ! db.auth( user , pass ) ){
+ throw "couldn't login";
+ }
+ }
+
+ return db;
+}
+
+
diff --git a/shell/query.js b/shell/query.js
new file mode 100644
index 00000000000..cce25b6edef
--- /dev/null
+++ b/shell/query.js
@@ -0,0 +1,159 @@
+// query.js
+
+if ( typeof DBQuery == "undefined" ){
+ print( "defining DBQuery" );
+ DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip ){
+
+ this._mongo = mongo; // 0
+ this._db = db; // 1
+ this._collection = collection; // 2
+ this._ns = ns; // 3
+
+ this._query = query || {}; // 4
+ this._fields = fields; // 5
+ this._limit = limit || 0; // 6
+ this._skip = skip || 0; // 7
+
+ this._cursor = null;
+ this._numReturned = 0;
+ this._special = false;
+ }
+}
+
+
+DBQuery.prototype.clone = function(){
+ var q = new DBQuery( this._mongo , this._db , this._collection , this._ns ,
+ this._query , this._fields ,
+ this._limit , this._skip );
+ q._special = this._special;
+ return q;
+}
+
+DBQuery.prototype._ensureSpecial = function(){
+ if ( this._special )
+ return;
+
+ var n = { query : this._query };
+ this._query = n;
+ this._special = true;
+}
+
+DBQuery.prototype._checkModify = function(){
+ if ( this._cursor )
+ throw "query already executed";
+}
+
+DBQuery.prototype._exec = function(){
+ if ( ! this._cursor ){
+ assert.eq( 0 , this._numReturned );
+ this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip );
+ this._cursorSeen = 0;
+ }
+ return this._cursor;
+}
+
+DBQuery.prototype.limit = function( limit ){
+ this._checkModify();
+ this._limit = limit;
+ return this;
+}
+
+DBQuery.prototype.skip = function( skip ){
+ this._checkModify();
+ this._skip = skip;
+ return this;
+}
+
+DBQuery.prototype.hasNext = function(){
+ this._exec();
+
+ if ( this._limit > 0 && this._cursorSeen >= this._limit )
+ return false;
+ var o = this._cursor.hasNext();
+ if ( o )
+ this._cursorSeen++;
+ return o;
+}
+
+DBQuery.prototype.next = function(){
+ this._exec();
+
+ var ret = this._cursor.next();
+ if ( ret.$err && this._numReturned == 0 && ! this.hasNext() )
+ throw "error: " + tojson( ret );
+
+ this._numReturned++;
+ return ret;
+}
+
+DBQuery.prototype.toArray = function(){
+ if ( this._arr )
+ return this._arr;
+
+ var a = [];
+ while ( this.hasNext() )
+ a.push( this.next() );
+ this._arr = a;
+ return a;
+}
+
+DBQuery.prototype.count = function(){
+ var cmd = { count: this._collection.getName() };
+ if ( this._query ){
+ if ( this._special )
+ cmd.query = this._query.query;
+ else
+ cmd.query = this._query;
+ }
+
+ var res = this._db.runCommand( cmd );
+ if( res && res.n != null ) return res.n;
+ throw { exception: "count failed", res: res };
+}
+
+DBQuery.prototype.length = function(){
+ return this.toArray().length;
+}
+
+DBQuery.prototype.sort = function( sortBy ){
+ this._ensureSpecial();
+ this._query.orderby = sortBy;
+ return this;
+}
+
+DBQuery.prototype.forEach = function( func ){
+ while ( this.hasNext() )
+ func( this.next() );
+}
+
+DBQuery.prototype.arrayAccess = function( idx ){
+ return this.toArray()[idx];
+}
+
+DBQuery.prototype.explain = function(){
+ var n = this.clone();
+ n._ensureSpecial();
+ n._query.$explain = true;
+ return n.next();
+}
+
+DBQuery.prototype.shellPrint = function(){
+ try {
+ var n = 0;
+ while ( this.hasNext() && n < 10 ){
+ var s = tojson( this.next() );
+ print( s );
+ n++;
+ }
+ if ( this.hasNext() )
+ print( "has more" );
+ }
+ catch ( e ){
+ print( e );
+ }
+
+}
+
+DBQuery.prototype.toString = function(){
+ return "DBQuery: " + this._ns + " -> " + tojson( this.query );
+}
diff --git a/shell/utils.js b/shell/utils.js
new file mode 100644
index 00000000000..a49d7a7f63b
--- /dev/null
+++ b/shell/utils.js
@@ -0,0 +1,187 @@
+
+assert = function( b , msg ){
+ if ( b )
+ return;
+
+ throw "assert failed : " + msg;
+}
+
+assert.eq = function( a , b , msg ){
+ if ( a == b )
+ return;
+
+ throw "[" + a + "] != [" + b + "] are not equal : " + msg;
+}
+
+Object.extend = function( dst , src ){
+ for ( var k in src ){
+ dst[k] = src[k];
+ }
+ return dst;
+}
+
+argumentsToArray = function( a ){
+ var arr = [];
+ for ( var i=0; i<a.length; i++ )
+ arr[i] = a[i];
+ return arr;
+}
+
+isString = function( x ){
+ return typeof( x ) == "string";
+}
+
+isObject = function( x ){
+ return typeof( x ) == "object";
+}
+
+String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g,"");
+}
+String.prototype.ltrim = function() {
+ return this.replace(/^\s+/,"");
+}
+String.prototype.rtrim = function() {
+ return this.replace(/\s+$/,"");
+}
+
+Date.prototype.tojson = function(){
+ return "\"" + this.toString() + "\"";
+}
+
+RegExp.prototype.tojson = RegExp.prototype.toString;
+
+Array.prototype.tojson = function(){
+ var s = "[";
+ for ( var i=0; i<this.length; i++){
+ if ( i > 0 )
+ s += ",";
+ s += tojson( this[i] );
+ }
+ s += "]";
+ return s;
+}
+
+ObjectId.prototype.toString = function(){
+ return this.str;
+}
+
+ObjectId.prototype.tojson = function(){
+ return "\"" + this.str + "\"";
+}
+
+ObjectId.prototype.isObjectId = true;
+
+tojson = function( x ){
+ if ( x == null || x == undefined )
+ return "";
+
+ switch ( typeof x ){
+
+ case "string":
+ return "\"" + x + "\"";
+
+ case "number":
+ case "boolean":
+ return "" + x;
+
+ case "object":
+ return tojsonObject( x );
+
+ default:
+ throw "can't handle type " + ( typeof v );
+ }
+
+}
+
+tojsonObject = function( x ){
+ assert( typeof x == "object" , "tojsonObject needs object" );
+
+ if ( x.tojson )
+ return x.tojson();
+
+ var s = "{";
+
+ var first = true;
+ for ( var k in x ){
+ if ( first ) first = false;
+ else s += " , ";
+
+ s += "\"" + k + "\" : " + tojson( x[k] );
+ }
+
+ return s + "}";
+}
+
+shellPrint = function( x ){
+ if ( x != undefined )
+ shellPrintHelper( x );
+
+ if ( db ){
+ var e = db.getPrevError();
+ if ( e.err ) {
+ if( e.nPrev <= 1 )
+ print( "error on last call: " + tojson( e.err ) );
+ else
+ print( "an error " + tojson(e.err) + " occurred " + e.nPrev + " operations back in the command invocation" );
+ }
+ db.resetError();
+ }
+}
+
+shellPrintHelper = function( x ){
+
+ if ( typeof x != "object" )
+ return print( x );
+
+ var p = x.shellPrint;
+ if ( typeof p == "function" )
+ return x.shellPrint();
+
+ var p = x.tojson;
+ if ( typeof p == "function" )
+ print( x.tojson() );
+ else
+ print( tojson( x ) );
+}
+
+shellHelper = function( command , rest ){
+ command = command.trim();
+ var args = rest.trim().replace(/;$/,"").split( "\s+" );
+
+ if ( ! shellHelper[command] )
+ throw "no command [" + command + "]";
+
+ return shellHelper[command].apply( null , args );
+}
+
+help = shellHelper.help = function(){
+ print( "HELP" );
+ print( "\t" + "show (dbs|collections|users)" );
+ print( "\t" + "use <db name>" );
+ print( "\t" + "db.help() help on DB methods");
+ print( "\t" + "db.foo.find()" );
+ print( "\t" + "db.foo.find( { a : 1 } )" );
+ print( "\t" + "db.foo.help() help on collection methods");
+}
+
+shellHelper.use = function( dbname ){
+ db = db.getMongo().getDB( dbname );
+ print( "switched to db " + db.getName() );
+}
+
+shellHelper.show = function( what ){
+ assert( typeof what == "string" );
+
+ if ( what == "users" )
+ return db.system.users.find();
+
+ if ( what == "collections" || what == "tables" )
+ return db.getCollectionNames();
+
+ if ( what == "dbs" )
+ return db.getMongo().getDBNames();
+
+ throw "don't know how to show [" + what + "]";
+
+}