diff options
author | Aaron <aaron@10gen.com> | 2010-09-29 10:10:10 -0700 |
---|---|---|
committer | Aaron <aaron@10gen.com> | 2010-09-29 11:16:25 -0700 |
commit | 5e538ccb8ab0d99129c3b5c9316f2b25e877d3cf (patch) | |
tree | 8304b2ec277680d14c0a8c7b24ae270c965b3471 | |
parent | 2a4c95a3f44b12968f819e77799c8b0af9434625 (diff) | |
download | mongo-5e538ccb8ab0d99129c3b5c9316f2b25e877d3cf.tar.gz |
SERVER-394 implement rename modifier
-rw-r--r-- | db/jsobjmanipulator.h | 4 | ||||
-rw-r--r-- | db/update.cpp | 154 | ||||
-rw-r--r-- | db/update.h | 41 | ||||
-rw-r--r-- | dbtests/repltests.cpp | 54 | ||||
-rw-r--r-- | jstests/rename4.js | 113 | ||||
-rw-r--r-- | mongo.xcodeproj/project.pbxproj | 2 |
6 files changed, 354 insertions, 14 deletions
diff --git a/db/jsobjmanipulator.h b/db/jsobjmanipulator.h index fed96762f28..bd4089b1410 100644 --- a/db/jsobjmanipulator.h +++ b/db/jsobjmanipulator.h @@ -103,6 +103,10 @@ namespace mongo { } } } + + void rewriteFieldName( const char *name ) { + strcpy( data() + 1, name ); + } private: char *data() { return nonConst( _element.rawdata() ); } char *value() { return nonConst( _element.value() ); } diff --git a/db/update.cpp b/db/update.cpp index 41cf9c35bd6..186440ce531 100644 --- a/db/update.cpp +++ b/db/update.cpp @@ -31,7 +31,7 @@ namespace mongo { const char* Mod::modNames[] = { "$inc", "$set", "$push", "$pushAll", "$pull", "$pullAll" , "$pop", "$unset" , - "$bitand" , "$bitor" , "$bit" , "$addToSet" }; + "$bitand" , "$bitor" , "$bit" , "$addToSet", "$rename", "$rename" }; unsigned Mod::modNamesNum = sizeof(Mod::modNames)/sizeof(char*); bool Mod::_pullElementMatch( BSONElement& toMatch ) const { @@ -85,6 +85,10 @@ namespace mongo { template< class Builder > void Mod::apply( Builder& b , BSONElement in , ModState& ms ) const { + if ( ms.dontApply ) { + return; + } + switch ( op ){ case INC: { @@ -307,6 +311,15 @@ namespace mongo { break; } + case RENAME_FROM: { + break; + } + + case RENAME_TO: { + b.appendAs( ms.newVal , shortFieldName, ms.newVal ); + break; + } + default: stringstream ss; ss << "Mod::apply can't handle type: " << op; @@ -314,6 +327,48 @@ namespace mongo { } } + // -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(); + } + + bool nameRewritePossible( const char *a, const char *b ) { + const char *ar = strrchr( a, '.' ); + const char *br = strrchr( b, '.' ); + if ( ar && br ) { + if ( ( ar - a ) != ( br - b ) ) { + return false; + } + if ( strncmp( a, b, ar - a ) != 0 ) { + return false; + } + if ( strlen( ar ) != strlen( br ) ) { + return false; + } + } else if ( !ar && !br ) { + if ( strlen( a ) != strlen( b ) ) { + return false; + } + } else { + return false; + } + return true; + } + auto_ptr<ModSetState> ModSet::prepare(const BSONObj &obj) const { DEBUGUPDATE( "\t start prepare" ); ModSetState * mss = new ModSetState( obj ); @@ -330,6 +385,28 @@ namespace mongo { ms.m = &m; ms.old = e; + 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; + } + + 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() ); + mss->amIInPlacePossible( target == 0 && nameRewritePossible( m.renameFrom(), m.fieldName ) ); + } else { + ms.dontApply = true; + } + continue; + } + if ( e.eoo() ) { mss->amIInPlacePossible( m.op == Mod::UNSET ); continue; @@ -425,6 +502,10 @@ namespace mongo { } void ModState::appendForOpLog( BSONObjBuilder& b ) const { + if ( dontApply ) { + return; + } + if ( incType ){ DEBUGUPDATE( "\t\t\t\t\t appendForOpLog inc fieldname: " << m->fieldName << " short:" << m->shortFieldName ); BSONObjBuilder bb( b.subobjStart( "$set" ) ); @@ -432,16 +513,32 @@ namespace mongo { bb.done(); return; } + + if ( m->op == Mod::RENAME_FROM ) { + DEBUGUPDATE( "\t\t\t\t\t appendForOpLog RENAME_FROM fielName:" << m->fieldName ); + BSONObjBuilder bb( b.subobjStart( "$unset" ) ); + bb.append( m->fieldName, 1 ); + bb.done(); + return; + } + + if ( m->op == Mod::RENAME_TO ) { + DEBUGUPDATE( "\t\t\t\t\t appendForOpLog RENAME_TO fielName:" << m->fieldName ); + BSONObjBuilder bb( b.subobjStart( "$set" ) ); + bb.appendAs( newVal, m->fieldName ); + return; + } 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 ) ); - if ( fixed ) + if ( fixed ) { bb.appendAs( *fixed , m->fieldName ); - else + } else { bb.appendAs( m->elt , m->fieldName ); + } bb.done(); } @@ -456,15 +553,23 @@ namespace mongo { void ModSetState::ApplyModsInPlace() { for ( ModStateHolder::iterator i = _mods.begin(); i != _mods.end(); ++i ) { - ModState& m = i->second; + ModState& m = i->second; + if ( m.dontApply ) { + continue; + } + switch ( m.m->op ){ case Mod::UNSET: case Mod::PULL: case Mod::PULL_ALL: case Mod::ADDTOSET: + case Mod::RENAME_FROM: // this should have been handled by prepare break; // [dm] the BSONElementManipulator statements below are for replication (correct?) + case Mod::RENAME_TO: + BSONElementManipulator( m.newVal ).rewriteFieldName( m.m->shortFieldName ); + break; case Mod::INC: m.m->IncrementMe( m.old ); m.fixedOpName = "$set"; @@ -633,7 +738,7 @@ namespace mongo { BSONObj ModSetState::createNewFromMods() { BSONObjBuilder b( (int)(_obj.objsize() * 1.1) ); createNewFromMods( "" , b , _obj ); - return b.obj(); + return _newFromMods = b.obj(); } string ModSetState::toString() const { @@ -717,17 +822,44 @@ namespace mongo { 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 ) ); + if ( op == Mod::RENAME_TO ) { + uassert( 13476, "$rename target must be a string", f.type() == String ); + const char *target = f.valuestr(); + uassert( 13477, "$rename source must differ from target", strcmp( fieldName, target ) != 0 ); + uassert( 13478, "invalid mod field name, source may not be empty", fieldName[0] ); + 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; + + DEBUGUPDATE( "\t\t " << fieldName << "\t" << from.fieldName << "\t" << to.fieldName ); + continue; + } + _hasDynamicArray = _hasDynamicArray || strstr( fieldName , ".$" ) > 0; Mod m; m.init( op , f ); m.setFieldName( f.fieldName() ); - - if ( m.isIndexed( idxKeys ) || - (backgroundKeys && m.isIndexed(*backgroundKeys)) ) { - _isIndexed++; - } - + updateIsIndexed( m, idxKeys, backgroundKeys ); _mods[m.fieldName] = m; DEBUGUPDATE( "\t\t " << fieldName << "\t" << m.fieldName << "\t" << _hasDynamicArray ); diff --git a/db/update.h b/db/update.h index 306166042b3..45234bed2b3 100644 --- a/db/update.h +++ b/db/update.h @@ -32,8 +32,8 @@ namespace mongo { */ struct Mod { // See opFromStr below - // 0 1 2 3 4 5 6 7 8 9 10 11 - enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BITAND, BITOR , BIT , ADDTOSET } op; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BITAND, BITOR , BIT , ADDTOSET, RENAME_FROM, RENAME_TO } op; static const char* modNames[]; static unsigned modNamesNum; @@ -231,6 +231,10 @@ namespace mongo { } } + const char *renameFrom() const { + massert( 13492, "mod must be RENAME_TO type", op == Mod::RENAME_TO ); + return elt.fieldName(); + } }; /** @@ -327,6 +331,13 @@ namespace mongo { return Mod::ADDTOSET; } + break; + } + case 'r': { + if ( fn[2] == 'e' && fn[3] == 'n' && fn[4] == 'a' && fn[5] == 'm' && fn[6] =='e' ) { + return Mod::RENAME_TO; // with this return code we handle both RENAME_TO and RENAME_FROM + } + break; } default: break; } @@ -336,6 +347,13 @@ namespace mongo { ModSet(){} + void updateIsIndexed( const Mod &m, const set<string> &idxKeys, const set<string> *backgroundKeys ) { + if ( m.isIndexed( idxKeys ) || + (backgroundKeys && m.isIndexed(*backgroundKeys)) ) { + _isIndexed++; + } + } + public: ModSet( const BSONObj &from , @@ -403,6 +421,7 @@ namespace mongo { public: const Mod * m; BSONElement old; + BSONElement newVal; const char * fixedOpName; BSONElement * fixed; @@ -413,11 +432,14 @@ namespace mongo { double incdouble; long long inclong; + bool dontApply; + ModState(){ fixedOpName = 0; fixed = 0; pushStartSize = -1; incType = EOO; + dontApply = false; } Mod::Op op() const { @@ -429,10 +451,16 @@ namespace mongo { } bool needOpLogRewrite() const { + if ( dontApply ) + return false; + if ( fixed || fixedOpName || incType ) return true; switch( op() ){ + case Mod::RENAME_FROM: + case Mod::RENAME_TO: + return true; case Mod::BIT: case Mod::BITAND: case Mod::BITOR: @@ -483,6 +511,7 @@ namespace mongo { const BSONObj& _obj; ModStateHolder _mods; bool _inPlacePossible; + BSONObj _newFromMods; // keep this data alive, as oplog generation may depend on it ModSetState( const BSONObj& obj ) : _obj( obj ) , _inPlacePossible(true){ @@ -505,6 +534,10 @@ namespace mongo { template< class Builder > void appendNewFromMod( ModState& ms , Builder& b ){ + if ( ms.dontApply ) { + return; + } + //const Mod& m = *(ms.m); // HACK Mod& m = *((Mod*)(ms.m)); // HACK @@ -541,6 +574,10 @@ namespace mongo { b.appendAs( m.elt, m.shortFieldName ); break; } + // shouldn't see RENAME_FROM here + case Mod::RENAME_TO: + b.appendAs( ms.newVal, m.shortFieldName, ms.newVal ); + break; default: stringstream ss; ss << "unknown mod in appendNewFromMod: " << m.op; diff --git a/dbtests/repltests.cpp b/dbtests/repltests.cpp index a190dc88b12..4ae78cd8a96 100644 --- a/dbtests/repltests.cpp +++ b/dbtests/repltests.cpp @@ -955,8 +955,57 @@ namespace ReplTests { } }; - + class Rename : public Base { + public: + void doIt() const { + client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$rename:{a:'b'}}" ) ); + client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$set:{a:50}}" ) ); + } + using ReplTests::Base::check; + void check() const { + ASSERT_EQUALS( 1, count() ); + check( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) , one( fromjson( "{'_id':0}" ) ) ); + } + void reset() const { + deleteAll( ns() ); + insert( fromjson( "{'_id':0,a:3}" ) ); + } + }; + class RenameOverwrite : public Base { + public: + void doIt() const { + client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$rename:{a:'b'}}" ) ); + client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$set:{a:50}}" ) ); + } + using ReplTests::Base::check; + void check() const { + ASSERT_EQUALS( 1, count() ); + check( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) , one( fromjson( "{'_id':0}" ) ) ); + } + void reset() const { + deleteAll( ns() ); + insert( fromjson( "{'_id':0,a:3,b:100}" ) ); + } + }; + + class NoRename : public Base { + public: + void doIt() const { + client()->update( ns(), BSON( "_id" << 0 ), fromjson( "{$rename:{c:'b'},$set:{z:1}}" ) ); + } + using ReplTests::Base::check; + void check() const { + ASSERT_EQUALS( 1, count() ); + check( BSON( "_id" << 0 << "a" << 3 << "z" << 1 ) , one( fromjson( "{'_id':0}" ) ) ); + } + void reset() const { + deleteAll( ns() ); + insert( fromjson( "{'_id':0,a:3}" ) ); + } + }; + + } // namespace Idempotence class DeleteOpIsIdBased : public Base { @@ -1140,6 +1189,9 @@ namespace ReplTests { add< Idempotence::Pop >(); add< Idempotence::PopReverse >(); add< Idempotence::BitOp >(); + add< Idempotence::Rename >(); + add< Idempotence::RenameOverwrite >(); + add< Idempotence::NoRename >(); add< DeleteOpIsIdBased >(); add< DbIdsTest >(); add< MemIdsTest >(); diff --git a/jstests/rename4.js b/jstests/rename4.js new file mode 100644 index 00000000000..22c0fddfa59 --- /dev/null +++ b/jstests/rename4.js @@ -0,0 +1,113 @@ +t = db.jstests_rename4; +t.drop(); + +function c( f ) { + assert( !db.getLastError(), "error" ); + eval( f ); + assert( db.getLastError(), "no error" ); + db.resetError(); +} + +c( "t.update( {}, {$rename:{'a':'a'}} )" ); +c( "t.update( {}, {$rename:{'':'a'}} )" ); +c( "t.update( {}, {$rename:{'a':''}} )" ); +c( "t.update( {}, {$rename:{'_id':'a'}} )" ); +c( "t.update( {}, {$rename:{'a':'_id'}} )" ); +c( "t.update( {}, {$rename:{'_id.a':'b'}} )" ); +c( "t.update( {}, {$rename:{'b':'_id.a'}} )" ); +c( "t.update( {}, {$rename:{'_id.a':'_id.b'}} )" ); +c( "t.update( {}, {$rename:{'_id.b':'_id.a'}} )" ); +c( "t.update( {}, {$rename:{'.a':'b'}} )" ); +c( "t.update( {}, {$rename:{'a':'.b'}} )" ); +c( "t.update( {}, {$rename:{'a.':'b'}} )" ); +c( "t.update( {}, {$rename:{'a':'b.'}} )" ); +c( "t.update( {}, {$rename:{'a.b':'a'}} )" ); +c( "t.update( {}, {$rename:{'a.$':'b'}} )" ); +c( "t.update( {}, {$rename:{'a':'b.$'}} )" ); +c( "t.update( {}, {$set:{b:1},$rename:{'a':'b'}} )" ); +c( "t.update( {}, {$rename:{'a':'b'},$set:{b:1}} )" ); +c( "t.update( {}, {$rename:{'a':'b'},$set:{a:1}} )" ); +c( "t.update( {}, {$set:{'b.c':1},$rename:{'a':'b'}} )" ); +c( "t.update( {}, {$set:{b:1},$rename:{'a':'b.c'}} )" ); +c( "t.update( {}, {$rename:{'a':'b'},$set:{'b.c':1}} )" ); +c( "t.update( {}, {$rename:{'a':'b.c'},$set:{b:1}} )" ); + +t.save( {a:[1],b:{c:[1]},d:[{e:1}],f:1} ); +c( "t.update( {}, {$rename:{'a.0':'f'}} )" ); +c( "t.update( {}, {$rename:{'a.0':'g'}} )" ); +c( "t.update( {}, {$rename:{'f':'a.0'}} )" ); +c( "t.update( {}, {$rename:{'b.c.0':'f'}} )" ); +c( "t.update( {}, {$rename:{'f':'b.c.0'}} )" ); +c( "t.update( {}, {$rename:{'d.e':'d.f'}} )" ); +c( "t.update( {}, {$rename:{'d.e':'f'}} )" ); +c( "t.update( {}, {$rename:{'d.f':'d.e'}} )" ); +c( "t.update( {}, {$rename:{'f':'d.e'}} )" ); +c( "t.update( {}, {$rename:{'d.0.e':'d.f'}} )" ); +c( "t.update( {}, {$rename:{'d.0.e':'f'}} )" ); +c( "t.update( {}, {$rename:{'d.f':'d.0.e'}} )" ); +c( "t.update( {}, {$rename:{'f':'d.0.e'}} )" ); +c( "t.update( {}, {$rename:{'f.g':'a'}} )" ); +c( "t.update( {}, {$rename:{'a':'f.g'}} )" ); + +function v( start, mod, expected ) { + t.remove(); + t.save( start ); + t.update( {}, mod ); + assert( !db.getLastError() ); + var got = t.findOne(); + delete got._id; + assert.eq( expected, got ); +} + +v( {a:1}, {$rename:{a:'b'}}, {b:1} ); +v( {a:1}, {$rename:{a:'bb'}}, {bb:1} ); +v( {b:1}, {$rename:{b:'a'}}, {a:1} ); +v( {bb:1}, {$rename:{bb:'a'}}, {a:1} ); +v( {a:{y:1}}, {$rename:{'a.y':'a.z'}}, {a:{z:1}} ); +v( {a:{yy:1}}, {$rename:{'a.yy':'a.z'}}, {a:{z:1}} ); +v( {a:{z:1}}, {$rename:{'a.z':'a.y'}}, {a:{y:1}} ); +v( {a:{zz:1}}, {$rename:{'a.zz':'a.y'}}, {a:{y:1}} ); +v( {a:{c:1}}, {$rename:{a:'b'}}, {b:{c:1}} ); +v( {aa:{c:1}}, {$rename:{aa:'b'}}, {b:{c:1}} ); +v( {a:1,b:2}, {$rename:{a:'b'}}, {b:1} ); +v( {aa:1,b:2}, {$rename:{aa:'b'}}, {b:1} ); +v( {a:1,bb:2}, {$rename:{a:'bb'}}, {bb:1} ); +v( {a:1}, {$rename:{a:'b.c'}}, {b:{c:1}} ); +v( {aa:1}, {$rename:{aa:'b.c'}}, {b:{c:1}} ); +v( {a:1,b:{}}, {$rename:{a:'b.c'}}, {b:{c:1}} ); +v( {aa:1,b:{}}, {$rename:{aa:'b.c'}}, {b:{c:1}} ); +v( {a:1}, {$rename:{b:'c'}}, {a:1} ); +v( {aa:1}, {$rename:{b:'c'}}, {aa:1} ); +v( {}, {$rename:{b:'c'}}, {} ); +v( {a:{b:1,c:2}}, {$rename:{'a.b':'d'}}, {a:{c:2},d:1} ); +v( {a:{bb:1,c:2}}, {$rename:{'a.bb':'d'}}, {a:{c:2},d:1} ); +v( {a:{b:1}}, {$rename:{'a.b':'d'}}, {a:{},d:1} ); +v( {a:[5]}, {$rename:{a:'b'}}, {b:[5]} ); +v( {aa:[5]}, {$rename:{aa:'b'}}, {b:[5]} ); +v( {'0':1}, {$rename:{'0':'5'}}, {'5':1} ); +v( {a:1,b:2}, {$rename:{a:'c'},$set:{b:5}}, {c:1,b:5} ); +v( {aa:1,b:2}, {$rename:{aa:'c'},$set:{b:5}}, {b:5,c:1} ); +v( {a:1,b:2}, {$rename:{z:'c'},$set:{b:5}}, {a:1,b:5} ); +v( {aa:1,b:2}, {$rename:{z:'c'},$set:{b:5}}, {aa:1,b:5} ); + +// invalid target, but missing source +v( {a:1,c:4}, {$rename:{b:'c.d'}}, {a:1,c:4} ); + +// check index +t.drop(); +t.ensureIndex( {a:1} ); + +function l( start, mod, expected ) { + t.remove(); + t.save( start ); + t.update( {}, mod ); + assert( !db.getLastError() ); + var got = t.find().hint( {a:1} ).next(); + delete got._id; + assert.eq( expected, got ); +} + +l( {a:1}, {$rename:{a:'b'}}, {b:1} ); +l( {a:1}, {$rename:{a:'bb'}}, {bb:1} ); +l( {b:1}, {$rename:{b:'a'}}, {a:1} ); +l( {bb:1}, {$rename:{bb:'a'}}, {a:1} ); diff --git a/mongo.xcodeproj/project.pbxproj b/mongo.xcodeproj/project.pbxproj index e84420fd8b4..e4f2896729e 100644 --- a/mongo.xcodeproj/project.pbxproj +++ b/mongo.xcodeproj/project.pbxproj @@ -445,6 +445,7 @@ 936B895C0F4C899400934AF2 /* md5.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = md5.hpp; sourceTree = "<group>"; }; 936B895E0F4C899400934AF2 /* message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = message.cpp; sourceTree = "<group>"; }; 936B895F0F4C899400934AF2 /* message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = message.h; sourceTree = "<group>"; }; + 936D5EC71251BA2A0015722C /* rename4.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = rename4.js; sourceTree = "<group>"; }; 9378842D11C6C987007E85F5 /* indexh.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = indexh.js; sourceTree = "<group>"; }; 937884E811C80B22007E85F5 /* or8.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = or8.js; sourceTree = "<group>"; }; 937C493311C0358D00836543 /* or7.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = or7.js; sourceTree = "<group>"; }; @@ -840,6 +841,7 @@ 934BEB9A10DFFA9600178102 /* jstests */ = { isa = PBXGroup; children = ( + 936D5EC71251BA2A0015722C /* rename4.js */, 93B771FE124024A4007C8F0C /* evald.js */, 93B77135123F0BB2007C8F0C /* indexk.js */, 938D493D122C99A3001D83EE /* ne3.js */, |