Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

940 lines (742 sloc) 34.924 kb
// sm_db.cpp
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// hacked in right now from engine_spidermonkey.cpp
#include "../client/syncclusterconnection.h"
#include "../util/base64.h"
namespace mongo {
bool haveLocalShardingInfo( const string& ns );
// ------------ some defs needed ---------------
JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName );
// ------------ utils ------------------
bool isSpecialName( const string& name ){
static set<string> names;
if ( names.size() == 0 ){
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;
}
// ------ cursor ------
class CursorHolder {
public:
CursorHolder( auto_ptr< DBClientCursor > &cursor, const shared_ptr< DBClientWithCommands > &connection ) :
connection_( connection ),
cursor_( cursor ) {
assert( cursor_.get() );
}
DBClientCursor *get() const { return cursor_.get(); }
private:
shared_ptr< DBClientWithCommands > connection_;
auto_ptr< DBClientCursor > cursor_;
};
DBClientCursor *getCursor( JSContext *cx, JSObject *obj ) {
CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
uassert( 10235 , "no cursor!" , holder );
return holder->get();
}
JSBool internal_cursor_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
uassert( 10236 , "no args to internal_cursor_constructor" , argc == 0 );
assert( JS_SetPrivate( cx , obj , 0 ) ); // just for safety
return JS_TRUE;
}
void internal_cursor_finalize( JSContext * cx , JSObject * obj ){
CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
if ( holder ){
delete holder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
DBClientCursor *cursor = getCursor( cx, obj );
*rval = cursor->more() ? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE;
}
JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
DBClientCursor *cursor = getCursor( cx, obj );
if ( ! cursor->more() ){
JS_ReportError( cx , "cursor at the end" );
return JS_FALSE;
}
Convertor c(cx);
BSONObj n = cursor->next();
*rval = c.toval( &n );
return JS_TRUE;
}
JSFunctionSpec internal_cursor_functions[] = {
{ "hasNext" , internal_cursor_hasNext , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "next" , internal_cursor_next , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
JSClass internal_cursor_class = {
"InternalCursor" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, internal_cursor_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
// ------ mongo stuff ------
JSBool mongo_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
uassert( 10237 , "mongo_constructor not implemented yet" , 0 );
throw -1;
}
JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
shared_ptr< DBClientWithCommands > client( createDirectClient() );
assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( client ) ) ) );
jsval host = c.toval( "EMBEDDED" );
assert( JS_SetProperty( cx , obj , "host" , &host ) );
return JS_TRUE;
}
JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
uassert( 10238 , "0 or 1 args to Mongo" , argc <= 1 );
string host = "127.0.0.1";
if ( argc > 0 )
host = c.toString( argv[0] );
int numCommas = DBClientBase::countCommas( host );
shared_ptr< DBClientWithCommands > conn;
string errmsg;
if ( numCommas == 0 ){
DBClientConnection * c = new DBClientConnection( true );
conn.reset( c );
if ( ! c->connect( host , errmsg ) ){
JS_ReportError( cx , ((string)"couldn't connect: " + errmsg).c_str() );
return JS_FALSE;
}
ScriptEngine::runConnectCallback( *c );
}
else if ( numCommas == 1 ){ // paired
DBClientPaired * c = new DBClientPaired();
conn.reset( c );
if ( ! c->connect( host ) ){
JS_ReportError( cx , "couldn't connect to pair" );
return JS_FALSE;
}
}
else if ( numCommas == 2 ){
conn.reset( new SyncClusterConnection( host ) );
}
else {
JS_ReportError( cx , "1 (paired) or 2(quorum) commas are allowed" );
return JS_FALSE;
}
assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
jsval host_val = c.toval( host.c_str() );
assert( JS_SetProperty( cx , obj , "host" , &host_val ) );
return JS_TRUE;
}
DBClientWithCommands *getConnection( JSContext *cx, JSObject *obj ) {
shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
uassert( 10239 , "no connection!" , connHolder && connHolder->get() );
return connHolder->get();
}
void mongo_finalize( JSContext * cx , JSObject * obj ){
shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
if ( connHolder ){
delete connHolder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
JSClass mongo_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){
uassert( 10240 , "mongo_find neesd 6 args" , argc == 6 );
shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
uassert( 10241 , "no connection!" , connHolder && connHolder->get() );
DBClientWithCommands *conn = connHolder->get();
Convertor c( cx );
string ns = c.toString( argv[0] );
BSONObj q = c.toObject( argv[1] );
BSONObj f = c.toObject( argv[2] );
int nToReturn = (int) c.toNumber( argv[3] );
int nToSkip = (int) c.toNumber( argv[4] );
bool slaveOk = c.getBoolean( obj , "slaveOk" );
int batchSize = (int) c.toNumber( argv[5] );
try {
auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0 , slaveOk ? QueryOption_SlaveOk : 0 , batchSize );
if ( ! cursor.get() ){
log() << "query failed : " << ns << " " << q << " to: " << conn->toString() << endl;
JS_ReportError( cx , "error doing query: failed" );
return JS_FALSE;
}
JSObject * mycursor = JS_NewObject( cx , &internal_cursor_class , 0 , 0 );
CHECKNEWOBJECT( mycursor, cx, "internal_cursor_class" );
assert( JS_SetPrivate( cx , mycursor , new CursorHolder( cursor, *connHolder ) ) );
*rval = OBJECT_TO_JSVAL( mycursor );
return JS_TRUE;
}
catch ( ... ){
JS_ReportError( cx , "error doing query: unknown" );
return JS_FALSE;
}
}
JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
smuassert( cx , "mongo_find needs at elast 3 args" , argc >= 3 );
smuassert( cx , "2nd param to update has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
smuassert( cx , "3rd param to update has to be an object" , JSVAL_IS_OBJECT( argv[2] ) );
Convertor c( cx );
if ( c.getBoolean( obj , "readOnly" ) ){
JS_ReportError( cx , "js db in read only mode - mongo_update" );
return JS_FALSE;
}
DBClientWithCommands * conn = getConnection( cx, obj );
uassert( 10245 , "no connection!" , conn );
string ns = c.toString( argv[0] );
bool upsert = argc > 3 && c.toBoolean( argv[3] );
bool multi = argc > 4 && c.toBoolean( argv[4] );
try {
conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert , multi );
return JS_TRUE;
}
catch ( ... ){
JS_ReportError( cx , "error doing update" );
return JS_FALSE;
}
}
JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
smuassert( cx , "mongo_insert needs 2 args" , argc == 2 );
smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
Convertor c( cx );
if ( c.getBoolean( obj , "readOnly" ) ){
JS_ReportError( cx , "js db in read only mode - mongo_insert" );
return JS_FALSE;
}
DBClientWithCommands * conn = getConnection( cx, obj );
uassert( 10248 , "no connection!" , conn );
string ns = c.toString( argv[0] );
BSONObj o = c.toObject( argv[1] );
// TODO: add _id
try {
conn->insert( ns , o );
return JS_TRUE;
}
catch ( std::exception& e ){
stringstream ss;
ss << "error doing insert:" << e.what();
string s = ss.str();
JS_ReportError( cx , s.c_str() );
return JS_FALSE;
}
catch ( ... ){
JS_ReportError( cx , "error doing insert" );
return JS_FALSE;
}
}
JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
smuassert( cx , "mongo_remove needs 2 arguments" , argc == 2 );
smuassert( cx , "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
Convertor c( cx );
if ( c.getBoolean( obj , "readOnly" ) ){
JS_ReportError( cx , "js db in read only mode - mongo_remove" );
return JS_FALSE;
}
DBClientWithCommands * conn = getConnection( cx, obj );
uassert( 10251 , "no connection!" , conn );
string ns = c.toString( argv[0] );
BSONObj o = c.toObject( argv[1] );
try {
conn->remove( ns , o );
return JS_TRUE;
}
catch ( ... ){
JS_ReportError( cx , "error doing remove" );
return JS_FALSE;
}
}
JSFunctionSpec mongo_functions[] = {
{ "find" , mongo_find , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "update" , mongo_update , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "insert" , mongo_insert , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "remove" , mongo_remove , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
// ------------- db_collection -------------
JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
smuassert( cx , "db_collection_constructor wrong args" , argc == 4 );
assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
assert( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) );
assert( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) );
assert( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) );
Convertor c(cx);
if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ){
JS_ReportError( cx , "can't use sharded collection from db.eval" );
return JS_FALSE;
}
return JS_TRUE;
}
JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
Convertor c( cx );
string collname = c.toString( id );
if ( isSpecialName( collname ) )
return JS_TRUE;
if ( obj == c.getGlobalPrototype( "DBCollection" ) )
return JS_TRUE;
JSObject * proto = JS_GetPrototype( cx , obj );
if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() ) ) )
return JS_TRUE;
string name = c.toString( c.getProperty( obj , "_shortName" ) );
name += ".";
name += collname;
jsval db = c.getProperty( obj , "_db" );
if ( ! JSVAL_IS_OBJECT( db ) )
return JS_TRUE;
JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name );
if ( ! coll )
return JS_FALSE;
c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
*objp = obj;
return JS_TRUE;
}
JSClass db_collection_class = {
"DBCollection" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, (JSResolveOp)(&db_collection_resolve) , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ){
Convertor c(cx);
assert( c.hasProperty( db , "_mongo" ) );
assert( c.hasProperty( db , "_name" ) );
JSObject * coll = JS_NewObject( cx , &db_collection_class , 0 , 0 );
CHECKNEWOBJECT( coll, cx, "doCreateCollection" );
c.setProperty( coll , "_mongo" , c.getProperty( db , "_mongo" ) );
c.setProperty( coll , "_db" , OBJECT_TO_JSVAL( db ) );
c.setProperty( coll , "_shortName" , c.toval( shortName.c_str() ) );
string name = c.toString( c.getProperty( db , "_name" ) );
name += "." + shortName;
c.setProperty( coll , "_fullName" , c.toval( name.c_str() ) );
if ( haveLocalShardingInfo( name ) ){
JS_ReportError( cx , "can't use sharded collection from db.eval" );
return 0;
}
return coll;
}
// -------------- DB ---------------
JSBool db_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
smuassert( cx, "wrong number of arguments to DB" , argc == 2 );
assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
assert( JS_SetProperty( cx , obj , "_name" , &(argv[1]) ) );
return JS_TRUE;
}
JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
Convertor c( cx );
if ( obj == c.getGlobalPrototype( "DB" ) )
return JS_TRUE;
string collname = c.toString( id );
if ( isSpecialName( collname ) )
return JS_TRUE;
JSObject * proto = JS_GetPrototype( cx , obj );
if ( proto && c.hasProperty( proto , collname.c_str() ) )
return JS_TRUE;
JSObject * coll = doCreateCollection( cx , obj , collname );
if ( ! coll )
return JS_FALSE;
c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
*objp = obj;
return JS_TRUE;
}
JSClass db_class = {
"DB" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, (JSResolveOp)(&db_resolve) , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
// -------------- object id -------------
JSBool object_id_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
OID oid;
if ( argc == 0 ){
oid.init();
}
else {
smuassert( cx , "object_id_constructor can't take more than 1 param" , argc == 1 );
string s = c.toString( argv[0] );
try {
Scope::validateObjectIdString( s );
} catch ( const MsgAssertionException &m ) {
static string error = m.toString();
JS_ReportError( cx, error.c_str() );
return JS_FALSE;
}
oid.init( s );
}
if ( ! JS_InstanceOf( cx , obj , &object_id_class , 0 ) ){
obj = JS_NewObject( cx , &object_id_class , 0 , 0 );
CHECKNEWOBJECT( obj, cx, "object_id_constructor" );
*rval = OBJECT_TO_JSVAL( obj );
}
jsval v = c.toval( oid.str().c_str() );
assert( JS_SetProperty( cx , obj , "str" , &v ) );
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 (JSBool) (*rval = c.getProperty( obj , "str" ));
}
JSFunctionSpec object_id_functions[] = {
{ "toString" , object_id_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
// dbpointer
JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
if ( argc == 2 ){
if ( ! JSVAL_IS_OID( argv[1] ) ){
JS_ReportError( cx , "2nd arg to DBPointer needs to be oid" );
return JS_FALSE;
}
assert( JS_SetProperty( cx , obj , "ns" , &(argv[0]) ) );
assert( JS_SetProperty( cx , obj , "id" , &(argv[1]) ) );
return JS_TRUE;
}
else {
JS_ReportError( cx , "DBPointer needs 2 arguments" );
return JS_FALSE;
}
}
JSClass dbpointer_class = {
"DBPointer" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSFunctionSpec dbpointer_functions[] = {
{ 0 }
};
JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
if ( argc == 2 ){
JSObject * o = JS_NewObject( cx , NULL , NULL, NULL );
CHECKNEWOBJECT( o, cx, "dbref_constructor" );
assert( JS_SetProperty( cx, o , "$ref" , &argv[ 0 ] ) );
assert( JS_SetProperty( cx, o , "$id" , &argv[ 1 ] ) );
BSONObj bo = c.toObject( o );
assert( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( bo.getOwned() ) ) ) );
return JS_TRUE;
}
else {
JS_ReportError( cx , "DBRef needs 2 arguments" );
assert( JS_SetPrivate( cx , obj , (void*)(new BSONHolder( BSONObj().getOwned() ) ) ) );
return JS_FALSE;
}
}
JSClass dbref_class = bson_class; // name will be fixed later
// BinData
JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
Convertor c( cx );
if ( argc == 2 ){
int type = (int)c.toNumber( argv[ 0 ] );
string encoded = c.toString( argv[ 1 ] );
string decoded = base64::decode( encoded );
assert( JS_SetPrivate( cx, obj, new BinDataHolder( decoded.data(), decoded.length() ) ) );
c.setProperty( obj, "len", c.toval( decoded.length() ) );
c.setProperty( obj, "type", c.toval( type ) );
return JS_TRUE;
}
else {
JS_ReportError( cx , "BinData needs 2 arguments" );
return JS_FALSE;
}
}
JSBool bindata_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
int type = (int)c.getNumber( obj , "type" );
int len = (int)c.getNumber( obj, "len" );
void *holder = JS_GetPrivate( cx, obj );
assert( holder );
const char *data = ( ( BinDataHolder* )( holder ) )->c_;
stringstream ss;
ss << "BinData( type: " << type << ", base64: \"";
base64::encode( ss, (const char *)data, len );
ss << "\" )";
string ret = ss.str();
return *rval = c.toval( ret.c_str() );
}
void bindata_finalize( JSContext * cx , JSObject * obj ){
Convertor c(cx);
void *holder = JS_GetPrivate( cx, obj );
if ( holder ){
delete ( BinDataHolder* )holder;
assert( JS_SetPrivate( cx , obj , 0 ) );
}
}
JSClass bindata_class = {
"BinData" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, bindata_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSFunctionSpec bindata_functions[] = {
{ "toString" , bindata_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
// Map
bool specialMapString( const string& s ){
return s == "put" || s == "get" || s == "_get" || s == "values" || s == "_data" || s == "constructor" ;
}
JSBool map_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
if ( argc > 0 ){
JS_ReportError( cx , "Map takes no arguments" );
return JS_FALSE;
}
JSObject * array = JS_NewObject( cx , 0 , 0 , 0 );
CHECKNEWOBJECT( array, cx, "map_constructor" );
jsval a = OBJECT_TO_JSVAL( array );
JS_SetProperty( cx , obj , "_data" , &a );
return JS_TRUE;
}
JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ){
Convertor c(cx);
if ( specialMapString( c.toString( idval ) ) )
return JS_TRUE;
log() << "illegal prop access: " << c.toString( idval ) << endl;
JS_ReportError( cx , "can't use array access with Map" );
return JS_FALSE;
}
JSClass map_class = {
"Map" , JSCLASS_HAS_PRIVATE ,
map_prop, JS_PropertyStub, map_prop, map_prop,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSFunctionSpec map_functions[] = {
{ 0 }
};
// -----
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 numberlong_class = {
"NumberLong" , JSCLASS_HAS_PRIVATE ,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) );
}
JSBool numberlong_tonumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
return numberlong_valueof( cx, obj, argc, argv, rval );
}
JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
Convertor c(cx);
stringstream ss;
ss << c.toNumberLongUnsafe( obj );
string ret = ss.str();
return *rval = c.toval( ret.c_str() );
}
JSFunctionSpec numberlong_functions[] = {
{ "valueOf" , numberlong_valueof , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "toNumber" , numberlong_tonumber , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ "toString" , numberlong_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
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
JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
smuassert( cx , "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 {
JSObject * temp = JS_NewObject( cx , 0 , 0 , 0 );
CHECKNEWOBJECT( temp, cx, "dbquery_constructor" );
c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( temp ) );
}
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 );
if ( argc > 8 && JSVAL_IS_NUMBER( argv[8] ) )
c.setProperty( obj , "_batchSize" , argv[8] );
else
c.setProperty( obj , "_batchSize" , JSVAL_ZERO );
c.setProperty( obj , "_cursor" , JSVAL_NULL );
c.setProperty( obj , "_numReturned" , JSVAL_ZERO );
c.setProperty( obj , "_special" , JSVAL_FALSE );
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 = JSVAL_VOID;
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
};
// ---- other stuff ----
void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ){
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 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &timestamp_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &numberlong_class , 0 , 0 , 0 , numberlong_functions , 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 ) );
assert( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
assert( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
static const char *dbrefName = "DBRef";
dbref_class.name = dbrefName;
assert( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 2 , 0 , bson_functions , 0 , 0 ) );
scope->exec( jsconcatcode );
}
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ){
if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ){
OID oid;
oid.init( c->getString( o , "str" ) );
b.append( name.c_str() , oid );
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;
}
if ( JS_InstanceOf( c->_context , o , &numberlong_class , 0 ) ){
b.append( name.c_str() , c->toNumberLongUnsafe( o ) );
return true;
}
if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ){
b.appendDBRef( name.c_str() , c->getString( o , "ns" ).c_str() , c->toOID( c->getProperty( o , "id" ) ) );
return true;
}
if ( JS_InstanceOf( c->_context , o , &bindata_class , 0 ) ){
void *holder = JS_GetPrivate( c->_context , o );
const char *data = ( ( BinDataHolder * )( holder ) )->c_;
b.appendBinData( name.c_str() ,
(int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) ,
data
);
return true;
}
#if defined( SM16 ) || defined( MOZJS )
#warning dates do not work in your version of spider monkey
{
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
if ( d ){
b.appendDate( name.c_str() , Date_t(d) );
return true;
}
}
#elif defined( XULRUNNER )
if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ){
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
b.appendDate( name.c_str() , Date_t(d) );
return true;
}
#else
if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ){
jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
//TODO: make signed
b.appendDate( name.c_str() , Date_t((unsigned long long)d) );
return true;
}
#endif
if ( JS_InstanceOf( c->_context , o , &dbquery_class , 0 ) ||
JS_InstanceOf( c->_context , o , &mongo_class , 0 ) ||
JS_InstanceOf( c->_context , o , &db_collection_class , 0 ) ){
b.append( name.c_str() , c->toString( val ) );
return true;
}
#if defined( XULRUNNER )
if ( JS_InstanceOf( c->_context , o , globalSMEngine->_regexClass , 0 ) ){
c->appendRegex( b , name , c->toString( val ) );
return true;
}
#elif defined( SM18 )
if ( JS_InstanceOf( c->_context , o , &js_RegExpClass , 0 ) ){
c->appendRegex( b , name , c->toString( val ) );
return true;
}
#endif
return false;
}
bool isDate( JSContext * cx , JSObject * o ){
#if defined( SM16 ) || defined( MOZJS ) || defined( XULRUNNER )
return js_DateGetMsecSinceEpoch( cx , o ) != 0;
#else
return JS_InstanceOf( cx , o, &js_DateClass, 0 );
#endif
}
}
Jump to Line
Something went wrong with that request. Please try again.