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

« back to all changes in this revision

Viewing changes to db/matcher.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
// matcher.cpp
 
2
 
 
3
/* Matcher is our boolean expression evaluator for "where" clauses */
 
4
 
 
5
/**
 
6
*    Copyright (C) 2008 10gen Inc.
 
7
*
 
8
*    This program is free software: you can redistribute it and/or  modify
 
9
*    it under the terms of the GNU Affero General Public License, version 3,
 
10
*    as published by the Free Software Foundation.
 
11
*
 
12
*    This program is distributed in the hope that it will be useful,
 
13
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
*    GNU Affero General Public License for more details.
 
16
*
 
17
*    You should have received a copy of the GNU Affero General Public License
 
18
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
*/
 
20
 
 
21
#include "stdafx.h"
 
22
#include "matcher.h"
 
23
#include "../util/goodies.h"
 
24
#include "../util/unittest.h"
 
25
#include "storage.h"
 
26
#include "../scripting/engine.h"
 
27
#include "db.h"
 
28
#include "client.h"
 
29
 
 
30
namespace mongo {
 
31
    
 
32
    //#include "minilex.h"
 
33
    //MiniLex minilex;
 
34
    
 
35
    class Where {
 
36
    public:
 
37
        Where() {
 
38
            jsScope = 0;
 
39
            func = 0;
 
40
        }
 
41
        ~Where() {
 
42
 
 
43
            if ( scope.get() )
 
44
                scope->execSetup( "_mongo.readOnly = false;" , "make not read only" );
 
45
 
 
46
            if ( jsScope ){
 
47
                delete jsScope;
 
48
                jsScope = 0;
 
49
            }
 
50
            func = 0;
 
51
        }
 
52
        
 
53
        auto_ptr<Scope> scope;
 
54
        ScriptingFunction func;
 
55
        BSONObj *jsScope;
 
56
        
 
57
        void setFunc(const char *code) {
 
58
            massert( 10341 ,  "scope has to be created first!" , scope.get() );
 
59
            func = scope->createFunction( code );
 
60
        }
 
61
        
 
62
    };
 
63
 
 
64
    Matcher::~Matcher() {
 
65
        delete where;
 
66
        where = 0;
 
67
    }
 
68
 
 
69
    ElementMatcher::ElementMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareOp( _op ) {
 
70
        if ( _op == BSONObj::opMOD ){
 
71
            BSONObj o = _e.embeddedObject().firstElement().embeddedObject();
 
72
            mod = o["0"].numberInt();
 
73
            modm = o["1"].numberInt();
 
74
            
 
75
            uassert( 10073 ,  "mod can't be 0" , mod );
 
76
        }
 
77
        else if ( _op == BSONObj::opTYPE ){
 
78
            type = (BSONType)(_e.embeddedObject().firstElement().numberInt());
 
79
        }
 
80
        else if ( _op == BSONObj::opELEM_MATCH ){
 
81
            BSONElement m = toMatch.embeddedObjectUserCheck().firstElement();
 
82
            uassert( 12517 , "$elemMatch needs an Object" , m.type() == Object );
 
83
            subMatcher.reset( new Matcher( m.embeddedObject() ) );
 
84
        }
 
85
    }
 
86
 
 
87
 
 
88
    ElementMatcher::~ElementMatcher(){
 
89
    }
 
90
 
 
91
 
 
92
 
 
93
} // namespace mongo
 
94
 
 
95
#include "pdfile.h"
 
96
 
 
97
namespace {
 
98
    inline pcrecpp::RE_Options flags2options(const char* flags){
 
99
        pcrecpp::RE_Options options;
 
100
        options.set_utf8(true);
 
101
        while ( flags && *flags ) {
 
102
            if ( *flags == 'i' )
 
103
                options.set_caseless(true);
 
104
            else if ( *flags == 'm' )
 
105
                options.set_multiline(true);
 
106
            else if ( *flags == 'x' )
 
107
                options.set_extended(true);
 
108
            flags++;
 
109
        }
 
110
        return options;
 
111
    }
 
112
}
 
