~ubuntu-branches/ubuntu/trusty/mongodb/trusty-proposed

« back to all changes in this revision

Viewing changes to scripting/sm_db.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Antonin Kral
  • Date: 2010-01-29 19:48:45 UTC
  • Revision ID: james.westby@ubuntu.com-20100129194845-8wbmkf626fwcavc9
Tags: upstream-1.3.1
ImportĀ upstreamĀ versionĀ 1.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// sm_db.cpp
 
2
 
 
3
/*    Copyright 2009 10gen Inc.
 
4
 *
 
5
 *    Licensed under the Apache License, Version 2.0 (the "License");
 
6
 *    you may not use this file except in compliance with the License.
 
7
 *    You may obtain a copy of the License at
 
8
 *
 
9
 *    http://www.apache.org/licenses/LICENSE-2.0
 
10
 *
 
11
 *    Unless required by applicable law or agreed to in writing, software
 
12
 *    distributed under the License is distributed on an "AS IS" BASIS,
 
13
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
 *    See the License for the specific language governing permissions and
 
15
 *    limitations under the License.
 
16
 */
 
17
 
 
18
// hacked in right now from engine_spidermonkey.cpp
 
19
 
 
20
#include "../client/syncclusterconnection.h"
 
21
 
 
22
namespace mongo {
 
23
 
 
24
    bool haveLocalShardingInfo( const string& ns );
 
25
 
 
26
    // ------------    some defs needed ---------------
 
27
    
 
28
    JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName );
 
29
        
 
30
    // ------------     utils          ------------------
 
31
         
 
32
 
 
33
    bool isSpecialName( const string& name ){
 
34
        static set<string> names;
 
35
        if ( names.size() == 0 ){
 
36
            names.insert( "tojson" );
 
37
            names.insert( "toJson" );
 
38
            names.insert( "toString" );
 
39
        }
 
40
 
 
41
        if ( name.length() == 0 )
 
42
            return false;
 
43
        
 
44
        if ( name[0] == '_' )
 
45
            return true;
 
46
        
 
47
        return names.count( name ) > 0;
 
48
    }
 
49
 
 
50
 
 
51
    // ------ cursor ------
 
52
 
 
53
    class CursorHolder {
 
54
    public:
 
55
        CursorHolder( auto_ptr< DBClientCursor > &cursor, const shared_ptr< DBClientWithCommands > &connection ) :
 
56
        connection_( connection ),
 
57
        cursor_( cursor ) {
 
58
            assert( cursor_.get() );
 
59
        }
 
60
        DBClientCursor *get() const { return cursor_.get(); }
 
61
    private:
 
62
        shared_ptr< DBClientWithCommands > connection_;
 
63
        auto_ptr< DBClientCursor > cursor_;
 
64
    };
 
65
    
 
66
    DBClientCursor *getCursor( JSContext *cx, JSObject *obj ) {
 
67
        CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
 
68
        uassert( 10235 ,  "no cursor!" , holder );
 
69
        return holder->get();
 
70
    }
 
71
    
 
72
    JSBool internal_cursor_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
73
        uassert( 10236 ,  "no args to internal_cursor_constructor" , argc == 0 );
 
74
        assert( JS_SetPrivate( cx , obj , 0 ) ); // just for safety
 
75
        return JS_TRUE;
 
76
    }
 
77
 
 
78
    void internal_cursor_finalize( JSContext * cx , JSObject * obj ){
 
79
        CursorHolder * holder = (CursorHolder*)JS_GetPrivate( cx , obj );
 
80
        if ( holder ){
 
81
            delete holder;
 
82
            assert( JS_SetPrivate( cx , obj , 0 ) );
 
83
        }
 
84
    }
 
85
 
 
86
    JSBool internal_cursor_hasNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
 
87
        DBClientCursor *cursor = getCursor( cx, obj );
 
88
        *rval = cursor->more() ? JSVAL_TRUE : JSVAL_FALSE;
 
89
        return JS_TRUE;
 
90
    }
 
91
 
 
92
    JSBool internal_cursor_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
 
93
        DBClientCursor *cursor = getCursor( cx, obj );
 
94
        if ( ! cursor->more() ){
 
95
            JS_ReportError( cx , "cursor at the end" );
 
96
            return JS_FALSE;
 
97
        }
 
98
        Convertor c(cx);
 
99
 
 
100
        BSONObj n = cursor->next();
 
101
        *rval = c.toval( &n );
 
102
        return JS_TRUE;
 
103
    }
 
