summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/sharding/array_shard_key.js127
-rw-r--r--s/d_split.cpp5
-rw-r--r--s/shardkey.cpp3
-rw-r--r--s/strategy_shard.cpp21
-rwxr-xr-xshell/servers.js16
-rw-r--r--shell/utils.js5
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++ )