113
 
 
114
namespace mongo {
 
115
    
 
116
    CoveredIndexMatcher::CoveredIndexMatcher(const BSONObj &jsobj, const BSONObj &indexKeyPattern) :
 
117
        _keyMatcher(jsobj.filterFieldsUndotted(indexKeyPattern, true), 
 
118
        indexKeyPattern),
 
119
        _docMatcher(jsobj) 
 
120
    {
 
121
        _needRecord = ! ( 
 
122
                         _docMatcher.keyMatch() && 
 
123
                         _keyMatcher.jsobj.nFields() == _docMatcher.jsobj.nFields()
 
124
                          );
 
125
    }
 
126
    
 
127
    bool CoveredIndexMatcher::matches(const BSONObj &key, const DiskLoc &recLoc ) {
 
128
        if ( _keyMatcher.keyMatch() ) {
 
129
            if ( !_keyMatcher.matches(key) ) {
 
130
                return false;
 
131
            }
 
132
        }
 
133
        
 
134
        if ( ! _needRecord ){
 
135
            return true;
 
136
        }
 
137
 
 
138
        return _docMatcher.matches(recLoc.rec());
 
139
    }
 
140
    
 
141
    
 
142
    /* _jsobj          - the query pattern
 
143
    */
 
144
    Matcher::Matcher(const BSONObj &_jsobj, const BSONObj &constrainIndexKey) :
 
145
        where(0), jsobj(_jsobj), haveSize(), all(), hasArray(0), _atomic(false), nRegex(0) {
 
146
 
 
147
        BSONObjIterator i(jsobj);
 
148
        while ( i.more() ) {
 
149
            BSONElement e = i.next();
 
150
 
 
151
            if ( ( e.type() == CodeWScope || e.type() == Code || e.type() == String ) && strcmp(e.fieldName(), "$where")==0 ) {
 
152
                // $where: function()...
 
153
                uassert( 10066 ,  "$where occurs twice?", where == 0 );
 
154
                uassert( 10067 ,  "$where query, but no script engine", globalScriptEngine );
 
155
                where = new Where();
 
156
                where->scope = globalScriptEngine->getPooledScope( cc().ns() );
 
157
                where->scope->localConnect( cc().database()->name.c_str() );
 
158
 
 
159
                if ( e.type() == CodeWScope ) {
 
160
                    where->setFunc( e.codeWScopeCode() );
 
161
                    where->jsScope = new BSONObj( e.codeWScopeScopeData() , 0 );
 
162
                }
 
163
                else {
 
164
                    const char *code = e.valuestr();
 
165
                    where->setFunc(code);
 
166
                }
 
167
                
 
168
                where->scope->execSetup( "_mongo.readOnly = true;" , "make read only" );
 
169
 
 
170
                continue;
 
171
            }
 
172
 
 
173
            if ( e.type() == RegEx ) {
 
174
                if ( nRegex >= 4 ) {
 
175
                    out() << "ERROR: too many regexes in query" << endl;
 
176
                }
 
177
                else {
 
178
                    RegexMatcher& rm = regexs[nRegex];
 
179
                    rm.re = new pcrecpp::RE(e.regex(), flags2options(e.regexFlags()));
 
180
                    rm.fieldName = e.fieldName();
 
181
                    nRegex++;
 
182
                }
 
183
                continue;
 
184
            }
 
185
            
 
186
            // greater than / less than...
 
187
            // e.g., e == { a : { $gt : 3 } }
 
188
            //       or
 
189
            //            { a : { $in : [1,2,3] } }
 
190
            if ( e.type() == Object ) {
 
191
                // support {$regex:"a|b", $options:"imx"}
 
192
                const char* regex = NULL;
 
193
                const char* flags = "";
 
194
                
 
195
                // e.g., fe == { $gt : 3 }
 
196
                BSONObjIterator j(e.embeddedObject());
 
197
                bool isOperator = false;
 
198
                while ( j.more() ) {
 
199
                    BSONElement fe = j.next();
 
200
                    const char *fn = fe.fieldName();
 
201
                    
 
202
                    if ( fn[0] == '$' && fn[1] ) {
 
203
                        int op = fe.getGtLtOp( -1 );
 
204
 
 
205
                        if ( op == -1 ){
 
206
                            if ( fn[1] == 'r' && fn[2] == 'e' && fn[3] == 'f' && fn[4] == 0 ){
 
207
                                break; // { $ref : xxx } - treat as normal object
 
208
                            }
 
209
                            uassert( 10068 ,  (string)"invalid operator: " + fn , op != -1 );
 
210
                        }
 
211
 
 
212
                        isOperator = true;
 
213
                        
 
214
                        switch ( op ){
 
215
                        case BSONObj::GT:
 
216
                        case BSONObj::GTE:
 
217
                        case BSONObj::LT:
 
218
                        case BSONObj::LTE:{
 
219
                            shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() );
 
220
                            _builders.push_back( b );
 
221
                            b->appendAs(fe, e.fieldName());
 
222
                            addBasic(b->done().firstElement(), op);
 
223
                            isOperator = true;
 
224
                            break;
 
225
                        }
 
226
                        case BSONObj::NE:{
 
227
                            shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() );
 
228
                            _builders.push_back( b );
 
229
                            b->appendAs(fe, e.fieldName());
 
230
                            addBasic(b->done().firstElement(), BSONObj::NE);
 
231
                            break;
 
232
                        }
 
233
                        case BSONObj::opALL:
 
234
                            all = true;
 
235
                        case BSONObj::opIN:
 
236
                        case BSONObj::NIN:
 
237
                            basics.push_back( ElementMatcher( e , op , fe.embeddedObject() ) );
 
238
                            break;
 
239
                        case BSONObj::opMOD:
 
240
                        case BSONObj::opTYPE:
 
241
                        case BSONObj::opELEM_MATCH:
 
242
                            // these are types where ElementMatcher has all the info
 
243
                            basics.push_back( ElementMatcher( e , op ) );
 
244
                            break;
 
245
                        case BSONObj::opSIZE:{
 
246
                            shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() );
 
247
                            _builders.push_back( b );
 
248
                            b->appendAs(fe, e.fieldName());
 
249
                            addBasic(b->done().firstElement(), BSONObj::opSIZE);    
 
250
                            haveSize = true;
 
251
                            break;
 
252
                        }
 
253
                        case BSONObj::opEXISTS:{
 
254
                            shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() );
 
255
                            _builders.push_back( b );
 
256
                            b->appendAs(fe, e.fieldName());
 
257
                            addBasic(b->done().firstElement(), BSONObj::opEXISTS);
 
258
                            break;
 
259
                        }
 
260
                        case BSONObj::opREGEX:{
 
261
                            regex = fe.valuestrsafe();
 
262
                            break;
 
263
                        }
 
264
                        case BSONObj::opOPTIONS:{
 
265
                            flags = fe.valuestrsafe();
 
266
                            break;
 
267
                        }
 
268
                        default:
 
269
                            uassert( 10069 ,  (string)"BUG - can't operator for: " + fn , 0 );
 
270
                        }
 
271
                        
 
272
                    }
 
273
                    else {
 
274
                        isOperator = false;
 
275
                        break;
 
276
                    }
 
277
                }
 