104
    
 
105
 
 
106
    JSFunctionSpec internal_cursor_functions[] = {
 
107
        { "hasNext" , internal_cursor_hasNext , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
108
        { "next" , internal_cursor_next , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
109
        { 0 }
 
110
    };
 
111
 
 
112
    JSClass internal_cursor_class = {
 
113
        "InternalCursor" , JSCLASS_HAS_PRIVATE  ,
 
114
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
115
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, internal_cursor_finalize,
 
116
        JSCLASS_NO_OPTIONAL_MEMBERS
 
117
    };
 
118
 
 
119
    
 
120
    // ------ mongo stuff ------
 
121
 
 
122
    JSBool mongo_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
123
        uassert( 10237 ,  "mongo_constructor not implemented yet" , 0 );
 
124
        throw -1;
 
125
    }
 
126
    
 
127
    JSBool mongo_local_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
128
        Convertor c( cx );
 
129
 
 
130
        shared_ptr< DBClientWithCommands > client( createDirectClient() );
 
131
        assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( client ) ) ) );
 
132
 
 
133
        jsval host = c.toval( "EMBEDDED" );
 
134
        assert( JS_SetProperty( cx , obj , "host" , &host ) );
 
135
 
 
136
        return JS_TRUE;
 
137
    }
 
138
 
 
139
    JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
140
        Convertor c( cx );
 
141
        
 
142
        uassert( 10238 ,  "0 or 1 args to Mongo" , argc <= 1 );
 
143
        
 
144
        string host = "127.0.0.1";
 
145
        if ( argc > 0 )
 
146
            host = c.toString( argv[0] );
 
147
 
 
148
        shared_ptr< DBClientWithCommands > conn;
 
149
        
 
150
        string errmsg;
 
151
        if ( host.find( "," ) == string::npos ){
 
152
            DBClientConnection * c = new DBClientConnection( true );
 
153
            conn.reset( c );
 
154
            if ( ! c->connect( host , errmsg ) ){
 
155
                JS_ReportError( cx , ((string)"couldn't connect: " + errmsg).c_str() );
 
156
                return JS_FALSE;
 
157
            }
 
158
        }
 
159
        else { // paired
 
160
            int numCommas = 0;
 
161
            for ( uint i=0; i<host.size(); i++ )
 
162
                if ( host[i] == ',' )
 
163
                    numCommas++;
 
164
            
 
165
            assert( numCommas > 0 );
 
166
 
 
167
            if ( numCommas == 1 ){
 
168
                DBClientPaired * c = new DBClientPaired();
 
169
                conn.reset( c );
 
170
                if ( ! c->connect( host ) ){
 
171
                    JS_ReportError( cx , "couldn't connect to pair" );
 
172
                    return JS_FALSE;
 
173
                }
 
174
            }
 
175
            else if ( numCommas == 2 ){
 
176
                conn.reset( new SyncCluterConnection( host ) );
 
177
            }
 
178
            else {
 
179
                JS_ReportError( cx , "1 (paired) or 2(quorum) commas are allowed" );
 
180
                return JS_FALSE;
 
181
            }
 
182
        }
 
183
        
 
184
        
 
185
        assert( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
 
186
        jsval host_val = c.toval( host.c_str() );
 
187
        assert( JS_SetProperty( cx , obj , "host" , &host_val ) );
 
188
        return JS_TRUE;
 
189
 
 
190
    }
 
191
 
 
192
    DBClientWithCommands *getConnection( JSContext *cx, JSObject *obj ) {
 
193
        shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
 
194
        uassert( 10239 ,  "no connection!" , connHolder && connHolder->get() );
 
195
        return connHolder->get();
 
196
    }
 
197
    
 
198
    void mongo_finalize( JSContext * cx , JSObject * obj ){
 
199
        shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
 
200
        if ( connHolder ){
 
201
            delete connHolder;
 
202
            assert( JS_SetPrivate( cx , obj , 0 ) );
 
203
        }
 
204
    }
 
205
 
 
206
    JSClass mongo_class = {
 
207
        "Mongo" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE ,
 
208
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
209
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, mongo_finalize,
 
210
        JSCLASS_NO_OPTIONAL_MEMBERS
 
211
     };
 
