Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'smshell'

Conflicts:
	jstests/count.js
	scripting/engine_spidermonkey.cpp
  • Loading branch information...
commit 2db1e0ca72753543cbb468e95442396149169f08 2 parents 6b8c0c1 + 0516fad
@erh erh authored
Showing with 1,161 additions and 266 deletions.
  1. +6 −31 SConstruct
  2. +6 −0 db/jsobj.h
  3. +1 −1  db/matcher.cpp
  4. +102 −1 dbtests/jstests.cpp
  5. +13 −12 jstests/apitest_dbcollection.js
  6. +11 −10 jstests/count.js
  7. +8 −0 jstests/date1.js
  8. +8 −8 jstests/drop.js
  9. +1 −1  jstests/hint1.js
  10. +2 −2 jstests/index3.js
  11. +5 −5 jstests/objid1.js
  12. +1 −1  jstests/objid3.js
  13. +20 −0 jstests/query1.js
  14. +8 −0 jstests/repl/pair1.js
  15. +14 −0 jstests/sub1.js
  16. +4 −4 jstests/update3.js
  17. +3 −2 jstests/where1.js
  18. +4 −0 mongo.xcodeproj/project.pbxproj
  19. +1 −1  s/config.cpp
  20. +5 −0 s/server.cpp
  21. +24 −0 scripting/engine.cpp
  22. +6 −3 scripting/engine.h
  23. +125 −8 scripting/engine_spidermonkey.cpp
  24. +6 −1 scripting/engine_spidermonkey.h
  25. +283 −18 scripting/sm_db.cpp
  26. +0 −82 shell/ShellUtils.cpp
  27. +6 −4 shell/collection.js
  28. +1 −1  shell/db.js
  29. +31 −56 shell/dbshell.cpp
  30. +1 −0  shell/query.js
  31. +394 −0 shell/utils.cpp
  32. +23 −0 shell/utils.h
  33. +30 −14 shell/utils.js
  34. +8 −0 tools/Tool.cpp