278
                if (regex){
 
279
                    if ( nRegex >= 4 ) {
 
280
                        out() << "ERROR: too many regexes in query" << endl;
 
281
                    } else {
 
282
                        RegexMatcher& rm = regexs[nRegex];
 
283
                        rm.re = new pcrecpp::RE(regex, flags2options(flags));
 
284
                        rm.fieldName = e.fieldName();
 
285
                        nRegex++;
 
286
                    }
 
287
                }
 
288
                if ( isOperator )
 
289
                    continue;
 
290
            }
 
291
 
 
292
            if ( e.type() == Array ){
 
293
                hasArray = true;
 
294
            }
 
295
            else if( strcmp(e.fieldName(), "$atomic") == 0 ) {
 
296
                _atomic = e.trueValue();
 
297
                continue;
 
298
            }
 
299
            
 
300
            // normal, simple case e.g. { a : "foo" }
 
301
            addBasic(e, BSONObj::Equality);
 
302
        }
 
303
        
 
304
        constrainIndexKey_ = constrainIndexKey;
 
305
    }
 
306
 
 
307
    inline int Matcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, const ElementMatcher& bm) {
 
308
        assert( op != BSONObj::NE && op != BSONObj::NIN );
 
309
        
 
310
        if ( op == BSONObj::Equality )
 
311
            return l.valuesEqual(r);
 
312
        
 
313
        if ( op == BSONObj::opIN ) {
 
314
            // { $in : [1,2,3] }
 
315
            return bm.myset->count(l);
 
316
        }
 
317
 
 
318
        if ( op == BSONObj::opSIZE ) {
 
319
            if ( l.type() != Array )
 
320
                return 0;
 
321
            int count = 0;
 
322
            BSONObjIterator i( l.embeddedObject() );
 
323
            while( i.moreWithEOO() ) {
 
324
                BSONElement e = i.next();
 
325
                if ( e.eoo() )
 
326
                    break;
 
327
                ++count;
 
328
            }
 
329
            return count == r.number();
 
330
        }
 
331
        
 
332
        if ( op == BSONObj::opMOD ){
 
333
            if ( ! l.isNumber() )
 
334
                return false;
 
335
            
 
336
            return l.numberLong() % bm.mod == bm.modm;
 
337
        }
 
338
        
 
339
        if ( op == BSONObj::opTYPE ){
 
340
            return bm.type == l.type();
 
341
        }
 
342
 
 
343
        /* check LT, GTE, ... */
 
344
        if ( l.canonicalType() != r.canonicalType() )
 
345
            return false;
 
346
        int c = compareElementValues(l, r);
 
347
        if ( c < -1 ) c = -1;
 
348
        if ( c > 1 ) c = 1;
 
349
        int z = 1 << (c+1);
 
350
        return (op & z);
 
351
    }
 
