int oldElemCompare(const BSONElement&l , const BSONElement& r) { int lt = (int) l.canonicalType(); int rt = (int) r.canonicalType(); int x = lt - rt; if( x ) return x; return oldCompareElementValues(l, r); }
bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e) const { if (e.canonicalType() != _rhs.canonicalType()) { // some special cases // jstNULL and undefined are treated the same if (e.canonicalType() + _rhs.canonicalType() == 5) { return matchType() == EQ || matchType() == LTE || matchType() == GTE; } if (_rhs.type() == MaxKey || _rhs.type() == MinKey) { return matchType() != EQ; } return false; } // Special case handling for NaN. NaN is equal to NaN but // otherwise always compares to false. if (std::isnan(e.numberDouble()) || std::isnan(_rhs.numberDouble())) { bool bothNaN = std::isnan(e.numberDouble()) && std::isnan(_rhs.numberDouble()); switch (matchType()) { case LT: return false; case LTE: return bothNaN; case EQ: return bothNaN; case GT: return false; case GTE: return bothNaN; default: // This is a comparison match expression, so it must be either // a $lt, $lte, $gt, $gte, or equality expression. fassertFailed(17448); } } int x = compareElementValues(e, _rhs, _collator); switch (matchType()) { case LT: return x < 0; case LTE: return x <= 0; case EQ: return x == 0; case GT: return x > 0; case GTE: return x >= 0; default: // This is a comparison match expression, so it must be either // a $lt, $lte, $gt, $gte, or equality expression. fassertFailed(16828); } }
bool ComparisonMatchExpression::matchesSingleElement( const BSONElement& e ) const { //log() << "\t ComparisonMatchExpression e: " << e << " _rhs: " << _rhs << "\n" //<< toString() << std::endl; if ( e.canonicalType() != _rhs.canonicalType() ) { // some special cases // jstNULL and undefined are treated the same if ( e.canonicalType() + _rhs.canonicalType() == 5 ) { return matchType() == EQ || matchType() == LTE || matchType() == GTE; } if ( _rhs.type() == MaxKey || _rhs.type() == MinKey ) { return matchType() != EQ; } return false; } if ( _rhs.type() == Array ) { if ( matchType() != EQ ) { return false; } } int x = compareElementValues( e, _rhs ); //log() << "\t\t" << x << endl; switch ( matchType() ) { case LT: return x < 0; case LTE: return x <= 0; case EQ: return x == 0; case GT: return x > 0; case GTE: return x >= 0; default: throw 1; } throw 1; }
string Command::parseNsCollectionRequired(const string& dbname, const BSONObj& cmdObj) const { // Accepts both BSON String and Symbol for collection name per SERVER-16260 // TODO(kangas) remove Symbol support in MongoDB 3.0 after Ruby driver audit BSONElement first = cmdObj.firstElement(); uassert(17009, "no collection name specified", first.canonicalType() == canonicalizeBSONType(mongo::String) && first.valuestrsize() > 0); std::string coll = first.valuestr(); return dbname + '.' + coll; }
string Command::parseNsFullyQualified(const string& dbname, const BSONObj& cmdObj) { BSONElement first = cmdObj.firstElement(); uassert(ErrorCodes::BadValue, str::stream() << "collection name has invalid type " << typeName(first.type()), first.canonicalType() == canonicalizeBSONType(mongo::String)); const NamespaceString nss(first.valueStringData()); uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid namespace specified '" << nss.ns() << "'", nss.isValid()); return nss.ns(); }
NamespaceString Command::parseNsCollectionRequired(const string& dbname, const BSONObj& cmdObj) const { // Accepts both BSON String and Symbol for collection name per SERVER-16260 // TODO(kangas) remove Symbol support in MongoDB 3.0 after Ruby driver audit BSONElement first = cmdObj.firstElement(); uassert(40072, str::stream() << "collection name has invalid type " << typeName(first.type()), first.canonicalType() == canonicalizeBSONType(mongo::String)); NamespaceString nss(dbname, first.valuestr()); uassert(ErrorCodes::InvalidNamespace, "Not a valid namespace", nss.isValid()); return nss; }
/* wo = "well ordered" */ int BSONElement::woCompare( const BSONElement &e, bool considerFieldName ) const { int lt = (int) canonicalType(); int rt = (int) e.canonicalType(); int x = lt - rt; if( x != 0 && (!isNumber() || !e.isNumber()) ) return x; if ( considerFieldName ) { x = strcmp(fieldName(), e.fieldName()); if ( x != 0 ) return x; } x = compareElementValues(*this, e); return x; }
int BSONElement::woCompare(const BSONElement& elem, ComparisonRulesSet rules, const StringData::ComparatorInterface* comparator) const { if (type() != elem.type()) { int lt = (int)canonicalType(); int rt = (int)elem.canonicalType(); if (int diff = lt - rt) return diff; } if (rules & ComparisonRules::kConsiderFieldName) { if (int diff = fieldNameStringData().compare(elem.fieldNameStringData())) return diff; } return compareElements(*this, elem, rules, comparator); }
bool InternalExprEqMatchExpression::matchesSingleElement(const BSONElement& elem, MatchDetails* details) const { // We use NonLeafArrayBehavior::kMatchSubpath traversal in InternalExprEqMatchExpression. This // means matchesSinglElement() will be called when an array is found anywhere along the patch we // are matching against. When this occurs, we return 'true' and depend on the corresponding // ExprMatchExpression node to filter properly. if (elem.type() == BSONType::Array) { return true; } if (elem.canonicalType() != _rhs.canonicalType()) { return false; } auto comp = BSONElement::compareElements( elem, _rhs, BSONElement::ComparisonRules::kConsiderFieldName, _collator); return comp == 0; }
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 BSONComparatorInterfaceBase<T>::hashCombineBSONElement( size_t& hash, BSONElement elemToHash, bool considerFieldName, const StringData::ComparatorInterface* stringComparator) { boost::hash_combine(hash, elemToHash.canonicalType()); const StringData fieldName = elemToHash.fieldNameStringData(); if (considerFieldName && !fieldName.empty()) { SimpleStringDataComparator::kInstance.hash_combine(hash, fieldName); } switch (elemToHash.type()) { // Order of types is the same as in compareElementValues(). case mongo::EOO: case mongo::Undefined: case mongo::jstNULL: case mongo::MaxKey: case mongo::MinKey: // These are valueless types break; case mongo::Bool: boost::hash_combine(hash, elemToHash.boolean()); break; case mongo::bsonTimestamp: boost::hash_combine(hash, elemToHash.timestamp().asULL()); break; case mongo::Date: boost::hash_combine(hash, elemToHash.date().asInt64()); break; case mongo::NumberDecimal: { const Decimal128 dcml = elemToHash.numberDecimal(); if (dcml.toAbs().isGreater(Decimal128(std::numeric_limits<double>::max(), Decimal128::kRoundTo34Digits, Decimal128::kRoundTowardZero)) && !dcml.isInfinite() && !dcml.isNaN()) { // Normalize our decimal to force equivalent decimals // in the same cohort to hash to the same value Decimal128 dcmlNorm(dcml.normalize()); boost::hash_combine(hash, dcmlNorm.getValue().low64); boost::hash_combine(hash, dcmlNorm.getValue().high64); break; } // Else, fall through and convert the decimal to a double and hash. // At this point the decimal fits into the range of doubles, is infinity, or is NaN, // which doubles have a cheaper representation for. } case mongo::NumberDouble: case mongo::NumberLong: case mongo::NumberInt: { // This converts all numbers to doubles, which ignores the low-order bits of // NumberLongs > 2**53 and precise decimal numbers without double representations, // but that is ok since the hash will still be the same for equal numbers and is // still likely to be different for different numbers. (Note: this issue only // applies for decimals when they are outside of the valid double range. See // the above case.) // SERVER-16851 const double dbl = elemToHash.numberDouble(); if (std::isnan(dbl)) { boost::hash_combine(hash, std::numeric_limits<double>::quiet_NaN()); } else { boost::hash_combine(hash, dbl); } break; } case mongo::jstOID: elemToHash.__oid().hash_combine(hash); break; case mongo::String: { if (stringComparator) { stringComparator->hash_combine(hash, elemToHash.valueStringData()); } else { SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); } break; } case mongo::Code: case mongo::Symbol: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); break; case mongo::Object: case mongo::Array: hashCombineBSONObj(hash, elemToHash.embeddedObject(), true, // considerFieldName stringComparator); break; case mongo::DBRef: case mongo::BinData: // All bytes of the value are required to be identical. SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.value(), elemToHash.valuesize())); break; case mongo::RegEx: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regex()); SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regexFlags()); break; case mongo::CodeWScope: { SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.codeWScopeCode(), elemToHash.codeWScopeCodeLen())); hashCombineBSONObj(hash, elemToHash.codeWScopeObject(), true, // considerFieldName &SimpleStringDataComparator::kInstance); break; } } }
/* must be same type when called, unless both sides are #s */ int compareElementValues(const BSONElement& l, const BSONElement& r) { int f; double x; switch ( l.type() ) { case EOO: case Undefined: case jstNULL: case MaxKey: case MinKey: f = l.canonicalType() - r.canonicalType(); if ( f<0 ) return -1; return f==0 ? 0 : 1; case Bool: return *l.value() - *r.value(); case Timestamp: case Date: if ( l.date() < r.date() ) return -1; return l.date() == r.date() ? 0 : 1; case NumberLong: if( r.type() == NumberLong ) { long long L = l._numberLong(); long long R = r._numberLong(); if( L < R ) return -1; if( L == R ) return 0; return 1; } // else fall through case NumberInt: case NumberDouble: { double left = l.number(); double right = r.number(); bool lNan = !( left <= numeric_limits< double >::max() && left >= -numeric_limits< double >::max() ); bool rNan = !( right <= numeric_limits< double >::max() && right >= -numeric_limits< double >::max() ); if ( lNan ) { if ( rNan ) { return 0; } else { return -1; } } else if ( rNan ) { return 1; } x = left - right; if ( x < 0 ) return -1; return x == 0 ? 0 : 1; } case jstOID: return memcmp(l.value(), r.value(), 12); case Code: case Symbol: case String: /* todo: utf version */ return strcmp(l.valuestr(), r.valuestr()); case Object: case Array: return l.embeddedObject().woCompare( r.embeddedObject() ); case DBRef: { int lsz = l.valuesize(); int rsz = r.valuesize(); if ( lsz - rsz != 0 ) return lsz - rsz; return memcmp(l.value(), r.value(), lsz); } case BinData: { int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte int rsz = r.objsize(); if ( lsz - rsz != 0 ) return lsz - rsz; return memcmp(l.value()+4, r.value()+4, lsz+1); } case RegEx: { int c = strcmp(l.regex(), r.regex()); if ( c ) return c; return strcmp(l.regexFlags(), r.regexFlags()); } case CodeWScope : { f = l.canonicalType() - r.canonicalType(); if ( f ) return f; f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() ); if ( f ) return f; f = strcmp( l.codeWScopeScopeData() , r.codeWScopeScopeData() ); if ( f ) return f; return 0; } default: out() << "compareElementValues: bad type " << (int) l.type() << endl; assert(false); } return -1; }
int BSONElement::compareElements(const BSONElement& l, const BSONElement& r, ComparisonRulesSet rules, const StringData::ComparatorInterface* comparator) { switch (l.type()) { case BSONType::EOO: case BSONType::Undefined: // EOO and Undefined are same canonicalType case BSONType::jstNULL: case BSONType::MaxKey: case BSONType::MinKey: { auto f = l.canonicalType() - r.canonicalType(); if (f < 0) return -1; return f == 0 ? 0 : 1; } case BSONType::Bool: return *l.value() - *r.value(); case BSONType::bsonTimestamp: // unsigned compare for timestamps - note they are not really dates but (ordinal + // time_t) if (l.timestamp() < r.timestamp()) return -1; return l.timestamp() == r.timestamp() ? 0 : 1; case BSONType::Date: // Signed comparisons for Dates. { const Date_t a = l.Date(); const Date_t b = r.Date(); if (a < b) return -1; return a == b ? 0 : 1; } case BSONType::NumberInt: { // All types can precisely represent all NumberInts, so it is safe to simply convert to // whatever rhs's type is. switch (r.type()) { case NumberInt: return compareInts(l._numberInt(), r._numberInt()); case NumberLong: return compareLongs(l._numberInt(), r._numberLong()); case NumberDouble: return compareDoubles(l._numberInt(), r._numberDouble()); case NumberDecimal: return compareIntToDecimal(l._numberInt(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberLong: { switch (r.type()) { case NumberLong: return compareLongs(l._numberLong(), r._numberLong()); case NumberInt: return compareLongs(l._numberLong(), r._numberInt()); case NumberDouble: return compareLongToDouble(l._numberLong(), r._numberDouble()); case NumberDecimal: return compareLongToDecimal(l._numberLong(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberDouble: { switch (r.type()) { case NumberDouble: return compareDoubles(l._numberDouble(), r._numberDouble()); case NumberInt: return compareDoubles(l._numberDouble(), r._numberInt()); case NumberLong: return compareDoubleToLong(l._numberDouble(), r._numberLong()); case NumberDecimal: return compareDoubleToDecimal(l._numberDouble(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberDecimal: { switch (r.type()) { case NumberDecimal: return compareDecimals(l._numberDecimal(), r._numberDecimal()); case NumberInt: return compareDecimalToInt(l._numberDecimal(), r._numberInt()); case NumberLong: return compareDecimalToLong(l._numberDecimal(), r._numberLong()); case NumberDouble: return compareDecimalToDouble(l._numberDecimal(), r._numberDouble()); default: MONGO_UNREACHABLE; } } case BSONType::jstOID: return memcmp(l.value(), r.value(), OID::kOIDSize); case BSONType::Code: return compareElementStringValues(l, r); case BSONType::Symbol: case BSONType::String: { if (comparator) { return comparator->compare(l.valueStringData(), r.valueStringData()); } else { return compareElementStringValues(l, r); } } case BSONType::Object: case BSONType::Array: { return l.embeddedObject().woCompare( r.embeddedObject(), BSONObj(), rules | BSONElement::ComparisonRules::kConsiderFieldName, comparator); } case BSONType::DBRef: { int lsz = l.valuesize(); int rsz = r.valuesize(); if (lsz - rsz != 0) return lsz - rsz; return memcmp(l.value(), r.value(), lsz); } case BSONType::BinData: { int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte int rsz = r.objsize(); if (lsz - rsz != 0) return lsz - rsz; return memcmp(l.value() + 4, r.value() + 4, lsz + 1 /*+1 for subtype byte*/); } case BSONType::RegEx: { int c = strcmp(l.regex(), r.regex()); if (c) return c; return strcmp(l.regexFlags(), r.regexFlags()); } case BSONType::CodeWScope: { int cmp = StringData(l.codeWScopeCode(), l.codeWScopeCodeLen() - 1) .compare(StringData(r.codeWScopeCode(), r.codeWScopeCodeLen() - 1)); if (cmp) return cmp; // When comparing the scope object, we should consider field names. Special string // comparison semantics do not apply to strings nested inside the CodeWScope scope // object, so we do not pass through the string comparator. return l.codeWScopeObject().woCompare( r.codeWScopeObject(), BSONObj(), rules | BSONElement::ComparisonRules::kConsiderFieldName); } } MONGO_UNREACHABLE; }
bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const { if (e.canonicalType() != _rhs.canonicalType()) { // We can't call 'compareElements' on elements of different canonical types. Usually // elements with different canonical types should never match any comparison, but there are // a few exceptions, handled here. // jstNULL and undefined are treated the same if (e.canonicalType() + _rhs.canonicalType() == 5) { return matchType() == EQ || matchType() == LTE || matchType() == GTE; } if (_rhs.type() == MaxKey || _rhs.type() == MinKey) { switch (matchType()) { // LT and LTE need no distinction here because the two elements that we are // comparing do not even have the same canonical type and are thus not equal // (i.e.the case where we compare MinKey against MinKey would not reach this switch // statement at all). The same reasoning follows for the lack of distinction // between GTE and GT. case LT: case LTE: return _rhs.type() == MaxKey; case EQ: return false; case GT: case GTE: return _rhs.type() == MinKey; default: // This is a comparison match expression, so it must be either // a $lt, $lte, $gt, $gte, or equality expression. MONGO_UNREACHABLE; } } return false; } // Special case handling for NaN. NaN is equal to NaN but // otherwise always compares to false. if (std::isnan(e.numberDouble()) || std::isnan(_rhs.numberDouble())) { bool bothNaN = std::isnan(e.numberDouble()) && std::isnan(_rhs.numberDouble()); switch (matchType()) { case LT: return false; case LTE: return bothNaN; case EQ: return bothNaN; case GT: return false; case GTE: return bothNaN; default: // This is a comparison match expression, so it must be either // a $lt, $lte, $gt, $gte, or equality expression. fassertFailed(17448); } } int x = BSONElement::compareElements( e, _rhs, BSONElement::ComparisonRules::kConsiderFieldName, _collator); switch (matchType()) { case LT: return x < 0; case LTE: return x <= 0; case EQ: return x == 0; case GT: return x > 0; case GTE: return x >= 0; default: // This is a comparison match expression, so it must be either // a $lt, $lte, $gt, $gte, or equality expression. fassertFailed(16828); } }