summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/ops/update_internal.cpp25
-rw-r--r--src/mongo/db/ops/update_internal.h48
-rw-r--r--src/mongo/dbtests/updatetests.cpp64
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 );
}
};