352
 
 
353
    int Matcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, const ElementMatcher& bm ) {
 
354
        int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, bm );
 
355
        if ( bm.toMatch.type() != jstNULL )
 
356
            return ( ret <= 0 ) ? 1 : 0;
 
357
        else
 
358
            return -ret;
 
359
    }
 
360
 
 
361
    int retMissing( const ElementMatcher &bm ) {
 
362
        if ( bm.compareOp != BSONObj::opEXISTS )
 
363
            return 0;
 
364
        return bm.toMatch.boolean() ? -1 : 1;
 
365
    }
 
366
    
 
367
    /* Check if a particular field matches.
 
368
 
 
369
       fieldName - field to match "a.b" if we are reaching into an embedded object.
 
370
       toMatch   - element we want to match.
 
371
       obj       - database object to check against
 
372
       compareOp - Equality, LT, GT, etc.
 
373
       isArr     -
 
374
 
 
375
       Special forms:
 
376
 
 
377
         { "a.b" : 3 }             means       obj.a.b == 3
 
378
         { a : { $lt : 3 } }       means       obj.a < 3
 
379
         { a : { $in : [1,2] } }   means       [1,2].contains(obj.a)
 
380
         
 
381
         return value
 
382
       -1 mismatch
 
383
        0 missing element
 
384
        1 match
 
385
    */
 
