00001
00002
00019 #include "../pch.h"
00020 #include "jsobj.h"
00021 #include "../util/embedded_builder.h"
00022 #include "matcher.h"
00023
00024 namespace mongo {
00025
00026 class ModState;
00027 class ModSetState;
00028
00029
00030
00031
00032
00033 struct Mod {
00034
00035
00036 enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BITAND, BITOR , BIT , ADDTOSET, RENAME_FROM, RENAME_TO } op;
00037
00038 static const char* modNames[];
00039 static unsigned modNamesNum;
00040
00041 const char *fieldName;
00042 const char *shortFieldName;
00043
00044 BSONElement elt;
00045 boost::shared_ptr<Matcher> matcher;
00046 bool matcherOnPrimitive;
00047
00048 void init( Op o , BSONElement& e ) {
00049 op = o;
00050 elt = e;
00051 if ( op == PULL && e.type() == Object ) {
00052 BSONObj t = e.embeddedObject();
00053 if ( t.firstElement().getGtLtOp() == 0 ) {
00054 matcher.reset( new Matcher( t ) );
00055 matcherOnPrimitive = false;
00056 }
00057 else {
00058 matcher.reset( new Matcher( BSON( "" << t ) ) );
00059 matcherOnPrimitive = true;
00060 }
00061 }
00062 }
00063
00064 void setFieldName( const char * s ) {
00065 fieldName = s;
00066 shortFieldName = strrchr( fieldName , '.' );
00067 if ( shortFieldName )
00068 shortFieldName++;
00069 else
00070 shortFieldName = fieldName;
00071 }
00072
00076 void incrementMe( BSONElement& in ) const {
00077 BSONElementManipulator manip( in );
00078 switch ( in.type() ) {
00079 case NumberDouble:
00080 manip.setNumber( elt.numberDouble() + in.numberDouble() );
00081 break;
00082 case NumberLong:
00083 manip.setLong( elt.numberLong() + in.numberLong() );
00084 break;
00085 case NumberInt:
00086 manip.setInt( elt.numberInt() + in.numberInt() );
00087 break;
00088 default:
00089 assert(0);
00090 }
00091 }
00092 void IncrementMe( BSONElement& in ) const {
00093 BSONElementManipulator manip( in );
00094 switch ( in.type() ) {
00095 case NumberDouble:
00096 manip.SetNumber( elt.numberDouble() + in.numberDouble() );
00097 break;
00098 case NumberLong:
00099 manip.SetLong( elt.numberLong() + in.numberLong() );
00100 break;
00101 case NumberInt:
00102 manip.SetInt( elt.numberInt() + in.numberInt() );
00103 break;
00104 default:
00105 assert(0);
00106 }
00107 }
00108
00109 template< class Builder >
00110 void appendIncremented( Builder& bb , const BSONElement& in, ModState& ms ) const;
00111
00112 bool operator<( const Mod &other ) const {
00113 return strcmp( fieldName, other.fieldName ) < 0;
00114 }
00115
00116 bool arrayDep() const {
00117 switch (op) {
00118 case PUSH:
00119 case PUSH_ALL:
00120 case POP:
00121 return true;
00122 default:
00123 return false;
00124 }
00125 }
00126
00127 static bool isIndexed( const string& fullName , const set<string>& idxKeys ) {
00128 const char * fieldName = fullName.c_str();
00129
00130 for( const char *dot = strchr( fieldName, '.' ); dot; dot = strchr( dot + 1, '.' ) )
00131 if ( idxKeys.count( string( fieldName, dot - fieldName ) ) )
00132 return true;
00133
00134
00135 if ( idxKeys.count(fullName) )
00136 return true;
00137
00138 set< string >::const_iterator j = idxKeys.upper_bound( fullName );
00139 if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[fullName.size()] == '.' )
00140 return true;
00141
00142 return false;
00143 }
00144
00145 bool isIndexed( const set<string>& idxKeys ) const {
00146 string fullName = fieldName;
00147
00148 if ( isIndexed( fullName , idxKeys ) )
00149 return true;
00150
00151 if ( strstr( fieldName , "." ) ) {
00152
00153 StringBuilder buf( fullName.size() + 1 );
00154 for ( size_t i=0; i<fullName.size(); i++ ) {
00155 char c = fullName[i];
00156
00157 if ( c == '$' &&
00158 i > 0 && fullName[i-1] == '.' &&
00159 i+1<fullName.size() &&
00160 fullName[i+1] == '.' ) {
00161 i++;
00162 continue;
00163 }
00164
00165 buf << c;
00166
00167 if ( c != '.' )
00168 continue;
00169
00170 if ( ! isdigit( fullName[i+1] ) )
00171 continue;
00172
00173 bool possible = true;
00174 size_t j=i+2;
00175 for ( ; j<fullName.size(); j++ ) {
00176 char d = fullName[j];
00177 if ( d == '.' )
00178 break;
00179 if ( isdigit( d ) )
00180 continue;
00181 possible = false;
00182 break;
00183 }
00184
00185 if ( possible )
00186 i = j;
00187 }
00188 string x = buf.str();
00189 if ( isIndexed( x , idxKeys ) )
00190 return true;
00191 }
00192
00193 return false;
00194 }
00195
00196 template< class Builder >
00197 void apply( Builder& b , BSONElement in , ModState& ms ) const;
00198
00202 bool _pullElementMatch( BSONElement& toMatch ) const;
00203
00204 void _checkForAppending( const BSONElement& e ) const {
00205 if ( e.type() == Object ) {
00206
00207
00208
00209
00210 uassert( 12527 , "not okForStorage" , e.embeddedObject().okForStorage() );
00211 }
00212 }
00213
00214 bool isEach() const {
00215 if ( elt.type() != Object )
00216 return false;
00217 BSONElement e = elt.embeddedObject().firstElement();
00218 if ( e.type() != Array )
00219 return false;
00220 return strcmp( e.fieldName() , "$each" ) == 0;
00221 }
00222
00223 BSONObj getEach() const {
00224 return elt.embeddedObjectUserCheck().firstElement().embeddedObjectUserCheck();
00225 }
00226
00227 void parseEach( BSONElementSet& s ) const {
00228 BSONObjIterator i(getEach());
00229 while ( i.more() ) {
00230 s.insert( i.next() );
00231 }
00232 }
00233
00234 const char *renameFrom() const {
00235 massert( 13492, "mod must be RENAME_TO type", op == Mod::RENAME_TO );
00236 return elt.fieldName();
00237 }
00238 };
00239
00244 class ModSet : boost::noncopyable {
00245 typedef map<string,Mod> ModHolder;
00246 ModHolder _mods;
00247 int _isIndexed;
00248 bool _hasDynamicArray;
00249
00250 static void extractFields( map< string, BSONElement > &fields, const BSONElement &top, const string &base );
00251
00252 FieldCompareResult compare( const ModHolder::iterator &m, map< string, BSONElement >::iterator &p, const map< string, BSONElement >::iterator &pEnd ) const {
00253 bool mDone = ( m == _mods.end() );
00254 bool pDone = ( p == pEnd );
00255 assert( ! mDone );
00256 assert( ! pDone );
00257 if ( mDone && pDone )
00258 return SAME;
00259
00260 if ( mDone )
00261 return RIGHT_BEFORE;
00262 if ( pDone )
00263 return LEFT_BEFORE;
00264
00265 return compareDottedFieldNames( m->first, p->first.c_str() );
00266 }
00267
00268 bool mayAddEmbedded( map< string, BSONElement > &existing, string right ) {
00269 for( string left = EmbeddedBuilder::splitDot( right );
00270 left.length() > 0 && left[ left.length() - 1 ] != '.';
00271 left += "." + EmbeddedBuilder::splitDot( right ) ) {
00272 if ( existing.count( left ) > 0 && existing[ left ].type() != Object )
00273 return false;
00274 if ( haveModForField( left.c_str() ) )
00275 return false;
00276 }
00277 return true;
00278 }
00279 static Mod::Op opFromStr( const char *fn ) {
00280 assert( fn[0] == '$' );
00281 switch( fn[1] ) {
00282 case 'i': {
00283 if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 )
00284 return Mod::INC;
00285 break;
00286 }
00287 case 's': {
00288 if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 )
00289 return Mod::SET;
00290 break;
00291 }
00292 case 'p': {
00293 if ( fn[2] == 'u' ) {
00294 if ( fn[3] == 's' && fn[4] == 'h' ) {
00295 if ( fn[5] == 0 )
00296 return Mod::PUSH;
00297 if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' && fn[8] == 0 )
00298 return Mod::PUSH_ALL;
00299 }
00300 else if ( fn[3] == 'l' && fn[4] == 'l' ) {
00301 if ( fn[5] == 0 )
00302 return Mod::PULL;
00303 if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' && fn[8] == 0 )
00304 return Mod::PULL_ALL;
00305 }
00306 }
00307 else if ( fn[2] == 'o' && fn[3] == 'p' && fn[4] == 0 )
00308 return Mod::POP;
00309 break;
00310 }
00311 case 'u': {
00312 if ( fn[2] == 'n' && fn[3] == 's' && fn[4] == 'e' && fn[5] == 't' && fn[6] == 0 )
00313 return Mod::UNSET;
00314 break;
00315 }
00316 case 'b': {
00317 if ( fn[2] == 'i' && fn[3] == 't' ) {
00318 if ( fn[4] == 0 )
00319 return Mod::BIT;
00320 if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn[7] == 0 )
00321 return Mod::BITAND;
00322 if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 )
00323 return Mod::BITOR;
00324 }
00325 break;
00326 }
00327 case 'a': {
00328 if ( fn[2] == 'd' && fn[3] == 'd' ) {
00329
00330 if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn[7] == 'e' && fn[8] == 't' && fn[9] == 0 )
00331 return Mod::ADDTOSET;
00332
00333 }
00334 break;
00335 }
00336 case 'r': {
00337 if ( fn[2] == 'e' && fn[3] == 'n' && fn[4] == 'a' && fn[5] == 'm' && fn[6] =='e' ) {
00338 return Mod::RENAME_TO;
00339 }
00340 break;
00341 }
00342 default: break;
00343 }
00344 uassert( 10161 , "Invalid modifier specified " + string( fn ), false );
00345 return Mod::INC;
00346 }
00347
00348 ModSet() {}
00349
00350 void updateIsIndexed( const Mod &m, const set<string> &idxKeys, const set<string> *backgroundKeys ) {
00351 if ( m.isIndexed( idxKeys ) ||
00352 (backgroundKeys && m.isIndexed(*backgroundKeys)) ) {
00353 _isIndexed++;
00354 }
00355 }
00356
00357 public:
00358
00359 ModSet( const BSONObj &from ,
00360 const set<string>& idxKeys = set<string>(),
00361 const set<string>* backgroundKeys = 0
00362 );
00363
00364
00365 ModSet * fixDynamicArray( const char * elemMatchKey ) const;
00366
00367 bool hasDynamicArray() const { return _hasDynamicArray; }
00368
00373 auto_ptr<ModSetState> prepare( const BSONObj& obj ) const;
00374
00379 BSONObj createNewFromQuery( const BSONObj& query );
00380
00384 int isIndexed() const {
00385 return _isIndexed;
00386 }
00387
00388 unsigned size() const { return _mods.size(); }
00389
00390 bool haveModForField( const char *fieldName ) const {
00391 return _mods.find( fieldName ) != _mods.end();
00392 }
00393
00394 bool haveConflictingMod( const string& fieldName ) {
00395 size_t idx = fieldName.find( '.' );
00396 if ( idx == string::npos )
00397 idx = fieldName.size();
00398
00399 ModHolder::const_iterator start = _mods.lower_bound(fieldName.substr(0,idx));
00400 for ( ; start != _mods.end(); start++ ) {
00401 FieldCompareResult r = compareDottedFieldNames( fieldName , start->first );
00402 switch ( r ) {
00403 case LEFT_SUBFIELD: return true;
00404 case LEFT_BEFORE: return false;
00405 case SAME: return true;
00406 case RIGHT_BEFORE: return false;
00407 case RIGHT_SUBFIELD: return true;
00408 }
00409 }
00410 return false;
00411
00412
00413 }
00414
00415 };
00416
00420 class ModState {
00421 public:
00422 const Mod * m;
00423 BSONElement old;
00424 BSONElement newVal;
00425 BSONObj _objData;
00426
00427 const char * fixedOpName;
00428 BSONElement * fixed;
00429 int pushStartSize;
00430
00431 BSONType incType;
00432 int incint;
00433 double incdouble;
00434 long long inclong;
00435
00436 bool dontApply;
00437
00438 ModState() {
00439 fixedOpName = 0;
00440 fixed = 0;
00441 pushStartSize = -1;
00442 incType = EOO;
00443 dontApply = false;
00444 }
00445
00446 Mod::Op op() const {
00447 return m->op;
00448 }
00449
00450 const char * fieldName() const {
00451 return m->fieldName;
00452 }
00453
00454 bool needOpLogRewrite() const {
00455 if ( dontApply )
00456 return false;
00457
00458 if ( fixed || fixedOpName || incType )
00459 return true;
00460
00461 switch( op() ) {
00462 case Mod::RENAME_FROM:
00463 case Mod::RENAME_TO:
00464 return true;
00465 case Mod::BIT:
00466 case Mod::BITAND:
00467 case Mod::BITOR:
00468
00469 return false;
00470 default:
00471 return false;
00472 }
00473 }
00474
00475 void appendForOpLog( BSONObjBuilder& b ) const;
00476
00477 template< class Builder >
00478 void apply( Builder& b , BSONElement in ) {
00479 m->apply( b , in , *this );
00480 }
00481
00482 template< class Builder >
00483 void appendIncValue( Builder& b , bool useFullName ) const {
00484 const char * n = useFullName ? m->fieldName : m->shortFieldName;
00485
00486 switch ( incType ) {
00487 case NumberDouble:
00488 b.append( n , incdouble ); break;
00489 case NumberLong:
00490 b.append( n , inclong ); break;
00491 case NumberInt:
00492 b.append( n , incint ); break;
00493 default:
00494 assert(0);
00495 }
00496 }
00497
00498 string toString() const;
00499
00500 template< class Builder >
00501 void handleRename( Builder &newObjBuilder, const char *shortFieldName );
00502 };
00503
00508 class ModSetState : boost::noncopyable {
00509 struct FieldCmp {
00510 bool operator()( const string &l, const string &r ) const {
00511 return lexNumCmp( l.c_str(), r.c_str() ) < 0;
00512 }
00513 };
00514 typedef map<string,ModState,FieldCmp> ModStateHolder;
00515 const BSONObj& _obj;
00516 ModStateHolder _mods;
00517 bool _inPlacePossible;
00518 BSONObj _newFromMods;
00519
00520 ModSetState( const BSONObj& obj )
00521 : _obj( obj ) , _inPlacePossible(true) {
00522 }
00523
00527 bool amIInPlacePossible( bool inPlacePossible ) {
00528 if ( ! inPlacePossible )
00529 _inPlacePossible = false;
00530 return _inPlacePossible;
00531 }
00532
00533 template< class Builder >
00534 void createNewFromMods( const string& root , Builder& b , const BSONObj &obj );
00535
00536 template< class Builder >
00537 void _appendNewFromMods( const string& root , ModState& m , Builder& b , set<string>& onedownseen );
00538
00539 template< class Builder >
00540 void appendNewFromMod( ModState& ms , Builder& b ) {
00541 if ( ms.dontApply ) {
00542 return;
00543 }
00544
00545
00546 Mod& m = *((Mod*)(ms.m));
00547
00548 switch ( m.op ) {
00549
00550 case Mod::PUSH:
00551 case Mod::ADDTOSET: {
00552 if ( m.isEach() ) {
00553 b.appendArray( m.shortFieldName , m.getEach() );
00554 }
00555 else {
00556 BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) );
00557 arr.appendAs( m.elt, "0" );
00558 arr.done();
00559 }
00560 break;
00561 }
00562
00563 case Mod::PUSH_ALL: {
00564 b.appendAs( m.elt, m.shortFieldName );
00565 break;
00566 }
00567
00568 case Mod::UNSET:
00569 case Mod::PULL:
00570 case Mod::PULL_ALL:
00571
00572 break;
00573
00574 case Mod::INC:
00575 ms.fixedOpName = "$set";
00576 case Mod::SET: {
00577 m._checkForAppending( m.elt );
00578 b.appendAs( m.elt, m.shortFieldName );
00579 break;
00580 }
00581
00582 case Mod::RENAME_TO:
00583 ms.handleRename( b, m.shortFieldName );
00584 break;
00585 default:
00586 stringstream ss;
00587 ss << "unknown mod in appendNewFromMod: " << m.op;
00588 throw UserException( 9015, ss.str() );
00589 }
00590
00591 }
00592
00593 public:
00594
00595 bool canApplyInPlace() const {
00596 return _inPlacePossible;
00597 }
00598
00603 void applyModsInPlace( bool isOnDisk );
00604
00605 BSONObj createNewFromMods();
00606
00607
00608
00609 bool needOpLogRewrite() const {
00610 for ( ModStateHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ )
00611 if ( i->second.needOpLogRewrite() )
00612 return true;
00613 return false;
00614 }
00615
00616 BSONObj getOpLogRewrite() const {
00617 BSONObjBuilder b;
00618 for ( ModStateHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ )
00619 i->second.appendForOpLog( b );
00620 return b.obj();
00621 }
00622
00623 bool haveArrayDepMod() const {
00624 for ( ModStateHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ )
00625 if ( i->second.m->arrayDep() )
00626 return true;
00627 return false;
00628 }
00629
00630 void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const {
00631 for ( ModStateHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ ) {
00632 const ModState& m = i->second;
00633 if ( m.m->arrayDep() ) {
00634 if ( m.pushStartSize == -1 )
00635 b.appendNull( m.fieldName() );
00636 else
00637 b << m.fieldName() << BSON( "$size" << m.pushStartSize );
00638 }
00639 }
00640 }
00641
00642 string toString() const;
00643
00644 friend class ModSet;
00645 };
00646
00647 }
00648