212
 
 
213
    JSBool mongo_find(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
 
214
        uassert( 10240 ,  "mongo_find neesd 5 args" , argc == 5 );
 
215
        shared_ptr< DBClientWithCommands > * connHolder = (shared_ptr< DBClientWithCommands >*)JS_GetPrivate( cx , obj );
 
216
        uassert( 10241 ,  "no connection!" , connHolder && connHolder->get() );
 
217
        DBClientWithCommands *conn = connHolder->get();
 
218
                      
 
219
        Convertor c( cx );
 
220
 
 
221
        string ns = c.toString( argv[0] );
 
222
        
 
223
        BSONObj q = c.toObject( argv[1] );
 
224
        BSONObj f = c.toObject( argv[2] );
 
225
        
 
226
        int nToReturn = (int) c.toNumber( argv[3] );
 
227
        int nToSkip = (int) c.toNumber( argv[4] );
 
228
        bool slaveOk = c.getBoolean( obj , "slaveOk" );
 
229
 
 
230
        try {
 
231
 
 
232
            auto_ptr<DBClientCursor> cursor = conn->query( ns , q , nToReturn , nToSkip , f.nFields() ? &f : 0  , slaveOk ? QueryOption_SlaveOk : 0 );
 
233
            if ( ! cursor.get() ){
 
234
                JS_ReportError( cx , "error doing query: failed" );
 
235
                return JS_FALSE;
 
236
            }
 
237
            JSObject * mycursor = JS_NewObject( cx , &internal_cursor_class , 0 , 0 );
 
238
            assert( JS_SetPrivate( cx , mycursor , new CursorHolder( cursor, *connHolder ) ) );
 
239
            *rval = OBJECT_TO_JSVAL( mycursor );
 
240
            return JS_TRUE;
 
241
        }
 
242
        catch ( ... ){
 
243
            JS_ReportError( cx , "error doing query: unknown" );
 
244
            return JS_FALSE;
 
245
        }
 
246
    }
 
247
 
 
248
    JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
 
249
        smuassert( cx ,  "mongo_find needs at elast 3 args" , argc >= 3 );
 
250
        smuassert( cx ,  "2nd param to update has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
 
251
        smuassert( cx ,  "3rd param to update has to be an object" , JSVAL_IS_OBJECT( argv[2] ) );
 
252
 
 
253
        Convertor c( cx );
 
254
        if ( c.getBoolean( obj , "readOnly" ) ){
 
255
            JS_ReportError( cx , "js db in read only mode - mongo_update" );
 
256
            return JS_FALSE;
 
257
        }
 
258
 
 
259
        DBClientWithCommands * conn = getConnection( cx, obj );
 
260
        uassert( 10245 ,  "no connection!" , conn );
 
261
 
 
262
        string ns = c.toString( argv[0] );
 
263
 
 
264
        bool upsert = argc > 3 && c.toBoolean( argv[3] );
 
265
        bool multi = argc > 4 && c.toBoolean( argv[4] );
 
266
 
 
267
        try {
 
268
            conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert , multi );
 
269
            return JS_TRUE;
 
270
        }
 
271
        catch ( ... ){
 
272
            JS_ReportError( cx , "error doing update" );
 
273
            return JS_FALSE;
 
274
        }
 
275
    }
 
276
 
 
277
    JSBool mongo_insert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){    
 
278
        smuassert( cx ,  "mongo_insert needs 2 args" , argc == 2 );
 
279
        smuassert( cx ,  "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
 
280
 
 
281
        Convertor c( cx );
 
282
        if ( c.getBoolean( obj , "readOnly" ) ){
 
283
            JS_ReportError( cx , "js db in read only mode - mongo_insert" );
 
284
            return JS_FALSE;
 
285
        }
 
286
        
 
287
        DBClientWithCommands * conn = getConnection( cx, obj );
 
288
        uassert( 10248 ,  "no connection!" , conn );
 
289
        
 
290
        
 
291
        string ns = c.toString( argv[0] );
 
292
        BSONObj o = c.toObject( argv[1] );
 
293
 
 
294
        // TODO: add _id
 
295
        
 
296
        try {
 
297
            conn->insert( ns , o );
 
298
            return JS_TRUE;
 
299
        }
 
300
        catch ( std::exception& e ){
 
301
            stringstream ss;
 
302
            ss << "error doing insert:" << e.what();
 
303
            string s = ss.str();
 
304
            JS_ReportError( cx , s.c_str() );
 
305
            return JS_FALSE;
 
306
        }
 
307
        catch ( ... ){
 
308
            JS_ReportError( cx , "error doing insert" );
 
309
            return JS_FALSE;
 
310
        }
 
311
    }
 
312
 
 
313
    JSBool mongo_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){    
 
314
        smuassert( cx ,  "mongo_remove needs 2 arguments" , argc == 2 );
 
315
        smuassert( cx ,  "2nd param to insert has to be an object" , JSVAL_IS_OBJECT( argv[1] ) );
 
316
 
 
317
        Convertor c( cx );
 
318
        if ( c.getBoolean( obj , "readOnly" ) ){
 
319
            JS_ReportError( cx , "js db in read only mode - mongo_remove" );
 
320
            return JS_FALSE;
 
321
        }
 
322
 
 
323
        DBClientWithCommands * conn = getConnection( cx, obj );
 
324
        uassert( 10251 ,  "no connection!" , conn );
 
325
        
 
326
        string ns = c.toString( argv[0] );
 
327
        BSONObj o = c.toObject( argv[1] );
 
328
 
 
329
        try {
 
330
            conn->remove( ns , o );
 
331
            return JS_TRUE;
 
332
        }
 
333
        catch ( ... ){
 
334
            JS_ReportError( cx , "error doing remove" );
 
335
            return JS_FALSE;
 
336
        }
 
337
        
 
338
    }
 