386
    int Matcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const ElementMatcher& bm , bool isArr) {
 
387
 
 
388
        if ( compareOp == BSONObj::opALL ) {
 
389
            if ( bm.myset->size() == 0 )
 
390
                return -1; // is this desired?
 
391
            BSONObjSetDefaultOrder actualKeys;
 
392
            IndexSpec( BSON( fieldName << 1 ) ).getKeys( obj, actualKeys );
 
393
            if ( actualKeys.size() == 0 )
 
394
                return 0;
 
395
            for( set< BSONElement, element_lt >::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) {
 
396
                // ignore nulls
 
397
                if ( i->type() == jstNULL )
 
398
                    continue;
 
399
                // parallel traversal would be faster worst case I guess
 
400
                BSONObjBuilder b;
 
401
                b.appendAs( *i, "" );
 
402
                if ( !actualKeys.count( b.done() ) )
 
403
                    return -1;
 
404
            }
 
405
            return 1;
 
406
        }
 
407
 
 
408
        if ( compareOp == BSONObj::NE )
 
409
            return matchesNe( fieldName, toMatch, obj, bm );
 
410
        if ( compareOp == BSONObj::NIN ) {
 
411
            for( set<BSONElement,element_lt>::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) {
 
412
                int ret = matchesNe( fieldName, *i, obj, bm );
 
413
                if ( ret != 1 )
 
414
                    return ret;
 
415
            }
 
416
            return 1;
 
417
        }
 
418
        
 
419
        BSONElement e;
 
420
        bool indexed = !constrainIndexKey_.isEmpty();
 
421
        if ( indexed ) {
 
422
            e = obj.getFieldUsingIndexNames(fieldName, constrainIndexKey_);
 
423
            assert( !e.eoo() );
 
424
        } else {
 
425
            if ( isArr ) {
 
426
                BSONObjIterator ai(obj);
 
427
                bool found = false;
 
428
                while ( ai.moreWithEOO() ) {
 
429
                    BSONElement z = ai.next();
 
430
                    if ( z.type() == Object ) {
 
431
                        BSONObj eo = z.embeddedObject();
 
432
                        int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, bm, false);
 
433
                        if ( cmp > 0 ) {
 
434
                            return 1;
 
435
                        } else if ( cmp < 0 ) {
 
436
                            found = true;
 
437
                        }
 
438
                    }
 
439
                }
 
440
                return found ? -1 : retMissing( bm );
 
441
            }
 
442
            const char *p = strchr(fieldName, '.');
 
443
            if ( p ) {
 
444
                string left(fieldName, p-fieldName);
 
445
 
 
446
                BSONElement se = obj.getField(left.c_str());
 
447
                if ( se.eoo() )
 
448
                    return retMissing( bm );
 
449
                if ( se.type() != Object && se.type() != Array )
 
450
                    return retMissing( bm );
 
451
 
 
452
                BSONObj eo = se.embeddedObject();
 
453
                return matchesDotted(p+1, toMatch, eo, compareOp, bm, se.type() == Array);
 
454
            } else {
 
455
                e = obj.getField(fieldName);
 
456
            }
 
457
        }
 
458
 
 
459
        if ( compareOp == BSONObj::opEXISTS ) {
 
460
            return ( e.eoo() ^ toMatch.boolean() ) ? 1 : -1;
 
461
        } else if ( ( e.type() != Array || indexed || compareOp == BSONObj::opSIZE ) &&
 
462
            valuesMatch(e, toMatch, compareOp, bm ) ) {
 
463
            return 1;
 
464
        } else if ( e.type() == Array && compareOp != BSONObj::opSIZE ) {
 
465
            
 
466
            BSONObjIterator ai(e.embeddedObject());
 
467
 
 
468
            while ( ai.moreWithEOO() ) {
 
469
                BSONElement z = ai.next();
 
470
                
 
471
                if ( compareOp == BSONObj::opELEM_MATCH ){
 
472
                    // SERVER-377
 
473
                    if ( z.type() == Object && bm.subMatcher->matches( z.embeddedObject() ) )
 
474
                        return 1;
 
475
                }
 
476
                else {
 
477
                    if ( valuesMatch( z, toMatch, compareOp, bm) ) {
 
478
                        return 1;
 
479
                    }
 
480
                }
 
481
 
 
482
            }
 
483
            
 
484
            if ( compareOp == BSONObj::Equality && e.woCompare( toMatch ) == 0 ){
 
485
                // match an entire array to itself
 
486
                return 1;
 
487
            }
 
488
            
 
489
        }
 