View
37 SConstruct
@@ -207,12 +207,13 @@ coreServerFiles = [ "util/message_server_port.cpp" , "util/message_server_asio.c
serverOnlyFiles = Split( "db/query.cpp db/introspect.cpp db/btree.cpp db/clientcursor.cpp db/tests.cpp db/repl.cpp db/btreecursor.cpp db/cloner.cpp db/namespace.cpp db/matcher.cpp db/dbcommands.cpp db/dbeval.cpp db/dbwebserver.cpp db/dbinfo.cpp db/dbhelpers.cpp db/instance.cpp db/pdfile.cpp db/cursor.cpp db/security_commands.cpp db/security.cpp util/miniwebserver.cpp db/storage.cpp db/reccache.cpp db/queryoptimizer.cpp" )
if usesm:
- serverOnlyFiles += [ "scripting/engine_spidermonkey.cpp" ]
+ commonFiles += [ "scripting/engine_spidermonkey.cpp" ]
nojni = True
elif not nojni:
- serverOnlyFiles += [ "scripting/engine_java.cpp" ]
+ commonFiles += [ "scripting/engine_java.cpp" ]
else:
- serverOnlyFiles += [ "scripting/engine_none.cpp" ]
+ commonFiles += [ "scripting/engine_none.cpp" ]
+ nojni = True
coreShardFiles = []
shardServerFiles = coreShardFiles + Glob( "s/strategy*.cpp" ) + [ "s/commands_admin.cpp" , "s/commands_public.cpp" , "s/request.cpp" , "s/cursors.cpp" , "s/server.cpp" ] + [ "s/shard.cpp" , "s/shardkey.cpp" , "s/config.cpp" ]
@@ -499,12 +500,6 @@ def doConfigure( myenv , needJava=True , needPcre=True , shell=False ):
return False
- if shell:
- if windows:
- myenv.Append( LIBS=["v8"] )
- else:
- myCheckLib( "v8" , True )
-
if needPcre and not conf.CheckCXXHeader( 'pcrecpp.h' ):
print( "can't find pcre" )
Exit(1)
@@ -582,15 +577,6 @@ def doConfigure( myenv , needJava=True , needPcre=True , shell=False ):
return conf.Finish()
env = doConfigure( env )
-# --- v8 ---
-
-v8Home = GetOption( "v8home" )
-
-if not os.path.exists( v8Home ):
- for poss in [ "../v8" , "../../v8/" , "../open-source/v8" ]:
- if os.path.exists( poss ):
- v8Home = poss
- break
# --- js concat ---
@@ -715,16 +701,12 @@ if darwin or clientEnv["_HAVEPCAP"]:
sniffEnv.Program( "mongosniff" , "tools/sniffer.cpp" )
# --- shell ---
-# shell is complicated by the fact that v8 doesn't work 64-bit yet
env.JSConcat( "shell/mongo.jsall" , Glob( "shell/*.js" ) )
env.JSHeader( "shell/mongo.jsall" )
shellEnv = env.Clone();
-shellEnv.Append( CPPPATH=[ "../" , v8Home + "/include/" ] )
-shellEnv.Append( LIBPATH=[ v8Home] )
-
if release and ( ( darwin and force64 ) or linux64 ):
shellEnv["LINKFLAGS"] = env["LINKFLAGS_CLEAN"]
shellEnv["LIBS"] = env["LIBS_CLEAN"]
@@ -733,14 +715,7 @@ if release and ( ( darwin and force64 ) or linux64 ):
if noshell:
print( "not building shell" )
elif not onlyServer:
- weird = linux64 or force64
-
- if linux64:
- shellEnv.Append( CFLAGS="-m32" )
- shellEnv.Append( CXXFLAGS="-m32" )
- shellEnv.Append( LINKFLAGS="-m32" )
- shellEnv.Append( LIBPATH=[ "/usr/lib32" , "/usr/lib" ] )
- shellEnv["LIBPATH"].remove( "/usr/lib64" )
+ weird = force64
if force64:
shellEnv["CFLAGS"].remove("-m64")
@@ -762,7 +737,7 @@ elif not onlyServer:
if windows:
shellEnv.Append( LIBS=["winmm.lib"] )
- coreShellFiles = [ "shell/MongoJS.cpp" , "shell/ShellUtils.cpp" , "shell/dbshell.cpp" , "scripting/engine_v8.cpp" ]
+ coreShellFiles = [ "shell/dbshell.cpp" , "shell/utils.cpp" ]
if weird:
shell32BitFiles = coreShellFiles
View
6 db/jsobj.h
@@ -28,6 +28,7 @@
#include "../stdafx.h"
#include "../util/builder.h"
+#include "../util/optime.h"
#include "boost/utility.hpp"
#include <set>
@@ -1007,6 +1008,11 @@ namespace mongo {
b.append( fieldName );
b.append( val );
}
+
+ void appendTimestamp( const char *fieldName , unsigned long long time , unsigned int inc ){
+ OpTime t( time / 1000 , inc );
+ appendTimestamp( fieldName , t.asDate() );
+ }
/* Deprecated (but supported) */
void appendDBRef( const char *fieldName, const char *ns, const OID &oid ) {
View
2  db/matcher.cpp
@@ -143,7 +143,7 @@ namespace mongo {
if ( e.eoo() )
break;
- if ( ( e.type() == CodeWScope || e.type() == Code ) && strcmp(e.fieldName(), "$where")==0 ) {
+ if ( ( e.type() == CodeWScope || e.type() == Code || e.type() == String ) && strcmp(e.fieldName(), "$where")==0 ) {
// $where: function()...
uassert( "$where occurs twice?", where == 0 );
where = new Where();
View
103 dbtests/jstests.cpp
@@ -118,6 +118,12 @@ namespace JSTests {
s->setThis( & o );
s->invoke( "return this.z;" , BSONObj() );
ASSERT_EQUALS( "sara" , s->getString( "return" ) );
+
+ s->invoke( "this.z == 'sara';" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "return" ) );
+
+ s->invoke( "this.z == 'asara';" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "return" ) );
s->invoke( "return this.x == 17;" , BSONObj() );
ASSERT_EQUALS( true , s->getBoolean( "return" ) );
@@ -214,7 +220,100 @@ namespace JSTests {
delete s;
}
};
-
+
+ class OtherJSTypes {
+ public:
+ void run(){
+ Scope * s = globalScriptEngine->createScope();
+
+ { // date
+ BSONObj o;
+ {
+ BSONObjBuilder b;
+ b.appendDate( "d" , 123456789 );
+ o = b.obj();
+ }
+ s->setObject( "x" , o );
+
+ s->invoke( "return x.d.getTime() != 12;" , BSONObj() );
+ ASSERT_EQUALS( true, s->getBoolean( "return" ) );
+
+ s->invoke( "z = x.d.getTime();" , BSONObj() );
+ ASSERT_EQUALS( 123456789 , s->getNumber( "z" ) );
+
+ s->invoke( "z = { z : x.d }" , BSONObj() );
+ BSONObj out = s->getObject( "z" );
+ ASSERT( out["z"].type() == Date );
+ }
+
+ { // regex
+ BSONObj o;
+ {
+ BSONObjBuilder b;
+ b.appendRegex( "r" , "^a" , "i" );
+ o = b.obj();
+ }
+ s->setObject( "x" , o );
+
+ s->invoke( "z = x.r.test( 'b' );" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "z" ) );
+
+ s->invoke( "z = x.r.test( 'a' );" , BSONObj() );
+ ASSERT_EQUALS( true , s->getBoolean( "z" ) );
+
+ s->invoke( "z = x.r.test( 'ba' );" , BSONObj() );
+ ASSERT_EQUALS( false , s->getBoolean( "z" ) );
+
+ s->invoke( "z = { a : x.r };" , BSONObj() );
+
+ BSONObj out = s->getObject("z");
+ ASSERT_EQUALS( (string)"^a" , out["a"].regex() );
+ ASSERT_EQUALS( (string)"i" , out["a"].regexFlags() );
+
+ }
+
+ delete s;
+ }
+ };
+
+ class SpecialDBTypes {
+ public:
+ void run(){
+ Scope * s = globalScriptEngine->createScope();
+
+ BSONObjBuilder b;
+ b.appendTimestamp( "a" , 123456789 );
+ b.appendMinKey( "b" );
+ b.appendMaxKey( "c" );
+ b.appendTimestamp( "d" , 1234000 , 9876 );
+
+
+ {
+ BSONObj t = b.done();
+ ASSERT_EQUALS( 1234000 , t["d"].timestampTime() );
+ ASSERT_EQUALS( 9876 , t["d"].timestampInc() );
+ }
+
+ s->setObject( "z" , b.obj() );
+
+ assert( s->invoke( "y = { a : z.a , b : z.b , c : z.c , d: z.d }" , BSONObj() ) == 0 );
+
+ BSONObj out = s->getObject( "y" );
+ ASSERT_EQUALS( Timestamp , out["a"].type() );
+ ASSERT_EQUALS( MinKey , out["b"].type() );
+ ASSERT_EQUALS( MaxKey , out["c"].type() );
+ ASSERT_EQUALS( Timestamp , out["d"].type() );
+
+ ASSERT_EQUALS( 9876 , out["d"].timestampInc() );
+ ASSERT_EQUALS( 1234000 , out["d"].timestampTime() );
+ ASSERT_EQUALS( 123456789 , out["a"].date() );
+
+
+ delete s;
+ }
+ };
+
+
class All : public Suite {
public:
All() {
@@ -226,6 +325,8 @@ namespace JSTests {
add< ObjectDecoding >();
add< JSOIDTests >();
add< ObjectModTests >();
+ add< OtherJSTypes >();
+ add< SpecialDBTypes >();
}
};
View
25 jstests/apitest_dbcollection.js
@@ -64,51 +64,52 @@ assert(v.result.toString().match(/nrecords\?:(\d+)/)[1] == 100,11);
db.getCollection( "test_db" ).drop();
assert(db.getCollection( "test_db" ).count() == 0,12);
db.getCollection( "test_db" ).dropIndexes();
-assert(db.getCollection( "test_db" ).getIndexes().length() == 0,13);
+assert(db.getCollection( "test_db" ).getIndexes().length == 0,13);
db.getCollection( "test_db" ).save({a:10});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 1,14);
+assert(db.getCollection( "test_db" ).getIndexes().length == 1,14);
db.getCollection( "test_db" ).ensureIndex({a:1});
db.getCollection( "test_db" ).save({a:10});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 2,15);
+print( tojson( db.getCollection( "test_db" ).getIndexes() ) );
+assert.eq(db.getCollection( "test_db" ).getIndexes().length , 2,15);
db.getCollection( "test_db" ).dropIndex({a:1});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 1,16);
+assert(db.getCollection( "test_db" ).getIndexes().length == 1,16);
db.getCollection( "test_db" ).save({a:10});
db.getCollection( "test_db" ).ensureIndex({a:1});
db.getCollection( "test_db" ).save({a:10});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 2,17);
+assert(db.getCollection( "test_db" ).getIndexes().length == 2,17);
db.getCollection( "test_db" ).dropIndex("a_1");
-assert.eq( db.getCollection( "test_db" ).getIndexes().length() , 1,18);
+assert.eq( db.getCollection( "test_db" ).getIndexes().length , 1,18);
db.getCollection( "test_db" ).save({a:10, b:11});
db.getCollection( "test_db" ).ensureIndex({a:1});
db.getCollection( "test_db" ).ensureIndex({b:1});
db.getCollection( "test_db" ).save({a:10, b:12});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 3,19);
+assert(db.getCollection( "test_db" ).getIndexes().length == 3,19);
db.getCollection( "test_db" ).dropIndex({b:1});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 2,20);
+assert(db.getCollection( "test_db" ).getIndexes().length == 2,20);
db.getCollection( "test_db" ).dropIndex({a:1});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 1,21);
+assert(db.getCollection( "test_db" ).getIndexes().length == 1,21);
db.getCollection( "test_db" ).save({a:10, b:11});
db.getCollection( "test_db" ).ensureIndex({a:1});
db.getCollection( "test_db" ).ensureIndex({b:1});
db.getCollection( "test_db" ).save({a:10, b:12});
-assert(db.getCollection( "test_db" ).getIndexes().length() == 3,22);
+assert(db.getCollection( "test_db" ).getIndexes().length == 3,22);
db.getCollection( "test_db" ).dropIndexes();
-assert(db.getCollection( "test_db" ).getIndexes().length() == 1,23);
+assert(db.getCollection( "test_db" ).getIndexes().length == 1,23);
db.getCollection( "test_db" ).find();
db.getCollection( "test_db" ).drop();
-assert(db.getCollection( "test_db" ).getIndexes().length() == 0,24);
+assert(db.getCollection( "test_db" ).getIndexes().length == 0,24);
View
21 jstests/count.js
@@ -3,22 +3,23 @@ t = db.jstests_count;
t.drop();
t.save( { i: 1 } );
t.save( { i: 2 } );
-assert.eq( 1, t.find( { i: 1 } ).count() );
-assert.eq( 1, t.count( { i: 1 } ) );
-assert.eq( 2, t.find().count() );
-assert.eq( 2, t.find( undefined ).count() );
-assert.eq( 2, t.find( null ).count() );
-assert.eq( 2, t.count() );
+assert.eq( 1, t.find( { i: 1 } ).count(), "A" );
+assert.eq( 1, t.count( { i: 1 } ) , "B" );
+assert.eq( 2, t.find().count() , "C" );
+assert.eq( 2, t.find( undefined ).count() , "D" );
+assert.eq( 2, t.find( null ).count() , "E" );
+assert.eq( 2, t.count() , "F" );
t.drop();
t.save( {a:true,b:false} );
t.ensureIndex( {b:1,a:1} );
-assert.eq( 1, t.find( {a:true,b:false} ).count() );
-assert.eq( 1, t.find( {b:false,a:true} ).count() );
+assert.eq( 1, t.find( {a:true,b:false} ).count() , "G" );
+assert.eq( 1, t.find( {b:false,a:true} ).count() , "H" );
t.drop();
t.save( {a:true,b:false} );
t.ensureIndex( {b:1,a:1,c:1} );
-assert.eq( 1, t.find( {a:true,b:false} ).count() );
-assert.eq( 1, t.find( {b:false,a:true} ).count() );
+
+assert.eq( 1, t.find( {a:true,b:false} ).count() , "I" );
+assert.eq( 1, t.find( {b:false,a:true} ).count() , "J" );
View
8 jstests/date1.js
@@ -0,0 +1,8 @@
+
+t = db.date1;
+t.drop();
+
+d = new Date()
+t.save( { a : 1 , d : d } );
+
+assert.eq( d , t.findOne().d , "A" )
View
16 jstests/drop.js
@@ -2,20 +2,20 @@ f = db.jstests_drop;
f.drop();
-assert.eq( 0, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 0, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "A" );
f.save( {} );
-assert.eq( 1, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 1, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "B" );
f.ensureIndex( {a:1} );
-assert.eq( 2, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 2, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "C" );
assert.commandWorked( db.runCommand( {drop:"jstests_drop"} ) );
-assert.eq( 0, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 0, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "D" );
-f = db.jstests_drop;
+f.resetIndexCache();
f.ensureIndex( {a:1} );
-assert.eq( 2, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 2, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "E" );
assert.commandWorked( db.runCommand( {deleteIndexes:"jstests_drop",index:"*"} ) );
-assert.eq( 1, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() );
+assert.eq( 1, db.system.indexes.find( {ns:"test.jstests_drop"} ).count() , "G" );
// make sure we can still use it
f.save( {} );
-assert.eq( 1, f.find().hint( {_id:ObjectId( "000000000000000000000000" )} ).toArray().length );
+assert.eq( 1, f.find().hint( {_id:new ObjectId( "000000000000000000000000" )} ).toArray().length , "H" );
View
2  jstests/hint1.js
@@ -5,4 +5,4 @@ p.ensureIndex( { ts: 1 } );
e = p.find( { live: true, ts: { $lt: new Date( 1234119308272 ) }, cls: "entry", verticals: " alleyinsider" } ).sort( { ts: -1 } ).hint( { ts: 1 } ).explain();
assert.eq( e.startKey.ts.getTime(), new Date( 1234119308272 ).getTime() );
-assert.eq( 1, e.endKey.ts.$minElement );
+assert.eq( 1, e.endKey.ts.$minElement );
View
4 jstests/index3.js
@@ -3,7 +3,7 @@
t = db.index3;
t.drop();
-assert( t.getIndexes().length() == 0 );
+assert( t.getIndexes().length == 0 );
t.ensureIndex( { name : 1 } );
@@ -11,6 +11,6 @@ t.save( { name : "a" } );
t.ensureIndex( { name : 1 } );
-assert( t.getIndexes().length() == 2 );
+assert( t.getIndexes().length == 2 );
assert(t.validate().valid);
View
10 jstests/objid1.js
@@ -2,12 +2,12 @@ t = db.objid1;
t.drop();
b = new ObjectId();
-assert( b.str , "objid1 test1" );
+assert( b.str , "A" );
-a = ObjectId( b.str );
-assert.eq( a.str , b.str );
+a = new ObjectId( b.str );
+assert.eq( a.str , b.str , "B" );
t.save( { a : a } )
-assert( t.findOne().a.isObjectId );
-assert.eq( a.str , t.findOne().a.str );
+assert( t.findOne().a.isObjectId , "C" );
+assert.eq( a.str , t.findOne().a.str , "D" );
View
2  jstests/objid3.js
@@ -3,7 +3,7 @@ t.drop();
t.save( { a : "bob" , _id : 517 } );
for ( var k in t.findOne() ){
- assert.eq( k , "_id" );
+ assert.eq( k , "_id" , "keys out of order" );
break;
}
View
20 jstests/query1.js
@@ -0,0 +1,20 @@
+
+t = db.query1;
+t.drop();
+
+t.save( { num : 1 } );
+t.save( { num : 3 } )
+t.save( { num : 4 } );
+
+num = 0;
+total = 0;
+
+t.find().forEach(
+ function(z){
+ num++;
+ total += z.num;
+ }
+);
+
+assert.eq( num , 3 , "num" )
+assert.eq( total , 8 , "total" )
View
8 jstests/repl/pair1.js
@@ -2,6 +2,10 @@
var baseName = "jstests_pair1test";
+debug = function( p ) {
+// print( p );
+}
+
ismaster = function( n ) {
var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
// print( "ismaster: " + tojson( im ) );
@@ -67,18 +71,22 @@ doTest = function( signal ) {
checkWrite( rp.master(), rp.slave() );
+ debug( "kill first" );
rp.killNode( rp.master(), signal );
rp.waitForSteadyState( [ 1, null ], rp.slave().host );
writeOne( rp.master() );
+ debug( "restart first" );
rp.start( true );
rp.waitForSteadyState();
check( rp.slave() );
checkWrite( rp.master(), rp.slave() );
+ debug( "kill second" );
rp.killNode( rp.master(), signal );
rp.waitForSteadyState( [ 1, null ], rp.slave().host );
+ debug( "restart second" );
rp.start( true );
rp.waitForSteadyState( [ 1, 0 ], rp.master().host );
checkWrite( rp.master(), rp.slave() );
View
14 jstests/sub1.js
@@ -0,0 +1,14 @@
+// sub1.js
+
+t = db.sub1;
+t.drop();
+
+x = { a : 1 , b : { c : { d : 2 } } }
+
+t.save( x );
+
+y = t.findOne();
+
+assert.eq( 1 , y.a );
+assert.eq( 2 , y.b.c.d );
+print( tojson( y ) );
View
8 jstests/update3.js
@@ -5,19 +5,19 @@ f = db.jstests_update3;
f.drop();
f.save( { a:1 } );
f.update( {}, {$inc:{ a:1 }} );
-assert.eq( 2, f.findOne().a );
+assert.eq( 2, f.findOne().a , "A" );
f.drop();
f.save( { a:{ b: 1 } } );
f.update( {}, {$inc:{ "a.b":1 }} );
-assert.eq( 2, f.findOne().a.b );
+assert.eq( 2, f.findOne().a.b , "B" );
f.drop();
f.save( { a:{ b: 1 } } );
f.update( {}, {$set:{ "a.b":5 }} );
-assert.eq( 5, f.findOne().a.b );
+assert.eq( 5, f.findOne().a.b , "C" );
f.drop();
f.save( {'_id':0} );
f.update( {}, {$set:{'_id':5}} );
-assert.eq( 0, f.findOne()._id );
+assert.eq( 0, f.findOne()._id , "D" );
View
5 jstests/where1.js
@@ -8,6 +8,7 @@ t.save( { a : 3 } );
assert.eq( 1 , t.find( function(){ return this.a == 2; } ).length() , "A" );
-assert.eq( 1 , t.find( { $where : "this.a == 2" } ).toArray().length , "B" );
+assert.eq( 1 , t.find( { $where : "return this.a == 2" } ).toArray().length , "B" );
+assert.eq( 1 , t.find( { $where : "this.a == 2" } ).toArray().length , "C" );
-assert.eq( 1 , t.find( "this.a == 2" ).toArray().length , "C" );
+assert.eq( 1 , t.find( "this.a == 2" ).toArray().length , "D" );
View
4 mongo.xcodeproj/project.pbxproj
@@ -27,6 +27,8 @@
931187AE0F85463700A6DC44 /* pair3.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = pair3.js; sourceTree = "<group>"; };
931974610FB8C2BD001FE537 /* count.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = count.js; sourceTree = "<group>"; };
931974EE0FB9C3A5001FE537 /* diskfull.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = diskfull.js; sourceTree = "<group>"; };
+ 931979800FBC67FB001FE537 /* mongo_vstudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mongo_vstudio.cpp; sourceTree = "<group>"; };
+ 931979810FBC67FB001FE537 /* utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utils.cpp; sourceTree = "<group>"; };
931A027A0F58AA4400147C0E /* jsobjmanipulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsobjmanipulator.h; sourceTree = "<group>"; };
93202DE40F879CB600AF3B71 /* all.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = all.js; sourceTree = "<group>"; };
93278F570F72D32900844664 /* gridfs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gridfs.cpp; sourceTree = "<group>"; };
@@ -323,6 +325,8 @@
9302D9920F30AB8C00DFA4EF /* shell */ = {
isa = PBXGroup;
children = (
+ 931979800FBC67FB001FE537 /* mongo_vstudio.cpp */,
+ 931979810FBC67FB001FE537 /* utils.cpp */,
9302D9930F30AB8C00DFA4EF /* collection.js */,
9302D9940F30AB8C00DFA4EF /* db.js */,
9302D9950F30AB8C00DFA4EF /* dbshell.cpp */,
View
2  s/config.cpp
@@ -95,7 +95,7 @@ namespace mongo {
for ( map<string,ShardKeyPattern>::reverse_iterator i=_sharded.rbegin(); i != _sharded.rend(); i++){
a.append( i->first.c_str() , i->second.key() );
}
- to.appendArray( "sharded" , a.obj() );
+ to.append( "sharded" , a.obj() );
}
}
View
5 s/server.cpp
@@ -82,6 +82,11 @@ namespace mongo {
server->run();
}
+ DBClientBase *createDirectClient(){
+ uassert( "createDirectClient not implemented for sharding yet" , 0 );
+ return 0;
+ }
+
} // namespace mongo
using namespace mongo;
View
24 scripting/engine.cpp
@@ -2,6 +2,7 @@
#include "stdafx.h"
#include "engine.h"
+#include "../util/file.h"
namespace mongo {
@@ -23,5 +24,28 @@ namespace mongo {
return invoke( func , args );
}
+ bool Scope::execFile( const string& filename , bool printResult , bool reportError , bool assertOnError ){
+
+ path p( filename );
+ if ( is_directory( p ) ){
+ cerr << "can't read directory [" << filename << "]" << endl;
+ if ( assertOnError )
+ assert( 0 );
+ return false;
+ }
+
+ File f;
+ f.open( filename.c_str() );
+
+ char * data = (char*)malloc( f.len() + 1 );
+ data[f.len()] = 0;
+
+ f.read( 0 , data , f.len() );
+
+ return exec( data , filename , printResult , reportError , assertOnError );
+ }
+
+
+
ScriptEngine * globalScriptEngine;
}
View
9 scripting/engine.h
@@ -25,6 +25,7 @@ namespace mongo {
}
virtual void localConnect( const char * dbName ) = 0;
+ virtual void externalSetup( bool master ) = 0;
virtual double getNumber( const char *field ) = 0;
virtual string getString( const char *field ) = 0;
@@ -48,8 +49,11 @@ namespace mongo {
virtual string getError() = 0;
int invoke( const char* code , const BSONObj& args );
-
- virtual void injectNative( const char *field, NativeFunction func ) {}
+
+ virtual bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError ) = 0;
+ virtual bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError );
+
+ virtual void injectNative( const char *field, NativeFunction func ) = 0;
};
class ScriptEngine : boost::noncopyable {
@@ -64,6 +68,5 @@ namespace mongo {
static void setup();
};
-
extern ScriptEngine * globalScriptEngine;
}
View
133 scripting/engine_spidermonkey.cpp
@@ -109,6 +109,11 @@ namespace mongo {
BSONObjBuilder b;
+ jsval theid = getProperty( o , "_id" );
+ if ( ! JSVAL_IS_VOID( theid ) ){
+ append( b , "_id" , theid );
+ }
+
JSIdArray * properties = JS_Enumerate( _context , o );
assert( properties );
@@ -117,6 +122,8 @@ namespace mongo {
jsval nameval;
assert( JS_IdToValue( _context ,id , &nameval ) );
string name = toString( nameval );
+ if ( name == "_id" )
+ continue;
append( b , name , getProperty( o , name.c_str() ) );
}
@@ -142,6 +149,7 @@ namespace mongo {
}
void append( BSONObjBuilder& b , string name , jsval val ){
+ //cout << "name: " << name << "\t" << typeString( val ) << endl;
switch ( JS_TypeOfValue( _context , val ) ){
case JSTYPE_VOID: b.appendUndefined( name.c_str() ); break;
@@ -149,10 +157,14 @@ namespace mongo {
case JSTYPE_NUMBER: b.append( name.c_str() , toNumber( val ) ); break;
case JSTYPE_STRING: b.append( name.c_str() , toString( val ) ); break;
-
+ case JSTYPE_BOOLEAN: b.appendBool( name.c_str() , toBoolean( val ) ); break;
+
case JSTYPE_OBJECT: {
JSObject * o = JSVAL_TO_OBJECT( val );
- if ( ! appendSpecialDBObject( this , b , name , o ) ){
+ if ( ! o || o == JSVAL_NULL ){
+ b.appendNull( name.c_str() );
+ }
+ else if ( ! appendSpecialDBObject( this , b , name , o ) ){
BSONObj sub = toObject( o );
if ( JS_IsArrayObject( _context , o ) ){
b.appendArray( name.c_str() , sub );
@@ -163,7 +175,19 @@ namespace mongo {
}
break;
}
- case JSTYPE_FUNCTION: b.appendCode( name.c_str() , getFunctionCode( val ).c_str() ); break;
+
+ case JSTYPE_FUNCTION: {
+ string s = toString(val);
+ if ( s[0] == '/' ){
+ s = s.substr(1);
+ string::size_type end = s.rfind( '/' );
+ b.appendRegex( name.c_str() , s.substr( 0 , end ).c_str() , s.substr( end + 1 ).c_str() );
+ }
+ else {
+ b.appendCode( name.c_str() , getFunctionCode( val ).c_str() );
+ }
+ break;
+ }
default: uassert( (string)"can't append field. name:" + name + " type: " + typeString( val ) , 0 );
}
@@ -278,13 +302,26 @@ namespace mongo {
assert( r );
return OBJECT_TO_JSVAL( r );
}
- case 13:{
+ case Code:{
JSFunction * func = compileFunction( e.valuestr() );
return OBJECT_TO_JSVAL( JS_GetFunctionObject( func ) );
}
case Date:
return OBJECT_TO_JSVAL( js_NewDateObjectMsec( _context , (jsdouble) e.date() ) );
+ case MinKey:
+ return OBJECT_TO_JSVAL( JS_NewObject( _context , &minkey_class , 0 , 0 ) );
+
+ case MaxKey:
+ return OBJECT_TO_JSVAL( JS_NewObject( _context , &maxkey_class , 0 , 0 ) );
+
+ case Timestamp: {
+ JSObject * o = JS_NewObject( _context , &timestamp_class , 0 , 0 );
+ setProperty( o , "t" , toval( (double)(e.timestampTime()) ) );
+ setProperty( o , "i" , toval( (double)(e.timestampInc()) ) );
+ return OBJECT_TO_JSVAL( o );
+ }
+
default:
log() << "toval can't handle type: " << (int)(e.type()) << endl;
}
@@ -334,6 +371,10 @@ namespace mongo {
bool getBoolean( JSObject * o , const char * field ){
return toBoolean( getProperty( o , field ) );
}
+
+ double getNumber( JSObject * o , const char * field ){
+ return toNumber( getProperty( o , field ) );
+ }
string getString( JSObject * o , const char * field ){
return toString( getProperty( o , field ) );
@@ -437,8 +478,35 @@ namespace mongo {
return JS_TRUE;
}
+ JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
+ Convertor c(cx);
+ uassert( "native_helper needs at least 1 arg" , argc >= 1 );
+
+ NativeFunction func = (NativeFunction)JSVAL_TO_PRIVATE( argv[0] );
+
+ BSONObjBuilder args;
+ for ( uintN i=1; i<argc; i++ ){
+ c.append( args , args.numStr( i ) , argv[i] );
+ }
+
+ BSONObj out = func( args.obj() );
+
+ if ( out.isEmpty() ){
+ *rval = JSVAL_VOID;
+ }
+ else {
+ *rval = c.toval( out.firstElement() );
+ }
+
+ return JS_TRUE;
+ }
+
+ JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval );
+
JSFunctionSpec globalHelpers[] = {
{ "print" , &native_print , 0 , 0 , 0 } ,
+ { "nativeHelper" , &native_helper , 1 , 0 , 0 } ,
+ { "load" , &native_load , 1 , 0 , 0 } ,
{ 0 , 0 , 0 , 0 , 0 }
};
@@ -586,8 +654,12 @@ namespace mongo {
}
+ void externalSetup( bool master ){
+ initMongoJS( this , _context , _global , false, master );
+ }
+
void localConnect( const char * dbName ){
- initMongoJS( this , _context , _global , true );
+ initMongoJS( this , _context , _global , true, true );
exec( "_mongo = new Mongo();" );
exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
@@ -674,10 +746,25 @@ namespace mongo {
currentScope.reset( this );
}
- void exec( const char * code ){
+ bool exec( const string& code , const string& name = "(anon)" , bool printResult = false , bool reportError = true , bool assertOnError = true ){
precall();
- jsval ret;
- assert( JS_EvaluateScript( _context , _global , code , strlen( code ) , "anon" , 0 , &ret ) );
+
+ jsval ret = JSVAL_VOID;
+
+ JSBool worked = JS_EvaluateScript( _context , _global , code.c_str() , strlen( code.c_str() ) , name.c_str() , 0 , &ret );
+
+ if ( assertOnError )
+ uassert( name + " exec failed" , worked );
+
+ if ( reportError && ! _error.empty() ){
+ // cout << "exec error: " << _error << endl;
+ // already printed in reportError, so... TODO
+ }
+
+ if ( worked && printResult && ! JSVAL_IS_VOID( ret ) )
+ cout << _convertor->toString( ret ) << endl;
+
+ return worked;
}
int invoke( JSFunction * func , const BSONObj& args ){
@@ -714,6 +801,18 @@ namespace mongo {
return _error;
}
+ void injectNative( const char *field, NativeFunction func ){
+ string name = field;
+ _convertor->setProperty( _global , (name + "_").c_str() , PRIVATE_TO_JSVAL( func ) );
+
+ stringstream code;
+ code << field << " = function(){ var a = [ " << field << "_ ]; for ( var i=0; i<arguments.length; i++ ){ a.push( arguments[i] ); } return nativeHelper.apply( null , a ); }";
+ exec( code.str().c_str() );
+
+ }
+
+ JSContext *context() const { return _context; }
+
private:
JSContext * _context;
Convertor * _convertor;
@@ -738,6 +837,24 @@ namespace mongo {
// TODO: send to Scope
}
+ JSBool native_load( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ){
+ Convertor c(cx);
+
+ Scope * s = currentScope.get();
+
+ for ( uintN i=0; i<argc; i++ ){
+ string filename = c.toString( argv[i] );
+ cout << "should load [" << filename << "]" << endl;
+
+ if ( ! s->execFile( filename , false , true , false ) ){
+ JS_ReportError( cx , ((string)"error loading file: " + filename ).c_str() );
+ return JS_FALSE;
+ }
+ }
+
+ return JS_TRUE;
+ }
+
void SMEngine::runTest(){
View
7 scripting/engine_spidermonkey.h
@@ -2,6 +2,7 @@
#pragma once
+#define JS_THREADSAFE
#include "engine.h"
@@ -40,7 +41,11 @@ namespace mongo {
extern JSClass bson_class;
extern JSClass bson_ro_class;
+
extern JSClass object_id_class;
+ extern JSClass timestamp_class;
+ extern JSClass minkey_class;
+ extern JSClass maxkey_class;
// internal things
void dontDeleteScope( SMScope * s ){}
@@ -52,6 +57,6 @@ namespace mongo {
// mongo
- void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local );
+ void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local, bool master );
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , JSObject * o );
}
View
301 scripting/sm_db.cpp
@@ -2,6 +2,8 @@
// hacked in right now from engine_spidermonkey.cpp
+#include <boost/smart_ptr.hpp>
+
namespace mongo {
// ------------ some defs needed ---------------
@@ -19,7 +21,17 @@ namespace mongo {
names.insert( "_name" );
names.insert( "_fullName" );
names.insert( "_shortName" );
+ names.insert( "tojson" );
+ names.insert( "toJson" );
+ names.insert( "toString" );
}
+
+ if ( name.length() == 0 )
+ return false;
+
+ if ( name[0] == '_' )
+ return true;
+
return names.count( name ) > 0;
}
@@ -93,6 +105,30 @@ namespace mongo {
return JS_TRUE;
}
+ JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ Convertor c( cx );
+
+ uassert( "0 or 1 args to Mongo" , argc <= 1 );
+
+ DBClientConnection * conn = new DBClientConnection( true );
+
+ string host = "127.0.0.1";
+ if ( argc > 0 )
+ host = c.toString( argv[0] );
+
+ string errmsg;
+ if ( ! conn->connect( host , errmsg ) ){
+ JS_ReportError( cx , ((string)"couldn't connect: " + errmsg).c_str() );
+ return JS_FALSE;
+ }
+
+ JS_SetPrivate( cx , obj , (void*)conn );
+ jsval host_val = c.toval( host.c_str() );
+ assert( JS_SetProperty( cx , obj , "host" , &host_val ) );
+ return JS_TRUE;
+
+ }
+
void mongo_finalize( JSContext * cx , JSObject * obj ){
DBClientBase * client = (DBClientBase*)JS_GetPrivate( cx , obj );
if ( client ){
@@ -106,13 +142,6 @@ namespace mongo {
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, mongo_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
- };
-
- JSClass mongo_local_class = {
- "Mongo" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, mongo_finalize,
- JSCLASS_NO_OPTIONAL_MEMBERS
};
JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
@@ -238,12 +267,12 @@ namespace mongo {
}
JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
- if ( flags & JSRESOLVE_ASSIGNING || flags & JSRESOLVE_DETECTING )
+ if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
Convertor c( cx );
string collname = c.toString( id );
-
+
if ( isSpecialName( collname ) )
return JS_TRUE;
@@ -306,7 +335,7 @@ namespace mongo {
}
JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
- if ( flags & JSRESOLVE_ASSIGNING || flags & JSRESOLVE_DETECTING )
+ if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
Convertor c( cx );
@@ -358,14 +387,14 @@ namespace mongo {
return JS_TRUE;
}
-
+
JSClass object_id_class = {
"ObjectId" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
+
JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
return *rval = c.getProperty( obj , "str" );
@@ -377,19 +406,230 @@ namespace mongo {
};
- // ---- other stuff ----
+ JSClass timestamp_class = {
+ "Timestamp" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSClass minkey_class = {
+ "MinKey" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSClass maxkey_class = {
+ "MaxKey" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ // dbquery
- void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ){
- uassert( "non-local not supported yet" , local );
+ JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ uassert( "DDQuery needs at least 4 args" , argc >= 4 );
+
+ Convertor c(cx);
+ c.setProperty( obj , "_mongo" , argv[0] );
+ c.setProperty( obj , "_db" , argv[1] );
+ c.setProperty( obj , "_collection" , argv[2] );
+ c.setProperty( obj , "_ns" , argv[3] );
+
+ if ( argc > 4 && JSVAL_IS_OBJECT( argv[4] ) )
+ c.setProperty( obj , "_query" , argv[4] );
+ else
+ c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( JS_NewObject( cx , 0 , 0 , 0 ) ) );
+
+ if ( argc > 5 && JSVAL_IS_OBJECT( argv[5] ) )
+ c.setProperty( obj , "_fields" , argv[5] );
+ else
+ c.setProperty( obj , "_fields" , JSVAL_NULL );
+
+
+ if ( argc > 6 && JSVAL_IS_NUMBER( argv[6] ) )
+ c.setProperty( obj , "_limit" , argv[6] );
+ else
+ c.setProperty( obj , "_limit" , JSVAL_ZERO );
+
+ if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) )
+ c.setProperty( obj , "_skip" , argv[7] );
+ else
+ c.setProperty( obj , "_skip" , JSVAL_ZERO );
+
+ c.setProperty( obj , "_cursor" , JSVAL_NULL );
+ c.setProperty( obj , "_numReturned" , JSVAL_ZERO );
+ c.setProperty( obj , "_special" , JSVAL_FALSE );
- assert( JS_InitClass( cx , global , 0 , &mongo_local_class , mongo_local_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
+ return JS_TRUE;
+ }
+
+ JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
+ if ( flags & JSRESOLVE_ASSIGNING )
+ return JS_TRUE;
+
+ if ( ! JSVAL_IS_NUMBER( id ) )
+ return JS_TRUE;
+
+ jsval val;
+ assert( JS_CallFunctionName( cx , obj , "arrayAccess" , 1 , &id , &val ) );
+ Convertor c(cx);
+ c.setProperty( obj , c.toString( id ).c_str() , val );
+ *objp = obj;
+ return JS_TRUE;
+ }
+
+ JSClass dbquery_class = {
+ "DBQuery" , JSCLASS_NEW_RESOLVE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, (JSResolveOp)(&dbquery_resolve) , JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ // thread
+
+ class JSThreadConfig {
+ public:
+ JSThreadConfig( JSContext *cx, JSObject *obj, uintN argc, jsval *argv ) : started_(), done_(), cx_( cx ), obj_( obj ) {
+ massert( "need at least one argument", argc > 0 );
+ massert( "first argument must be a function", JS_TypeOfValue( cx, argv[ 0 ] ) == JSTYPE_FUNCTION );
+ fun_ = argv[ 0 ];
+ f_ = JS_ValueToFunction( cx, fun_ );
+ argc_ = argc - 1;
+ argv_.reset( new jsval[ argc_ ] );
+ for( uintN i = 0; i < argc_; ++i )
+ argv_[ i ] = argv[ i + 1 ];
+ JS_AddRoot( cx, &obj_ );
+ JS_AddRoot( cx, &fun_ );
+ for( uintN i = 0; i < argc_; ++i )
+ JS_AddRoot( cx, &argv_[ i ] );
+ }
+ ~JSThreadConfig() {
+ if ( started_ )
+ thread_->join(); // don't want to deal with cleaning up while thread is running
+ JS_RemoveRoot( cx_, &obj_ );
+ JS_RemoveRoot( cx_, &fun_ );
+ for( uintN i = 0; i < argc_; ++i )
+ JS_RemoveRoot( cx_, &argv_[ i ] );
+ if ( done_ )
+ JS_RemoveRoot( scope_->context(), &returnData_ );
+ }
+ void start() {
+ massert( "Thread already started", !started_ );
+ scope_.reset( dynamic_cast< SMScope * >( globalScriptEngine->createScope() ) );
+ scope_->externalSetup( false );
+ // TODO install shell utils?
+ JSThread jt( *this );
+ thread_.reset( new boost::thread( jt ) );
+ started_ = true;
+ }
+ void join() {
+ if ( thread_.get() )
+ thread_->join();
+ }
+ jsval returnData() {
+ join();
+ return returnData_;
+ }
+ private:
+ class JSThread {
+ public:
+ JSThread( JSThreadConfig &config ) : config_( config ) {}
+ void operator()() {
+ JS_SetContextThread( config_.scope_->context() );
+ try {
+ massert( "function call failure",
+ JS_TRUE == JS_CallFunction( config_.scope_->context(), config_.obj_, config_.f_, config_.argc_, config_.argv_.get(), &config_.returnData_ ) );
+ JS_AddRoot( config_.scope_->context(), &config_.returnData_ );
+ config_.done_ = true;
+ } catch ( ... ) {
+ }
+ JS_ClearContextThread( config_.scope_->context() );
+ }
+ private:
+ JSThreadConfig &config_;
+ };
+
+ bool started_;
+ bool done_;
+ JSContext *cx_;
+ JSObject *obj_;
+ JSFunction *f_;
+ jsval fun_;
+ uintN argc_;
+ boost::scoped_array< jsval > argv_;
+ auto_ptr< boost::thread > thread_;
+ auto_ptr< SMScope > scope_;
+ jsval returnData_;
+ };
+
+ void thread_finalize( JSContext * cx , JSObject * obj ){
+ JSThreadConfig * config = (JSThreadConfig*)JS_GetPrivate( cx , obj );
+ if ( config ){
+ delete config;
+ JS_SetPrivate( cx , obj , 0 );
+ }
+ }
+
+ JSClass thread_class = {
+ "Thread" , JSCLASS_HAS_PRIVATE ,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, thread_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+ };
+
+ JSBool thread_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
+ JS_SetPrivate( cx , obj , (void*) new JSThreadConfig( cx, obj, argc, argv ) );
+ return JS_TRUE;
+ }
+
+ JSBool thread_start(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSThreadConfig * config = (JSThreadConfig*)JS_GetPrivate( cx , obj );
+ config->start();
+ return JS_TRUE;
+ }
+
+ JSBool thread_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSThreadConfig * config = (JSThreadConfig*)JS_GetPrivate( cx , obj );
+ config->join();
+ return JS_TRUE;
+ }
+
+ JSBool thread_returnData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+ JSThreadConfig * config = (JSThreadConfig*)JS_GetPrivate( cx , obj );
+ *rval = config->returnData();
+ return JS_TRUE;
+ }
+
+ JSFunctionSpec thread_functions[] = {
+ { "start" , thread_start , 0 , 0 , JSPROP_READONLY | JSPROP_PERMANENT } ,
+ { "join" , thread_join , 0 , 0 , JSPROP_READONLY | JSPROP_PERMANENT } ,
+ { "returnData" , thread_returnData , 0 , 0 , JSPROP_READONLY | JSPROP_PERMANENT } ,
+ { 0 }
+ };
+
+ // ---- other stuff ----
+
+ void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local, bool master ){
+
+ assert( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
+
assert( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , object_id_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &internal_cursor_class , internal_cursor_constructor , 0 , 0 , internal_cursor_functions , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &thread_class , thread_constructor , 0 , 0 , thread_functions , 0 , 0 ) );
- scope->exec( jsconcatcode );
-
+ assert( JS_InitClass( cx , global , 0 , &timestamp_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
+ assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
+
+ if ( master ) {
+ scope->exec( jsconcatcode );
+ }
}
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , JSObject * o ){
@@ -401,6 +641,31 @@ namespace mongo {
return true;
}
+ if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ){
+ b.appendMinKey( name.c_str() );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ){
+ b.appendMaxKey( name.c_str() );
+ return true;
+ }
+
+ if ( JS_InstanceOf( c->_context , o , &timestamp_class , 0 ) ){
+ b.appendTimestamp( name.c_str() , (unsigned long long)c->getNumber( o , "t" ) , (unsigned int )c->getNumber( o , "i" ) );
+ return true;
+ }
+
+ {
+ jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
+ if ( d ){
+ b.appendDate( name.c_str() , d );
+ return true;
+ }
+ }
+
+
+
return false;
}
View
82 shell/ShellUtils.cpp
@@ -21,17 +21,6 @@ using namespace v8;
using namespace boost::filesystem;
using namespace mongo;
-BSONObj makeUndefined() {
- BSONObjBuilder b;
- b.appendUndefined( "" );
- return b.obj();
-}
-BSONObj undefined_ = makeUndefined();
-
-BSONObj encapsulate( const BSONObj &obj ) {
- return BSON( "" << obj );
-}
-
BSONObj Print(const BSONObj &args) {
bool first = true;
BSONObjIterator i( args );
@@ -98,21 +87,6 @@ std::ostream& operator<<( std::ostream &s, const v8::TryCatch * try_catch ){
return s;
}
-BSONObj Load(const BSONObj& args) {
- BSONObjIterator i( args );
- while( i.more() ) {
- BSONElement e = i.next();
- if ( e.eoo() )
- break;
- assert( e.type() == mongo::String );
- Handle<v8::String> source = ReadFile(e.valuestr());
- massert( "error loading file", !source.IsEmpty() );
- massert( "error executing file", ExecuteString(source, v8::String::New(e.valuestr()), false, true));
- }
- return undefined_;
-}
-
-
BSONObj Quit(const BSONObj& args) {
// If not arguments are given first element will be EOO, which
// converts to the integer value 0.
@@ -195,60 +169,6 @@ bool ExecuteString(Handle<v8::String> source, Handle<v8::Value> name,
return true;
}
-void sleepms( int ms ) {
- boost::xtime xt;
- boost::xtime_get(&xt, boost::TIME_UTC);
- xt.sec += ( ms / 1000 );
- xt.nsec += ( ms % 1000 ) * 1000000;
- if ( xt.nsec >= 1000000000 ) {
- xt.nsec -= 1000000000;
- xt.sec++;
- }
- boost::thread::sleep(xt);
-}
-
-mongo::BSONObj JSSleep(const mongo::BSONObj &args){
- assert( args.nFields() == 1 );
- assert( args.firstElement().isNumber() );
- int ms = int( args.firstElement().number() );
- {
- v8::Unlocker u;
- sleepms( ms );
- }
- return undefined_;
-}
-
-BSONObj ListFiles(const BSONObj& args){
- jsassert( args.nFields() == 1 , "need to specify 1 argument to listFiles" );
-
- BSONObjBuilder lst;
-
- path root( args.firstElement().valuestrsafe() );
-
- directory_iterator end;
- directory_iterator i( root);
-
- int num =0;
- while ( i != end ){
- path p = *i;
-
- BSONObjBuilder b;
- b << "name" << p.string();
- b.appendBool( "isDirectory", is_directory( p ) );
- stringstream ss;
- ss << num;
- string name = ss.str();
- lst.append( name.c_str(), b.done() );
-
- num++;
- i++;
- }
-
- BSONObjBuilder ret;
- ret.appendArray( "", lst.done() );
- return ret.obj();
-}
-
void ReportException(v8::TryCatch* try_catch) {
cout << try_catch << endl;
}
@@ -642,8 +562,6 @@ BSONObj AllocatePorts( const BSONObj &args ) {
void installShellUtils( mongo::Scope &scope, v8::Handle<v8::ObjectTemplate>& global ) {
scope.injectNative( "sleep", JSSleep );
scope.injectNative( "print", Print );
- scope.injectNative( "load", Load );
- scope.injectNative( "listFiles", ListFiles );
scope.injectNative( "quit", Quit );
scope.injectNative( "version", Version );
global->Set( String::New( "threadInject" ), FunctionTemplate::New( ThreadInject ) );
View
10 shell/collection.js
@@ -124,7 +124,8 @@ DBCollection.prototype.update = function( query , obj , upsert ){
}
DBCollection.prototype.save = function( obj ){
- if ( ! obj._id ){
+ if ( typeof( obj._id ) == "undefined" ){
+ obj._id = new ObjectId();
return this.insert( obj );
}
else {
@@ -176,8 +177,9 @@ DBCollection.prototype.createIndex = function( keys , options ){
DBCollection.prototype.ensureIndex = function( keys , options ){
var name = this._indexSpec( keys, options ).name;
this._indexCache = this._indexCache || {};
- if ( this._indexCache[ name ] )
+ if ( this._indexCache[ name ] ){
return false;
+ }
this.createIndex( keys , options );
this._indexCache[name] = true;
@@ -236,11 +238,11 @@ DBCollection.prototype.validate = function() {
}
DBCollection.prototype.getIndexes = function(){
- return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } );
+ return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } ).toArray();
}
DBCollection.prototype.getIndexSpecs = function(){
- return this.getIndexes().toArray().map(
+ return this.getIndexes().map(
function(i){
return i;
}
View
2  shell/db.js
@@ -435,7 +435,7 @@ DB.prototype.getCollectionNames = function(){
}
DB.prototype.tojson = function(){
- return this.toString();
+ return this._name;
}
DB.prototype.toString = function(){
View
87 shell/dbshell.cpp
@@ -1,17 +1,15 @@
// dbshell.cpp
-#include <v8.h>
+#include <stdio.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
-#include "ShellUtils.h"
-#include "MongoJS.h"
#include "../scripting/engine.h"
-#include "../scripting/engine_v8.h"
-
+#include "../client/dbclient.h"
+#include "utils.h"
extern const char * jsconcatcode;
@@ -80,7 +78,7 @@ inline void printStackTrace() {
void quitAbruptly( int sig ) {
cout << "mongo got signal " << sig << " (" << strsignal( sig ) << "), stack trace: " << endl;
printStackTrace();
- KillMongoProgramInstances();
+ mongo::shellUtils::KillMongoProgramInstances();
exit(14);
}
@@ -121,34 +119,13 @@ string fixHost( string url , string host , string port ){
return newurl;
}
-v8::Handle< v8::Context > baseContext_;
-
int main(int argc, char* argv[]) {
setupSignals();
- RecordMyLocation( argv[ 0 ] );
+ mongo::shellUtils::RecordMyLocation( argv[ 0 ] );
mongo::ScriptEngine::setup();
- auto_ptr< mongo::V8Scope > scope( dynamic_cast< mongo::V8Scope* >( mongo::globalScriptEngine->createScope() ) );
-
- v8::Locker l;
- v8::HandleScope handle_scope;
-
- v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-
- scope->setGlobal( global );
-
- installShellUtils( *scope, global );
- installMongoGlobals( global );
-
- baseContext_ = v8::Context::New(NULL, global);
- v8::Context::Scope context_scope(baseContext_);
-
- { // init mongo code
- v8::HandleScope handle_scope;
- if ( ! ExecuteString( v8::String::New( jsconcatcode ) , v8::String::New( "(mongo init)" ) , false , true ) )
- return -1;
- }
+ auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->createScope() );
string url = "test";
string dbhost;
@@ -262,46 +239,38 @@ int main(int argc, char* argv[]) {
break;
}
+
+ scope->externalSetup( true );
+ mongo::shellUtils::installShellUtils( *scope );
- if ( !nodb ) { // init mongo code
- v8::HandleScope handle_scope;
+ if ( !nodb ) { // connect to db
cout << "url: " << url << endl;
string setup = (string)"db = connect( \"" + fixHost( url , dbhost , port ) + "\")";
- if ( ! ExecuteString( v8::String::New( setup.c_str() ) , v8::String::New( "(connect)" ) , false , true ) ){
+ if ( ! scope->exec( setup , "(connect)" , false , true , false ) )
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 ) ){
+ if ( ! scope->exec( ss.str() , "(auth)" , true , true , false ) ){
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;
- }
-
- MongoProgramScope s;
- if (!ExecuteString(source, file_name, false, true)){
- cout << "error processing: " << file_name << endl;
- return 1;
+ mongo::shellUtils::MongoProgramScope s;
+
+ if ( ! scope->execFile( str , false , true , false ) ){
+ return -3;
}
numFiles++;
@@ -312,13 +281,13 @@ int main(int argc, char* argv[]) {
if ( runShell ){
- MongoProgramScope s;
+ mongo::shellUtils::MongoProgramScope s;
shellHistoryInit();
cout << "type \"help\" for help" << endl;
- v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
+ //v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
while ( 1 ){
@@ -334,6 +303,7 @@ int main(int argc, char* argv[]) {
break;
}
+ /*
{
string cmd = line;
if ( cmd.find( " " ) > 0 )
@@ -346,12 +316,10 @@ int main(int argc, char* argv[]) {
}
}
+ */
- v8::HandleScope handle_scope;
- ExecuteString(v8::String::New( code.c_str() ),
- v8::String::New("(shell)"),
- true,
- true);
+ scope->setString( "__line__" , code.c_str() );
+ scope->exec( "execShellLine()" , "(shell)" , true , true , false);
shellHistoryAdd( line );
@@ -364,3 +332,10 @@ int main(int argc, char* argv[]) {
}
+namespace mongo {
+ DBClientBase * createDirectClient(){
+ uassert( "no createDirectClient in shell" , 0 );
+ return 0;
+ }
+}
+
View
1  shell/query.js
@@ -17,6 +17,7 @@ if ( typeof DBQuery == "undefined" ){
this._numReturned = 0;
this._special = false;
}
+ print( "DBQuery probably won't have array access " );
}
View
394 shell/utils.cpp
@@ -0,0 +1,394 @@
+// utils.cpp
+
+#include <boost/thread/xtime.hpp>
+
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <assert.h>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <vector>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include "../client/dbclient.h"
+#include "utils.h"
+
+namespace mongo {
+
+ namespace shellUtils {
+
+ const char *argv0 = 0;
+ void RecordMyLocation( const char *_argv0 ) { argv0 = _argv0; }
+
+ // helpers
+
+ BSONObj makeUndefined() {
+ BSONObjBuilder b;
+ b.appendUndefined( "" );
+ return b.obj();
+ }
+ BSONObj undefined_ = makeUndefined();
+
+ BSONObj encapsulate( const BSONObj &obj ) {
+ return BSON( "" << obj );
+ }
+
+ void sleepms( int ms ) {
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += ( ms / 1000 );
+ xt.nsec += ( ms % 1000 ) * 1000000;
+ if ( xt.nsec >= 1000000000 ) {
+ xt.nsec -= 1000000000;
+ xt.sec++;
+ }
+ boost::thread::sleep(xt);
+ }
+
+ // real methods
+
+
+
+ mongo::BSONObj JSSleep(const mongo::BSONObj &args){
+ assert( args.nFields() == 1 );
+ assert( args.firstElement().isNumber() );
+ int ms = int( args.firstElement().number() );
+ sleepms( ms );
+ return undefined_;
+ }
+
+ BSONObj listFiles(const BSONObj& args){
+ uassert( "need to specify 1 argument to listFiles" , args.nFields() == 1 );
+
+ BSONObjBuilder lst;
+
+ string rootname = args.firstElement().valuestrsafe();
+ path root( rootname );
+
+ directory_iterator end;
+ directory_iterator i( root);
+
+ int num =0;
+ while ( i != end ){
+ path p = *i;
+
+ BSONObjBuilder b;
+ b << "name" << p.string();
+ b.appendBool( "isDirectory", is_directory( p ) );
+ stringstream ss;
+ ss << num;
+ string name = ss.str();
+ lst.append( name.c_str(), b.done() );
+
+ num++;
+ i++;
+ }
+
+ BSONObjBuilder ret;
+ ret.appendArray( "", lst.done() );
+ return ret.obj();
+ }
+
+ BSONObj Quit(const BSONObj& args) {
+ // If not arguments are given first element will be EOO, which
+ // converts to the integer value 0.
+ int exit_code = int( args.firstElement().number() );
+ ::exit(exit_code);
+ return undefined_;
+ }
+
+#ifndef _WIN32
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+ BSONObj AllocatePorts( const BSONObj &args ) {
+ uassert( "allocatePorts takes exactly 1 argument", args.nFields() == 1 );
+ uassert( "allocatePorts needs to be passed an integer", args.firstElement().isNumber() );
+
+ int n = int( args.firstElement().number() );
+
+ vector< int > ports;
+ for( int i = 0; i < n; ++i ) {
+ int s = socket( AF_INET, SOCK_STREAM, 0 );
+ assert( s );
+
+ sockaddr_in address;
+ memset(address.sin_zero, 0, sizeof(address.sin_zero));
+ address.sin_family = AF_INET;
+ address.sin_port = 0;
+ address.sin_addr.s_addr = 0;
+ assert( 0 == ::bind( s, (sockaddr*)&address, sizeof( address ) ) );
+
+ sockaddr_in newAddress;
+ socklen_t len = sizeof( newAddress );
+ assert( 0 == getsockname( s, (sockaddr*)&newAddress, &len ) );
+ ports.push_back( ntohs( newAddress.sin_port ) );
+
+ assert( 0 == close( s ) );
+ }
+
+ sort( ports.begin(), ports.end() );
+ BSONObjBuilder b;
+ b.append( "", ports );
+ return b.obj();
+ }
+
+ map< int, pair< pid_t, int > > dbs;
+
+ char *copyString( const char *original ) {
+ char *ret = reinterpret_cast< char * >( malloc( strlen( original ) + 1 ) );
+ strcpy( ret, original );
+ return ret;
+ }
+
+ boost::mutex &mongoProgramOutputMutex( *( new boost::mutex ) );
+ stringstream mongoProgramOutput_;
+
+ void writeMongoProgramOutputLine( int port, const char *line ) {
+ boost::mutex::scoped_lock lk( mongoProgramOutputMutex );
+ stringstream buf;
+ buf << "m" << port << "| " << line;
+ cout << buf.str() << endl;
+ mongoProgramOutput_ << buf.str() << endl;
+ }
+
+ BSONObj RawMongoProgramOutput( const BSONObj &args ) {
+ boost::mutex::scoped_lock lk( mongoProgramOutputMutex );
+ return BSON( "" << mongoProgramOutput_.str() );
+ }
+
+ class MongoProgramRunner {
+ char **argv_;
+ int port_;
+ int pipe_;
+ public:
+ MongoProgramRunner( const BSONObj &args ) {
+ assert( args.nFields() > 0 );
+ string program( args.firstElement().valuestrsafe() );
+
+ assert( !program.empty() );
+ boost::filesystem::path programPath = ( boost::filesystem::path( argv0 ) ).branch_path() / program;
+ massert( "couldn't find " + programPath.native_file_string(), boost::filesystem::exists( programPath ) );
+
+ port_ = -1;
+ argv_ = new char *[ args.nFields() + 1 ];
+ {
+ string s = programPath.native_file_string();
+ if ( s == program )
+ s = "./" + s;
+ argv_[ 0 ] = copyString( s.c_str() );
+ }
+
+ BSONObjIterator j( args );
+ j.next();
+ for( int i = 1; i < args.nFields(); ++i ) {
+ BSONElement e = j.next();
+ string str;
+ if ( e.isNumber() ) {
+ stringstream ss;
+ ss << e.number();
+ str = ss.str();
+ } else {
+ assert( e.type() == mongo::String );
+ str = e.valuestr();
+ }
+ char *s = copyString( str.c_str() );
+ if ( string( "--port" ) == s )
+ port_ = -2;
+ else if ( port_ == -2 )
+ port_ = strtol( s, 0, 10 );
+ argv_[ i ] = s;
+ }
+ argv_[ args.nFields() ] = 0;
+
+ assert( port_ > 0 );
+ if ( dbs.count( port_ ) != 0 ){
+ cerr << "count for port: " << port_ << " is not 0 is: " << dbs.count( port_ ) << endl;
+ assert( dbs.count( port_ ) == 0 );
+ }
+ }
+
+ void start() {
+ int pipeEnds[ 2 ];
+ assert( pipe( pipeEnds ) != -1 );
+
+ fflush( 0 );
+ pid_t pid = fork();
+ assert( pid != -1 );
+
+ if ( pid == 0 ) {
+ assert( dup2( pipeEnds[ 1 ], STDOUT_FILENO ) != -1 );
+ assert( dup2( pipeEnds[ 1 ], STDERR_FILENO ) != -1 );
+ execvp( argv_[ 0 ], argv_ );
+ assert( "Unable to start program" == 0 );
+ }
+
+ cout << "shell: started mongo program";
+ int i = 0;
+ while( argv_[ i ] )
+ cout << " " << argv_[ i++ ];
+ cout << endl;
+
+ i = 0;
+ while( argv_[ i ] )
+ free( argv_[ i++ ] );
+ free( argv_ );
+
+ dbs.insert( make_pair( port_, make_pair( pid, pipeEnds[ 1 ] ) ) );
+ pipe_ = pipeEnds[ 0 ];
+ }
+
+ // Continue reading output
+ void operator()() {
+ // This assumes there aren't any 0's in the mongo program output.
+ // Hope that's ok.
+ char buf[ 1024 ];
+ char temp[ 1024 ];
+ char *start = buf;
+ while( 1 ) {
+ int lenToRead = 1023 - ( start - buf );
+ int ret = read( pipe_, (void *)start, lenToRead );
+ assert( ret != -1 );
+ start[ ret ] = '\0';
+ if ( strlen( start ) != unsigned( ret ) )
+ writeMongoProgramOutputLine( port_, "WARNING: mongod wrote null bytes to output" );
+ char *last = buf;
+ for( char *i = strchr( buf, '\n' ); i; last = i + 1, i = strchr( last, '\n' ) ) {
+ *i = '\0';
+ writeMongoProgramOutputLine( port_, last );
+ }
+ if ( ret == 0 ) {
+ if ( *last )
+ writeMongoProgramOutputLine( port_, last );
+ close( pipe_ );
+ break;
+ }
+ if ( last != buf ) {
+ strcpy( temp, last );
+ strcpy( buf, temp );
+ } else {
+ assert( strlen( buf ) < 1023 );
+ }
+ start = buf + strlen( buf );
+ }
+ }
+ };
+
+ BSONObj StartMongoProgram( const BSONObj &a ) {
+ MongoProgramRunner r( a );
+ r.start();
+ boost::thread t( r );
+ return undefined_;
+ }
+
+ BSONObj ResetDbpath( const BSONObj &a ) {
+ assert( a.nFields() == 1 );
+ string path = a.firstElement().valuestrsafe();
+ assert( !path.empty() );
+ if ( boost::filesystem::exists( path ) )
+ boost::filesystem::remove_all( path );
+ boost::filesystem::create_directory( path );
+ return undefined_;
+ }
+
+ void killDb( int port, int signal ) {
+ if( dbs.count( port ) != 1 ) {
+ cout << "No db started on port: " << port << endl;
+ return;
+ }
+
+ pid_t pid = dbs[ port ].first;
+ assert( 0 == kill( pid, signal ) );
+
+ int i = 0;
+ for( ; i < 65; ++i ) {
+ if ( i == 5 ) {
+ char now[64];
+ time_t_to_String(time(0), now);
+ now[ 20 ] = 0;
+ cout << now << " process on port " << port << ", with pid " << pid << " not terminated, sending sigkill" << endl;
+ assert( 0 == kill( pid, SIGKILL ) );
+ }
+ int temp;
+ int ret = waitpid( pid, &temp, WNOHANG );
+ if ( ret == pid )
+ break;
+ sleepms( 1000 );
+ }
+ if ( i == 65 ) {
+ char now[64];
+ time_t_to_String(time(0), now);
+ now[ 20 ] = 0;
+ cout << now << " failed to terminate process on port " << port << ", with pid " << pid << endl;
+ assert( "Failed to terminate process" == 0 );
+ }
+
+ close( dbs[ port ].second );
+ dbs.erase( port );
+ if ( i > 4 || signal == SIGKILL ) {
+ sleepms( 4000 ); // allow operating system to reclaim resources
+ }
+ }
+
+ BSONObj StopMongoProgram( const BSONObj &a ) {
+ assert( a.nFields() == 1 || a.nFields() == 2 );
+ assert( a.firstElement().isNumber() );
+ int port = int( a.firstElement().number() );
+ int signal = SIGTERM;
+ if ( a.nFields() == 2 ) {
+ BSONObjIterator i( a );
+ i.next();
+ BSONElement e = i.next();
+ assert( e.isNumber() );
+ signal = int( e.number() );
+ }
+ killDb( port, signal );
+ cout << "shell: stopped mongo program on port " << port << endl;
+ return undefined_;
+ }
+
+ void KillMongoProgramInstances() {
+ vector< int > ports;
+ for( map< int, pair< pid_t, int > >::iterator i = dbs.begin(); i != dbs.end(); ++i )
+ ports.push_back( i->first );
+ for( vector< int >::iterator i = ports.begin(); i != ports.end(); ++i )
+ killDb( *i, SIGTERM );
+ }
+
+ MongoProgramScope::~MongoProgramScope() {
+ try {
+ KillMongoProgramInstances();
+ } catch ( ... ) {
+ assert( false );
+ }
+ }
+
+#else
+ MongoProgramScope::~MongoProgramScope() {}
+ void KillMongoProgramInstances() {}
+#endif
+
+ void installShellUtils( Scope& scope ){
+ scope.injectNative( "listFiles" , listFiles );
+ scope.injectNative( "sleep" , JSSleep );
+ scope.injectNative( "quit", Quit );
+#if !defined(_WIN32)
+ scope.injectNative( "allocatePorts", AllocatePorts );
+ scope.injectNative( "_startMongoProgram", StartMongoProgram );
+ scope.injectNative( "stopMongod", StopMongoProgram );
+ scope.injectNative( "stopMongoProgram", StopMongoProgram );
+ scope.injectNative( "resetDbpath", ResetDbpath );
+ scope.injectNative( "rawMongoProgramOutput", RawMongoProgramOutput );
+#endif
+ }
+
+ }
+}
View
23 shell/utils.h
@@ -0,0 +1,23 @@
+// utils.h
+
+#pragma once
+
+#include "../scripting/engine.h"
+
+namespace mongo {
+
+ namespace shellUtils {
+
+ void RecordMyLocation( const char *_argv0 );
+ void installShellUtils( Scope& scope );
+
+ // Scoped management of mongo program instances. Simple implementation:
+ // destructor kills all mongod instances created by the shell.
+ struct MongoProgramScope {
+ MongoProgramScope() {} // Avoid 'unused variable' warning.
+ ~MongoProgramScope();
+ };
+ void KillMongoProgramInstances();
+
+ }
+}
View
44 shell/utils.js
@@ -10,10 +10,11 @@ assert.eq = function( a , b , msg ){
if ( a == b )
return;
- if ( a != null && b != null && a.toString() == b.toString() )
+ if ( ( a != null && b != null ) &&
+ ( a.toString() == b.toString() || a == b || tojson( a ) == tojson( b ) ) )
return;
- throw "[" + a + "] != [" + b + "] are not equal : " + msg;
+ throw "[" + tojson( a ) + "] != [" + tojson( b ) + "] are not equal : " + msg;
}
assert.neq = function( a , b , msg ){
@@ -169,15 +170,6 @@ ObjectId.prototype.tojson = function(){
ObjectId.prototype.isObjectId = true;
-Thread = function(){
- this.init.apply( this, arguments );
-}
-
-if ( typeof( threadIn