339
 
 
340
    JSFunctionSpec mongo_functions[] = {
 
341
        { "find" , mongo_find , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
342
        { "update" , mongo_update , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
343
        { "insert" , mongo_insert , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
344
        { "remove" , mongo_remove , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
345
        { 0 }
 
346
    };
 
347
 
 
348
 
 
349
     // -------------  db_collection -------------
 
350
 
 
351
     JSBool db_collection_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){    
 
352
         smuassert( cx ,  "db_collection_constructor wrong args" , argc == 4 );
 
353
         assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
 
354
         assert( JS_SetProperty( cx , obj , "_db" , &(argv[1]) ) );
 
355
         assert( JS_SetProperty( cx , obj , "_shortName" , &(argv[2]) ) );
 
356
         assert( JS_SetProperty( cx , obj , "_fullName" , &(argv[3]) ) );
 
357
         
 
358
         Convertor c(cx);
 
359
         if ( haveLocalShardingInfo( c.toString( argv[3] ) ) ){
 
360
             JS_ReportError( cx , "can't use sharded collection from db.eval" );
 
361
             return JS_FALSE;
 
362
         }
 
363
         
 
364
         return JS_TRUE;
 
365
     }
 
366
 
 
367
     JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
 
368
         if ( flags & JSRESOLVE_ASSIGNING )
 
369
             return JS_TRUE;
 
370
         
 
371
         Convertor c( cx );
 
372
         string collname = c.toString( id );
 
373
 
 
374
         if ( isSpecialName( collname ) )
 
375
             return JS_TRUE;
 
376
         
 
377
         if ( obj == c.getGlobalPrototype( "DBCollection" ) )
 
378
             return JS_TRUE;
 
379
         
 
380
         JSObject * proto = JS_GetPrototype( cx , obj );
 
381
         if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() )  ) )
 
382
             return JS_TRUE;
 
383
         
 
384
         string name = c.toString( c.getProperty( obj , "_shortName" ) );
 
385
         name += ".";
 
386
         name += collname;
 
387
         
 
388
         jsval db = c.getProperty( obj , "_db" );
 
389
         if ( ! JSVAL_IS_OBJECT( db ) )
 
390
             return JS_TRUE;
 
391
 
 
392
         JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name );
 
393
         if ( ! coll )
 
394
             return JS_FALSE;
 
395
         c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
 
396
         *objp = obj;
 
397
         return JS_TRUE;
 
398
     }
 
399
 
 
400
    JSClass db_collection_class = {
 
401
         "DBCollection" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE , 
 
402
         JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
403
         JS_EnumerateStub, (JSResolveOp)(&db_collection_resolve) , JS_ConvertStub, JS_FinalizeStub,
 
404
         JSCLASS_NO_OPTIONAL_MEMBERS
 
405
     };
 
406
 
 
407
 
 
408
     JSObject * doCreateCollection( JSContext * cx , JSObject * db , const string& shortName ){
 
409
         Convertor c(cx);
 
410
         
 
411
         assert( c.hasProperty( db , "_mongo" ) );
 
412
         assert( c.hasProperty( db , "_name" ) );
 
413
         
 
414
         JSObject * coll = JS_NewObject( cx , &db_collection_class , 0 , 0 );
 
415
         c.setProperty( coll , "_mongo" , c.getProperty( db , "_mongo" ) );
 
416
         c.setProperty( coll , "_db" , OBJECT_TO_JSVAL( db ) );
 
417
         c.setProperty( coll , "_shortName" , c.toval( shortName.c_str() ) );
 
418
         
 
419
         string name = c.toString( c.getProperty( db , "_name" ) );
 
420
         name += "." + shortName;
 
421
         c.setProperty( coll , "_fullName" , c.toval( name.c_str() ) );
 
422
         
 
423
         if ( haveLocalShardingInfo( name ) ){
 
424
             JS_ReportError( cx , "can't use sharded collection from db.eval" );
 
425
             return 0;
 
426
         }
 
427
 
 
428
         return coll;
 
429
    }
 