490
        else if ( e.eoo() ) {
 
491
            // 0 indicates "missing element"
 
492
            return 0;
 
493
        }
 
494
        return -1;
 
495
    }
 
496
 
 
497
    extern int dump;
 
498
 
 
499
    inline bool regexMatches(RegexMatcher& rm, const BSONElement& e) {
 
500
        char buf[64];
 
501
        const char *p = buf;
 
502
        if ( e.type() == String || e.type() == Symbol )
 
503
            p = e.valuestr();
 
504
        else if ( e.isNumber() ) {
 
505
            sprintf(buf, "%f", e.number());
 
506
        }
 
507
        else if ( e.type() == Date ) {
 
508
            Date_t d = e.date();
 
509
            time_t t = (d.millis/1000);
 
510
            time_t_to_String(t, buf);
 
511
        }
 
512
        else
 
513
            return false;
 
514
        return rm.re->PartialMatch(p);
 
515
    }
 
516
 
 
517
    /* See if an object matches the query.
 
518
    */
 
519
    bool Matcher::matches(const BSONObj& jsobj ) {
 
520
        /* assuming there is usually only one thing to match.  if more this
 
521
        could be slow sometimes. */
 
522
 
 
523
        // check normal non-regex cases:
 
524
        for ( unsigned i = 0; i < basics.size(); i++ ) {
 
525
            ElementMatcher& bm = basics[i];
 
526
            BSONElement& m = bm.toMatch;
 
527
            // -1=mismatch. 0=missing element. 1=match
 
528
            int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, bm );
 
529
            if ( cmp < 0 )
 
530
                return false;
 
531
            if ( cmp == 0 ) {
 
532
                /* missing is ok iff we were looking for null */
 
533
                if ( m.type() == jstNULL || m.type() == Undefined ) {
 
534
                    if ( bm.compareOp == BSONObj::NE ) {
 
535
                        return false;
 
536
                    }
 
537
                } else {
 
538
                    return false;
 
539
                }
 
540
            }
 
541
        }
 
542
 
 
543
        for ( int r = 0; r < nRegex; r++ ) {
 
544
            RegexMatcher& rm = regexs[r];
 
545
            BSONElementSet s;
 
546
            if ( !constrainIndexKey_.isEmpty() ) {
 
547
                BSONElement e = jsobj.getFieldUsingIndexNames(rm.fieldName, constrainIndexKey_);
 
548
                if ( !e.eoo() )
 
549
                    s.insert( e );
 
550
            } else {
 
551
                jsobj.getFieldsDotted( rm.fieldName, s );
 
552
            }
 
553
            bool match = false;
 
554
            for( BSONElementSet::const_iterator i = s.begin(); i != s.end(); ++i )
 
555
                if ( regexMatches(rm, *i) )
 
556
                    match = true;
 
557
            if ( !match )
 
558
                return false;
 
559
        }
 
