bool BSONObj::okForStorage() const { BSONObjIterator i( *this ); while ( i.more() ){ BSONElement e = i.next(); const char * name = e.fieldName(); if ( strchr( name , '.' ) || strchr( name , '$' ) ){ return strcmp( name , "$ref" ) == 0 || strcmp( name , "$id" ) == 0 ; } if ( e.mayEncapsulate() ){ switch ( e.type() ){ case Object: case Array: if ( ! e.embeddedObject().okForStorage() ) return false; break; case CodeWScope: if ( ! e.codeWScopeObject().okForStorage() ) return false; break; default: uassert( 12579, "unhandled cases in BSONObj okForStorage" , 0 ); } } } return true; }
void BSONElementHasher::recursiveHash( Hasher* h , const BSONElement& e , bool includeFieldName ) { int canonicalType = e.canonicalType(); h->addData( &canonicalType , sizeof( canonicalType ) ); if ( includeFieldName ){ h->addData( e.fieldName() , e.fieldNameSize() ); } if ( !e.mayEncapsulate() ){ //if there are no embedded objects (subobjects or arrays), //compute the hash, squashing numeric types to 64-bit ints if ( e.isNumber() ){ long long int i = e.safeNumberLong(); //well-defined for troublesome doubles h->addData( &i , sizeof( i ) ); } else { h->addData( e.value() , e.valuesize() ); } } else { //else identify the subobject. //hash any preceding stuff (in the case of codeWscope) //then each sub-element //then finish with the EOO element. BSONObj b; if ( e.type() == CodeWScope ) { h->addData( e.codeWScopeCode() , e.codeWScopeCodeLen() ); b = e.codeWScopeObject(); } else { b = e.embeddedObject(); } BSONObjIterator i(b); while( i.moreWithEOO() ) { BSONElement el = i.next(); recursiveHash( h , el , true ); } } }
void QueryPlan::init( const FieldRangeSetPair* originalFrsp, const BSONObj& startKey, const BSONObj& endKey ) { _endKeyInclusive = endKey.isEmpty(); _startOrEndSpec = !startKey.isEmpty() || !endKey.isEmpty(); BSONObj idxKey = _idxNo < 0 ? BSONObj() : _d->idx( _idxNo ).keyPattern(); if ( !_frs.matchPossibleForIndex( idxKey ) ) { _utility = Impossible; _scanAndOrderRequired = false; return; } if ( willScanTable() ) { if ( _order.isEmpty() || !strcmp( _order.firstElementFieldName(), "$natural" ) ) _scanAndOrderRequired = false; return; } _index = &_d->idx(_idxNo); // If the parsing or index indicates this is a special query, don't continue the processing if (!_special.empty() || ( _index->getSpec().getType() && _index->getSpec().getType()->suitability( _frs, _order ) != USELESS ) ) { _type = _index->getSpec().getType(); if (_special.empty()) _special = _index->getSpec().getType()->getPlugin()->getName(); massert( 13040 , (string)"no type for special: " + _special , _type ); // hopefully safe to use original query in these contexts; // don't think we can mix special with $or clause separation yet _scanAndOrderRequired = _type->scanAndOrderRequired( _originalQuery , _order ); return; } const IndexSpec &idxSpec = _index->getSpec(); BSONObjIterator o( _order ); BSONObjIterator k( idxKey ); if ( !o.moreWithEOO() ) _scanAndOrderRequired = false; while( o.moreWithEOO() ) { BSONElement oe = o.next(); if ( oe.eoo() ) { _scanAndOrderRequired = false; break; } if ( !k.moreWithEOO() ) break; BSONElement ke; while( 1 ) { ke = k.next(); if ( ke.eoo() ) goto doneCheckOrder; if ( strcmp( oe.fieldName(), ke.fieldName() ) == 0 ) break; if ( !_frs.range( ke.fieldName() ).equality() ) goto doneCheckOrder; } int d = elementDirection( oe ) == elementDirection( ke ) ? 1 : -1; if ( _direction == 0 ) _direction = d; else if ( _direction != d ) break; } doneCheckOrder: if ( _scanAndOrderRequired ) _direction = 0; BSONObjIterator i( idxKey ); int exactIndexedQueryCount = 0; int optimalIndexedQueryCount = 0; bool awaitingLastOptimalField = true; set<string> orderFieldsUnindexed; _order.getFieldNames( orderFieldsUnindexed ); while( i.moreWithEOO() ) { BSONElement e = i.next(); if ( e.eoo() ) break; const FieldRange& fr = _frs.range( e.fieldName() ); if ( awaitingLastOptimalField ) { if ( !fr.universal() ) ++optimalIndexedQueryCount; if ( !fr.equality() ) awaitingLastOptimalField = false; } else { if ( !fr.universal() ) optimalIndexedQueryCount = -1; } if ( fr.equality() ) { BSONElement e = fr.max(); if ( !e.isNumber() && !e.mayEncapsulate() && e.type() != RegEx ) ++exactIndexedQueryCount; } orderFieldsUnindexed.erase( e.fieldName() ); } if ( !_scanAndOrderRequired && ( optimalIndexedQueryCount == _frs.numNonUniversalRanges() ) ) _utility = Optimal; _frv.reset( new FieldRangeVector( _frs, idxSpec, _direction ) ); if ( // If all field range constraints are on indexed fields and ... _utility == Optimal && // ... the field ranges exactly represent the query and ... _frs.mustBeExactMatchRepresentation() && // ... all indexed ranges are represented in the field range vector ... _frv->hasAllIndexedRanges() ) { // ... then the field range vector is sufficient to perform query matching against index // keys. No matcher is required. _matcherNecessary = false; } if ( originalFrsp ) { _originalFrv.reset( new FieldRangeVector( originalFrsp->frsForIndex( _d, _idxNo ), idxSpec, _direction ) ); } else { _originalFrv = _frv; } if ( _startOrEndSpec ) { BSONObj newStart, newEnd; if ( !startKey.isEmpty() ) _startKey = startKey; else _startKey = _frv->startKey(); if ( !endKey.isEmpty() ) _endKey = endKey; else _endKey = _frv->endKey(); } if ( ( _scanAndOrderRequired || _order.isEmpty() ) && _frs.range( idxKey.firstElementFieldName() ).universal() ) { // NOTE SERVER-2140 _utility = Unhelpful; } if ( idxSpec.isSparse() && hasPossibleExistsFalsePredicate() ) { _utility = Disallowed; } if ( _parsedQuery && _parsedQuery->getFields() && !_d->isMultikey( _idxNo ) ) { // Does not check modifiedKeys() _keyFieldsOnly.reset( _parsedQuery->getFields()->checkKey( _index->keyPattern() ) ); } }
QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const IndexDetails *index ) : fbs_( fbs ), order_( order ), index_( index ), optimal_( false ), scanAndOrderRequired_( true ), keyMatch_( false ), exactKeyMatch_( false ), direction_( 0 ), unhelpful_( false ) { // full table scan case if ( !index_ ) { if ( order_.isEmpty() || !strcmp( order_.firstElement().fieldName(), "$natural" ) ) scanAndOrderRequired_ = false; return; } BSONObj idxKey = index->keyPattern(); BSONObjIterator o( order ); BSONObjIterator k( idxKey ); if ( !o.more() ) scanAndOrderRequired_ = false; while( o.more() ) { BSONElement oe = o.next(); if ( oe.eoo() ) { scanAndOrderRequired_ = false; break; } if ( !k.more() ) break; BSONElement ke; while( 1 ) { ke = k.next(); if ( ke.eoo() ) goto doneCheckOrder; if ( strcmp( oe.fieldName(), ke.fieldName() ) == 0 ) break; if ( !fbs.bound( ke.fieldName() ).equality() ) goto doneCheckOrder; } int d = oe.number() == ke.number() ? 1 : -1; if ( direction_ == 0 ) direction_ = d; else if ( direction_ != d ) break; } doneCheckOrder: if ( scanAndOrderRequired_ ) direction_ = 0; BSONObjIterator i( idxKey ); int indexedQueryCount = 0; int exactIndexedQueryCount = 0; int optimalIndexedQueryCount = 0; bool stillOptimalIndexedQueryCount = true; set< string > orderFieldsUnindexed; order.getFieldNames( orderFieldsUnindexed ); BSONObjBuilder startKeyBuilder; BSONObjBuilder endKeyBuilder; while( i.more() ) { BSONElement e = i.next(); if ( e.eoo() ) break; const FieldBound &fb = fbs.bound( e.fieldName() ); int number = (int) e.number(); // returns 0.0 if not numeric bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction_ >= 0 ? 1 : -1 ) > 0 ); startKeyBuilder.appendAs( forward ? fb.lower() : fb.upper(), "" ); endKeyBuilder.appendAs( forward ? fb.upper() : fb.lower(), "" ); if ( fb.nontrivial() ) ++indexedQueryCount; if ( stillOptimalIndexedQueryCount ) { if ( fb.nontrivial() ) ++optimalIndexedQueryCount; if ( !fb.equality() ) stillOptimalIndexedQueryCount = false; } else { if ( fb.nontrivial() ) optimalIndexedQueryCount = -1; } if ( fb.equality() ) { BSONElement e = fb.upper(); if ( !e.isNumber() && !e.mayEncapsulate() && e.type() != RegEx ) ++exactIndexedQueryCount; } orderFieldsUnindexed.erase( e.fieldName() ); } if ( !scanAndOrderRequired_ && ( optimalIndexedQueryCount == fbs.nNontrivialBounds() ) ) optimal_ = true; if ( indexedQueryCount == fbs.nNontrivialBounds() && orderFieldsUnindexed.size() == 0 ) { keyMatch_ = true; if ( exactIndexedQueryCount == fbs.nNontrivialBounds() ) exactKeyMatch_ = true; } startKey_ = startKeyBuilder.obj(); endKey_ = endKeyBuilder.obj(); if ( !keyMatch_ && ( scanAndOrderRequired_ || order_.isEmpty() ) && !fbs.bound( idxKey.firstElement().fieldName() ).nontrivial() ) unhelpful_ = true; }