430
    
 
431
    // --------------  DB ---------------
 
432
    
 
433
    
 
434
    JSBool db_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
435
        smuassert( cx,  "wrong number of arguments to DB" , argc == 2 );
 
436
        assert( JS_SetProperty( cx , obj , "_mongo" , &(argv[0]) ) );
 
437
        assert( JS_SetProperty( cx , obj , "_name" , &(argv[1]) ) );
 
438
 
 
439
        return JS_TRUE;
 
440
    }
 
441
 
 
442
    JSBool db_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
 
443
        if ( flags & JSRESOLVE_ASSIGNING )
 
444
            return JS_TRUE;
 
445
 
 
446
        Convertor c( cx );
 
447
 
 
448
        if ( obj == c.getGlobalPrototype( "DB" ) )
 
449
            return JS_TRUE;
 
450
 
 
451
        string collname = c.toString( id );
 
452
        
 
453
        if ( isSpecialName( collname ) )
 
454
             return JS_TRUE;
 
455
 
 
456
        JSObject * proto = JS_GetPrototype( cx , obj );
 
457
        if ( proto && c.hasProperty( proto , collname.c_str() ) )
 
458
            return JS_TRUE;
 
459
 
 
460
        JSObject * coll = doCreateCollection( cx , obj , collname );
 
461
        if ( ! coll )
 
462
            return JS_FALSE;
 
463
        c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
 
464
        
 
465
        *objp = obj;
 
466
        return JS_TRUE;
 
467
    }
 
468
 
 
469
    JSClass db_class = {
 
470
        "DB" , JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE , 
 
471
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
472
        JS_EnumerateStub, (JSResolveOp)(&db_resolve) , JS_ConvertStub, JS_FinalizeStub,
 
473
        JSCLASS_NO_OPTIONAL_MEMBERS
 
474
    };
 
475
    
 
476
 
 
477
    // -------------- object id -------------
 
478
 
 
479
    JSBool object_id_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
480
        Convertor c( cx );
 
481
 
 
482
        OID oid;
 
483
        if ( argc == 0 ){
 
484
            oid.init();
 
485
        }
 
486
        else {
 
487
            smuassert( cx ,  "object_id_constructor can't take more than 1 param" , argc == 1 );
 
488
            string s = c.toString( argv[0] );
 
489
 
 
490
            try {
 
491
                Scope::validateObjectIdString( s );
 
492
            } catch ( const MsgAssertionException &m ) {
 
493
                static string error = m.toString();
 
494
                JS_ReportError( cx, error.c_str() );
 
495
                return JS_FALSE;
 
496
            }
 
497
            oid.init( s );
 
498
        }
 
499
        
 
500
        if ( ! JS_InstanceOf( cx , obj , &object_id_class , 0 ) ){
 
501
            obj = JS_NewObject( cx , &object_id_class , 0 , 0 );
 
502
            assert( obj );
 
503
            *rval = OBJECT_TO_JSVAL( obj );
 
504
        }
 
505
        
 
506
        jsval v = c.toval( oid.str().c_str() );
 
507
        assert( JS_SetProperty( cx , obj , "str" , &v  ) );
 
508
 
 
509
        return JS_TRUE;
 
510
    }
 
511
    
 
512
    JSClass object_id_class = {
 
513
        "ObjectId" , JSCLASS_HAS_PRIVATE ,
 
514
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
515
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
516
        JSCLASS_NO_OPTIONAL_MEMBERS
 
517
    };
 
518
 
 
519
    JSBool object_id_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){    
 
520
        Convertor c(cx);
 
521
        return *rval = c.getProperty( obj , "str" );
 
522
    }
 