560
        
 
561
        if ( where ) {
 
562
            if ( where->func == 0 ) {
 
563
                uassert( 10070 , "$where compile error", false);
 
564
                return false; // didn't compile
 
565
            }
 
566
            
 
567
            if ( where->jsScope ){
 
568
                where->scope->init( where->jsScope );
 
569
            }
 
570
            where->scope->setThis( const_cast< BSONObj * >( &jsobj ) );
 
571
            where->scope->setObject( "obj", const_cast< BSONObj & >( jsobj ) );
 
572
            where->scope->setBoolean( "fullObject" , true ); // this is a hack b/c fullObject used to be relevant
 
573
            
 
574
            int err = where->scope->invoke( where->func , BSONObj() , 1000 * 60 , false );
 
575
            where->scope->setThis( 0 );
 
576
            if ( err == -3 ) { // INVOKE_ERROR
 
577
                stringstream ss;
 
578
                ss << "error on invocation of $where function:\n" 
 
579
                   << where->scope->getError();
 
580
                uassert( 10071 , ss.str(), false);
 
581
                return false;
 
582
            } else if ( err != 0 ) { // ! INVOKE_SUCCESS
 
583
                uassert( 10072 , "unknown error in invocation of $where function", false);
 
584
                return false;                
 
585
            }
 
586
            return where->scope->getBoolean( "return" ) != 0;
 
587
 
 
588
        }
 
589
 
 
590
        return true;
 
591
    }
 
592
 
 
593
    struct JSObj1 js1;
 
594
 
 
595
#pragma pack(1)
 
596
    struct JSObj2 {
 
597
        JSObj2() {
 
598
            totsize=sizeof(JSObj2);
 
599
            s = String;
 
600
            strcpy_s(sname, 7, "abcdef");
 
601
            slen = 10;
 
602
            strcpy_s(sval, 10, "123456789");
 
603
            eoo = EOO;
 
604
        }
 
605
        unsigned totsize;
 
606
        char s;
 
607
        char sname[7];
 
608
        unsigned slen;
 
609
        char sval[10];
 
610
        char eoo;
 
611
    } js2;
 
612
 
 
613
    struct JSUnitTest : public UnitTest {
 
614
        void run() {
 
615
 
 
616
            BSONObj j1((const char *) &js1);
 
617
            BSONObj j2((const char *) &js2);
 
618
            Matcher m(j2);
 
619
            assert( m.matches(j1) );
 
620
            js2.sval[0] = 'z';
 
621
            assert( !m.matches(j1) );
 
622
            Matcher n(j1);
 
623
            assert( n.matches(j1) );
 
624
            assert( !n.matches(j2) );
 
625
 
 
626
            BSONObj j0 = BSONObj();
 
627
//              BSONObj j0((const char *) &js0);
 
628
            Matcher p(j0);
 
629
            assert( p.matches(j1) );
 
630
            assert( p.matches(j2) );
 
631
        }
 
632
    } jsunittest;
 
633
 
 
634
#pragma pack()
 
635
 
 
636
    struct RXTest : public UnitTest {
 
637
 
 
638
        RXTest() {
 
639
        }
 
640
        
 
641
        void run() {
 
642
            /*
 
643
            static const boost::regex e("(\\d{4}[- ]){3}\\d{4}");
 
644
            static const boost::regex b(".....");
 
645
            out() << "regex result: " << regex_match("hello", e) << endl;
 
646
            out() << "regex result: " << regex_match("abcoo", b) << endl;
 
647
            */
 
648
 
 
649
            int ret = 0;
 
650
            
 
651
            pcre_config( PCRE_CONFIG_UTF8 , &ret );
 
652
            massert( 10342 ,  "pcre not compiled with utf8 support" , ret );
 
653
 
 
654
            pcrecpp::RE re1(")({a}h.*o");
 
655
            pcrecpp::RE re("h.llo");
 
656
            assert( re.FullMatch("hello") );
 
657
            assert( !re1.FullMatch("hello") );
 
658
 
 
659
 
 
660
            pcrecpp::RE_Options options;
 
661
            options.set_utf8(true);
 
662
            pcrecpp::RE part("dwi", options);
 
663
            assert( part.PartialMatch("dwight") );
 
664
 
 
665
            pcre_config( PCRE_CONFIG_UNICODE_PROPERTIES , &ret );
 
666
            if ( ! ret )
 
667
                cout << "warning: some regex utf8 things will not work.  pcre build doesn't have --enable-unicode-properties" << endl;
 
668
            
 
669
        }
 
670
    } rxtest;
 
671
 
 
672
} // namespace mongo