2009-10-19 10:13:23 -04:00
// update.cpp
/**
* Copyright ( C ) 2008 10 gen Inc .
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2011-06-20 17:54:02 -04:00
# include "pch.h"
2011-06-22 15:51:08 -04:00
# include "query.h"
2011-06-20 16:16:37 -04:00
# include "../pdfile.h"
# include "../jsobjmanipulator.h"
# include "../queryoptimizer.h"
# include "../repl.h"
# include "../btree.h"
2011-06-25 23:08:33 -04:00
# include "../../util/stringutils.h"
2009-12-07 23:41:58 -05:00
# include "update.h"
2011-06-20 16:16:37 -04:00
2010-08-25 16:56:27 -04:00
//#define DEBUGUPDATE(x) cout << x << endl;
# define DEBUGUPDATE(x)
2010-03-26 15:30:29 -04:00
2009-10-19 10:13:23 -04:00
namespace mongo {
2009-12-24 13:11:10 -05:00
const char * Mod : : modNames [ ] = { " $inc " , " $set " , " $push " , " $pushAll " , " $pull " , " $pullAll " , " $pop " , " $unset " ,
2011-01-04 00:40:41 -05:00
" $bitand " , " $bitor " , " $bit " , " $addToSet " , " $rename " , " $rename "
} ;
2009-12-24 13:11:10 -05:00
unsigned Mod : : modNamesNum = sizeof ( Mod : : modNames ) / sizeof ( char * ) ;
2009-12-11 12:33:16 -05:00
bool Mod : : _pullElementMatch ( BSONElement & toMatch ) const {
2011-01-04 00:40:41 -05:00
if ( elt . type ( ) ! = Object ) {
2009-12-11 12:33:16 -05:00
// if elt isn't an object, then comparison will work
return toMatch . woCompare ( elt , false ) = = 0 ;
}
2011-01-04 00:40:41 -05:00
2010-09-24 15:40:59 -04:00
if ( matcherOnPrimitive )
return matcher - > matches ( toMatch . wrap ( " " ) ) ;
2009-12-11 12:33:16 -05:00
2011-01-04 00:40:41 -05:00
if ( toMatch . type ( ) ! = Object ) {
2009-12-11 12:33:16 -05:00
// looking for an object, so this can't match
return false ;
}
2011-01-04 00:40:41 -05:00
2009-12-11 12:33:16 -05:00
// now we have an object on both sides
return matcher - > matches ( toMatch . embeddedObject ( ) ) ;
}
2010-03-01 15:04:01 -08:00
template < class Builder >
void Mod : : appendIncremented ( Builder & bb , const BSONElement & in , ModState & ms ) const {
2010-01-25 17:23:37 -05:00
BSONType a = in . type ( ) ;
BSONType b = elt . type ( ) ;
2011-01-04 00:40:41 -05:00
if ( a = = NumberDouble | | b = = NumberDouble ) {
2010-01-25 17:23:37 -05:00
ms . incType = NumberDouble ;
ms . incdouble = elt . numberDouble ( ) + in . numberDouble ( ) ;
}
2011-01-04 00:40:41 -05:00
else if ( a = = NumberLong | | b = = NumberLong ) {
2010-01-25 17:23:37 -05:00
ms . incType = NumberLong ;
ms . inclong = elt . numberLong ( ) + in . numberLong ( ) ;
}
else {
2010-11-01 23:38:17 -04:00
int x = elt . numberInt ( ) + in . numberInt ( ) ;
2011-01-04 00:40:41 -05:00
if ( x < 0 & & elt . numberInt ( ) > 0 & & in . numberInt ( ) > 0 ) {
2010-11-01 23:38:17 -04:00
// overflow
ms . incType = NumberLong ;
ms . inclong = elt . numberLong ( ) + in . numberLong ( ) ;
}
else {
ms . incType = NumberInt ;
ms . incint = elt . numberInt ( ) + in . numberInt ( ) ;
}
2010-01-25 17:23:37 -05:00
}
2011-01-04 00:40:41 -05:00
2010-05-27 11:39:48 -04:00
ms . appendIncValue ( bb , false ) ;
2010-01-25 17:23:37 -05:00
}
2010-03-02 23:09:34 -08:00
template < class Builder >
void appendUnset ( Builder & b ) {
}
2011-01-04 00:40:41 -05:00
2010-03-02 23:09:34 -08:00
template < >
void appendUnset ( BSONArrayBuilder & b ) {
b . appendNull ( ) ;
}
2011-01-04 00:40:41 -05:00
2010-03-01 15:04:01 -08:00
template < class Builder >
void Mod : : apply ( Builder & b , BSONElement in , ModState & ms ) const {
2010-09-29 10:10:10 -07:00
if ( ms . dontApply ) {
return ;
}
2011-01-04 00:40:41 -05:00
switch ( op ) {
2009-12-10 15:59:09 -05:00
case INC : {
2010-01-25 17:23:37 -05:00
appendIncremented ( b , in , ms ) ;
2009-12-10 14:29:14 -05:00
break ;
2009-12-10 15:59:09 -05:00
}
2011-01-04 00:40:41 -05:00
2009-12-10 15:59:09 -05:00
case SET : {
2010-01-16 11:16:30 -05:00
_checkForAppending ( elt ) ;
2009-12-10 14:29:14 -05:00
b . appendAs ( elt , shortFieldName ) ;
2009-12-08 17:00:25 -05:00
break ;
2009-12-10 15:59:09 -05:00
}
2009-12-11 16:02:53 -05:00
case UNSET : {
2010-03-02 23:09:34 -08:00
appendUnset ( b ) ;
2009-12-11 16:02:53 -05:00
break ;
}
2011-01-04 00:40:41 -05:00
2009-12-10 15:59:09 -05:00
case PUSH : {
2009-12-28 16:43:43 -05:00
uassert ( 10131 , " $push can only be applied to an array " , in . type ( ) = = Array ) ;
2009-12-10 15:59:09 -05:00
BSONObjBuilder bb ( b . subarrayStart ( shortFieldName ) ) ;
BSONObjIterator i ( in . embeddedObject ( ) ) ;
int n = 0 ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2009-12-10 15:59:09 -05:00
bb . append ( i . next ( ) ) ;
n + + ;
}
2010-01-25 17:23:37 -05:00
ms . pushStartSize = n ;
2009-12-10 15:59:09 -05:00
bb . appendAs ( elt , bb . numStr ( n ) ) ;
bb . done ( ) ;
break ;
}
2011-01-04 00:40:41 -05:00
2010-01-26 17:29:06 -05:00
case ADDTOSET : {
uassert ( 12592 , " $addToSet can only be applied to an array " , in . type ( ) = = Array ) ;
BSONObjBuilder bb ( b . subarrayStart ( shortFieldName ) ) ;
2011-01-04 00:40:41 -05:00
2010-02-27 22:05:59 -05:00
BSONObjIterator i ( in . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
int n = 0 ;
if ( isEach ( ) ) {
2010-02-27 22:05:59 -05:00
BSONElementSet toadd ;
parseEach ( toadd ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2010-02-27 22:05:59 -05:00
BSONElement cur = i . next ( ) ;
bb . append ( cur ) ;
2011-01-04 00:40:41 -05:00
n + + ;
2010-02-27 22:05:59 -05:00
toadd . erase ( cur ) ;
}
2011-01-04 00:40:41 -05:00
2010-09-27 17:04:15 -04:00
{
BSONObjIterator i ( getEach ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2010-09-27 17:04:15 -04:00
BSONElement e = i . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( toadd . count ( e ) ) {
2010-09-27 17:04:15 -04:00
bb . appendAs ( e , BSONObjBuilder : : numStr ( n + + ) ) ;
2010-12-19 01:36:41 -05:00
toadd . erase ( e ) ;
}
2010-09-27 17:04:15 -04:00
}
2010-02-27 22:05:59 -05:00
}
}
else {
bool found = false ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2010-02-27 22:05:59 -05:00
BSONElement cur = i . next ( ) ;
bb . append ( cur ) ;
n + + ;
if ( elt . woCompare ( cur , false ) = = 0 )
found = true ;
}
2011-01-04 00:40:41 -05:00
2010-02-27 22:05:59 -05:00
if ( ! found )
bb . appendAs ( elt , bb . numStr ( n ) ) ;
2011-01-04 00:40:41 -05:00
2010-01-26 17:29:06 -05:00
}
2011-01-04 00:40:41 -05:00
2010-01-26 17:29:06 -05:00
bb . done ( ) ;
break ;
}
2011-01-04 00:40:41 -05:00
2009-12-10 15:59:09 -05:00
case PUSH_ALL : {
2009-12-28 16:43:43 -05:00
uassert ( 10132 , " $pushAll can only be applied to an array " , in . type ( ) = = Array ) ;
uassert ( 10133 , " $pushAll has to be passed an array " , elt . type ( ) ) ;
2009-12-10 15:59:09 -05:00
BSONObjBuilder bb ( b . subarrayStart ( shortFieldName ) ) ;
2011-01-04 00:40:41 -05:00
2009-12-10 15:59:09 -05:00
BSONObjIterator i ( in . embeddedObject ( ) ) ;
int n = 0 ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2009-12-10 15:59:09 -05:00
bb . append ( i . next ( ) ) ;
n + + ;
}
2010-01-25 17:23:37 -05:00
ms . pushStartSize = n ;
2009-12-10 15:59:09 -05:00
i = BSONObjIterator ( elt . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2009-12-10 15:59:09 -05:00
bb . appendAs ( i . next ( ) , bb . numStr ( n + + ) ) ;
}
bb . done ( ) ;
break ;
}
2011-01-04 00:40:41 -05:00
2009-12-10 16:38:47 -05:00
case PULL :
case PULL_ALL : {
2009-12-28 16:43:43 -05:00
uassert ( 10134 , " $pull/$pullAll can only be applied to an array " , in . type ( ) = = Array ) ;
2009-12-10 16:38:47 -05:00
BSONObjBuilder bb ( b . subarrayStart ( shortFieldName ) ) ;
2011-01-04 00:40:41 -05:00
2009-12-10 16:38:47 -05:00
int n = 0 ;
BSONObjIterator i ( in . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2009-12-10 16:38:47 -05:00
BSONElement e = i . next ( ) ;
bool allowed = true ;
2011-01-04 00:40:41 -05:00
if ( op = = PULL ) {
2009-12-11 12:33:16 -05:00
allowed = ! _pullElementMatch ( e ) ;
2009-12-10 16:38:47 -05:00
}
else {
BSONObjIterator j ( elt . embeddedObject ( ) ) ;
while ( j . more ( ) ) {
BSONElement arrJ = j . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( e . woCompare ( arrJ , false ) = = 0 ) {
2009-12-10 16:38:47 -05:00
allowed = false ;
break ;
}
}
}
if ( allowed )
bb . appendAs ( e , bb . numStr ( n + + ) ) ;
}
2011-01-04 00:40:41 -05:00
2009-12-10 16:38:47 -05:00
bb . done ( ) ;
break ;
}
2009-12-10 15:59:09 -05:00
case POP : {
2009-12-28 16:43:43 -05:00
uassert ( 10135 , " $pop can only be applied to an array " , in . type ( ) = = Array ) ;
2009-12-10 15:59:09 -05:00
BSONObjBuilder bb ( b . subarrayStart ( shortFieldName ) ) ;
2011-01-04 00:40:41 -05:00
2009-12-10 15:59:09 -05:00
int n = 0 ;
BSONObjIterator i ( in . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
if ( elt . isNumber ( ) & & elt . number ( ) < 0 ) {
2009-12-10 15:59:09 -05:00
// pop from front
2011-01-04 00:40:41 -05:00
if ( i . more ( ) ) {
2009-12-10 15:59:09 -05:00
i . next ( ) ;
n + + ;
}
while ( i . more ( ) ) {
2010-07-20 12:59:47 -04:00
bb . appendAs ( i . next ( ) , bb . numStr ( n - 1 ) ) ;
2009-12-10 15:59:09 -05:00
n + + ;
}
}
else {
// pop from back
while ( i . more ( ) ) {
n + + ;
BSONElement arrI = i . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( i . more ( ) ) {
2009-12-10 15:59:09 -05:00
bb . append ( arrI ) ;
}
}
}
2010-01-25 17:23:37 -05:00
ms . pushStartSize = n ;
assert ( ms . pushStartSize = = in . embeddedObject ( ) . nFields ( ) ) ;
2009-12-10 15:59:09 -05:00
bb . done ( ) ;
break ;
}
2009-12-21 17:32:01 -05:00
case BIT : {
2009-12-28 16:43:43 -05:00
uassert ( 10136 , " $bit needs an array " , elt . type ( ) = = Object ) ;
uassert ( 10137 , " $bit can only be applied to numbers " , in . isNumber ( ) ) ;
2011-06-08 15:26:46 -04:00
uassert ( 10138 , " $bit cannot update a value of type double " , in . type ( ) ! = NumberDouble ) ;
2011-01-04 00:40:41 -05:00
2009-12-21 17:32:01 -05:00
int x = in . numberInt ( ) ;
long long y = in . numberLong ( ) ;
BSONObjIterator it ( elt . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( it . more ( ) ) {
2009-12-21 17:32:01 -05:00
BSONElement e = it . next ( ) ;
2009-12-28 16:43:43 -05:00
uassert ( 10139 , " $bit field must be number " , e . isNumber ( ) ) ;
2011-06-08 15:26:46 -04:00
if ( str : : equals ( e . fieldName ( ) , " and " ) ) {
2011-01-04 00:40:41 -05:00
switch ( in . type ( ) ) {
2009-12-21 17:32:01 -05:00
case NumberInt : x = x & e . numberInt ( ) ; break ;
case NumberLong : y = y & e . numberLong ( ) ; break ;
default : assert ( 0 ) ;
}
}
2011-06-08 15:26:46 -04:00
else if ( str : : equals ( e . fieldName ( ) , " or " ) ) {
2011-01-04 00:40:41 -05:00
switch ( in . type ( ) ) {
2009-12-21 17:32:01 -05:00
case NumberInt : x = x | e . numberInt ( ) ; break ;
case NumberLong : y = y | e . numberLong ( ) ; break ;
default : assert ( 0 ) ;
}
}
else {
2011-06-08 15:26:46 -04:00
uasserted ( 9016 , str : : stream ( ) < < " unknown $bit operation: " < < e . fieldName ( ) ) ;
2009-12-21 17:32:01 -05:00
}
}
2011-01-04 00:40:41 -05:00
switch ( in . type ( ) ) {
2009-12-21 17:32:01 -05:00
case NumberInt : b . append ( shortFieldName , x ) ; break ;
case NumberLong : b . append ( shortFieldName , y ) ; break ;
default : assert ( 0 ) ;
}
break ;
}
2010-09-29 10:10:10 -07:00
case RENAME_FROM : {
break ;
}
case RENAME_TO : {
2010-10-10 15:08:34 -07:00
ms . handleRename ( b , shortFieldName ) ;
2010-09-29 10:10:10 -07:00
break ;
}
2011-01-04 00:40:41 -05:00
2009-12-08 17:00:25 -05:00
default :
stringstream ss ;
ss < < " Mod::apply can't handle type: " < < op ;
2009-12-28 17:06:07 -05:00
throw UserException ( 9017 , ss . str ( ) ) ;
2009-12-08 17:00:25 -05:00
}
}
2010-09-29 10:10:10 -07:00
// -1 inside a non-object (non-object could be array)
// 0 missing
// 1 found
int validRenamePath ( BSONObj obj , const char * path ) {
while ( const char * p = strchr ( path , ' . ' ) ) {
string left ( path , p - path ) ;
BSONElement e = obj . getField ( left ) ;
if ( e . eoo ( ) ) {
return 0 ;
}
if ( e . type ( ) ! = Object ) {
return - 1 ;
}
obj = e . embeddedObject ( ) ;
path = p + 1 ;
}
return ! obj . getField ( path ) . eoo ( ) ;
}
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
auto_ptr < ModSetState > ModSet : : prepare ( const BSONObj & obj ) const {
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t start prepare " ) ;
2011-02-08 21:24:09 -05:00
auto_ptr < ModSetState > mss ( new ModSetState ( obj ) ) ;
2011-01-04 00:40:41 -05:00
2009-10-26 17:20:33 -04:00
// Perform this check first, so that we don't leave a partially modified object on uassert.
2009-12-08 17:00:25 -05:00
for ( ModHolder : : const_iterator i = _mods . begin ( ) ; i ! = _mods . end ( ) ; + + i ) {
2010-03-26 15:30:29 -04:00
DEBUGUPDATE ( " \t \t prepare : " < < i - > first ) ;
2010-01-25 17:23:37 -05:00
ModState & ms = mss - > _mods [ i - > first ] ;
2009-12-08 17:00:25 -05:00
const Mod & m = i - > second ;
2009-10-19 10:13:23 -04:00
BSONElement e = obj . getFieldDotted ( m . fieldName ) ;
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
ms . m = & m ;
ms . old = e ;
2010-09-29 10:10:10 -07:00
if ( m . op = = Mod : : RENAME_FROM ) {
int source = validRenamePath ( obj , m . fieldName ) ;
uassert ( 13489 , " $rename source field invalid " , source ! = - 1 ) ;
if ( source ! = 1 ) {
ms . dontApply = true ;
}
continue ;
}
2011-01-04 00:40:41 -05:00
2010-09-29 10:10:10 -07:00
if ( m . op = = Mod : : RENAME_TO ) {
int source = validRenamePath ( obj , m . renameFrom ( ) ) ;
if ( source = = 1 ) {
int target = validRenamePath ( obj , m . fieldName ) ;
uassert ( 13490 , " $rename target field invalid " , target ! = - 1 ) ;
ms . newVal = obj . getFieldDotted ( m . renameFrom ( ) ) ;
2010-09-29 11:14:35 -07:00
mss - > amIInPlacePossible ( false ) ;
2011-01-04 00:40:41 -05:00
}
else {
2010-09-29 10:10:10 -07:00
ms . dontApply = true ;
}
2011-01-04 00:40:41 -05:00
continue ;
2010-09-29 10:10:10 -07:00
}
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
if ( e . eoo ( ) ) {
2010-01-25 17:23:37 -05:00
mss - > amIInPlacePossible ( m . op = = Mod : : UNSET ) ;
continue ;
2011-01-04 00:40:41 -05:00
}
2010-01-25 17:23:37 -05:00
switch ( m . op ) {
case Mod : : INC :
uassert ( 10140 , " Cannot apply $inc modifier to non-number " , e . isNumber ( ) | | e . eoo ( ) ) ;
2011-01-04 00:40:41 -05:00
if ( mss - > amIInPlacePossible ( e . isNumber ( ) ) ) {
2010-01-26 11:54:45 -05:00
// check more typing info here
2011-01-04 00:40:41 -05:00
if ( m . elt . type ( ) ! = e . type ( ) ) {
2011-05-31 18:09:59 -04:00
// if i'm incrementing with a double, then the storage has to be a double
2011-01-04 00:40:41 -05:00
mss - > amIInPlacePossible ( m . elt . type ( ) ! = NumberDouble ) ;
2010-01-26 11:54:45 -05:00
}
2011-01-04 00:40:41 -05:00
2010-11-01 23:38:17 -04:00
// check for overflow
2011-01-04 00:40:41 -05:00
if ( e . type ( ) = = NumberInt & & e . numberLong ( ) + m . elt . numberLong ( ) > numeric_limits < int > : : max ( ) ) {
2010-11-01 23:38:17 -04:00
mss - > amIInPlacePossible ( false ) ;
}
2010-01-26 11:54:45 -05:00
}
2010-01-25 17:23:37 -05:00
break ;
2010-01-26 17:29:06 -05:00
2010-01-25 17:23:37 -05:00
case Mod : : SET :
mss - > amIInPlacePossible ( m . elt . type ( ) = = e . type ( ) & &
m . elt . valuesize ( ) = = e . valuesize ( ) ) ;
break ;
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
case Mod : : PUSH :
case Mod : : PUSH_ALL :
uassert ( 10141 , " Cannot apply $push/$pushAll modifier to non-array " , e . type ( ) = = Array | | e . eoo ( ) ) ;
mss - > amIInPlacePossible ( false ) ;
break ;
2010-01-26 17:29:06 -05:00
2010-01-25 17:23:37 -05:00
case Mod : : PULL :
case Mod : : PULL_ALL : {
uassert ( 10142 , " Cannot apply $pull/$pullAll modifier to non-array " , e . type ( ) = = Array | | e . eoo ( ) ) ;
BSONObjIterator i ( e . embeddedObject ( ) ) ;
while ( mss - > _inPlacePossible & & i . more ( ) ) {
BSONElement arrI = i . next ( ) ;
if ( m . op = = Mod : : PULL ) {
mss - > amIInPlacePossible ( ! m . _pullElementMatch ( arrI ) ) ;
2011-01-04 00:40:41 -05:00
}
2010-01-25 17:23:37 -05:00
else if ( m . op = = Mod : : PULL_ALL ) {
BSONObjIterator j ( m . elt . embeddedObject ( ) ) ;
while ( mss - > _inPlacePossible & & j . moreWithEOO ( ) ) {
BSONElement arrJ = j . next ( ) ;
if ( arrJ . eoo ( ) )
break ;
mss - > amIInPlacePossible ( arrI . woCompare ( arrJ , false ) ) ;
2009-10-19 10:13:23 -04:00
}
}
}
2010-01-25 17:23:37 -05:00
break ;
}
2010-01-26 17:29:06 -05:00
2010-01-25 17:23:37 -05:00
case Mod : : POP : {
uassert ( 10143 , " Cannot apply $pop modifier to non-array " , e . type ( ) = = Array | | e . eoo ( ) ) ;
mss - > amIInPlacePossible ( e . embeddedObject ( ) . isEmpty ( ) ) ;
break ;
}
2011-01-04 00:40:41 -05:00
2010-01-26 17:29:06 -05:00
case Mod : : ADDTOSET : {
uassert ( 12591 , " Cannot apply $addToSet modifier to non-array " , e . type ( ) = = Array | | e . eoo ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-01-26 17:29:06 -05:00
BSONObjIterator i ( e . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
if ( m . isEach ( ) ) {
2010-02-27 22:05:59 -05:00
BSONElementSet toadd ;
m . parseEach ( toadd ) ;
while ( i . more ( ) ) {
BSONElement arrI = i . next ( ) ;
toadd . erase ( arrI ) ;
}
mss - > amIInPlacePossible ( toadd . size ( ) = = 0 ) ;
}
else {
bool found = false ;
while ( i . more ( ) ) {
BSONElement arrI = i . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( arrI . woCompare ( m . elt , false ) = = 0 ) {
2010-02-27 22:05:59 -05:00
found = true ;
break ;
}
2010-01-26 17:29:06 -05:00
}
2010-02-27 22:05:59 -05:00
mss - > amIInPlacePossible ( found ) ;
2010-01-26 17:29:06 -05:00
}
break ;
}
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
default :
// mods we don't know about shouldn't be done in place
mss - > amIInPlacePossible ( false ) ;
2009-10-19 10:13:23 -04:00
}
}
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t mss \n " < < mss - > toString ( ) < < " \t -- " ) ;
2011-01-04 00:40:41 -05:00
2011-02-08 21:24:09 -05:00
return mss ;
2009-10-26 17:20:33 -04:00
}
2010-05-27 11:39:48 -04:00
void ModState : : appendForOpLog ( BSONObjBuilder & b ) const {
2010-09-29 10:10:10 -07:00
if ( dontApply ) {
return ;
}
2011-01-04 00:40:41 -05:00
if ( incType ) {
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t \t \t appendForOpLog inc fieldname: " < < m - > fieldName < < " short: " < < m - > shortFieldName ) ;
BSONObjBuilder bb ( b . subobjStart ( " $set " ) ) ;
appendIncValue ( bb , true ) ;
bb . done ( ) ;
return ;
}
2010-09-29 10:10:10 -07:00
if ( m - > op = = Mod : : RENAME_FROM ) {
2011-05-31 18:09:59 -04:00
DEBUGUPDATE ( " \t \t \t \t \t appendForOpLog RENAME_FROM fieldName: " < < m - > fieldName ) ;
2010-09-29 10:10:10 -07:00
BSONObjBuilder bb ( b . subobjStart ( " $unset " ) ) ;
bb . append ( m - > fieldName , 1 ) ;
bb . done ( ) ;
return ;
}
2011-01-04 00:40:41 -05:00
2010-09-29 10:10:10 -07:00
if ( m - > op = = Mod : : RENAME_TO ) {
2011-05-31 18:09:59 -04:00
DEBUGUPDATE ( " \t \t \t \t \t appendForOpLog RENAME_TO fieldName: " < < m - > fieldName ) ;
2010-09-29 10:10:10 -07:00
BSONObjBuilder bb ( b . subobjStart ( " $set " ) ) ;
bb . appendAs ( newVal , m - > fieldName ) ;
return ;
}
2011-01-04 00:40:41 -05:00
2010-05-27 11:39:48 -04:00
const char * name = fixedOpName ? fixedOpName : Mod : : modNames [ op ( ) ] ;
DEBUGUPDATE ( " \t \t \t \t \t appendForOpLog name: " < < name < < " fixed: " < < fixed < < " fn: " < < m - > fieldName ) ;
BSONObjBuilder bb ( b . subobjStart ( name ) ) ;
2010-09-29 10:10:10 -07:00
if ( fixed ) {
2010-05-27 11:39:48 -04:00
bb . appendAs ( * fixed , m - > fieldName ) ;
2011-01-04 00:40:41 -05:00
}
else {
2010-05-27 11:39:48 -04:00
bb . appendAs ( m - > elt , m - > fieldName ) ;
2010-09-29 10:10:10 -07:00
}
2010-05-27 11:39:48 -04:00
bb . done ( ) ;
}
string ModState : : toString ( ) const {
stringstream ss ;
if ( fixedOpName )
ss < < " fixedOpName: " < < fixedOpName ;
if ( fixed )
ss < < " fixed: " < < fixed ;
return ss . str ( ) ;
}
2011-01-04 00:40:41 -05:00
2010-10-10 15:08:34 -07:00
template < class Builder >
void ModState : : handleRename ( Builder & newObjBuilder , const char * shortFieldName ) {
newObjBuilder . appendAs ( newVal , shortFieldName ) ;
BSONObjBuilder b ;
b . appendAs ( newVal , shortFieldName ) ;
assert ( _objData . isEmpty ( ) ) ;
_objData = b . obj ( ) ;
2011-01-04 00:40:41 -05:00
newVal = _objData . firstElement ( ) ;
}
2010-11-02 00:21:14 -04:00
2010-11-02 20:15:31 -04:00
void ModSetState : : applyModsInPlace ( bool isOnDisk ) {
// TODO i think this assert means that we can get rid of the isOnDisk param
// and just use isOwned as the determination
DEV assert ( isOnDisk = = ! _obj . isOwned ( ) ) ;
2009-10-19 10:13:23 -04:00
2010-09-29 13:24:33 -04:00
for ( ModStateHolder : : iterator i = _mods . begin ( ) ; i ! = _mods . end ( ) ; + + i ) {
2011-01-04 00:40:41 -05:00
ModState & m = i - > second ;
2010-11-02 00:18:10 -04:00
if ( m . dontApply ) {
2010-09-29 11:28:23 -07:00
continue ;
2010-11-02 00:18:10 -04:00
}
2011-01-04 00:40:41 -05:00
switch ( m . m - > op ) {
2010-09-29 13:24:33 -04:00
case Mod : : UNSET :
case Mod : : ADDTOSET :
2010-09-29 11:28:23 -07:00
case Mod : : RENAME_FROM :
case Mod : : RENAME_TO :
2010-09-29 13:24:33 -04:00
// this should have been handled by prepare
break ;
2011-03-31 09:29:41 -04:00
case Mod : : PULL :
case Mod : : PULL_ALL :
2011-03-31 11:45:39 -04:00
// this should have been handled by prepare
break ;
2011-03-31 09:29:41 -04:00
case Mod : : POP :
assert ( m . old . eoo ( ) | | ( m . old . isABSONObj ( ) & & m . old . Obj ( ) . isEmpty ( ) ) ) ;
break ;
2011-01-04 00:40:41 -05:00
// [dm] the BSONElementManipulator statements below are for replication (correct?)
2009-10-21 00:21:09 -04:00
case Mod : : INC :
2010-11-02 20:15:31 -04:00
if ( isOnDisk )
m . m - > IncrementMe ( m . old ) ;
else
m . m - > incrementMe ( m . old ) ;
2010-04-26 15:24:16 -04:00
m . fixedOpName = " $set " ;
2010-01-25 17:23:37 -05:00
m . fixed = & ( m . old ) ;
2009-10-21 00:21:09 -04:00
break ;
case Mod : : SET :
2010-11-02 20:15:31 -04:00
if ( isOnDisk )
BSONElementManipulator ( m . old ) . ReplaceTypeAndValue ( m . m - > elt ) ;
else
BSONElementManipulator ( m . old ) . replaceTypeAndValue ( m . m - > elt ) ;
2009-10-21 00:21:09 -04:00
break ;
default :
2010-09-29 14:03:37 -04:00
uassert ( 13478 , " can't apply mod in place - shouldn't have gotten here " , 0 ) ;
2009-10-19 10:13:23 -04:00
}
}
}
void ModSet : : extractFields ( map < string , BSONElement > & fields , const BSONElement & top , const string & base ) {
if ( top . type ( ) ! = Object ) {
fields [ base + top . fieldName ( ) ] = top ;
return ;
}
BSONObjIterator i ( top . embeddedObject ( ) ) ;
bool empty = true ;
while ( i . moreWithEOO ( ) ) {
BSONElement e = i . next ( ) ;
if ( e . eoo ( ) )
break ;
extractFields ( fields , e , base + top . fieldName ( ) + " . " ) ;
empty = false ;
}
if ( empty )
2011-01-04 00:40:41 -05:00
fields [ base + top . fieldName ( ) ] = top ;
2009-10-19 10:13:23 -04:00
}
2011-01-04 00:40:41 -05:00
2010-03-01 15:04:01 -08:00
template < class Builder >
2011-01-04 00:40:41 -05:00
void ModSetState : : _appendNewFromMods ( const string & root , ModState & m , Builder & b , set < string > & onedownseen ) {
2010-01-25 17:23:37 -05:00
const char * temp = m . fieldName ( ) ;
2009-12-08 17:00:25 -05:00
temp + = root . size ( ) ;
2009-12-08 17:11:50 -05:00
const char * dot = strchr ( temp , ' . ' ) ;
2011-01-04 00:40:41 -05:00
if ( dot ) {
2010-01-25 17:23:37 -05:00
string nr ( m . fieldName ( ) , 0 , 1 + ( dot - m . fieldName ( ) ) ) ;
2009-12-08 17:00:25 -05:00
string nf ( temp , 0 , dot - temp ) ;
if ( onedownseen . count ( nf ) )
return ;
onedownseen . insert ( nf ) ;
2010-08-18 18:12:31 -04:00
BSONObjBuilder bb ( b . subobjStart ( nf ) ) ;
2010-03-02 10:20:10 -08:00
createNewFromMods ( nr , bb , BSONObj ( ) ) ; // don't infer an array from name
2009-12-08 17:00:25 -05:00
bb . done ( ) ;
}
2009-12-10 14:29:14 -05:00
else {
2009-12-08 17:00:25 -05:00
appendNewFromMod ( m , b ) ;
2009-12-10 14:29:14 -05:00
}
2011-01-04 00:40:41 -05:00
2009-12-08 17:00:25 -05:00
}
2011-01-04 00:40:41 -05:00
2010-03-01 15:04:01 -08:00
template < class Builder >
2011-01-04 00:40:41 -05:00
void ModSetState : : createNewFromMods ( const string & root , Builder & b , const BSONObj & obj ) {
2010-03-26 15:30:29 -04:00
DEBUGUPDATE ( " \t \t createNewFromMods root: " < < root ) ;
2009-12-18 01:51:43 -05:00
BSONObjIteratorSorted es ( obj ) ;
2009-12-08 17:00:25 -05:00
BSONElement e = es . next ( ) ;
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
ModStateHolder : : iterator m = _mods . lower_bound ( root ) ;
2010-08-25 18:30:51 -04:00
StringBuilder buf ( root . size ( ) + 2 ) ;
buf < < root < < ( char ) 255 ;
ModStateHolder : : iterator mend = _mods . lower_bound ( buf . str ( ) ) ;
2011-01-04 00:40:41 -05:00
2009-12-08 17:00:25 -05:00
set < string > onedownseen ;
2011-01-04 00:40:41 -05:00
while ( e . type ( ) & & m ! = mend ) {
2009-12-08 17:00:25 -05:00
string field = root + e . fieldName ( ) ;
2010-01-25 17:23:37 -05:00
FieldCompareResult cmp = compareDottedFieldNames ( m - > second . m - > fieldName , field ) ;
2010-03-26 15:30:29 -04:00
2010-08-25 02:42:25 -04:00
DEBUGUPDATE ( " \t \t \t field: " < < field < < " \t mod: " < < m - > second . m - > fieldName < < " \t cmp: " < < cmp < < " \t short: " < < e . fieldName ( ) ) ;
2011-01-04 00:40:41 -05:00
switch ( cmp ) {
2011-05-31 18:09:59 -04:00
case LEFT_SUBFIELD : { // Mod is embedded under this element
2010-10-31 22:25:31 -04:00
uassert ( 10145 , str : : stream ( ) < < " LEFT_SUBFIELD only supports Object: " < < field < < " not: " < < e . type ( ) , e . type ( ) = = Object | | e . type ( ) = = Array ) ;
2011-01-04 00:40:41 -05:00
if ( onedownseen . count ( e . fieldName ( ) ) = = 0 ) {
2009-12-28 15:37:18 -05:00
onedownseen . insert ( e . fieldName ( ) ) ;
2010-03-01 15:04:01 -08:00
if ( e . type ( ) = = Object ) {
BSONObjBuilder bb ( b . subobjStart ( e . fieldName ( ) ) ) ;
stringstream nr ; nr < < root < < e . fieldName ( ) < < " . " ;
createNewFromMods ( nr . str ( ) , bb , e . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
bb . done ( ) ;
}
else {
2010-03-01 15:04:01 -08:00
BSONArrayBuilder ba ( b . subarrayStart ( e . fieldName ( ) ) ) ;
stringstream nr ; nr < < root < < e . fieldName ( ) < < " . " ;
createNewFromMods ( nr . str ( ) , ba , e . embeddedObject ( ) ) ;
ba . done ( ) ;
}
2009-12-28 15:37:18 -05:00
// inc both as we handled both
e = es . next ( ) ;
m + + ;
}
2010-08-25 02:42:25 -04:00
else {
// this is a very weird case
// have seen it in production, but can't reproduce
// this assert prevents an inf. loop
// but likely isn't the correct solution
assert ( 0 ) ;
}
2009-12-08 17:00:25 -05:00
continue ;
}
2009-12-10 17:30:11 -05:00
case LEFT_BEFORE : // Mod on a field that doesn't exist
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t \t creating new field for: " < < m - > second . m - > fieldName ) ;
2009-12-10 16:38:47 -05:00
_appendNewFromMods ( root , m - > second , b , onedownseen ) ;
2009-12-08 17:00:25 -05:00
m + + ;
continue ;
case SAME :
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t \t applying mod on: " < < m - > second . m - > fieldName ) ;
2009-12-08 17:00:25 -05:00
m - > second . apply ( b , e ) ;
e = es . next ( ) ;
m + + ;
continue ;
2009-12-10 17:30:11 -05:00
case RIGHT_BEFORE : // field that doesn't have a MOD
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t \t just copying " ) ;
2010-03-02 13:16:20 -08:00
b . append ( e ) ; // if array, ignore field name
2009-12-08 17:00:25 -05:00
e = es . next ( ) ;
continue ;
case RIGHT_SUBFIELD :
2011-01-04 00:40:41 -05:00
massert ( 10399 , " ModSet::createNewFromMods - RIGHT_SUBFIELD should be impossible " , 0 ) ;
2009-12-08 17:00:25 -05:00
break ;
default :
2009-12-28 16:43:43 -05:00
massert ( 10400 , " unhandled case " , 0 ) ;
2009-12-08 17:00:25 -05:00
}
}
2011-01-04 00:40:41 -05:00
2009-12-10 17:30:11 -05:00
// finished looping the mods, just adding the rest of the elements
2011-01-04 00:40:41 -05:00
while ( e . type ( ) ) {
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t copying: " < < e . fieldName ( ) ) ;
2010-03-02 13:16:20 -08:00
b . append ( e ) ; // if array, ignore field name
2009-12-08 17:00:25 -05:00
e = es . next ( ) ;
}
2011-01-04 00:40:41 -05:00
2009-12-10 17:30:11 -05:00
// do mods that don't have fields already
2011-01-04 00:40:41 -05:00
for ( ; m ! = mend ; m + + ) {
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t \t \t appending from mod at end: " < < m - > second . m - > fieldName ) ;
2009-12-08 17:00:25 -05:00
_appendNewFromMods ( root , m - > second , b , onedownseen ) ;
}
}
2009-12-28 15:37:18 -05:00
2010-01-25 17:23:37 -05:00
BSONObj ModSetState : : createNewFromMods ( ) {
BSONObjBuilder b ( ( int ) ( _obj . objsize ( ) * 1.1 ) ) ;
createNewFromMods ( " " , b , _obj ) ;
2010-09-29 10:10:10 -07:00
return _newFromMods = b . obj ( ) ;
2009-12-07 23:41:58 -05:00
}
2009-12-16 21:53:12 -05:00
2010-05-27 11:39:48 -04:00
string ModSetState : : toString ( ) const {
stringstream ss ;
2011-01-04 00:40:41 -05:00
for ( ModStateHolder : : const_iterator i = _mods . begin ( ) ; i ! = _mods . end ( ) ; + + i ) {
2010-05-27 11:39:48 -04:00
ss < < " \t \t " < < i - > first < < " \t " < < i - > second . toString ( ) < < " \n " ;
}
return ss . str ( ) ;
}
2011-06-25 23:08:33 -04:00
bool ModSetState : : FieldCmp : : operator ( ) ( const string & l , const string & r ) const {
return lexNumCmp ( l . c_str ( ) , r . c_str ( ) ) < 0 ;
}
2011-01-04 00:40:41 -05:00
BSONObj ModSet : : createNewFromQuery ( const BSONObj & query ) {
2009-12-16 21:53:12 -05:00
BSONObj newObj ;
{
BSONObjBuilder bb ;
2010-01-16 10:25:32 -05:00
EmbeddedBuilder eb ( & bb ) ;
BSONObjIteratorSorted i ( query ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2009-12-16 21:53:12 -05:00
BSONElement e = i . next ( ) ;
2010-08-04 15:15:10 -04:00
if ( e . fieldName ( ) [ 0 ] = = ' $ ' ) // for $atomic and anything else we add
continue ;
2009-12-16 21:53:12 -05:00
2011-05-23 10:49:50 -04:00
if ( e . type ( ) = = Object & & e . embeddedObject ( ) . firstElementFieldName ( ) [ 0 ] = = ' $ ' ) {
2009-12-16 21:53:12 -05:00
// this means this is a $gt type filter, so don't make part of the new object
continue ;
}
2010-01-16 10:25:32 -05:00
eb . appendAs ( e , e . fieldName ( ) ) ;
2009-12-16 21:53:12 -05:00
}
2010-01-16 10:25:32 -05:00
eb . done ( ) ;
2009-12-16 21:53:12 -05:00
newObj = bb . obj ( ) ;
}
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
auto_ptr < ModSetState > mss = prepare ( newObj ) ;
if ( mss - > canApplyInPlace ( ) )
2010-11-02 20:15:31 -04:00
mss - > applyModsInPlace ( false ) ;
2009-12-16 21:53:12 -05:00
else
2010-01-25 17:23:37 -05:00
newObj = mss - > createNewFromMods ( ) ;
2011-01-04 00:40:41 -05:00
2009-12-16 21:53:12 -05:00
return newObj ;
}
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
/* get special operations like $inc
{ $ inc : { a : 1 , b : 1 } }
{ $ set : { a : 77 } }
{ $ push : { a : 55 } }
{ $ pushAll : { a : [ 77 , 88 ] } }
{ $ pull : { a : 66 } }
{ $ pullAll : { a : [ 99 , 1010 ] } }
NOTE : MODIFIES source from object !
*/
2010-02-04 14:25:49 -05:00
ModSet : : ModSet (
2011-01-04 00:40:41 -05:00
const BSONObj & from ,
2010-02-04 14:25:49 -05:00
const set < string > & idxKeys ,
const set < string > * backgroundKeys )
2010-03-06 22:12:58 -05:00
: _isIndexed ( 0 ) , _hasDynamicArray ( false ) {
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
BSONObjIterator it ( from ) ;
2011-01-04 00:40:41 -05:00
2009-10-21 10:14:45 -04:00
while ( it . more ( ) ) {
2009-10-19 10:13:23 -04:00
BSONElement e = it . next ( ) ;
const char * fn = e . fieldName ( ) ;
2011-01-04 00:40:41 -05:00
2011-05-19 11:11:36 -04:00
uassert ( 10147 , " Invalid modifier specified: " + string ( fn ) , e . type ( ) = = Object ) ;
2009-10-19 10:13:23 -04:00
BSONObj j = e . embeddedObject ( ) ;
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t " < < j ) ;
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
BSONObjIterator jt ( j ) ;
Mod : : Op op = opFromStr ( fn ) ;
2010-01-25 17:23:37 -05:00
2009-10-19 10:13:23 -04:00
while ( jt . more ( ) ) {
2009-12-23 15:59:47 -05:00
BSONElement f = jt . next ( ) ; // x:44
2009-12-11 12:33:16 -05:00
const char * fieldName = f . fieldName ( ) ;
2011-09-20 20:44:19 -04:00
uassert ( 15896 , " Modified field name may not start with $ " , fieldName [ 0 ] ! = ' $ ' | | op = = Mod : : UNSET ) ; // allow remove of invalid field name in case it was inserted before this check was added (~ version 2.1)
2009-12-28 16:43:43 -05:00
uassert ( 10148 , " Mod on _id not allowed " , strcmp ( fieldName , " _id " ) ! = 0 ) ;
uassert ( 10149 , " Invalid mod field name, may not end in a period " , fieldName [ strlen ( fieldName ) - 1 ] ! = ' . ' ) ;
uassert ( 10150 , " Field name duplication not allowed with modifiers " , ! haveModForField ( fieldName ) ) ;
2010-06-11 15:12:04 -04:00
uassert ( 10151 , " have conflicting mods in update " , ! haveConflictingMod ( fieldName ) ) ;
2009-12-28 16:43:43 -05:00
uassert ( 10152 , " Modifier $inc allowed for numbers only " , f . isNumber ( ) | | op ! = Mod : : INC ) ;
uassert ( 10153 , " Modifier $pushAll/pullAll allowed for arrays only " , f . type ( ) = = Array | | ( op ! = Mod : : PUSH_ALL & & op ! = Mod : : PULL_ALL ) ) ;
2011-01-04 00:40:41 -05:00
2010-09-29 10:10:10 -07:00
if ( op = = Mod : : RENAME_TO ) {
2010-09-29 10:23:19 -07:00
uassert ( 13494 , " $rename target must be a string " , f . type ( ) = = String ) ;
2010-09-29 10:10:10 -07:00
const char * target = f . valuestr ( ) ;
2010-09-29 10:23:19 -07:00
uassert ( 13495 , " $rename source must differ from target " , strcmp ( fieldName , target ) ! = 0 ) ;
2010-09-29 11:28:23 -07:00
uassert ( 13496 , " invalid mod field name, source may not be empty " , fieldName [ 0 ] ) ;
2010-09-29 10:10:10 -07:00
uassert ( 13479 , " invalid mod field name, target may not be empty " , target [ 0 ] ) ;
uassert ( 13480 , " invalid mod field name, source may not begin or end in period " , fieldName [ 0 ] ! = ' . ' & & fieldName [ strlen ( fieldName ) - 1 ] ! = ' . ' ) ;
uassert ( 13481 , " invalid mod field name, target may not begin or end in period " , target [ 0 ] ! = ' . ' & & target [ strlen ( target ) - 1 ] ! = ' . ' ) ;
uassert ( 13482 , " $rename affecting _id not allowed " , ! ( fieldName [ 0 ] = = ' _ ' & & fieldName [ 1 ] = = ' i ' & & fieldName [ 2 ] = = ' d ' & & ( ! fieldName [ 3 ] | | fieldName [ 3 ] = = ' . ' ) ) ) ;
uassert ( 13483 , " $rename affecting _id not allowed " , ! ( target [ 0 ] = = ' _ ' & & target [ 1 ] = = ' i ' & & target [ 2 ] = = ' d ' & & ( ! target [ 3 ] | | target [ 3 ] = = ' . ' ) ) ) ;
uassert ( 13484 , " field name duplication not allowed with $rename target " , ! haveModForField ( target ) ) ;
uassert ( 13485 , " conflicting mods not allowed with $rename target " , ! haveConflictingMod ( target ) ) ;
uassert ( 13486 , " $rename target may not be a parent of source " , ! ( strncmp ( fieldName , target , strlen ( target ) ) = = 0 & & fieldName [ strlen ( target ) ] = = ' . ' ) ) ;
uassert ( 13487 , " $rename source may not be dynamic array " , strstr ( fieldName , " .$ " ) = = 0 ) ;
uassert ( 13488 , " $rename target may not be dynamic array " , strstr ( target , " .$ " ) = = 0 ) ;
Mod from ;
from . init ( Mod : : RENAME_FROM , f ) ;
from . setFieldName ( fieldName ) ;
updateIsIndexed ( from , idxKeys , backgroundKeys ) ;
_mods [ from . fieldName ] = from ;
Mod to ;
to . init ( Mod : : RENAME_TO , f ) ;
to . setFieldName ( target ) ;
updateIsIndexed ( to , idxKeys , backgroundKeys ) ;
_mods [ to . fieldName ] = to ;
2011-01-04 00:40:41 -05:00
2010-09-29 10:10:10 -07:00
DEBUGUPDATE ( " \t \t " < < fieldName < < " \t " < < from . fieldName < < " \t " < < to . fieldName ) ;
continue ;
}
2011-01-04 00:40:41 -05:00
2010-03-09 21:24:36 -05:00
_hasDynamicArray = _hasDynamicArray | | strstr ( fieldName , " .$ " ) > 0 ;
2011-01-04 00:40:41 -05:00
2009-12-11 12:33:16 -05:00
Mod m ;
m . init ( op , f ) ;
m . setFieldName ( f . fieldName ( ) ) ;
2010-09-29 10:10:10 -07:00
updateIsIndexed ( m , idxKeys , backgroundKeys ) ;
2009-12-08 17:00:25 -05:00
_mods [ m . fieldName ] = m ;
2010-03-07 21:42:58 -05:00
2010-05-27 11:39:48 -04:00
DEBUGUPDATE ( " \t \t " < < fieldName < < " \t " < < m . fieldName < < " \t " < < _hasDynamicArray ) ;
2009-10-19 10:13:23 -04:00
}
}
2010-01-25 17:23:37 -05:00
2009-10-19 10:13:23 -04:00
}
2010-03-06 22:12:58 -05:00
ModSet * ModSet : : fixDynamicArray ( const char * elemMatchKey ) const {
ModSet * n = new ModSet ( ) ;
n - > _isIndexed = _isIndexed ;
n - > _hasDynamicArray = _hasDynamicArray ;
2011-01-04 00:40:41 -05:00
for ( ModHolder : : const_iterator i = _mods . begin ( ) ; i ! = _mods . end ( ) ; i + + ) {
2010-03-06 22:12:58 -05:00
string s = i - > first ;
2010-03-09 21:24:36 -05:00
size_t idx = s . find ( " .$ " ) ;
2011-01-04 00:40:41 -05:00
if ( idx = = string : : npos ) {
2010-03-06 22:12:58 -05:00
n - > _mods [ s ] = i - > second ;
continue ;
}
StringBuilder buf ( s . size ( ) + strlen ( elemMatchKey ) ) ;
2010-03-09 21:24:36 -05:00
buf < < s . substr ( 0 , idx + 1 ) < < elemMatchKey < < s . substr ( idx + 2 ) ;
2010-03-06 22:12:58 -05:00
string fixed = buf . str ( ) ;
2010-03-08 23:33:10 -05:00
DEBUGUPDATE ( " fixed dynamic: " < < s < < " -->> " < < fixed ) ;
2010-03-06 22:12:58 -05:00
n - > _mods [ fixed ] = i - > second ;
ModHolder : : iterator temp = n - > _mods . find ( fixed ) ;
temp - > second . setFieldName ( temp - > first . c_str ( ) ) ;
}
return n ;
}
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
void checkNoMods ( BSONObj o ) {
BSONObjIterator i ( o ) ;
while ( i . moreWithEOO ( ) ) {
BSONElement e = i . next ( ) ;
if ( e . eoo ( ) )
break ;
2009-12-28 16:43:43 -05:00
uassert ( 10154 , " Modifiers and non-modifiers cannot be mixed " , e . fieldName ( ) [ 0 ] ! = ' $ ' ) ;
2009-10-19 10:13:23 -04:00
}
}
2011-01-04 00:40:41 -05:00
2010-05-24 22:00:25 -07:00
class UpdateOp : public MultiCursor : : CursorOp {
2009-10-19 10:13:23 -04:00
public :
2011-08-22 15:53:10 -07:00
UpdateOp ( bool hasPositionalField , int orClauseIndex = - 1 ) :
_nscanned ( ) ,
_hasPositionalField ( hasPositionalField ) ,
_orClauseIndex ( orClauseIndex ) { }
2010-06-02 17:10:34 -07:00
virtual void _init ( ) {
2010-05-07 17:25:57 -04:00
_c = qp ( ) . newCursor ( ) ;
2010-06-02 17:10:34 -07:00
if ( ! _c - > ok ( ) ) {
2009-10-19 10:13:23 -04:00
setComplete ( ) ;
2010-06-02 17:10:34 -07:00
}
2009-10-19 10:13:23 -04:00
}
2010-08-02 15:43:53 -07:00
virtual bool prepareToYield ( ) {
2011-08-22 15:53:10 -07:00
if ( _orClauseIndex > 0 ) {
return false ;
}
2010-07-07 19:35:26 -07:00
if ( ! _cc ) {
_cc . reset ( new ClientCursor ( QueryOption_NoCursorTimeout , _c , qp ( ) . ns ( ) ) ) ;
}
2010-08-02 15:43:53 -07:00
return _cc - > prepareToYield ( _yieldData ) ;
2011-01-04 00:40:41 -05:00
}
2010-07-07 19:35:26 -07:00
virtual void recoverFromYield ( ) {
if ( ! ClientCursor : : recoverFromYield ( _yieldData ) ) {
_c . reset ( ) ;
_cc . reset ( ) ;
massert ( 13339 , " cursor dropped during update " , false ) ;
}
2011-01-04 00:40:41 -05:00
}
2010-08-03 20:09:16 -07:00
virtual long long nscanned ( ) {
2011-03-21 17:44:29 -07:00
return _c . get ( ) ? _c - > nscanned ( ) : _nscanned ;
2010-08-03 20:09:16 -07:00
}
2009-10-19 10:13:23 -04:00
virtual void next ( ) {
2010-03-06 22:12:58 -05:00
if ( ! _c - > ok ( ) ) {
2009-10-19 10:13:23 -04:00
setComplete ( ) ;
return ;
}
2011-03-22 09:18:36 -07:00
_nscanned = _c - > nscanned ( ) ;
2011-08-22 15:53:10 -07:00
if ( _orClauseIndex > 0 & & _nscanned > = 100 ) {
setComplete ( ) ;
return ;
}
2011-05-09 11:37:23 -04:00
if ( matcher ( _c ) - > matchesCurrent ( _c . get ( ) , & _details ) ) {
2009-10-19 10:13:23 -04:00
setComplete ( ) ;
return ;
}
2010-03-06 22:12:58 -05:00
_c - > advance ( ) ;
2009-10-19 10:13:23 -04:00
}
2010-05-08 22:05:36 -04:00
2009-10-19 10:13:23 -04:00
virtual bool mayRecordPlan ( ) const { return false ; }
2010-06-02 17:10:34 -07:00
virtual QueryOp * _createChild ( ) const {
2011-08-22 15:53:10 -07:00
return new UpdateOp ( _hasPositionalField , _orClauseIndex + 1 ) ;
2009-10-19 10:13:23 -04:00
}
2010-05-24 22:32:54 -07:00
// already scanned to the first match, so return _c
virtual shared_ptr < Cursor > newCursor ( ) const { return _c ; }
2010-06-03 12:24:13 -07:00
virtual bool alwaysUseRecord ( ) const { return _hasPositionalField ; }
2009-10-19 10:13:23 -04:00
private :
2010-03-06 22:12:58 -05:00
shared_ptr < Cursor > _c ;
long long _nscanned ;
2010-05-10 14:38:10 -04:00
bool _hasPositionalField ;
2010-03-06 22:12:58 -05:00
MatchDetails _details ;
2010-07-12 11:20:53 -07:00
ClientCursor : : CleanupPointer _cc ;
2010-07-07 19:35:26 -07:00
ClientCursor : : YieldData _yieldData ;
2011-08-22 20:03:10 -07:00
// Avoid yielding in the MultiPlanScanner when not the first $or clause - just a temporary implementaiton for now. SERVER-3555
2011-08-22 15:53:10 -07:00
int _orClauseIndex ;
2009-10-19 10:13:23 -04:00
} ;
2009-10-21 15:18:21 -04:00
2010-07-04 09:48:34 -04:00
static void checkTooLarge ( const BSONObj & newObj ) {
2010-10-11 11:26:41 -04:00
uassert ( 12522 , " $ operator made object too large " , newObj . objsize ( ) < = BSONObjMaxUserSize ) ;
2010-07-04 09:48:34 -04:00
}
2011-01-04 00:40:41 -05:00
/* note: this is only (as-is) called for
2010-07-04 09:48:34 -04:00
2010-07-04 09:28:11 -04:00
- not multi
- not mods is indexed
- not upsert
*/
2011-01-04 00:40:41 -05:00
static UpdateResult _updateById ( bool isOperatorUpdate , int idIdxNo , ModSet * mods , int profile , NamespaceDetails * d ,
2010-07-04 09:28:11 -04:00
NamespaceDetailsTransient * nsdt ,
2011-01-04 00:40:41 -05:00
bool god , const char * ns ,
const BSONObj & updateobj , BSONObj patternOrig , bool logop , OpDebug & debug ) {
2011-06-15 17:28:52 -04:00
2010-07-04 09:28:11 -04:00
DiskLoc loc ;
{
IndexDetails & i = d - > idx ( idIdxNo ) ;
2011-04-22 18:16:43 -04:00
BSONObj key = i . getKeyFromQuery ( patternOrig ) ;
loc = i . idxInterface ( ) . findSingle ( i , i . head , key ) ;
2011-01-04 00:40:41 -05:00
if ( loc . isNull ( ) ) {
2010-07-04 09:28:11 -04:00
// no upsert support in _updateById yet, so we are done.
return UpdateResult ( 0 , 0 , 0 ) ;
}
}
Record * r = loc . rec ( ) ;
2011-01-04 00:40:41 -05:00
2011-06-15 17:28:52 -04:00
if ( ! r - > likelyInPhysicalMemory ( ) ) {
{
auto_ptr < RWLockRecursive : : Shared > lk ( new RWLockRecursive : : Shared ( MongoFile : : mmmutex ) ) ;
2011-06-20 10:45:49 -04:00
dbtempreleasewritelock t ;
2011-06-15 17:28:52 -04:00
r - > touch ( ) ;
lk . reset ( 0 ) ; // we have to release mmmutex before we can re-acquire dbmutex
}
{
// we need to re-find in case something changed
2011-08-29 14:38:31 -04:00
d = nsdetails ( ns ) ;
if ( ! d ) {
// dropped
return UpdateResult ( 0 , 0 , 0 ) ;
}
nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
2011-06-15 17:28:52 -04:00
IndexDetails & i = d - > idx ( idIdxNo ) ;
BSONObj key = i . getKeyFromQuery ( patternOrig ) ;
loc = i . idxInterface ( ) . findSingle ( i , i . head , key ) ;
if ( loc . isNull ( ) ) {
// no upsert support in _updateById yet, so we are done.
return UpdateResult ( 0 , 0 , 0 ) ;
}
r = loc . rec ( ) ;
}
}
2010-07-04 09:28:11 -04:00
/* look for $inc etc. note as listed here, all fields to inc must be this type, you can't set some
regular ones at the moment . */
2011-01-04 00:40:41 -05:00
if ( isOperatorUpdate ) {
const BSONObj & onDisk = loc . obj ( ) ;
2010-07-04 09:28:11 -04:00
auto_ptr < ModSetState > mss = mods - > prepare ( onDisk ) ;
2011-01-04 00:40:41 -05:00
2010-07-04 09:28:11 -04:00
if ( mss - > canApplyInPlace ( ) ) {
2010-11-02 20:15:31 -04:00
mss - > applyModsInPlace ( true ) ;
2010-07-04 09:28:11 -04:00
DEBUGUPDATE ( " \t \t \t updateById doing in place update " ) ;
2011-01-04 00:40:41 -05:00
}
2010-07-04 09:28:11 -04:00
else {
BSONObj newObj = mss - > createNewFromMods ( ) ;
2010-07-04 09:48:34 -04:00
checkTooLarge ( newObj ) ;
2010-07-04 09:28:11 -04:00
assert ( nsdt ) ;
2011-04-03 19:10:36 -04:00
theDataFileMgr . updateRecord ( ns , d , nsdt , r , loc , newObj . objdata ( ) , newObj . objsize ( ) , debug ) ;
2010-07-04 09:28:11 -04:00
}
2011-01-04 00:40:41 -05:00
2010-07-04 09:28:11 -04:00
if ( logop ) {
DEV assert ( mods - > size ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-07-04 09:28:11 -04:00
BSONObj pattern = patternOrig ;
if ( mss - > haveArrayDepMod ( ) ) {
BSONObjBuilder patternBuilder ;
patternBuilder . appendElements ( pattern ) ;
mss - > appendSizeSpecForArrayDepMods ( patternBuilder ) ;
2011-01-04 00:40:41 -05:00
pattern = patternBuilder . obj ( ) ;
2010-07-04 09:28:11 -04:00
}
2011-01-04 00:40:41 -05:00
2010-07-04 09:28:11 -04:00
if ( mss - > needOpLogRewrite ( ) ) {
DEBUGUPDATE ( " \t rewrite update: " < < mss - > getOpLogRewrite ( ) ) ;
logOp ( " u " , ns , mss - > getOpLogRewrite ( ) , & pattern ) ;
}
else {
logOp ( " u " , ns , updateobj , & pattern ) ;
}
}
return UpdateResult ( 1 , 1 , 1 ) ;
} // end $operator update
2011-01-04 00:40:41 -05:00
2010-07-04 09:28:11 -04:00
// regular update
BSONElementManipulator : : lookForTimestamps ( updateobj ) ;
checkNoMods ( updateobj ) ;
assert ( nsdt ) ;
2010-12-12 11:19:50 -05:00
theDataFileMgr . updateRecord ( ns , d , nsdt , r , loc , updateobj . objdata ( ) , updateobj . objsize ( ) , debug ) ;
2010-07-04 09:28:11 -04:00
if ( logop ) {
2010-12-12 11:19:50 -05:00
logOp ( " u " , ns , updateobj , & patternOrig ) ;
2010-07-04 09:28:11 -04:00
}
return UpdateResult ( 1 , 0 , 1 ) ;
}
2011-01-04 00:40:41 -05:00
2010-08-03 09:47:03 -04:00
UpdateResult _updateObjects ( bool god , const char * ns , const BSONObj & updateobj , BSONObj patternOrig , bool upsert , bool multi , bool logop , OpDebug & debug , RemoveSaver * rs ) {
2010-03-08 23:33:10 -05:00
DEBUGUPDATE ( " update: " < < ns < < " update: " < < updateobj < < " query: " < < patternOrig < < " upsert: " < < upsert < < " multi: " < < multi ) ;
2010-07-04 09:28:11 -04:00
Client & client = cc ( ) ;
int profile = client . database ( ) - > profile ;
2011-05-07 17:00:57 -04:00
debug . updateobj = updateobj ;
2011-01-04 00:40:41 -05:00
2011-07-18 15:25:08 -07:00
// idea with these here it to make them loop invariant for multi updates, and thus be a bit faster for that case
2011-07-31 08:31:44 -07:00
// The pointers may be left invalid on a failed or terminal yield recovery.
2010-02-04 15:46:11 -05:00
NamespaceDetails * d = nsdetails ( ns ) ; // can be null if an upsert...
2010-02-03 17:31:52 -05:00
NamespaceDetailsTransient * nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
2011-01-04 00:40:41 -05:00
2010-01-25 17:23:37 -05:00
auto_ptr < ModSet > mods ;
2011-05-23 10:49:50 -04:00
bool isOperatorUpdate = updateobj . firstElementFieldName ( ) [ 0 ] = = ' $ ' ;
2010-01-26 12:19:40 -05:00
int modsIsIndexed = false ; // really the # of indexes
2011-01-04 00:40:41 -05:00
if ( isOperatorUpdate ) {
2011-01-14 16:26:26 -08:00
if ( d & & d - > indexBuildInProgress ) {
2010-02-04 14:25:49 -05:00
set < string > bgKeys ;
2011-01-14 16:26:26 -08:00
d - > inProgIdx ( ) . keyPattern ( ) . getFieldNames ( bgKeys ) ;
2010-02-04 14:25:49 -05:00
mods . reset ( new ModSet ( updateobj , nsdt - > indexKeys ( ) , & bgKeys ) ) ;
}
else {
mods . reset ( new ModSet ( updateobj , nsdt - > indexKeys ( ) ) ) ;
}
2010-01-25 17:23:37 -05:00
modsIsIndexed = mods - > isIndexed ( ) ;
}
2011-07-15 13:59:06 -04:00
if ( ! multi & & isSimpleIdQuery ( patternOrig ) & & d & & ! modsIsIndexed ) {
2010-07-04 09:28:11 -04:00
int idxNo = d - > findIdIndex ( ) ;
if ( idxNo > = 0 ) {
2011-05-07 17:00:57 -04:00
debug . idhack = true ;
2011-07-15 13:59:06 -04:00
UpdateResult result = _updateById ( isOperatorUpdate , idxNo , mods . get ( ) , profile , d , nsdt , god , ns , updateobj , patternOrig , logop , debug ) ;
2011-07-15 20:48:40 -04:00
if ( result . existing | | ! upsert ) {
2011-07-15 13:59:06 -04:00
return result ;
}
2011-07-15 20:48:40 -04:00
else if ( upsert & & ! isOperatorUpdate & & ! logop ) {
// this handles repl inserts
2011-07-15 13:59:06 -04:00
checkNoMods ( updateobj ) ;
debug . upsert = true ;
BSONObj no = updateobj ;
theDataFileMgr . insertWithObjMod ( ns , no , god ) ;
return UpdateResult ( 0 , 0 , 1 , no ) ;
}
2010-07-04 09:28:11 -04:00
}
}
2009-10-21 22:54:11 -04:00
int numModded = 0 ;
2010-05-08 22:18:43 -04:00
long long nscanned = 0 ;
2010-06-02 21:24:04 -07:00
shared_ptr < MultiCursor : : CursorOp > opPtr ( new UpdateOp ( mods . get ( ) & & mods - > hasDynamicArray ( ) ) ) ;
2010-07-07 19:35:26 -07:00
shared_ptr < MultiCursor > c ( new MultiCursor ( ns , patternOrig , BSONObj ( ) , opPtr , true ) ) ;
2011-01-04 00:40:41 -05:00
2011-07-18 15:25:08 -07:00
d = nsdetails ( ns ) ;
nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
2011-09-16 14:19:31 -04:00
bool autoDedup = c - > autoDedup ( ) ;
2011-07-18 15:25:08 -07:00
2011-05-07 13:03:56 -04:00
if ( c - > ok ( ) ) {
set < DiskLoc > seenObjects ;
MatchDetails details ;
auto_ptr < ClientCursor > cc ;
do {
nscanned + + ;
2010-05-24 22:00:25 -07:00
2011-05-07 13:03:56 -04:00
bool atomic = c - > matcher ( ) - > docMatcher ( ) . atomic ( ) ;
2011-06-15 17:28:52 -04:00
2011-07-19 00:30:40 -07:00
if ( ! atomic ) {
// *****************
if ( cc . get ( ) = = 0 ) {
shared_ptr < Cursor > cPtr = c ;
cc . reset ( new ClientCursor ( QueryOption_NoCursorTimeout , cPtr , ns ) ) ;
}
bool didYield ;
if ( ! cc - > yieldSometimes ( ClientCursor : : WillNeed , & didYield ) ) {
cc . release ( ) ;
break ;
}
if ( ! c - > ok ( ) ) {
break ;
}
2011-07-18 15:25:08 -07:00
2011-07-19 00:30:40 -07:00
if ( didYield ) {
d = nsdetails ( ns ) ;
nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
}
// *****************
2011-07-18 15:25:08 -07:00
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
// May have already matched in UpdateOp, but do again to get details set correctly
if ( ! c - > matcher ( ) - > matchesCurrent ( c . get ( ) , & details ) ) {
c - > advance ( ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( nscanned % 256 = = 0 & & ! atomic ) {
if ( cc . get ( ) = = 0 ) {
shared_ptr < Cursor > cPtr = c ;
cc . reset ( new ClientCursor ( QueryOption_NoCursorTimeout , cPtr , ns ) ) ;
}
if ( ! cc - > yield ( ) ) {
cc . release ( ) ;
// TODO should we assert or something?
break ;
}
if ( ! c - > ok ( ) ) {
break ;
}
2011-07-18 15:25:08 -07:00
d = nsdetails ( ns ) ;
nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
2010-08-02 22:27:53 -07:00
}
2011-05-07 13:03:56 -04:00
continue ;
2009-10-30 15:51:23 -04:00
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
Record * r = c - > _current ( ) ;
DiskLoc loc = c - > currLoc ( ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
// TODO Maybe this is unnecessary since we have seenObjects
2011-09-16 14:19:31 -04:00
if ( c - > getsetdup ( loc ) & & autoDedup ) {
2011-05-07 13:03:56 -04:00
c - > advance ( ) ;
continue ;
2009-10-19 10:13:23 -04:00
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
BSONObj js ( r ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
BSONObj pattern = patternOrig ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( logop ) {
BSONObjBuilder idPattern ;
BSONElement id ;
// NOTE: If the matching object lacks an id, we'll log
// with the original pattern. This isn't replay-safe.
// It might make sense to suppress the log instead
// if there's no id.
if ( js . getObjectID ( id ) ) {
idPattern . append ( id ) ;
pattern = idPattern . obj ( ) ;
}
else {
uassert ( 10157 , " multi-update requires all modified objects to have an _id " , ! multi ) ;
}
2010-05-24 22:00:25 -07:00
}
2011-01-04 00:40:41 -05:00
2011-05-07 17:00:57 -04:00
if ( profile & & ! multi )
2011-05-09 09:40:48 -04:00
debug . nscanned = ( int ) nscanned ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
/* look for $inc etc. note as listed here, all fields to inc must be this type, you can't set some
regular ones at the moment . */
if ( isOperatorUpdate ) {
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( multi ) {
c - > advance ( ) ; // go to next record in case this one moves
2011-09-16 14:19:31 -04:00
if ( autoDedup & & seenObjects . count ( loc ) )
2011-05-07 13:03:56 -04:00
continue ;
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
const BSONObj & onDisk = loc . obj ( ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
ModSet * useMods = mods . get ( ) ;
bool forceRewrite = false ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
auto_ptr < ModSet > mymodset ;
2011-06-14 22:29:55 -07:00
if ( details . _elemMatchKey & & mods - > hasDynamicArray ( ) ) {
useMods = mods - > fixDynamicArray ( details . _elemMatchKey ) ;
2011-05-07 13:03:56 -04:00
mymodset . reset ( useMods ) ;
forceRewrite = true ;
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
auto_ptr < ModSetState > mss = useMods - > prepare ( onDisk ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
bool indexHack = multi & & ( modsIsIndexed | | ! mss - > canApplyInPlace ( ) ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( indexHack ) {
if ( cc . get ( ) )
cc - > updateLocation ( ) ;
else
c - > noteLocation ( ) ;
2010-05-24 22:00:25 -07:00
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( modsIsIndexed < = 0 & & mss - > canApplyInPlace ( ) ) {
mss - > applyModsInPlace ( true ) ; // const_cast<BSONObj&>(onDisk) );
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
DEBUGUPDATE ( " \t \t \t doing in place update " ) ;
2011-05-07 17:00:57 -04:00
if ( profile & & ! multi )
debug . fastmod = true ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
if ( modsIsIndexed ) {
seenObjects . insert ( loc ) ;
}
2011-09-16 22:42:02 -04:00
d - > paddingFits ( ) ;
2010-05-08 22:05:36 -04:00
}
2010-05-24 22:00:25 -07:00
else {
2011-05-07 13:03:56 -04:00
if ( rs )
rs - > goingToDelete ( onDisk ) ;
BSONObj newObj = mss - > createNewFromMods ( ) ;
checkTooLarge ( newObj ) ;
DiskLoc newLoc = theDataFileMgr . updateRecord ( ns , d , nsdt , r , loc , newObj . objdata ( ) , newObj . objsize ( ) , debug ) ;
2011-09-16 14:19:31 -04:00
if ( newLoc ! = loc | | modsIsIndexed ) {
// log() << "Moved obj " << newLoc.obj()["_id"] << " from " << loc << " to " << newLoc << endl;
2011-05-07 13:03:56 -04:00
// object moved, need to make sure we don' get again
seenObjects . insert ( newLoc ) ;
}
2010-05-24 22:00:25 -07:00
}
2011-05-07 13:03:56 -04:00
if ( logop ) {
DEV assert ( mods - > size ( ) ) ;
if ( mss - > haveArrayDepMod ( ) ) {
BSONObjBuilder patternBuilder ;
patternBuilder . appendElements ( pattern ) ;
mss - > appendSizeSpecForArrayDepMods ( patternBuilder ) ;
pattern = patternBuilder . obj ( ) ;
}
if ( forceRewrite | | mss - > needOpLogRewrite ( ) ) {
DEBUGUPDATE ( " \t rewrite update: " < < mss - > getOpLogRewrite ( ) ) ;
logOp ( " u " , ns , mss - > getOpLogRewrite ( ) , & pattern ) ;
}
else {
logOp ( " u " , ns , updateobj , & pattern ) ;
}
2010-05-24 22:00:25 -07:00
}
2011-05-07 13:03:56 -04:00
numModded + + ;
if ( ! multi )
return UpdateResult ( 1 , 1 , numModded ) ;
if ( indexHack )
c - > checkLocation ( ) ;
if ( nscanned % 64 = = 0 & & ! atomic ) {
if ( cc . get ( ) = = 0 ) {
shared_ptr < Cursor > cPtr = c ;
cc . reset ( new ClientCursor ( QueryOption_NoCursorTimeout , cPtr , ns ) ) ;
}
if ( ! cc - > yield ( ) ) {
cc . release ( ) ;
break ;
}
if ( ! c - > ok ( ) ) {
break ;
}
2011-07-18 15:25:08 -07:00
d = nsdetails ( ns ) ;
nsdt = & NamespaceDetailsTransient : : get_w ( ns ) ;
2010-08-02 22:27:53 -07:00
}
2011-01-04 00:40:41 -05:00
2011-08-01 11:15:45 -04:00
getDur ( ) . commitIfNeeded ( ) ;
2011-01-10 16:24:27 -05:00
2011-05-07 13:03:56 -04:00
continue ;
}
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
uassert ( 10158 , " multi update only works with $ operators " , ! multi ) ;
2011-01-04 00:40:41 -05:00
2011-05-07 13:03:56 -04:00
BSONElementManipulator : : lookForTimestamps ( updateobj ) ;
checkNoMods ( updateobj ) ;
theDataFileMgr . updateRecord ( ns , d , nsdt , r , loc , updateobj . objdata ( ) , updateobj . objsize ( ) , debug , god ) ;
if ( logop ) {
DEV wassert ( ! god ) ; // god doesn't get logged, this would be bad.
logOp ( " u " , ns , updateobj , & pattern ) ;
}
return UpdateResult ( 1 , 0 , 1 ) ;
} while ( c - > ok ( ) ) ;
} // endif
2011-01-04 00:40:41 -05:00
2009-10-21 22:54:11 -04:00
if ( numModded )
return UpdateResult ( 1 , 1 , numModded ) ;
2009-10-19 10:13:23 -04:00
if ( profile )
2011-05-09 09:40:48 -04:00
debug . nscanned = ( int ) nscanned ;
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
if ( upsert ) {
2011-05-23 10:49:50 -04:00
if ( updateobj . firstElementFieldName ( ) [ 0 ] = = ' $ ' ) {
2009-10-19 10:13:23 -04:00
/* upsert of an $inc. build a default */
2010-02-12 14:43:27 -05:00
BSONObj newObj = mods - > createNewFromQuery ( patternOrig ) ;
2011-07-01 17:38:26 -04:00
checkNoMods ( newObj ) ;
2011-05-07 17:00:57 -04:00
debug . fastmodinsert = true ;
2011-05-29 00:31:17 -04:00
theDataFileMgr . insertWithObjMod ( ns , newObj , god ) ;
2009-10-19 10:13:23 -04:00
if ( logop )
logOp ( " i " , ns , newObj ) ;
2011-01-04 00:40:41 -05:00
2010-06-24 11:24:54 -04:00
return UpdateResult ( 0 , 1 , 1 , newObj ) ;
2009-10-19 10:13:23 -04:00
}
2009-12-28 16:43:43 -05:00
uassert ( 10159 , " multi update only works with $ operators " , ! multi ) ;
2010-01-25 17:23:37 -05:00
checkNoMods ( updateobj ) ;
2011-05-07 17:00:57 -04:00
debug . upsert = true ;
2010-01-25 17:23:37 -05:00
BSONObj no = updateobj ;
2011-05-29 00:31:17 -04:00
theDataFileMgr . insertWithObjMod ( ns , no , god ) ;
2009-10-19 10:13:23 -04:00
if ( logop )
2010-01-25 17:23:37 -05:00
logOp ( " i " , ns , no ) ;
2010-06-24 11:24:54 -04:00
return UpdateResult ( 0 , 0 , 1 , no ) ;
2009-10-19 10:13:23 -04:00
}
2011-09-18 17:23:28 -04:00
return UpdateResult ( 0 , isOperatorUpdate , 0 ) ;
2009-10-19 10:13:23 -04:00
}
2011-01-04 00:40:41 -05:00
2010-04-28 08:25:56 -04:00
UpdateResult updateObjects ( const char * ns , const BSONObj & updateobj , BSONObj patternOrig , bool upsert , bool multi , bool logop , OpDebug & debug ) {
uassert ( 10155 , " cannot update reserved $ collection " , strchr ( ns , ' $ ' ) = = 0 ) ;
if ( strstr ( ns , " .system. " ) ) {
/* dm: it's very important that system.indexes is never updated as IndexDetails has pointers into it */
2011-01-11 22:38:18 -05:00
uassert ( 10156 , str : : stream ( ) < < " cannot update system collection: " < < ns < < " q: " < < patternOrig < < " u: " < < updateobj , legalClientSystemNS ( ns , true ) ) ;
2010-04-28 08:25:56 -04:00
}
return _updateObjects ( false , ns , updateobj , patternOrig , upsert , multi , logop , debug ) ;
}
2011-01-04 00:40:41 -05:00
2009-10-19 10:13:23 -04:00
}