523
 
 
524
    JSFunctionSpec object_id_functions[] = {
 
525
        { "toString" , object_id_tostring , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
 
526
        { 0 }
 
527
    };
 
528
 
 
529
    // dbpointer
 
530
 
 
531
    JSBool dbpointer_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
532
        Convertor c( cx );
 
533
                
 
534
        if ( argc == 2 ){
 
535
 
 
536
            if ( ! JSVAL_IS_OID( argv[1] ) ){
 
537
                JS_ReportError( cx , "2nd arg to DBPointer needs to be oid" );
 
538
                return JS_FALSE;            
 
539
            }
 
540
            
 
541
            assert( JS_SetProperty( cx , obj , "ns" , &(argv[0]) ) );
 
542
            assert( JS_SetProperty( cx , obj , "id" , &(argv[1]) ) );
 
543
            return JS_TRUE;
 
544
        }
 
545
        else {
 
546
            JS_ReportError( cx , "DBPointer needs 2 arguments" );
 
547
            return JS_FALSE;            
 
548
        }
 
549
    }
 
550
 
 
551
    JSClass dbpointer_class = {
 
552
        "DBPointer" , JSCLASS_HAS_PRIVATE ,
 
553
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
554
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
555
        JSCLASS_NO_OPTIONAL_MEMBERS
 
556
    };
 
557
 
 
558
    JSFunctionSpec dbpointer_functions[] = {
 
559
        { 0 }
 
560
    };
 
561
 
 
562
 
 
563
    JSBool dbref_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
564
        Convertor c( cx );
 
565
                
 
566
        if ( argc == 2 ){
 
567
            assert( JS_SetProperty( cx , obj , "$ref" , &(argv[0]) ) );
 
568
            assert( JS_SetProperty( cx , obj , "$id" , &(argv[1]) ) );
 
569
            return JS_TRUE;
 
570
        }
 
571
        else {
 
572
            JS_ReportError( cx , "DBRef needs 2 arguments" );
 
573
            return JS_FALSE;            
 
574
        }
 
575
    }
 
576
 
 
577
    JSClass dbref_class = {
 
578
        "DBRef" , JSCLASS_HAS_PRIVATE ,
 
579
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
580
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
581
        JSCLASS_NO_OPTIONAL_MEMBERS
 
582
    };
 
583
 
 
584
    JSFunctionSpec dbref_functions[] = {
 
585
        { 0 }
 
586
    };
 
587
 
 
588
 
 
589
    // BinData
 
590
 
 
591
 
 
592
    JSBool bindata_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
593
        JS_ReportError( cx , "can't create a BinData yet" );
 
594
        return JS_FALSE;            
 
595
    }
 
596
 
 
597
    JSClass bindata_class = {
 
598
        "BinData" , JSCLASS_HAS_PRIVATE ,
 
599
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
600
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
601
        JSCLASS_NO_OPTIONAL_MEMBERS
 
602
    };
 
603
 
 
604
    JSFunctionSpec bindata_functions[] = {
 
605
        { 0 }
 
606
    };
 
607
    
 
608
    // Map
 
609
 
 
610
    bool specialMapString( const string& s ){
 
611
        return s == "put" || s == "get" || s == "_get" || s == "values" || s == "_data" || s == "constructor" ;
 
612
    }
 
613
 
 
614
    JSBool map_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
615
        if ( argc > 0 ){
 
616
            JS_ReportError( cx , "Map takes no arguments" );
 
617
            return JS_FALSE;
 
618
        }
 
619
 
 
620
        JSObject * array = JS_NewObject( cx , 0 , 0 , 0 );
 
621
        assert( array );
 
622
 
 
623
        jsval a = OBJECT_TO_JSVAL( array );
 
624
        JS_SetProperty( cx , obj , "_data" , &a );
 
625
 
 
626
        return JS_TRUE;
 
627
    }
 
628
 
 
629
    JSBool map_prop( JSContext *cx, JSObject *obj, jsval idval, jsval *vp ){
 
630
        Convertor c(cx);
 
631
        if ( specialMapString( c.toString( idval ) ) )
 
632
            return JS_TRUE;
 
633
        
 
634
        log() << "illegal prop access: " << c.toString( idval ) << endl;
 
635
        JS_ReportError( cx , "can't use array access with Map" );
 
636
        return JS_FALSE;
 
637
    }
 
638
    
 
639
    JSClass map_class = {
 
640
        "Map" , JSCLASS_HAS_PRIVATE ,
 
641
        map_prop, JS_PropertyStub, map_prop, map_prop,
 
642
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
643
        JSCLASS_NO_OPTIONAL_MEMBERS
 
644
    };
 
645
    
 
646
    JSFunctionSpec map_functions[] = {
 
647
        { 0 }
 
648
    };
 
649
    
 
650
 
 
651
    // -----
 
652
 
 
653
    JSClass timestamp_class = {
 
654
        "Timestamp" , JSCLASS_HAS_PRIVATE ,
 
655
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
656
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
657
        JSCLASS_NO_OPTIONAL_MEMBERS
 
658
    };
 
