diff options
-rw-r--r-- | src/mongo/db/ops/update_internal.cpp | 25 | ||||
-rw-r--r-- | src/mongo/db/ops/update_internal.h | 48 | ||||
-rw-r--r-- | src/mongo/dbtests/updatetests.cpp | 64 |
3 files changed, 68 insertions, 69 deletions
diff --git a/src/mongo/db/ops/update_internal.cpp b/src/mongo/db/ops/update_internal.cpp index 19dab221ff6..5d21ff69708 100644 --- a/src/mongo/db/ops/update_internal.cpp +++ b/src/mongo/db/ops/update_internal.cpp @@ -181,8 +181,8 @@ namespace mongo { ms.fixedArray = BSONArray( bb.done().getOwned() ); } - // If we're in the "push all" case with slice, we have to decide how much of each - // of the existing and parameter arrays to copy to the final object. + // If we're in the "push with a $each" case with slice, we have to decide how much + // of each of the existing and parameter arrays to copy to the final object. else if ( isSliceOnly() ) { long long slice = getSlice(); BSONObj eachArray = getEach(); @@ -237,7 +237,6 @@ namespace mongo { // decide how much of each of the resulting work area to copy to the final object. else { long long slice = getSlice(); - BSONObj sortPattern = getSort(); // Zero slice is equivalent to resetting the array in the final object, so // we only go into sorting if there is anything to sort. @@ -251,7 +250,8 @@ namespace mongo { while ( j.more() ) { workArea.push_back( j.next().Obj() ); } - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( sortPattern ) ); + ProjectKeyCmp cmp( getSort() ); + sort( workArea.begin(), workArea.end(), cmp ); long long skip = std::max( 0LL, (long long)workArea.size() - slice ); @@ -1240,8 +1240,20 @@ namespace mongo { ( fieldSortElem.type() == NumberDouble && fieldSortElem.numberDouble() == (long long) fieldSortElem.numberDouble() ) ) && - fieldSortElem.Number()*fieldSortElem.Number() - == 1.0); + ( fieldSortElem.Number() == 1 || + fieldSortElem.Number() == -1 ) ); + + FieldRef sortField; + sortField.parse( fieldSortElem.fieldName() ); + uassert( 16675, + "$sort field cannot be empty", + sortField.numParts() > 0 ); + + for ( size_t i = 0; i < sortField.numParts(); i++ ) { + uassert( 16676, + "empty field in dotted sort pattern", + sortField.getPart( i ).size() > 0 ); + } } // Finally, check if the $each is made of objects (as opposed @@ -1255,6 +1267,7 @@ namespace mongo { "$sort requires $each to be an array of objects", eachItem.type() == Object ); } + } else { uasserted( 16643, diff --git a/src/mongo/db/ops/update_internal.h b/src/mongo/db/ops/update_internal.h index d49ddd04fe6..1b46bbd9e48 100644 --- a/src/mongo/db/ops/update_internal.h +++ b/src/mongo/db/ops/update_internal.h @@ -229,7 +229,7 @@ namespace mongo { } long long getSlice() const { - // The $slice may be the second or the third elemen in the field object. + // The $slice may be the second or the third element in the field object. // { <field name>: { $each: [<each array>], $slice: -N, $sort: <pattern> } } // 'elt' here is the BSONElement above. BSONObj obj = elt.embeddedObject(); @@ -463,46 +463,12 @@ namespace mongo { */ struct ProjectKeyCmp { BSONObj sortPattern; - BSONObj projectionPattern; - ProjectKeyCmp( BSONObj pattern ) : sortPattern( pattern ) { - BSONObjBuilder projectBuilder; - BSONObjIterator i( sortPattern ); - while ( i.more() ) { - BSONElement elem = i.next(); - uassert( 16645, "sort pattern must be numeric", elem.isNumber() ); - - double val = elem.Number(); - uassert( 16646, "sort pattern must contain 1 or -1", val*val == 1.0); - - // - // If there are dots in the field name, check that they form a proper - // field path (e.g., no empty field parts). - // - - StringData field( elem.fieldName() ); - uassert( 16651, "sort pattern field name cannot be empty" , field.size() ); - - size_t pos = field.find('.'); - while ( pos != string::npos ) { - - uassert( 16639, - "empty field in dotted sort pattern", - (pos > 0) && (pos != field.size() - 1) ); - - field = field.substr( pos+1 ); - pos = field.find('.'); - } - - projectBuilder.append( elem.fieldName(), 1 ); - - } - projectionPattern = projectBuilder.obj(); - } + ProjectKeyCmp( BSONObj pattern ) : sortPattern( pattern) {} - int operator()( const BSONObj& left, const BSONObj& right ) { - BSONObj keyLeft = left.extractFields( projectionPattern, true ); - BSONObj keyRight = right.extractFields( projectionPattern, true ); + int operator()( const BSONObj& left, const BSONObj& right ) const { + BSONObj keyLeft = left.extractFields( sortPattern, true ); + BSONObj keyRight = right.extractFields( sortPattern, true ); return keyLeft.woCompare( keyRight, sortPattern ) < 0; } }; @@ -676,7 +642,6 @@ namespace mongo { } else if ( m.isSliceAndSort() ) { long long slice = m.getSlice(); - BSONObj sortPattern = m.getSort(); // Sort the $each array over sortPattern. vector<BSONObj> workArea; @@ -684,7 +649,8 @@ namespace mongo { while ( j.more() ) { workArea.push_back( j.next().Obj() ); } - sort( workArea.begin(), workArea.end(), ProjectKeyCmp( sortPattern) ); + ProjectKeyCmp cmp( m.getSort() ); + sort( workArea.begin(), workArea.end(), cmp ); // Slice to the appropriate size. If slice is zero, that's equivalent // to resetting the array, ie, a no-op. diff --git a/src/mongo/dbtests/updatetests.cpp b/src/mongo/dbtests/updatetests.cpp index 4cf5b5e0832..196cfbd058e 100644 --- a/src/mongo/dbtests/updatetests.cpp +++ b/src/mongo/dbtests/updatetests.cpp @@ -1259,35 +1259,55 @@ namespace UpdateTests { } }; - class PushSortInvalidSortPattern { + class PushSortInvalidSortPattern : public SetBase { public: void run() { - vector<BSONObj> dummy; + // Sort pattern validation is made during update command checking. Therefore, to + // catch bad patterns, we have to write updated that use them. + + BSONObj expected = fromjson( "{'_id':0,x:[{a:1}, {a:2}]}" ); + client().insert( ns(), expected ); + + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a..d:1} } } } + BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << + "$slice" << -2 << + "$sort" << BSON( "a..d" << 1 ) ); + client().update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); + BSONObj result = client().findOne( ns(), Query() ); + ASSERT_EQUALS( result, expected ); - ASSERT_THROWS( sort( dummy.begin(), - dummy.end(), - ProjectKeyCmp( fromjson( "{'a..d':-1}" ) ) ), - UserException ); - ASSERT_THROWS( sort( dummy.begin(), - dummy.end(), - ProjectKeyCmp( fromjson( "{'a.':-1}" ) ) ), - UserException ); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a.:1} } } } + pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << + "$slice" << -2 << + "$sort" << BSON( "a." << 1 ) ); + client().update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); + result = client().findOne( ns(), Query() ); + ASSERT_EQUALS( result, expected ); - ASSERT_THROWS( sort( dummy.begin(), - dummy.end(), - ProjectKeyCmp( fromjson( "{'.b':-1}" ) ) ), - UserException ); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.b:1} } } } + pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << + "$slice" << -2 << + "$sort" << BSON( ".b" << 1 ) ); + client().update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); + result = client().findOne( ns(), Query() ); + ASSERT_EQUALS( result, expected ); - ASSERT_THROWS( sort( dummy.begin(), - dummy.end(), - ProjectKeyCmp( fromjson( "{'.':-1}" ) ) ), - UserException ); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.:1} } } } + pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << + "$slice" << -2 << + "$sort" << BSON( "." << 1 ) ); + client().update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); + result = client().findOne( ns(), Query() ); + ASSERT_EQUALS( result, expected ); - ASSERT_THROWS( sort( dummy.begin(), - dummy.end(), - ProjectKeyCmp( fromjson( "{'':-1}" ) ) ), - UserException ); + // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {'':1} } } } + pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << + "$slice" << -2 << + "$sort" << BSON( "" << 1 ) ); + client().update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); + result = client().findOne( ns(), Query() ); + ASSERT_EQUALS( result, expected ); } }; |