diff --git a/db/ops/update.cpp b/db/ops/update.cpp index b4550c27953..26787f3e705 100644 --- a/db/ops/update.cpp +++ b/db/ops/update.cpp @@ -1061,7 +1061,7 @@ namespace mongo { debug.updateobj = updateobj; // idea with these here it to make them loop invariant for multi updates, and thus be a bit faster for that case - // The pointers may be left invalid on a failed yield recovery. + // The pointers may be left invalid on a failed or terminal yield recovery. NamespaceDetails *d = nsdetails(ns); // can be null if an upsert... NamespaceDetailsTransient *nsdt = &NamespaceDetailsTransient::get_w(ns); diff --git a/dbtests/basictests.cpp b/dbtests/basictests.cpp index 54372cece23..d9ddf7c9ced 100644 --- a/dbtests/basictests.cpp +++ b/dbtests/basictests.cpp @@ -411,6 +411,21 @@ namespace BasicTests { ASSERT_EQUALS( -1 , lexNumCmp( "a.b.c.d0" , "a.b.c.d00" ) ); ASSERT_EQUALS( 1 , lexNumCmp( "a.b.c.0.y" , "a.b.c.00.x" ) ); + ASSERT_EQUALS( -1, lexNumCmp( "a", "a-" ) ); + ASSERT_EQUALS( 1, lexNumCmp( "a-", "a" ) ); + ASSERT_EQUALS( 0, lexNumCmp( "a-", "a-" ) ); + + ASSERT_EQUALS( -1, lexNumCmp( "a", "a-c" ) ); + ASSERT_EQUALS( 1, lexNumCmp( "a-c", "a" ) ); + ASSERT_EQUALS( 0, lexNumCmp( "a-c", "a-c" ) ); + + ASSERT_EQUALS( 1, lexNumCmp( "a-c.t", "a.t" ) ); + ASSERT_EQUALS( -1, lexNumCmp( "a.t", "a-c.t" ) ); + ASSERT_EQUALS( 0, lexNumCmp( "a-c.t", "a-c.t" ) ); + + ASSERT_EQUALS( 1, lexNumCmp( "ac.t", "a.t" ) ); + ASSERT_EQUALS( -1, lexNumCmp( "a.t", "ac.t" ) ); + ASSERT_EQUALS( 0, lexNumCmp( "ac.t", "ac.t" ) ); } }; diff --git a/jstests/updateg.js b/jstests/updateg.js new file mode 100644 index 00000000000..f8d452f71b2 --- /dev/null +++ b/jstests/updateg.js @@ -0,0 +1,17 @@ +// SERVER-3370 check modifiers with field name characters comparing less than '.' character. + +t = db.jstests_updateg; + +t.drop(); +t.update({}, { '$inc' : { 'all.t' : 1, 'all-copy.t' : 1 }}, true); +assert.eq( 1, t.count( {all:{t:1},'all-copy':{t:1}} ) ); + +t.drop(); +t.save({ 'all' : {}, 'all-copy' : {}}); +t.update({}, { '$inc' : { 'all.t' : 1, 'all-copy.t' : 1 }}); +assert.eq( 1, t.count( {all:{t:1},'all-copy':{t:1}} ) ); + +t.drop(); +t.save({ 'all11' : {}, 'all2' : {}}); +t.update({}, { '$inc' : { 'all11.t' : 1, 'all2.t' : 1 }}); +assert.eq( 1, t.count( {all11:{t:1},'all2':{t:1}} ) ); diff --git a/util/stringutils.h b/util/stringutils.h index bab9f608f7e..93598aa520b 100644 --- a/util/stringutils.h +++ b/util/stringutils.h @@ -40,7 +40,11 @@ namespace mongo { return string(copy); } - // for convenience, '{' is greater than anything and stops number parsing + /** + * 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. + */ inline int lexNumCmp( const char *s1, const char *s2 ) { //cout << "START : " << s1 << "\t" << s2 << endl; @@ -48,6 +52,18 @@ namespace mongo { while( *s1 && *s2 ) { + bool d1 = ( *s1 == '.' ); + bool d2 = ( *s2 == '.' ); + if ( d1 && !d2 ) + return -1; + if ( d2 && !d1 ) + return 1; + if ( d1 && d2 ) { + ++s1; ++s2; + startWord = true; + continue; + } + bool p1 = ( *s1 == (char)255 ); bool p2 = ( *s2 == (char)255 ); //cout << "\t\t " << p1 << "\t" << p2 << endl; @@ -64,7 +80,6 @@ namespace mongo { if ( startWord ) { while ( *s1 == '0' ) s1++; while ( *s2 == '0' ) s2++; - startWord = false; } char * e1 = (char*)s1; @@ -94,6 +109,7 @@ namespace mongo { // otherwise, the numbers are equal s1 = e1; s2 = e2; + startWord = false; continue; } @@ -109,11 +125,8 @@ namespace mongo { if ( *s2 > *s1 ) return -1; - if ( *s1 == '.' ) - startWord = true; - else - startWord = false; s1++; s2++; + startWord = false; } if ( *s1 )