659
 
 
660
    JSClass minkey_class = {
 
661
        "MinKey" , JSCLASS_HAS_PRIVATE ,
 
662
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
663
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
664
        JSCLASS_NO_OPTIONAL_MEMBERS
 
665
    };
 
666
 
 
667
    JSClass maxkey_class = {
 
668
        "MaxKey" , JSCLASS_HAS_PRIVATE ,
 
669
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
670
        JS_EnumerateStub, JS_ResolveStub , JS_ConvertStub, JS_FinalizeStub,
 
671
        JSCLASS_NO_OPTIONAL_MEMBERS
 
672
    };
 
673
    
 
674
    // dbquery
 
675
 
 
676
    JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){
 
677
        smuassert( cx ,  "DDQuery needs at least 4 args" , argc >= 4 );
 
678
        
 
679
        Convertor c(cx);
 
680
        c.setProperty( obj , "_mongo" , argv[0] );
 
681
        c.setProperty( obj , "_db" , argv[1] );
 
682
        c.setProperty( obj , "_collection" , argv[2] );
 
683
        c.setProperty( obj , "_ns" , argv[3] );
 
684
 
 
685
        if ( argc > 4 && JSVAL_IS_OBJECT( argv[4] ) )
 
686
            c.setProperty( obj , "_query" , argv[4] );
 
687
        else 
 
688
            c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( JS_NewObject( cx , 0 , 0 , 0 ) ) );
 
689
        
 
690
        if ( argc > 5 && JSVAL_IS_OBJECT( argv[5] ) )
 
691
            c.setProperty( obj , "_fields" , argv[5] );
 
692
        else
 
693
            c.setProperty( obj , "_fields" , JSVAL_NULL );
 
694
        
 
695
        
 
696
        if ( argc > 6 && JSVAL_IS_NUMBER( argv[6] ) )
 
697
            c.setProperty( obj , "_limit" , argv[6] );
 
698
        else 
 
699
            c.setProperty( obj , "_limit" , JSVAL_ZERO );
 
700
        
 
701
        if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) )
 
702
            c.setProperty( obj , "_skip" , argv[7] );
 
703
        else 
 
704
            c.setProperty( obj , "_skip" , JSVAL_ZERO );
 
705
        
 
706
        c.setProperty( obj , "_cursor" , JSVAL_NULL );
 
707
        c.setProperty( obj , "_numReturned" , JSVAL_ZERO );
 
708
        c.setProperty( obj , "_special" , JSVAL_FALSE );
 
709
 
 
710
        return JS_TRUE;
 
711
    }
 
712
 
 
713
    JSBool dbquery_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ){
 
714
        if ( flags & JSRESOLVE_ASSIGNING )
 
715
            return JS_TRUE;
 
716
 
 
717
        if ( ! JSVAL_IS_NUMBER( id ) )
 
718
            return JS_TRUE;
 
719
 
 
720
        jsval val = JSVAL_VOID;
 
721
        assert( JS_CallFunctionName( cx , obj , "arrayAccess" , 1 , &id , &val ) );
 
722
        Convertor c(cx);
 
723
        c.setProperty( obj , c.toString( id ).c_str() , val );
 
724
        *objp = obj;
 
725
        return JS_TRUE;
 
726
    }
 
727
 
 
728
    JSClass dbquery_class = {
 
729
        "DBQuery" , JSCLASS_NEW_RESOLVE ,
 
730
        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 
731
        JS_EnumerateStub, (JSResolveOp)(&dbquery_resolve) , JS_ConvertStub, JS_FinalizeStub,
 
732
        JSCLASS_NO_OPTIONAL_MEMBERS
 
733
    };
 
734
    
 
735
    // ---- other stuff ----
 
736
    
 
737
    void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ){
 
738
 
 
739
        assert( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
 
740
        
 
741
        assert( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , object_id_functions , 0 , 0 ) );
 
742
        assert( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) );
 
743
        assert( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) );
 
744
        assert( JS_InitClass( cx , global , 0 , &internal_cursor_class , internal_cursor_constructor , 0 , 0 , internal_cursor_functions , 0 , 0 ) );
 
