diff options
-rw-r--r-- | jstests/sharding/array_shard_key.js | 127 | ||||
-rw-r--r-- | s/d_split.cpp | 5 | ||||
-rw-r--r-- | s/shardkey.cpp | 3 | ||||
-rw-r--r-- | s/strategy_shard.cpp | 21 | ||||
-rwxr-xr-x | shell/servers.js | 16 | ||||
-rw-r--r-- | shell/utils.js | 5 |
6 files changed, 164 insertions, 13 deletions
diff --git a/jstests/sharding/array_shard_key.js b/jstests/sharding/array_shard_key.js new file mode 100644 index 00000000000..1ea61e8d3a8 --- /dev/null +++ b/jstests/sharding/array_shard_key.js @@ -0,0 +1,127 @@ +// Ensure you can't shard on an array key + +var st = new ShardingTest({ name : jsTestName(), shards : 3 }) + +var mongos = st.s0 + +var coll = mongos.getCollection( jsTestName() + ".foo" ) + +st.shardColl( coll, { _id : 1, i : 1 }, { _id : ObjectId(), i : 1 } ) + +printjson( mongos.getDB("config").chunks.find().toArray() ) + +st.printShardingStatus() + +print( "1: insert some invalid data" ) + +var value = null + +var checkError = function( shouldError ){ + var error = coll.getDB().getLastError() + + if( error != null ) printjson( error ) + + if( error == null && ! shouldError ) return + if( error != null && shouldError ) return + + if( error == null ) print( "No error detected!" ) + else print( "Unexpected error!" ) + + assert( false ) +} + +// Insert an object with invalid array key +coll.insert({ i : [ 1, 2 ] }) +checkError( true ) + +// Insert an object with valid array key +coll.insert({ i : 1 }) +checkError( false ) + +// Update the value with valid other field +value = coll.findOne({ i : 1 }) +coll.update( value, { $set : { j : 2 } } ) +checkError( false ) + +// Update the value with invalid other fields +value = coll.findOne({ i : 1 }) +coll.update( value, Object.merge( value, { i : [ 3 ] } ) ) +checkError( true ) + +// Multi-update the value with invalid other fields +value = coll.findOne({ i : 1 }) +coll.update( value, Object.merge( value, { i : [ 3, 4 ] } ), false, true) +checkError( true ) + +// Single update the value with valid other fields +value = coll.findOne({ i : 1 }) +coll.update( Object.merge( value, { i : [ 3, 4 ] } ), value ) +checkError( true ) + +// Multi-update the value with other fields (won't work, but no error) +value = coll.findOne({ i : 1 }) +coll.update( Object.merge( value, { i : [ 1, 1 ] } ), { $set : { k : 4 } }, false, true) +checkError( false ) + +// Query the value with other fields (won't work, but no error) +value = coll.findOne({ i : 1 }) +coll.find( Object.merge( value, { i : [ 1, 1 ] } ) ).toArray() +checkError( false ) + +// Can't remove using multikey, but shouldn't error +value = coll.findOne({ i : 1 }) +coll.remove( Object.extend( value, { i : [ 1, 2, 3, 4 ] } ) ) +checkError( false ) + +// Can't remove using multikey, but shouldn't error +value = coll.findOne({ i : 1 }) +coll.remove( Object.extend( value, { i : [ 1, 2, 3, 4, 5 ] } ) ) +error = coll.getDB().getLastError() +assert.eq( error, null ) +assert.eq( coll.find().itcount(), 1 ) + +value = coll.findOne({ i : 1 }) +coll.remove( Object.extend( value, { i : 1 } ) ) +error = coll.getDB().getLastError() +assert.eq( error, null ) +assert.eq( coll.find().itcount(), 0 ) + +printjson( "Sharding-then-inserting-multikey tested, now trying inserting-then-sharding-multikey" ) + +// Insert a bunch of data then shard over key which is an array +var coll = mongos.getCollection( "" + coll + "2" ) +for( var i = 0; i < 10; i++ ){ + // TODO : does not check weird cases like [ i, i ] + coll.insert({ i : [ i, i + 1 ] }) + checkError( false ) +} + +coll.ensureIndex({ _id : 1, i : 1 }) + +try { + st.shardColl( coll, { _id : 1, i : 1 }, { _id : ObjectId(), i : 1 } ) +} +catch( e ){ + print( "Correctly threw error on sharding with multikey index." ) +} + +st.printShardingStatus() + +// Insert a bunch of data then shard over key which is not an array +var coll = mongos.getCollection( "" + coll + "3" ) +for( var i = 0; i < 10; i++ ){ + // TODO : does not check weird cases like [ i, i ] + coll.insert({ i : i }) + checkError( false ) +} + +coll.ensureIndex({ _id : 1, i : 1 }) + +st.shardColl( coll, { _id : 1, i : 1 }, { _id : ObjectId(), i : 1 } ) + +st.printShardingStatus() + + + +// Finish +st.stop() diff --git a/s/d_split.cpp b/s/d_split.cpp index c195f7b7668..6bf9c4e0a6a 100644 --- a/s/d_split.cpp +++ b/s/d_split.cpp @@ -177,6 +177,11 @@ namespace mongo { return false; } + if( d->isMultikey( d->idxNo( *idx ) ) ) { + errmsg = "index is multikey, cannot use for sharding"; + return false; + } + BtreeCursor * bc = BtreeCursor::make( d , d->idxNo(*idx) , *idx , min , max , false , 1 ); shared_ptr<Cursor> c( bc ); auto_ptr<ClientCursor> cc( new ClientCursor( QueryOption_NoCursorTimeout , c , ns ) ); diff --git a/s/shardkey.cpp b/s/shardkey.cpp index 9602b8566e5..49458f2d7c7 100644 --- a/s/shardkey.cpp +++ b/s/shardkey.cpp @@ -55,7 +55,8 @@ namespace mongo { */ for(set<string>::const_iterator it = patternfields.begin(); it != patternfields.end(); ++it) { - if(obj.getFieldDotted(it->c_str()).eoo()) + BSONElement e = obj.getFieldDotted(it->c_str()); + if(e.eoo() || e.type() == Array) return false; } return true; diff --git a/s/strategy_shard.cpp b/s/strategy_shard.cpp index 31d696d63a1..24f39ad8682 100644 --- a/s/strategy_shard.cpp +++ b/s/strategy_shard.cpp @@ -141,8 +141,8 @@ namespace mongo { } if ( bad ) { - log() << "tried to insert object without shard key: " << r.getns() << " " << o << endl; - uasserted( 8011 , "tried to insert object without shard key" ); + log() << "tried to insert object with no valid shard key: " << r.getns() << " " << o << endl; + uasserted( 8011 , "tried to insert object with no valid shard key" ); } } @@ -210,8 +210,8 @@ namespace mongo { } if ( bad ) { - log() << "tried to insert object without shard key: " << nsChunkLookup << " " << o << endl; - uasserted( 14842 , "tried to insert object without shard key" ); + log() << "tried to insert object with no valid shard key: " << nsChunkLookup << " " << o << endl; + uasserted( 14842 , "tried to insert object with no valid shard key" ); } } @@ -258,7 +258,7 @@ namespace mongo { bool multi = flags & UpdateOption_Multi; if (upsert) { - uassert(8012, "can't upsert something without shard key", + uassert(8012, "can't upsert something without valid shard key", (manager->hasShardKey(toupdate) || (toupdate.firstElementFieldName()[0] == '$' && manager->hasShardKey(query)))); @@ -273,7 +273,8 @@ namespace mongo { if ( multi ) { } else if ( strcmp( query.firstElementFieldName() , "_id" ) || query.nFields() != 1 ) { - throw UserException( 8013 , "can't do non-multi update with query that doesn't have the shard key" ); + log() << "Query " << query << endl; + throw UserException( 8013 , "can't do non-multi update with query that doesn't have a valid shard key" ); } else { save = true; @@ -306,7 +307,7 @@ namespace mongo { } else { uasserted(12376, - str::stream() << "shard key must be in update object for collection: " << manager->getns() ); + str::stream() << "valid shard key must be in update object for collection: " << manager->getns() ); } } @@ -351,7 +352,7 @@ namespace mongo { bool multi = flags & UpdateOption_Multi; if (upsert) { - uassert(14854, "can't upsert something without shard key", + uassert(14854, "can't upsert something without valid shard key", (manager->hasShardKey(toupdate) || (toupdate.firstElementFieldName()[0] == '$' && manager->hasShardKey(query)))); @@ -366,7 +367,7 @@ namespace mongo { if ( multi ) { } else if ( strcmp( query.firstElementFieldName() , "_id" ) || query.nFields() != 1 ) { - throw UserException( 14850 , "can't do non-multi update with query that doesn't have the shard key" ); + throw UserException( 14850 , "can't do non-multi update with query that doesn't have a valid shard key" ); } else { save = true; @@ -399,7 +400,7 @@ namespace mongo { } else { uasserted(14857, - str::stream() << "shard key must be in update object for collection: " << manager->getns() ); + str::stream() << "valid shard key must be in update object for collection: " << manager->getns() ); } } diff --git a/shell/servers.js b/shell/servers.js index ff29245bb9a..8b9495eb7da 100755 --- a/shell/servers.js +++ b/shell/servers.js @@ -746,18 +746,30 @@ ShardingTest.prototype.shardGo = function( collName , key , split , move , dbNam if( collName.getDB ) c = "" + collName + var isEmpty = this.s.getCollection( c ).count() == 0 + if( ! this.isSharded( dbName ) ) this.s.adminCommand( { enableSharding : dbName } ) - this.s.adminCommand( { shardcollection : c , key : key } ); - this.s.adminCommand( { split : c , middle : split } ); + var result = this.s.adminCommand( { shardcollection : c , key : key } ) + if( ! result.ok ){ + printjson( result ) + assert( false ) + } + result = this.s.adminCommand( { split : c , middle : split } ); + if( ! result.ok ){ + printjson( result ) + assert( false ) + } + var result = null for( var i = 0; i < 5; i++ ){ result = this.s.adminCommand( { movechunk : c , find : move , to : this.getOther( this.getServer( dbName ) ).name } ); if( result.ok ) break; sleep( 5 * 1000 ); } + printjson( result ) assert( result.ok ) }; diff --git a/shell/utils.js b/shell/utils.js index 8729afde988..ee0dd1aefe6 100644 --- a/shell/utils.js +++ b/shell/utils.js @@ -250,6 +250,11 @@ Object.extend = function( dst , src , deep ){ return dst; } +Object.merge = function( dst, src, deep ){ + var clone = Object.extend( {}, dst, deep ) + return Object.extend( clone, src, deep ) +} + argumentsToArray = function( a ){ var arr = []; for ( var i=0; i<a.length; i++ ) |