Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

SERVER-4776 only use numeric comparison in updates when applying modi…

…fiers to an array
  • Loading branch information...
commit b17579687420f13afcb173f29556d89475d3f31b 1 parent dfee494
@astaple astaple authored
View
11 jstests/set7.js
@@ -39,6 +39,17 @@ t.update( {}, {$set:{"a.f":1}} );
assert( db.getLastError() );
assert.eq( [], t.findOne().a );
+// Test requiring proper ordering of multiple mods.
+t.drop();
+t.save( {a:[0,1,2,3,4,5,6,7,8,9,10]} );
+t.update( {}, {$set:{"a.11":11,"a.2":-2}} );
+assert.eq( [0,1,-2,3,4,5,6,7,8,9,10,11], t.findOne().a );
+
+// Test upsert case
+t.drop();
+t.update( {a:[0,1,2,3,4,5,6,7,8,9,10]}, {$set:{"a.11":11} }, true );
+assert.eq( [0,1,2,3,4,5,6,7,8,9,10,11], t.findOne().a );
+
// SERVER-3750
t.drop();
t.save( {a:[]} );
View
2  jstests/update6.js
@@ -10,7 +10,7 @@ assert.eq( "c,d" , Object.keySet( t.findOne().b ).toString() , "B" );
t.update( { a : 1 } , { $inc : { "b.0e" : 1 } } );
assert.eq( 1 , t.findOne().b["0e"] , "C" );
-assert.eq( "c,d,0e" , Object.keySet( t.findOne().b ).toString() , "D" );
+assert.eq( "0e,c,d" , Object.keySet( t.findOne().b ).toString() , "D" );
// -----
View
6 jstests/updatee.js
@@ -29,7 +29,7 @@ t.update({"profile-id" : "test"}, {$set: {"actual.02": "v4"}});
q = t.findOne();
assert.eq(q.actual["02"], "v4", "A4");
-assert(!q.actual["002"], "A5");
+assert.eq(q.actual["002"], "val4", "A5");
t.update({"_id" : 1}, {$set : {"actual.2139043290148390248219423941.b" : 4}});
q = t.findOne();
@@ -51,12 +51,12 @@ assert.eq(q.actual["000"], "val000", "A8 zeros");
t.update({"_id" : 1}, {$set : {"actual.00" : "val00"}});
q = t.findOne();
assert.eq(q.actual["00"], "val00", "A8 00");
-assert(!q.actual["000"], "A9");
+assert.eq(q.actual["000"], "val000", "A9");
t.update({"_id" : 1}, {$set : {"actual.000" : "val000"}});
q = t.findOne();
assert.eq(q.actual["000"], "val000", "A9");
-assert(!q.actual["00"], "A10");
+assert.eq(q.actual["00"], "val00", "A10");
t.update({"_id" : 1}, {$set : {"actual.01" : "val01"}});
q = t.findOne();
View
13 jstests/updatek.js
@@ -0,0 +1,13 @@
+// Test modifier operations on numerically equivalent string field names. SERVER-4776
+
+t = db.jstests_updatek;
+
+t.drop();
+t.save( { _id:0, '1':{}, '01':{} } );
+t.update( {}, { $set:{ '1.b':1, '1.c':2 } } );
+assert.eq( { '01':{}, '1':{ b:1, c:2 }, _id:0 }, t.findOne() );
+
+t.drop();
+t.save( { _id:0, '1':{}, '01':{} } );
+t.update( {}, { $set:{ '1.b':1, '01.c':2 } } );
+assert.eq( { '01':{ c:2 }, '1':{ b:1 }, _id:0 }, t.findOne() );
View
4 src/mongo/bson/bsonmisc.h
@@ -48,7 +48,9 @@ namespace mongo {
RIGHT_SUBFIELD = 2
};
- FieldCompareResult compareDottedFieldNames( const std::string& l , const std::string& r );
+ class LexNumCmp;
+ FieldCompareResult compareDottedFieldNames( const std::string& l , const std::string& r ,
+ const LexNumCmp& cmp );
/** Use BSON macro to build a BSONObj from a stream
View
28 src/mongo/bson/bsonobjiterator.h
@@ -82,11 +82,10 @@ namespace mongo {
const char* _theend;
};
- class BSONObjIteratorSorted {
+ /** Base class implementing ordered iteration through BSONElements. */
+ class BSONIteratorSorted {
public:
- BSONObjIteratorSorted( const BSONObj& o );
-
- ~BSONObjIteratorSorted() {
+ ~BSONIteratorSorted() {
assert( _fields );
delete[] _fields;
_fields = 0;
@@ -103,15 +102,32 @@ namespace mongo {
return BSONElement();
}
- private:
-
+ protected:
class ElementFieldCmp;
+ BSONIteratorSorted( const BSONObj &o, const ElementFieldCmp &cmp );
+ private:
const char ** _fields;
int _nfields;
int _cur;
};
+ /** Provides iteration of a BSONObj's BSONElements in lexical field order. */
+ class BSONObjIteratorSorted : public BSONIteratorSorted {
+ public:
+ BSONObjIteratorSorted( const BSONObj &object );
+ };
+
+ /**
+ * Provides iteration of a BSONArray's BSONElements in numeric field order.
+ * The elements of a bson array should always be numerically ordered by field name, but this
+ * implementation re-sorts them anyway.
+ */
+ class BSONArrayIteratorSorted : public BSONIteratorSorted {
+ public:
+ BSONArrayIteratorSorted( const BSONArray &array );
+ };
+
/** transform a BSON array into a vector of BSONElements.
we match array # positions with their vector position, and ignore
any fields with non-numeric field names.
View
4 src/mongo/db/indexkey.cpp
@@ -22,6 +22,7 @@
#include "btree.h"
#include "ops/query.h"
#include "background.h"
+#include "../util/stringutils.h"
#include "../util/text.h"
namespace mongo {
@@ -423,7 +424,8 @@ namespace mongo {
BSONObjIterator y(b);
while ( y.more() ) {
BSONElement f = y.next();
- FieldCompareResult res = compareDottedFieldNames( e.fieldName() , f.fieldName() );
+ FieldCompareResult res = compareDottedFieldNames( e.fieldName() , f.fieldName() ,
+ LexNumCmp( false ) );
if ( res == SAME || res == LEFT_SUBFIELD || res == RIGHT_SUBFIELD )
return true;
}
View
29 src/mongo/db/jsobj.cpp
@@ -334,7 +334,8 @@ namespace mongo {
return fe.getGtLtOp();
}
- FieldCompareResult compareDottedFieldNames( const string& l , const string& r ) {
+ FieldCompareResult compareDottedFieldNames( const string& l , const string& r ,
+ const LexNumCmp& cmp ) {
static int maxLoops = 1024 * 1024;
size_t lstart = 0;
@@ -351,7 +352,7 @@ namespace mongo {
const string& c = l.substr( lstart , lend - lstart );
const string& d = r.substr( rstart , rend - rstart );
- int x = LexNumCmp::cmp( c.c_str(), d.c_str() );
+ int x = cmp.cmp( c.c_str(), d.c_str() );
if ( x < 0 )
return LEFT_BEFORE;
@@ -1199,21 +1200,26 @@ namespace mongo {
return !(l.more() || r.more()); // false if lhs and rhs have diff nFields()
}
- /** Compare two bson elements, provided as const char *'s, by their field names. */
- class BSONObjIteratorSorted::ElementFieldCmp {
+ /** Compare two bson elements, provided as const char *'s, by field name. */
+ class BSONIteratorSorted::ElementFieldCmp {
public:
+ ElementFieldCmp( bool isArray );
bool operator()( const char *s1, const char *s2 ) const;
private:
LexNumCmp _cmp;
};
- bool BSONObjIteratorSorted::ElementFieldCmp::operator()( const char *s1, const char *s2 )
+ BSONIteratorSorted::ElementFieldCmp::ElementFieldCmp( bool isArray ) :
+ _cmp( !isArray ) {
+ }
+
+ bool BSONIteratorSorted::ElementFieldCmp::operator()( const char *s1, const char *s2 )
const {
- // Skip type byte and compare field names.
+ // Skip the type byte and compare field names.
return _cmp( s1 + 1, s2 + 1 );
}
- BSONObjIteratorSorted::BSONObjIteratorSorted( const BSONObj& o ) {
+ BSONIteratorSorted::BSONIteratorSorted( const BSONObj &o, const ElementFieldCmp &cmp ) {
_nfields = o.nFields();
_fields = new const char*[_nfields];
int x = 0;
@@ -1223,10 +1229,17 @@ namespace mongo {
assert( _fields[x-1] );
}
assert( x == _nfields );
- ElementFieldCmp cmp;
std::sort( _fields , _fields + _nfields , cmp );
_cur = 0;
}
+
+ BSONObjIteratorSorted::BSONObjIteratorSorted( const BSONObj &object ) :
+ BSONIteratorSorted( object, ElementFieldCmp( false ) ) {
+ }
+
+ BSONArrayIteratorSorted::BSONArrayIteratorSorted( const BSONArray &array ) :
+ BSONIteratorSorted( array, ElementFieldCmp( true ) ) {
+ }
bool BSONObjBuilder::appendAsNumber( const StringData& fieldName , const string& data ) {
if ( data.size() == 0 || data == "-" || data == ".")
View
54 src/mongo/db/ops/update.cpp
@@ -605,7 +605,8 @@ namespace mongo {
}
template< class Builder >
- void ModSetState::_appendNewFromMods( const string& root , ModState& m , Builder& b , set<string>& onedownseen ) {
+ void ModSetState::_appendNewFromMods( const string& root , ModState& m , Builder& b ,
+ set<string>& onedownseen ) {
const char * temp = m.fieldName();
temp += root.size();
const char * dot = strchr( temp , '.' );
@@ -616,13 +617,13 @@ namespace mongo {
return;
onedownseen.insert( nf );
BSONObjBuilder bb ( b.subobjStart( nf ) );
- createNewFromMods( nr , bb , BSONObj() ); // don't infer an array from name
+ // Always insert an object, even if the field name is numeric.
+ createNewObjFromMods( nr , bb , BSONObj() );
bb.done();
}
else {
appendNewFromMod( m , b );
}
-
}
bool ModSetState::duplicateFieldName( const BSONElement &a, const BSONElement &b ) {
@@ -633,16 +634,40 @@ namespace mongo {
str::equals( a.fieldName(), b.fieldName() );
}
- template< class Builder >
- void ModSetState::createNewFromMods( const string& root , Builder& b , const BSONObj &obj ) {
- DEBUGUPDATE( "\t\t createNewFromMods root: " << root );
- BSONObjIteratorSorted es( obj );
- BSONElement e = es.next();
-
- ModStateHolder::iterator m = _mods.lower_bound( root );
+ ModSetState::ModStateRange ModSetState::modsForRoot( const string &root ) {
+ ModStateHolder::iterator mstart = _mods.lower_bound( root );
StringBuilder buf;
buf << root << (char)255;
ModStateHolder::iterator mend = _mods.lower_bound( buf.str() );
+ return make_pair( mstart, mend );
+ }
+
+ void ModSetState::createNewObjFromMods( const string &root , BSONObjBuilder &b ,
+ const BSONObj &obj ) {
+ BSONObjIteratorSorted es( obj );
+ createNewFromMods( root, b, es, modsForRoot( root ), LexNumCmp( true ) );
+ }
+
+ void ModSetState::createNewArrayFromMods( const string &root , BSONArrayBuilder &b ,
+ const BSONArray &arr ) {
+ BSONArrayIteratorSorted es( arr );
+ ModStateRange objectOrderedRange = modsForRoot( root );
+ ModStateHolder arrayOrderedMods( LexNumCmp( false ) );
+ arrayOrderedMods.insert( objectOrderedRange.first, objectOrderedRange.second );
+ ModStateRange arrayOrderedRange( arrayOrderedMods.begin(), arrayOrderedMods.end() );
+ createNewFromMods( root, b, es, arrayOrderedRange, LexNumCmp( false ) );
+ }
+
+ template< class Builder >
+ void ModSetState::createNewFromMods( const string& root , Builder& b ,
+ BSONIteratorSorted& es ,
+ const ModStateRange& modRange ,
+ const LexNumCmp& lexNumCmp ) {
+
+ DEBUGUPDATE( "\t\t createNewFromMods root: " << root );
+ ModStateHolder::iterator m = modRange.first;
+ const ModStateHolder::const_iterator mend = modRange.second;
+ BSONElement e = es.next();
set<string> onedownseen;
BSONElement prevE;
@@ -658,7 +683,8 @@ namespace mongo {
prevE = e;
string field = root + e.fieldName();
- FieldCompareResult cmp = compareDottedFieldNames( m->second.m->fieldName , field );
+ FieldCompareResult cmp = compareDottedFieldNames( m->second->m->fieldName , field ,
+ lexNumCmp );
DEBUGUPDATE( "\t\t\t field:" << field << "\t mod:" << m->second->m->fieldName << "\t cmp:" << cmp << "\t short: " << e.fieldName() );
@@ -671,13 +697,13 @@ namespace mongo {
if ( e.type() == Object ) {
BSONObjBuilder bb( b.subobjStart( e.fieldName() ) );
stringstream nr; nr << root << e.fieldName() << ".";
- createNewFromMods( nr.str() , bb , e.embeddedObject() );
+ createNewObjFromMods( nr.str() , bb , e.Obj() );
bb.done();
}
else {
BSONArrayBuilder ba( b.subarrayStart( e.fieldName() ) );
stringstream nr; nr << root << e.fieldName() << ".";
- createNewFromMods( nr.str() , ba , e.embeddedObject() );
+ createNewArrayFromMods( nr.str() , ba , BSONArray( e.embeddedObject() ) );
ba.done();
}
// inc both as we handled both
@@ -730,7 +756,7 @@ namespace mongo {
BSONObj ModSetState::createNewFromMods() {
BSONObjBuilder b( (int)(_obj.objsize() * 1.1) );
- createNewFromMods( "" , b , _obj );
+ createNewObjFromMods( "" , b , _obj );
return _newFromMods = b.obj();
}
View
14 src/mongo/db/ops/update.h
@@ -419,7 +419,8 @@ namespace mongo {
ModHolder::const_iterator start = _mods.lower_bound(fieldName.substr(0,idx));
for ( ; start != _mods.end(); start++ ) {
- FieldCompareResult r = compareDottedFieldNames( fieldName , start->first );
+ FieldCompareResult r = compareDottedFieldNames( fieldName , start->first ,
+ LexNumCmp( true ) );
switch ( r ) {
case LEFT_SUBFIELD: return true;
case LEFT_BEFORE: return false;
@@ -535,7 +536,7 @@ namespace mongo {
BSONObj _newFromMods; // keep this data alive, as oplog generation may depend on it
ModSetState( const BSONObj& obj )
- : _obj( obj ) , _inPlacePossible(true) {
+ : _obj( obj ) , _mods( LexNumCmp( true ) ) , _inPlacePossible(true) {
}
/**
@@ -547,8 +548,15 @@ namespace mongo {
return _inPlacePossible;
}
+ ModStateRange modsForRoot( const string &root );
+
+ void createNewObjFromMods( const string &root, BSONObjBuilder &b, const BSONObj &obj );
+ void createNewArrayFromMods( const string &root, BSONArrayBuilder &b,
+ const BSONArray &arr );
+
template< class Builder >
- void createNewFromMods( const string& root , Builder& b , const BSONObj &obj );
+ void createNewFromMods( const string& root , Builder& b , BSONIteratorSorted& es ,
+ const ModStateRange& modRange , const LexNumCmp& lexNumCmp );
template< class Builder >
void _appendNewFromMods( const string& root , ModState& m , Builder& b , set<string>& onedownseen );
View
17 src/mongo/dbtests/basictests.cpp
@@ -334,8 +334,10 @@ namespace BasicTests {
class LexNumCmp {
public:
- static void assertCmp( int expected, const char *s1, const char *s2 ) {
- mongo::LexNumCmp cmp;
+ static void assertCmp( int expected, const char *s1, const char *s2,
+ bool lexOnly = false ) {
+ mongo::LexNumCmp cmp( lexOnly );
+ ASSERT_EQUALS( expected, cmp.cmp( s1, s2, lexOnly ) );
ASSERT_EQUALS( expected, cmp.cmp( s1, s2 ) );
ASSERT_EQUALS( expected < 0, cmp( s1, s2 ) );
ASSERT_EQUALS( expected < 0, cmp( string( s1 ), string( s2 ) ) );
@@ -435,6 +437,16 @@ namespace BasicTests {
assertCmp( 0, "ac.t", "ac.t" );
}
};
+
+ class LexNumCmpLexOnly : public LexNumCmp {
+ public:
+ void run() {
+ assertCmp( -1, "0", "00", true );
+ assertCmp( 1, "1", "01", true );
+ assertCmp( -1, "1", "11", true );
+ assertCmp( 1, "2", "11", true );
+ }
+ };
class DatabaseValidNames {
public:
@@ -674,6 +686,7 @@ namespace BasicTests {
add< ArrayTests::basic1 >();
add< LexNumCmp >();
+ add< LexNumCmpLexOnly >();
add< DatabaseValidNames >();
add< DatabaseOwnsNS >();
View
59 src/mongo/dbtests/jsobjtests.cpp
@@ -25,6 +25,7 @@
#include "../db/repl.h"
#include "../db/extsort.h"
#include "dbtests.h"
+#include "../util/stringutils.h"
#include "../util/mongoutils/checksum.h"
#include "../db/key.h"
#include "../db/btree.h"
@@ -1686,20 +1687,47 @@ namespace JsobjTests {
class CompareDottedFieldNamesTest {
public:
void t( FieldCompareResult res , const string& l , const string& r ) {
- ASSERT_EQUALS( res , compareDottedFieldNames( l , r ) );
- ASSERT_EQUALS( -1 * res , compareDottedFieldNames( r , l ) );
+ LexNumCmp cmp( true );
+ ASSERT_EQUALS( res , compareDottedFieldNames( l , r , cmp ) );
+ ASSERT_EQUALS( -1 * res , compareDottedFieldNames( r , l , cmp ) );
}
void run() {
t( SAME , "x" , "x" );
t( SAME , "x.a" , "x.a" );
+ t( SAME , "x.4" , "x.4" );
t( LEFT_BEFORE , "a" , "b" );
t( RIGHT_BEFORE , "b" , "a" );
+ t( LEFT_BEFORE , "x.04" , "x.4" );
t( LEFT_SUBFIELD , "a.x" , "a" );
+ t( LEFT_SUBFIELD , "a.4" , "a" );
}
};
+ class CompareDottedArrayFieldNamesTest {
+ public:
+ void t( FieldCompareResult res , const string& l , const string& r ) {
+ LexNumCmp cmp( false ); // Specify numeric comparison for array field names.
+ ASSERT_EQUALS( res , compareDottedFieldNames( l , r , cmp ) );
+ ASSERT_EQUALS( -1 * res , compareDottedFieldNames( r , l , cmp ) );
+ }
+
+ void run() {
+ t( SAME , "0" , "0" );
+ t( SAME , "1" , "1" );
+ t( SAME , "0.1" , "0.1" );
+ t( SAME , "0.a" , "0.a" );
+ t( LEFT_BEFORE , "0" , "1" );
+ t( LEFT_BEFORE , "2" , "10" );
+ t( RIGHT_BEFORE , "1" , "0" );
+ t( RIGHT_BEFORE , "10" , "2" );
+
+ t( LEFT_SUBFIELD , "5.4" , "5" );
+ t( LEFT_SUBFIELD , "5.x" , "5" );
+ }
+ };
+
struct NestedDottedConversions {
void t(const BSONObj& nest, const BSONObj& dot) {
ASSERT_EQUALS( nested2dotted(nest), dot);
@@ -1820,9 +1848,34 @@ namespace JsobjTests {
//unsigned long long tm = t.micros();
//cout << "time: " << tm << endl;
}
+
+ BSONObj o2 = BSON( "2" << "a" << "11" << "b" );
+ BSONObjIteratorSorted i2( o2 );
+ // First field in sorted order should be "11" due use of a lexical comparison.
+ ASSERT_EQUALS( "11", string( i2.next().fieldName() ) );
}
};
+
+ class BSONArrayIteratorSorted {
+ public:
+ void run() {
+ BSONArrayBuilder bab;
+ for( int i = 0; i < 11; ++i ) {
+ bab << "a";
+ }
+ BSONArray arr = bab.arr();
+ // The sorted iterator should perform numeric comparisons and return results in the same
+ // order as the unsorted iterator.
+ BSONObjIterator unsorted( arr );
+ mongo::BSONArrayIteratorSorted sorted( arr );
+ while( unsorted.more() ) {
+ ASSERT( sorted.more() );
+ ASSERT_EQUALS( string( unsorted.next().fieldName() ), sorted.next().fieldName() );
+ }
+ ASSERT( !sorted.more() );
+ }
+ };
class checkForStorageTests {
public:
@@ -2187,11 +2240,13 @@ namespace JsobjTests {
add< external_sort::D1 >();
add< CompatBSON >();
add< CompareDottedFieldNamesTest >();
+ add< CompareDottedArrayFieldNamesTest >();
add< NestedDottedConversions >();
add< BSONArrayBuilderTest >();
add< ArrayMacroTest >();
add< NumberParsing >();
add< bson2settest >();
+ add< BSONArrayIteratorSorted >();
add< checkForStorageTests >();
add< InvalidIDFind >();
add< ElementSetTest >();
View
15 src/mongo/dbtests/updatetests.cpp
@@ -653,6 +653,20 @@ namespace UpdateTests {
}
};
+ class IncRewriteNestedArray {
+ public:
+ void run() {
+ BSONObj obj = BSON( "a" << BSON_ARRAY( 2 ) );
+ BSONObj mod = BSON( "$inc" << BSON( "a.0" << 1 ) );
+ ModSet modSet( mod );
+ auto_ptr<ModSetState> modSetState = modSet.prepare( obj );
+ modSetState->createNewFromMods();
+ ASSERT( modSetState->needOpLogRewrite() );
+ ASSERT_EQUALS( BSON( "$set" << BSON( "a.0" << 3 ) ),
+ modSetState->getOpLogRewrite() );
+ }
+ };
+
};
namespace basic {
@@ -919,6 +933,7 @@ namespace UpdateTests {
add< ModSetTests::set1 >();
add< ModSetTests::push1 >();
add< ModSetTests::IncRewrite >();
+ add< ModSetTests::IncRewriteNestedArray >();
add< basic::inc1 >();
add< basic::inc2 >();
View
96 src/mongo/util/stringutils.h
@@ -40,14 +40,20 @@ namespace mongo {
return string(copy);
}
+ /** Functor for combining lexical and numeric comparisons. */
class LexNumCmp {
public:
+ /** @param lexOnly - compare all characters lexically, including digits. */
+ LexNumCmp( bool lexOnly ) :
+ _lexOnly( lexOnly ) {
+ }
/**
* Non numeric characters are compared lexicographically; numeric substrings
* are compared numerically; dots separate ordered comparable subunits.
* For convenience, character 255 is greater than anything else.
+ * @param lexOnly - compare all characters lexically, including digits.
*/
- static int cmp( const char *s1, const char *s2 ) {
+ static int cmp( const char *s1, const char *s2, bool lexOnly ) {
//cout << "START : " << s1 << "\t" << s2 << endl;
bool startWord = true;
@@ -74,53 +80,56 @@ namespace mongo {
if ( p2 && !p1 )
return -1;
- bool n1 = isNumber( *s1 );
- bool n2 = isNumber( *s2 );
-
- if ( n1 && n2 ) {
- // get rid of leading 0s
- if ( startWord ) {
- while ( *s1 == '0' ) s1++;
- while ( *s2 == '0' ) s2++;
- }
-
- char * e1 = (char*)s1;
- char * e2 = (char*)s2;
+ if ( !lexOnly ) {
- // find length
- // if end of string, will break immediately ('\0')
- while ( isNumber (*e1) ) e1++;
- while ( isNumber (*e2) ) e2++;
+ bool n1 = isNumber( *s1 );
+ bool n2 = isNumber( *s2 );
- int len1 = (int)(e1-s1);
- int len2 = (int)(e2-s2);
+ if ( n1 && n2 ) {
+ // get rid of leading 0s
+ if ( startWord ) {
+ while ( *s1 == '0' ) s1++;
+ while ( *s2 == '0' ) s2++;
+ }
+
+ char * e1 = (char*)s1;
+ char * e2 = (char*)s2;
+
+ // find length
+ // if end of string, will break immediately ('\0')
+ while ( isNumber (*e1) ) e1++;
+ while ( isNumber (*e2) ) e2++;
+
+ int len1 = (int)(e1-s1);
+ int len2 = (int)(e2-s2);
+
+ int result;
+ // if one is longer than the other, return
+ if ( len1 > len2 ) {
+ return 1;
+ }
+ else if ( len2 > len1 ) {
+ return -1;
+ }
+ // if the lengths are equal, just strcmp
+ else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
+ return result;
+ }
+
+ // otherwise, the numbers are equal
+ s1 = e1;
+ s2 = e2;
+ startWord = false;
+ continue;
+ }
- int result;
- // if one is longer than the other, return
- if ( len1 > len2 ) {
+ if ( n1 )
return 1;
- }
- else if ( len2 > len1 ) {
- return -1;
- }
- // if the lengths are equal, just strcmp
- else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
- return result;
- }
- // otherwise, the numbers are equal
- s1 = e1;
- s2 = e2;
- startWord = false;
- continue;
+ if ( n2 )
+ return -1;
}
- if ( n1 )
- return 1;
-
- if ( n2 )
- return -1;
-
if ( *s1 > *s2 )
return 1;
@@ -137,12 +146,17 @@ namespace mongo {
return -1;
return 0;
}
+ int cmp( const char *s1, const char *s2 ) const {
+ return cmp( s1, s2, _lexOnly );
+ }
bool operator()( const char *s1, const char *s2 ) const {
return cmp( s1, s2 ) < 0;
}
bool operator()( const string &s1, const string &s2 ) const {
return (*this)( s1.c_str(), s2.c_str() );
}
+ private:
+ bool _lexOnly;
};
} // namespace mongo
View
2  src/mongo/util/version.cpp
@@ -252,7 +252,7 @@ namespace mongo {
return -1;
}
- return LexNumCmp::cmp(rhs.data(), lhs.data());
+ return LexNumCmp::cmp(rhs.data(), lhs.data(), false);
}
class VersionCmpTest : public UnitTest {
Please sign in to comment.
Something went wrong with that request. Please try again.