745
        assert( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
 
746
        assert( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
 
747
        assert( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 0 , 0 , dbref_functions , 0 , 0 ) );
 
748
        assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
 
749
 
 
750
        assert( JS_InitClass( cx , global , 0 , &timestamp_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
 
751
        assert( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
 
752
        assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
 
753
 
 
754
        assert( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) );
 
755
        
 
756
        assert( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
 
757
        assert( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
 
758
        
 
759
        scope->exec( jsconcatcode );
 
760
    }
 
761
 
 
762
    bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const string& name , jsval val , JSObject * o ){
 
763
        
 
764
        if ( JS_InstanceOf( c->_context , o , &object_id_class , 0 ) ){
 
765
            OID oid;
 
766
            oid.init( c->getString( o , "str" ) );
 
767
            b.append( name.c_str() , oid );
 
768
            return true;
 
769
        }
 
770
 
 
771
        if ( JS_InstanceOf( c->_context , o , &minkey_class , 0 ) ){
 
772
            b.appendMinKey( name.c_str() );
 
773
            return true;
 
774
        }
 
775
 
 
776
        if ( JS_InstanceOf( c->_context , o , &maxkey_class , 0 ) ){
 
777
            b.appendMaxKey( name.c_str() );
 
778
            return true;
 
779
        }
 
780
        
 
781
        if ( JS_InstanceOf( c->_context , o , &timestamp_class , 0 ) ){
 
782
            b.appendTimestamp( name.c_str() , (unsigned long long)c->getNumber( o , "t" ) , (unsigned int )c->getNumber( o , "i" ) );
 
783
            return true;
 
784
        }
 
785
 
 
786
        if ( JS_InstanceOf( c->_context , o , &dbpointer_class , 0 ) ){
 
787
            b.appendDBRef( name.c_str() , c->getString( o , "ns" ).c_str() , c->toOID( c->getProperty( o , "id" ) ) );
 
788
            return true;
 
789
        }
 
790
        
 
791
        if ( JS_InstanceOf( c->_context , o , &bindata_class , 0 ) ){
 
792
            b.appendBinData( name.c_str() , 
 
793
                             (int)(c->getNumber( o , "len" )) , (BinDataType)((char)(c->getNumber( o , "type" ) ) ) , 
 
794
                             (char*)JS_GetPrivate( c->_context , o ) + 1
 
795
                             );
 
796
            return true;
 
797
        }
 
798
        
 
799
#if defined( SM16 ) || defined( MOZJS )
 
800
#warning dates do not work in your version of spider monkey
 
801
        {
 
802
            jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
 
803
            if ( d ){
 
804
                b.appendDate( name.c_str() , Date_t(d) );
 
805
                return true;
 
806
            }
 
807
        }
 
808
#elif defined( XULRUNNER )
 
809
        if ( JS_InstanceOf( c->_context , o, globalSMEngine->_dateClass , 0 ) ){
 
810
            jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
 
811
            b.appendDate( name.c_str() , Date_t(d) );
 
812
            return true;
 
813
        }
 
814
#else
 
815
        if ( JS_InstanceOf( c->_context , o, &js_DateClass , 0 ) ){
 
816
            jsdouble d = js_DateGetMsecSinceEpoch( c->_context , o );
 
817
            //TODO: make signed
 
818
            b.appendDate( name.c_str() , Date_t((unsigned long long)d) );
 
819
            return true;
 
820
        }
 
821
#endif
 
822
 
 
823
        
 
824
        if ( JS_InstanceOf( c->_context , o , &dbquery_class , 0 ) ||
 
825
             JS_InstanceOf( c->_context , o , &mongo_class , 0 ) || 
 
826
             JS_InstanceOf( c->_context , o , &db_collection_class , 0 ) ){
 
827
            b.append( name.c_str() , c->toString( val ) );
 
828
            return true;
 
829
        }
 
830
 
 
831
#if defined( XULRUNNER ) 
 
832
        if ( JS_InstanceOf( c->_context , o , globalSMEngine->_regexClass , 0 ) ){
 
833
            c->appendRegex( b , name , c->toString( val ) );
 
834
            return true;
 
835
        }
 
836
#elif defined( SM18 ) 
 
837
        if ( JS_InstanceOf( c->_context , o , &js_RegExpClass , 0 ) ){
 
838
            c->appendRegex( b , name , c->toString( val ) );
 
839
            return true;
 
840
        }
 
841
#endif
 
842
        
 
843
        return false;
 
844
    }
 
845
 
 
846
    bool isDate( JSContext * cx , JSObject * o ){
 
847
#if defined( SM16 ) || defined( MOZJS ) || defined( XULRUNNER )
 
848
        return js_DateGetMsecSinceEpoch( cx , o ) != 0;
 
849
#else
 
850
        return JS_InstanceOf( cx , o, &js_DateClass, 0 );
 
851
#endif
 
852
    }
 
853
    
 
854
}