Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f570771a5d | ||
|
|
94377aa2b3 | ||
|
|
e753f6e482 | ||
|
|
0a3b5f08e6 | ||
|
|
0adb962353 | ||
|
|
0bab4de510 | ||
|
|
ea9c49e06a | ||
|
|
ffb92e80ed | ||
|
|
31150cee4c | ||
|
|
109f6fbde0 | ||
|
|
ededb03f21 | ||
|
|
5c2f3da947 | ||
|
|
511cbba4d2 | ||
|
|
baaa9118f9 | ||
|
|
0a6bac4569 | ||
|
|
38f42b2a42 | ||
|
|
c0ce2108cc | ||
|
|
c5c64e3ace | ||
|
|
e330e5269e | ||
|
|
d857206880 | ||
|
|
64d9280591 | ||
|
|
c814cb2868 | ||
|
|
d5a4514a06 | ||
|
|
f7afbab2c3 | ||
|
|
e4d0c89135 | ||
|
|
2d43e87a51 | ||
|
|
aecd951b3d | ||
|
|
832a4c0ccf | ||
|
|
6f22c3af01 | ||
|
|
f33a8ce322 | ||
|
|
97df5708b9 | ||
|
|
b7b2db71ba | ||
|
|
8885784cf1 | ||
|
|
4473320aca | ||
|
|
9094e68b9b | ||
|
|
0878621290 | ||
|
|
82755a0350 | ||
|
|
c28a406b1f | ||
|
|
c5f1c1474b | ||
|
|
cdc2db7f6b | ||
|
|
c31da72e8a | ||
|
|
5baf992b90 | ||
|
|
8014832a4a | ||
|
|
d03939e182 | ||
|
|
2758842136 | ||
|
|
e342644da3 | ||
|
|
c0888b3579 | ||
|
|
f98f20603b | ||
|
|
79a3b1cada | ||
|
|
d1b43b61a5 | ||
|
|
69655c6c6e | ||
|
|
f3700f4ec3 | ||
|
|
5a4b6a0acc | ||
|
|
fed35f0c08 | ||
|
|
cb2e7e34d5 | ||
|
|
c527cc73e2 | ||
|
|
9032d392d0 | ||
|
|
448ef26e3d | ||
|
|
c6039b222e |
@@ -294,6 +294,7 @@ env = Environment( BUILD_DIR=variantDir,
|
||||
CLIENT_SCONSTRUCT='#distsrc/client/SConstruct',
|
||||
DIST_ARCHIVE_SUFFIX='.tgz',
|
||||
EXTRAPATH=get_option("extrapath"),
|
||||
MODULE_BANNERS=[],
|
||||
MODULETEST_LIST='#build/moduletests.txt',
|
||||
MSVS_ARCH=msarch ,
|
||||
PYTHON=utils.find_python(),
|
||||
|
||||
@@ -2,7 +2,7 @@ MongoDB uses third-party libraries or other resources that may
|
||||
be distributed under licenses different than the MongoDB software.
|
||||
|
||||
In the event that we accidentally failed to list a required notice,
|
||||
please bring it to our attention through any of the ways detailed here :
|
||||
please bring it to our attention through any of the ways detailed here :
|
||||
|
||||
mongodb-dev@googlegroups.com
|
||||
|
||||
@@ -235,20 +235,20 @@ New BSD License
|
||||
Copyright (c) 1998-2006, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
@@ -287,9 +287,9 @@ For applicable files:
|
||||
Netscape Communications Corporation.
|
||||
Portions created by the Initial Developer are Copyright (C) 1998
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
|
||||
Contributor(s):
|
||||
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
@@ -302,4 +302,75 @@ For applicable files:
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
|
||||
7) License notice for Linenoise
|
||||
-------------------------------
|
||||
|
||||
Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Redis nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
8) License notice for S2 Geometry Library
|
||||
-----------------------------------------
|
||||
Copyright 2005 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
9) License notice for MurmurHash
|
||||
--------------------------------
|
||||
|
||||
Copyright (c) 2010-2012 Austin Appleby
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
End
|
||||
@@ -3,7 +3,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = MongoDB
|
||||
PROJECT_NUMBER = 2.2.2-rc0
|
||||
PROJECT_NUMBER = 2.2.3
|
||||
OUTPUT_DIRECTORY = docs/doxygen
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
18
jstests/find_and_modify_server7660.js
Normal file
18
jstests/find_and_modify_server7660.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
t = db.find_and_modify_server7660;
|
||||
t.drop();
|
||||
|
||||
a = t.findAndModify({
|
||||
query : { foo : 'bar' },
|
||||
update : { $set : { bob : 'john' } },
|
||||
sort: { foo : 1},
|
||||
upsert: true,
|
||||
new : true
|
||||
});
|
||||
|
||||
b = t.findOne();
|
||||
assert.eq( a, b );
|
||||
assert.eq( "bar", a.foo );
|
||||
assert.eq( "john", a.bob )
|
||||
|
||||
|
||||
33
jstests/geo_box1_noindex.js
Normal file
33
jstests/geo_box1_noindex.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// SERVER-7343: allow $within without a geo index.
|
||||
t = db.geo_box1_noindex;
|
||||
t.drop();
|
||||
|
||||
num = 0;
|
||||
for ( x=0; x<=20; x++ ){
|
||||
for ( y=0; y<=20; y++ ){
|
||||
o = { _id : num++ , loc : [ x , y ] }
|
||||
t.save( o )
|
||||
}
|
||||
}
|
||||
|
||||
searches = [
|
||||
[ [ 1 , 2 ] , [ 4 , 5 ] ] ,
|
||||
[ [ 1 , 1 ] , [ 2 , 2 ] ] ,
|
||||
[ [ 0 , 2 ] , [ 4 , 5 ] ] ,
|
||||
[ [ 1 , 1 ] , [ 2 , 8 ] ] ,
|
||||
];
|
||||
|
||||
for ( i=0; i<searches.length; i++ ){
|
||||
b = searches[i];
|
||||
|
||||
q = { loc : { $within : { $box : b } } }
|
||||
numWanted = ( 1 + b[1][0] - b[0][0] ) * ( 1 + b[1][1] - b[0][1] );
|
||||
assert.eq( numWanted , t.find(q).itcount() , "itcount: " + tojson( q ) );
|
||||
printjson( t.find(q).explain() )
|
||||
}
|
||||
|
||||
assert.eq( 0 , t.find( { loc : { $within : { $box : [ [100 , 100 ] , [ 110 , 110 ] ] } } } ).itcount() , "E1" )
|
||||
assert.eq( 0 , t.find( { loc : { $within : { $box : [ [100 , 100 ] , [ 110 , 110 ] ] } } } ).count() , "E2" )
|
||||
assert.eq( num , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).count() , "E3" )
|
||||
assert.eq( num , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).itcount() , "E4" )
|
||||
assert.eq( 57 , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).limit(57).itcount() , "E5" )
|
||||
29
jstests/geo_circle1_noindex.js
Normal file
29
jstests/geo_circle1_noindex.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// SERVER-7343: allow $within without a geo index.
|
||||
t = db.geo_circle1_noindex;
|
||||
t.drop();
|
||||
|
||||
searches = [
|
||||
[ [ 5 , 5 ] , 3 ] ,
|
||||
[ [ 5 , 5 ] , 1 ] ,
|
||||
[ [ 5 , 5 ] , 5 ] ,
|
||||
[ [ 0 , 5 ] , 5 ] ,
|
||||
];
|
||||
correct = searches.map( function(z){ return []; } );
|
||||
|
||||
num = 0;
|
||||
|
||||
for ( x=0; x<=20; x++ ){
|
||||
for ( y=0; y<=20; y++ ){
|
||||
o = { _id : num++ , loc : [ x , y ] }
|
||||
t.save( o )
|
||||
for ( i=0; i<searches.length; i++ )
|
||||
if ( Geo.distance( [ x , y ] , searches[i][0] ) <= searches[i][1] )
|
||||
correct[i].push( o );
|
||||
}
|
||||
}
|
||||
|
||||
for ( i=0; i<searches.length; i++ ){
|
||||
q = { loc : { $within : { $center : searches[i] } } }
|
||||
assert.eq( correct[i].length , t.find( q ).itcount() , "itcount : " + tojson( searches[i] ) );
|
||||
assert.eq( correct[i].length , t.find( q ).count() , "count : " + tojson( searches[i] ) );
|
||||
}
|
||||
47
jstests/geo_polygon1_noindex.js
Normal file
47
jstests/geo_polygon1_noindex.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// SERVER-7343: allow $within without a geo index.
|
||||
|
||||
t = db.geo_polygon1_noindex;
|
||||
t.drop();
|
||||
|
||||
num = 0;
|
||||
for ( x=1; x < 9; x++ ){
|
||||
for ( y= 1; y < 9; y++ ){
|
||||
o = { _id : num++ , loc : [ x , y ] };
|
||||
t.save( o );
|
||||
}
|
||||
}
|
||||
|
||||
triangle = [[0,0], [1,1], [0,2]];
|
||||
|
||||
// Look at only a small slice of the data within a triangle
|
||||
assert.eq( 1 , t.find( { loc: { "$within": { "$polygon" : triangle }}} ).count() , "Triangle Test" );
|
||||
|
||||
boxBounds = [ [0,0], [0,10], [10,10], [10,0] ];
|
||||
|
||||
assert.eq( num , t.find( { loc : { "$within" : { "$polygon" : boxBounds } } } ).count() , "Bounding Box Test" );
|
||||
|
||||
//Make sure we can add object-based polygons
|
||||
assert.eq( num, t.find( { loc : { $within : { $polygon : { a : [-10, -10], b : [-10, 10], c : [10, 10], d : [10, -10] } } } } ).count() )
|
||||
|
||||
// Look in a box much bigger than the one we have data in
|
||||
boxBounds = [[-100,-100], [-100, 100], [100,100], [100,-100]];
|
||||
assert.eq( num , t.find( { loc : { "$within" : { "$polygon" : boxBounds } } } ).count() , "Big Bounding Box Test" );
|
||||
|
||||
t.drop();
|
||||
|
||||
pacman = [
|
||||
[0,2], [0,4], [2,6], [4,6], // Head
|
||||
[6,4], [4,3], [6,2], // Mouth
|
||||
[4,0], [2,0] // Bottom
|
||||
];
|
||||
|
||||
t.save({loc: [1,3] }); // Add a point that's in
|
||||
assert.isnull( db.getLastError() )
|
||||
|
||||
assert.eq( 1 , t.find({loc : { $within : { $polygon : pacman }}} ).count() , "Pacman single point" );
|
||||
|
||||
t.save({ loc : [5, 3] }) // Add a point that's out right in the mouth opening
|
||||
t.save({ loc : [3, 7] }) // Add a point above the center of the head
|
||||
t.save({ loc : [3,-1] }) // Add a point below the center of the bottom
|
||||
|
||||
assert.eq( 1 , t.find({loc : { $within : { $polygon : pacman }}} ).count() , "Pacman double point" );
|
||||
15
jstests/geo_withinquery.js
Normal file
15
jstests/geo_withinquery.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// SERVER-7343: allow $within without a geo index.
|
||||
t = db.geo_withinquery;
|
||||
t.drop();
|
||||
|
||||
num = 0;
|
||||
for ( x=0; x<=20; x++ ){
|
||||
for ( y=0; y<=20; y++ ){
|
||||
o = { _id : num++ , loc : [ x , y ] }
|
||||
t.save( o )
|
||||
}
|
||||
}
|
||||
|
||||
assert.eq(21 * 21 - 1, t.find({ $and: [ {loc: {$ne:[0,0]}},
|
||||
{loc: {$within: {$box: [[0,0], [100,100]]}}},
|
||||
]}).itcount(), "UHOH!")
|
||||
@@ -176,7 +176,9 @@ catch ( e ){
|
||||
y = e;
|
||||
}
|
||||
|
||||
assert.eq( x , y , "assert format" )
|
||||
assert.eq( x.code , y.code , "assert format" )
|
||||
assert.eq( x.errmsg , y.errmsg , "assert format" )
|
||||
assert.eq( x.ok , y.ok , "assert format" )
|
||||
|
||||
// isMaster and query-wrapped-command
|
||||
isMaster = db.runCommand({isMaster:1});
|
||||
|
||||
@@ -11,14 +11,6 @@ function writeToConfigTest(){
|
||||
|
||||
assert( gleObj.ok );
|
||||
|
||||
printjson( gleObj );
|
||||
assert( gleObj.hasOwnProperty( 'shardRawGLE' ),
|
||||
'missing shardRawGLE from get last error fields!' );
|
||||
|
||||
var shardGLE = gleObj.shardRawGLE[ st.config0.host ];
|
||||
assert( shardGLE.ok );
|
||||
assert.neq( null, shardGLE.err );
|
||||
|
||||
st.stop();
|
||||
}
|
||||
|
||||
|
||||
46
jstests/sharding/listDatabases.js
Normal file
46
jstests/sharding/listDatabases.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// tests that listDatabases doesn't show config db on a shard, even if it is there
|
||||
|
||||
var test = new ShardingTest({shards: 1, mongos: 1, config: 1, other: {chunksize:1, separateConfig:true}})
|
||||
|
||||
var mongos = test.s0
|
||||
var mongod = test.shard0;
|
||||
|
||||
//grab the config db instance by name
|
||||
var getDBSection = function (dbsArray, dbToFind) {
|
||||
for(var pos in dbsArray) {
|
||||
if (dbsArray[pos].name && dbsArray[pos].name === dbToFind)
|
||||
return dbsArray[pos];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
mongos.getDB("blah").foo.insert({_id:1})
|
||||
mongos.getDB("foo").foo.insert({_id:1})
|
||||
mongos.getDB("raw").foo.insert({_id:1})
|
||||
//wait for writes to finish
|
||||
mongos.getDB("raw").getLastError()
|
||||
|
||||
//verify that the config db is not on a shard
|
||||
var res = mongos.adminCommand("listDatabases");
|
||||
var dbArray = res.databases;
|
||||
assert(getDBSection(dbArray, "config"), "config db not found! 1")
|
||||
assert(!getDBSection(dbArray, "config").shards, "config db is on a shard! 1")
|
||||
|
||||
//add doc in config/admin db on the shard
|
||||
mongod.getDB("config").foo.insert({_id:1})
|
||||
mongod.getDB("admin").foo.insert({_id:1})
|
||||
|
||||
//add doc in admin db (via mongos)
|
||||
mongos.getDB("admin").foo.insert({_id:1})
|
||||
|
||||
//verify that the config db is not on a shard
|
||||
var res = mongos.adminCommand("listDatabases");
|
||||
var dbArray = res.databases;
|
||||
//check config db
|
||||
assert(getDBSection(dbArray, "config"), "config db not found! 2")
|
||||
assert(!getDBSection(dbArray, "config").shards, "config db is on a shard! 2")
|
||||
//check admin db
|
||||
assert(getDBSection(dbArray, "admin"), "admin db not found! 2")
|
||||
assert(!getDBSection(dbArray, "admin").shards, "admin db is on a shard! 2")
|
||||
|
||||
test.stop()
|
||||
60
jstests/sharding/noUpdateButN1inAnotherCollection.js
Normal file
60
jstests/sharding/noUpdateButN1inAnotherCollection.js
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
function debug( str ) {
|
||||
print( "---\n" + str + "\n-----" );
|
||||
}
|
||||
|
||||
var name = "badNonUpdate";
|
||||
debug("Starting sharded cluster test stuff");
|
||||
|
||||
s = new ShardingTest( {name: name, shards : 2, mongos : 2, separateConfig : true, verbose:5, nopreallocj : true });
|
||||
|
||||
var mongosA=s.s0;
|
||||
var mongosB=s.s1;
|
||||
|
||||
ns = "test.coll";
|
||||
ns2 = "test.coll2";
|
||||
|
||||
adminSA = mongosA.getDB( "admin" );
|
||||
adminSA.runCommand({ enableSharding : "test"});
|
||||
|
||||
adminSA.runCommand( { moveprimary : "test", to : "shard0000" } );
|
||||
adminSA.runCommand( { moveprimary : "test2", to : "shard0001" } );
|
||||
|
||||
adminSA.runCommand({ shardCollection : ns, key : { _id : 1 } });
|
||||
|
||||
try {
|
||||
s.stopBalancer();
|
||||
} catch (e) {
|
||||
print("coundn't stop balancer via command");
|
||||
}
|
||||
|
||||
adminSA.settings.update({ _id: 'balancer' }, { $set: { stopped: true }});
|
||||
|
||||
var db = mongosA.getDB( "test" );
|
||||
var coll = db.coll;
|
||||
var coll2 = db.coll2;
|
||||
|
||||
numDocs = 10;
|
||||
for (var i = 1; i < numDocs; i++) {
|
||||
coll.insert({_id:i, control:0});
|
||||
coll2.insert({_id:i, control:0});
|
||||
}
|
||||
|
||||
debug("Inserted docs, now split chunks");
|
||||
|
||||
adminSA.runCommand( { split: ns, find : { _id : 3} });
|
||||
adminSA.runCommand( { movechunk: ns, find : { _id : 10}, to: "shard0001" });
|
||||
|
||||
var command = 'db.coll.update({_id:9},{$set:{"a":"9"}},true);printjson(db.getLastErrorObj())';
|
||||
|
||||
// without this first query through mongo, the second time doesn't "fail"
|
||||
debug("Try query first time");
|
||||
var GLE2=runMongoProgram( "mongo", "--quiet", "--port", "" + s._mongos[1].port, "--eval", command );
|
||||
|
||||
mongosB.getDB("test").coll2.update({_id:0}, {$set:{"c":"333"}});
|
||||
var GLE3=mongosB.getDB("test").getLastErrorObj();
|
||||
assert.eq( 0, GLE3.n );
|
||||
|
||||
|
||||
s.stop();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
var NODE_COUNT = 3;
|
||||
var st = new ShardingTest({ shards: { rs0: { nodes: NODE_COUNT, oplogSize: 10 }},
|
||||
separateConfig: true });
|
||||
separateConfig: true, config : 3 });
|
||||
var replTest = st.rs0;
|
||||
var mongos = st.s;
|
||||
|
||||
|
||||
63
jstests/sharding/wbl_not_cleared.js
Normal file
63
jstests/sharding/wbl_not_cleared.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Tests whether the WBL gets reset without subsequent events.
|
||||
*/
|
||||
|
||||
var st = new ShardingTest({ shards : 2, mongos : 2, other : { mongosOptions : { verbose : 5 } } });
|
||||
|
||||
st.stopBalancer();
|
||||
|
||||
var mongos = st.s0;
|
||||
var staleMongos = st.s1;
|
||||
var admin = mongos.getDB("admin");
|
||||
var config = mongos.getDB("config");
|
||||
var shards = config.shards.find().toArray();
|
||||
var coll = mongos.getCollection("foo.bar");
|
||||
|
||||
jsTest.log("Sharding collection...");
|
||||
|
||||
printjson(admin.runCommand({ enableSharding : coll.getDB() + "" }));
|
||||
printjson(admin.runCommand({ movePrimary : coll.getDB() + "", to : shards[0]._id }));
|
||||
printjson(admin.runCommand({ shardCollection : coll + "", key : { _id : 1 } }));
|
||||
printjson(admin.runCommand({ split : coll + "", middle : { _id : 0 } }));
|
||||
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[1]._id }));
|
||||
|
||||
jsTest.log("Collection now sharded...");
|
||||
st.printShardingStatus();
|
||||
|
||||
jsTest.log("Making mongos stale...");
|
||||
|
||||
coll.insert({ _id : 0 });
|
||||
coll.getDB().getLastErrorObj();
|
||||
|
||||
// Make sure the stale mongos knows about the collection at the original version
|
||||
assert.neq(null, staleMongos.getCollection(coll + "").findOne());
|
||||
|
||||
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[0]._id, _waitForDelete : true }));
|
||||
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[1]._id, _waitForDelete : true }));
|
||||
|
||||
jsTest.log("Running a stale insert...");
|
||||
|
||||
staleMongos.getCollection(coll + "").insert({ _id : 0, dup : "key" });
|
||||
|
||||
jsTest.log("Getting initial GLE result...");
|
||||
|
||||
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
|
||||
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
|
||||
st.printShardingStatus();
|
||||
|
||||
jsTest.log("Performing insert op on the same shard...");
|
||||
|
||||
staleMongos.getCollection(coll + "").insert({ _id : 1, key : "isOk" })
|
||||
|
||||
jsTest.log("Getting GLE result...");
|
||||
|
||||
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
|
||||
assert.eq(null, staleMongos.getDB(coll.getDB() + "").getLastError());
|
||||
|
||||
jsTest.log("DONE!");
|
||||
|
||||
st.stop();
|
||||
|
||||
|
||||
|
||||
|
||||
94
jstests/sharding/writeback_server7958.js
Normal file
94
jstests/sharding/writeback_server7958.js
Normal file
@@ -0,0 +1,94 @@
|
||||
jsTest.log("Starting sharded cluster for wrong duplicate error setup");
|
||||
|
||||
s = new ShardingTest( name="writeback_server7958", shards = 2, verbose=0, mongos = 4 );
|
||||
|
||||
var mongosA=s.s0;
|
||||
var mongosB=s.s1;
|
||||
var mongosC=s.s2;
|
||||
var mongosD=s.s3;
|
||||
|
||||
ns1 = "test.trans";
|
||||
ns2 = "test.node";
|
||||
|
||||
adminSA = mongosA.getDB( "admin" );
|
||||
adminSB = mongosB.getDB( "admin" );
|
||||
adminSD = mongosD.getDB( "admin" );
|
||||
adminSA.runCommand({ enableSharding : "test"});
|
||||
adminSA.runCommand({ shardCollection : ns1, key : { owner : 1 }, unique: true });
|
||||
//adminSA.runCommand({ shardCollection : ns1, key : { owner : 1 } });
|
||||
|
||||
try {
|
||||
s.stopBalancer();
|
||||
} catch (e) {
|
||||
print("coundn't stop balancer via command");
|
||||
}
|
||||
|
||||
adminSA.settings.update({ _id: 'balancer' }, { $set: { stopped: true }});
|
||||
|
||||
var db = mongosA.getDB( "test" );
|
||||
var dbB = mongosB.getDB( "test" );
|
||||
var dbC = mongosC.getDB( "test" );
|
||||
var dbD = mongosD.getDB( "test" );
|
||||
var trans = db.trans;
|
||||
var node = db.node;
|
||||
var transB = dbB.trans;
|
||||
var nodeB = dbB.node;
|
||||
var transC = dbC.trans;
|
||||
var nodeC = dbC.node;
|
||||
var transD = dbD.trans;
|
||||
var nodeD = dbD.node;
|
||||
|
||||
var primary = s.getServerName("test");
|
||||
var shard1 = s._shardNames[0];
|
||||
var shard2 = s._shardNames[1];
|
||||
if (primary == shard1) {
|
||||
other = shard2;
|
||||
} else {
|
||||
other = shard1;
|
||||
}
|
||||
|
||||
|
||||
trans.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
|
||||
db.runCommand({getLastError:1, j:1});
|
||||
|
||||
node.insert({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890"), "counts":0});
|
||||
db.runCommand({getLastError:1, j:1});
|
||||
for (var i=0; i<1000; i++) {
|
||||
trans.insert({"owner":NumberLong(i),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
|
||||
node.insert({"owner":NumberLong(i),"parent":NumberLong(i+1000),_id:NumberLong(i+1234567890), "counts":0});
|
||||
}
|
||||
|
||||
transB.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
|
||||
var r1=dbB.runCommand( { getLastError: 1, w: 1 } );
|
||||
assert( r1.n == 0 && r1.err.length > 0 && r1.hasOwnProperty("code"), tojson( r1 ) );
|
||||
|
||||
jsTest.log("Inserted dup (failed), now split chunks and move data");
|
||||
|
||||
adminSD.runCommand( { split: ns1, middle : { owner : 100} });
|
||||
adminSD.runCommand( { movechunk: ns1, find : { owner : 105}, to: other});
|
||||
|
||||
jsTest.log("Kicking off dup inserts and updates");
|
||||
|
||||
errors=[];
|
||||
i=0;
|
||||
trans.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
|
||||
var r1=db.runCommand( { getLastError: 1, w: 1 } );
|
||||
assert( r1.n == 0 && r1.err.length > 0 && r1.hasOwnProperty("code"), tojson( r1 ) );
|
||||
transB.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
|
||||
var rB1=dbB.runCommand( { getLastError: 1, w: 1 } );
|
||||
assert( rB1.n == 0 && rB1.err.length > 0 && rB1.hasOwnProperty("code"), tojson( r1 ) );
|
||||
|
||||
nodeB.update({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890")},{"$inc":{"counts":1}});
|
||||
var resultB = dbB.runCommand( { getLastError: 1, w: 1 } )
|
||||
node.update({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890")},{"$inc":{"counts":1}});
|
||||
var result = db.runCommand( { getLastError: 1, w: 1 } )
|
||||
|
||||
assert.eq( 2, node.findOne().counts );
|
||||
|
||||
printjson( result )
|
||||
printjson( resultB )
|
||||
|
||||
assert( result.n==1 && result.updatedExisting==true && result.err == null, "update succeeded on collection node on mongos A but GLE was\nn=" + result.n + ",\nupdatedExisting=" + result.updatedExisting + ",\nerr=" + result.err);
|
||||
assert( resultB.n==1 && resultB.updatedExisting==true && resultB.err == null, "update succeeded on collection node on mongos B but GLE was\nn=" + resultB.n + ",\nupdatedExisting=" + resultB.updatedExisting + ",\nerr=" + resultB.err);
|
||||
|
||||
s.stop();
|
||||
16
jstests/slowNightly/server7428.js
Normal file
16
jstests/slowNightly/server7428.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// Regression test for SERVER-7428.
|
||||
//
|
||||
// Verify that the copyDatabase command works appropriately when the
|
||||
// target mongo instance has authentication enabled.
|
||||
|
||||
// Setup fromDb with no auth
|
||||
var fromDb = MongoRunner.runMongod({ port: 29000 });
|
||||
|
||||
// Setup toDb with auth
|
||||
var toDb = MongoRunner.runMongod({auth : "", port : 31001});
|
||||
var admin = toDb.getDB("admin");
|
||||
admin.addUser("foo","bar");
|
||||
admin.auth("foo","bar");
|
||||
|
||||
admin.copyDatabase('test', 'test', fromDb.host)
|
||||
|
||||
@@ -23,19 +23,37 @@ num = 0;
|
||||
|
||||
counts = {}
|
||||
|
||||
function doUpdate( includeString ){
|
||||
//
|
||||
// TODO: Rewrite to make much clearer.
|
||||
//
|
||||
// The core behavior of this test is to add a bunch of documents to a sharded collection, then
|
||||
// incrementally update each document and make sure the counts in the document match our update
|
||||
// counts while balancing occurs (doUpdate()). Every once in a while we also check (check())
|
||||
// our counts via a query.
|
||||
//
|
||||
// If during a chunk migration an update is missed, we trigger an assertion and fail.
|
||||
//
|
||||
|
||||
|
||||
function doUpdate( includeString, optionalId ){
|
||||
var up = { $inc : { x : 1 } }
|
||||
if ( includeString )
|
||||
up["$set"] = { s : bigString };
|
||||
var myid = Random.randInt( N )
|
||||
var myid = optionalId == undefined ? Random.randInt( N ) : optionalId
|
||||
db.foo.update( { _id : myid } , up , true );
|
||||
|
||||
counts[myid] = ( counts[myid] ? counts[myid] : 0 ) + 1;
|
||||
return myid;
|
||||
}
|
||||
|
||||
for ( i=0; i<N*10; i++ ){
|
||||
doUpdate( true )
|
||||
// Initially update all documents from 1 to N, otherwise later checks can fail because no document
|
||||
// previously existed
|
||||
for ( i = 0; i < N; i++ ){
|
||||
doUpdate( true, i )
|
||||
}
|
||||
|
||||
for ( i=0; i<N*9; i++ ){
|
||||
doUpdate( false )
|
||||
}
|
||||
db.getLastError();
|
||||
|
||||
@@ -117,11 +135,6 @@ function diff1(){
|
||||
return Math.max( x.shard0000 , x.shard0001 ) - Math.min( x.shard0000 , x.shard0001 );
|
||||
}
|
||||
|
||||
function sum(){
|
||||
var x = s.chunkCounts( "foo" )
|
||||
return x.shard0000 + x.shard0001;
|
||||
}
|
||||
|
||||
assert.lt( 20 , diff1() ,"initial load" );
|
||||
print( diff1() )
|
||||
|
||||
|
||||
79
jstests/tool/tool_replset.js
Normal file
79
jstests/tool/tool_replset.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Test to ensure that (dump/restore/export/import/oplog) works with a replica set connection string
|
||||
* 1. Start a replica set.
|
||||
* 2. Add data to a collection.
|
||||
* 3. Take a dump of the database.
|
||||
* 4. Drop the db.
|
||||
* 5. Restore the db.
|
||||
* 6. Export a collection.
|
||||
* 7. Drop the collection.
|
||||
* 8. Import the collection.
|
||||
* 9. Add data to the oplog.rs collection.
|
||||
* 10. Ensure that the document doesn't exist yet.
|
||||
* 11. Now play the mongooplog tool.
|
||||
* 12. Make sure that the oplog was played
|
||||
*/
|
||||
|
||||
// Load utility methods for replica set tests
|
||||
load("jstests/replsets/rslib.js");
|
||||
|
||||
var replTest = new ReplSetTest({ name: 'rs', nodes: 2, oplogSize: 5 });
|
||||
var nodes = replTest.startSet();
|
||||
replTest.initiate();
|
||||
var master = replTest.getMaster();
|
||||
var docNum = 100;
|
||||
for (var i = 0; i < docNum; i++) {
|
||||
master.getDB("foo").bar.insert({ a: i });
|
||||
}
|
||||
replTest.awaitReplication();
|
||||
|
||||
var replSetConnString = "rs/127.0.0.1:" + replTest.ports[0] + ",127.0.0.1:" + replTest.ports[1];
|
||||
|
||||
// Test with mongodump/mongorestore
|
||||
print("dump the db");
|
||||
var data = "/data/db/dumprestore11-dump1/";
|
||||
runMongoProgram("mongodump", "--host", replSetConnString, "--out", data);
|
||||
|
||||
master.getDB("foo").dropDatabase();
|
||||
replTest.awaitReplication();
|
||||
|
||||
print("restore the db");
|
||||
runMongoProgram("mongorestore", "--host", replSetConnString, "--dir", data);
|
||||
|
||||
var x = master.getDB("foo").getCollection("bar").count();
|
||||
assert.eq(x, docNum, "mongorestore should have successfully restored the collection" + docNum);
|
||||
|
||||
replTest.awaitReplication();
|
||||
|
||||
// Test with mongoexport/mongoimport
|
||||
print("export the collection");
|
||||
var extFile = "/data/db/exportimport_replSet/export";
|
||||
runMongoProgram("mongoexport", "--host", replSetConnString, "--out", extFile,
|
||||
"-d", "foo", "-c", "bar");
|
||||
|
||||
master.getDB("foo").getCollection("bar").drop();
|
||||
replTest.awaitReplication();
|
||||
|
||||
print("import the collection");
|
||||
runMongoProgram("mongoimport", "--host", replSetConnString, "--file", extFile,
|
||||
"-d", "foo", "-c", "bar");
|
||||
|
||||
var x = master.getDB("foo").getCollection("bar").count();
|
||||
assert.eq(x, docNum, "mongoimport should have successfully imported the collection" + docNum);
|
||||
|
||||
master.getDB("foo").getCollection("bar").drop();
|
||||
|
||||
// Test with mongooplog
|
||||
var doc = { _id : 5, x : 17 };
|
||||
master.getDB("local").oplog.rs.insert({ ts : new Timestamp(), "op" : "i", "ns" : "foo.bar",
|
||||
"o" : doc });
|
||||
|
||||
assert.eq(0, master.getDB("foo").getCollection("bar").count(), "before");
|
||||
|
||||
var replSetConnString = "rs/127.0.0.1:" + replTest.ports[0] + ",127.0.0.1:" + replTest.ports[1];
|
||||
runMongoProgram("mongooplog" , "--from", "127.0.0.1:" + replTest.ports[0],
|
||||
"--host", replSetConnString);
|
||||
|
||||
assert.eq(101, master.getDB("foo").getCollection("bar").count(), "after")
|
||||
|
||||
replTest.stopSet();
|
||||
113
jstests/update_arraymatch8.js
Normal file
113
jstests/update_arraymatch8.js
Normal file
@@ -0,0 +1,113 @@
|
||||
// Checking for positional array updates with either .$ or .0 at the end
|
||||
// SERVER-7511
|
||||
|
||||
// array.$.name
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'name': 'old'}]} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {'array.name': 'old'}, {$set: {'array.$.name': 'new'}} );
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( !t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// array.$ (failed in 2.2.2)
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'name': 'old'}]} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {'array.name': 'old'}, {$set: {'array.$': {'name':'new'}}} );
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( !t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// array.0.name
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'name': 'old'}]} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {'array.name': 'old'}, {$set: {'array.0.name': 'new'}} );
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( !t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// array.0 (failed in 2.2.2)
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'name': 'old'}]} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {'array.name': 'old'}, {$set: {'array.0': {'name':'new'}}} );
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( !t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// // array.12.name
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
arr = new Array();
|
||||
for (var i=0; i<20; i++) {
|
||||
arr.push({'name': 'old'});
|
||||
}
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {_id:0, 'array': arr} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {_id:0}, {$set: {'array.12.name': 'new'}} );
|
||||
// note: both documents now have to be in the array
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// array.12 (failed in 2.2.2)
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
arr = new Array();
|
||||
for (var i=0; i<20; i++) {
|
||||
arr.push({'name': 'old'});
|
||||
}
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {_id:0, 'array': arr} );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
t.update( {_id:0}, {$set: {'array.12': {'name':'new'}}} );
|
||||
// note: both documents now have to be in the array
|
||||
assert( t.findOne({'array.name': 'new'}) );
|
||||
assert( t.findOne({'array.name': 'old'}) );
|
||||
|
||||
// array.$.123a.name
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.123a.name': 1} );
|
||||
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
|
||||
assert( t.findOne({'array.123a.name': 'old'}) );
|
||||
t.update( {'array.123a.name': 'old'}, {$set: {'array.$.123a.name': 'new'}} );
|
||||
assert( t.findOne({'array.123a.name': 'new'}) );
|
||||
assert( !t.findOne({'array.123a.name': 'old'}) );
|
||||
|
||||
// array.$.123a
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
|
||||
assert( t.findOne({'array.123a.name': 'old'}) );
|
||||
t.update( {'array.123a.name': 'old'}, {$set: {'array.$.123a': {'name': 'new'}}} );
|
||||
assert( t.findOne({'array.123a.name': 'new'}) );
|
||||
assert( !t.findOne({'array.123a.name': 'old'}) );
|
||||
|
||||
// array.0.123a.name
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.123a.name': 1} );
|
||||
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
|
||||
assert( t.findOne({'array.123a.name': 'old'}) );
|
||||
t.update( {'array.123a.name': 'old'}, {$set: {'array.0.123a.name': 'new'}} );
|
||||
assert( t.findOne({'array.123a.name': 'new'}) );
|
||||
assert( !t.findOne({'array.123a.name': 'old'}) );
|
||||
|
||||
// array.0.123a
|
||||
t = db.jstests_update_arraymatch8;
|
||||
t.drop();
|
||||
t.ensureIndex( {'array.name': 1} );
|
||||
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
|
||||
assert( t.findOne({'array.123a.name': 'old'}) );
|
||||
t.update( {'array.123a.name': 'old'}, {$set: {'array.0.123a': {'name': 'new'}}} );
|
||||
assert( t.findOne({'array.123a.name': 'new'}) );
|
||||
assert( !t.findOne({'array.123a.name': 'old'}) );
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name: mongo-10gen
|
||||
Conflicts: mongo, mongo-10gen-unstable
|
||||
Obsoletes: mongo-stable
|
||||
Version: 2.2.1
|
||||
Version: 2.2.3
|
||||
Release: mongodb_1%{?dist}
|
||||
Summary: mongodb client shell and tools
|
||||
License: AGPL 3.0
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# This SConscript describes build rules for the "mongo" project.
|
||||
|
||||
import os
|
||||
import itertools
|
||||
from buildscripts import utils
|
||||
|
||||
Import("env")
|
||||
@@ -343,6 +344,16 @@ env.CppUnitTest( "balancer_policy_test" , [ "s/balancer_policy_tests.cpp" ] ,
|
||||
LIBDEPS=["mongoscore", "coreshard","mongocommon","coreserver","coredb","dbcmdline","mongodandmongos"] ,
|
||||
NO_CRUTCH=True)
|
||||
|
||||
env.CppUnitTest("shard_test", [ "s/shard_test.cpp" ],
|
||||
LIBDEPS=[ "mongoscore",
|
||||
"coreshard",
|
||||
"mongocommon",
|
||||
"coreserver",
|
||||
"coredb",
|
||||
"dbcmdline",
|
||||
"mongodandmongos"],
|
||||
NO_CRUTCH=True)
|
||||
|
||||
serverOnlyFiles += [ "s/d_logic.cpp",
|
||||
"s/d_writeback.cpp",
|
||||
"s/d_migrate.cpp",
|
||||
@@ -564,18 +575,36 @@ if installSetup.libraries:
|
||||
if has_option( "sharedclient" ):
|
||||
env.Install( "$INSTALL_DIR/$NIX_LIB_DIR", '#${SHLIBPREFIX}mongoclient${SHLIBSUFFIX}')
|
||||
|
||||
# Stage the top-level mongodb banners
|
||||
distsrc = env.Dir('#distsrc')
|
||||
env.Append(MODULE_BANNERS = [distsrc.File('README'),
|
||||
distsrc.File('THIRD-PARTY-NOTICES')])
|
||||
|
||||
# If no module has introduced a file named LICENSE.txt, then inject the AGPL.
|
||||
if sum(itertools.imap(lambda x: x.name == "LICENSE.txt", env['MODULE_BANNERS'])) == 0:
|
||||
env.Append(MODULE_BANNERS = [distsrc.File('GNU-AGPL-3.0')])
|
||||
|
||||
# All module banners get staged to the top level of the tarfile, so we
|
||||
# need to fail if we are going to have a name collision.
|
||||
module_banner_filenames = set([f.name for f in env['MODULE_BANNERS']])
|
||||
if not len(module_banner_filenames) == len(env['MODULE_BANNERS']):
|
||||
# TODO: Be nice and identify conflicts in error.
|
||||
print "ERROR: Filename conflicts exist in module banners."
|
||||
Exit(-1)
|
||||
|
||||
# Build a set of directories containing module banners, and use that
|
||||
# to build a --transform option for each directory so that the files
|
||||
# are tar'ed up to the proper location.
|
||||
module_banner_dirs = set([Dir('#').rel_path(f.get_dir()) for f in env['MODULE_BANNERS']])
|
||||
module_banner_transforms = ["--transform %s=$SERVER_DIST_BASENAME" % d for d in module_banner_dirs]
|
||||
|
||||
env.Command(
|
||||
'#/${SERVER_ARCHIVE}',
|
||||
['#buildscripts/make_archive.py',
|
||||
'#distsrc/GNU-AGPL-3.0',
|
||||
'#distsrc/README',
|
||||
'#distsrc/THIRD-PARTY-NOTICES',
|
||||
distBinaries],
|
||||
'$PYTHON ${SOURCES[0]} -o $TARGET '
|
||||
'--transform distsrc=$SERVER_DIST_BASENAME '
|
||||
'--transform ${str(Dir(BUILD_DIR))}/mongo/stripped=$SERVER_DIST_BASENAME/bin '
|
||||
'--transform ${str(Dir(BUILD_DIR))}/mongo=$SERVER_DIST_BASENAME/bin '
|
||||
'${TEMPFILE(SOURCES[1:])}')
|
||||
['#buildscripts/make_archive.py'] + env["MODULE_BANNERS"] + distBinaries,
|
||||
' '.join(['$PYTHON ${SOURCES[0]} -o $TARGET'] + module_banner_transforms + [
|
||||
'--transform ${str(Dir(BUILD_DIR))}/mongo/stripped=$SERVER_DIST_BASENAME/bin',
|
||||
'--transform ${str(Dir(BUILD_DIR))}/mongo=$SERVER_DIST_BASENAME/bin',
|
||||
'${TEMPFILE(SOURCES[1:])}']))
|
||||
|
||||
#final alias
|
||||
env.Alias( "install", "$INSTALL_DIR" )
|
||||
|
||||
@@ -154,6 +154,45 @@ namespace mongo {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ConnectionString::sameLogicalEndpoint( const ConnectionString& other ) const {
|
||||
if ( _type != other._type )
|
||||
return false;
|
||||
|
||||
switch ( _type ) {
|
||||
case INVALID:
|
||||
return true;
|
||||
case MASTER:
|
||||
return _servers[0] == other._servers[0];
|
||||
case PAIR:
|
||||
if ( _servers[0] == other._servers[0] )
|
||||
return _servers[1] == other._servers[1];
|
||||
return
|
||||
( _servers[0] == other._servers[1] ) &&
|
||||
( _servers[1] == other._servers[0] );
|
||||
case SET:
|
||||
return _setName == other._setName;
|
||||
case SYNC:
|
||||
// The servers all have to be the same in each, but not in the same order.
|
||||
if ( _servers.size() != other._servers.size() )
|
||||
return false;
|
||||
for ( unsigned i = 0; i < _servers.size(); i++ ) {
|
||||
bool found = false;
|
||||
for ( unsigned j = 0; j < other._servers.size(); j++ ) {
|
||||
if ( _servers[i] == other._servers[j] ) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ! found )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case CUSTOM:
|
||||
return _string == other._string;
|
||||
}
|
||||
verify( false );
|
||||
}
|
||||
|
||||
ConnectionString ConnectionString::parse( const string& host , string& errmsg ) {
|
||||
|
||||
string::size_type i = host.find( '/' );
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace mongo {
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ ) {
|
||||
if ( i > 0 )
|
||||
ss << ",";
|
||||
ss << _nodes[i].addr.toString();
|
||||
_nodes[i].addr.append( ss );
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
|
||||
@@ -248,6 +248,14 @@ namespace mongo {
|
||||
|
||||
ConnectionType type() const { return _type; }
|
||||
|
||||
/**
|
||||
* This returns true if this and other point to the same logical entity.
|
||||
* For single nodes, thats the same address.
|
||||
* For replica sets, thats just the same replica set name.
|
||||
* For pair (deprecated) or sync cluster connections, that's the same hosts in any ordering.
|
||||
*/
|
||||
bool sameLogicalEndpoint( const ConnectionString& other ) const;
|
||||
|
||||
static ConnectionString parse( const string& url , string& errmsg );
|
||||
|
||||
static string typeToString( ConnectionType type );
|
||||
|
||||
@@ -319,9 +319,9 @@ namespace mongo {
|
||||
_lockTimeout( lockTimeout == 0 ? LOCK_TIMEOUT : lockTimeout ), _maxClockSkew( _lockTimeout / LOCK_SKEW_FACTOR ), _maxNetSkew( _maxClockSkew ), _lockPing( _maxClockSkew ),
|
||||
_mutex( "DistributedLock" )
|
||||
{
|
||||
log( logLvl - 1 ) << "created new distributed lock for " << name << " on " << conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )" << endl;
|
||||
log( logLvl ) << "created new distributed lock for " << name << " on " << conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )" << endl;
|
||||
|
||||
|
||||
}
|
||||
@@ -492,6 +492,11 @@ namespace mongo {
|
||||
// This should always be true, if not, we are using the lock incorrectly.
|
||||
verify( _name != "" );
|
||||
|
||||
log( logLvl ) << "trying to acquire new distributed lock for " << _name << " on " << _conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << _processId << " )"
|
||||
<< endl;
|
||||
|
||||
// write to dummy if 'other' is null
|
||||
BSONObj dummyOther;
|
||||
if ( other == NULL )
|
||||
|
||||
@@ -384,11 +384,9 @@ namespace mongo {
|
||||
cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
|
||||
::_exit(-1);
|
||||
}
|
||||
|
||||
setupCoreSignals();
|
||||
setupSignals( true );
|
||||
}
|
||||
|
||||
setupSignals( true );
|
||||
|
||||
if (params.count("syslog")) {
|
||||
StringBuilder sb;
|
||||
sb << cmdLine.binaryName << "." << cmdLine.port;
|
||||
|
||||
@@ -270,7 +270,7 @@ namespace mongo {
|
||||
uassert(13330, "upsert mode requires query field", !origQuery.isEmpty());
|
||||
db.update(ns, origQuery, update.embeddedObjectUserCheck(), true);
|
||||
|
||||
BSONObj gle = db.getLastErrorDetailed();
|
||||
BSONObj gle = db.getLastErrorDetailed(dbname);
|
||||
result.append("lastErrorObject", gle);
|
||||
if (gle["err"].type() == String) {
|
||||
errmsg = gle["err"].String();
|
||||
@@ -292,7 +292,7 @@ namespace mongo {
|
||||
uassert(12515, "can't remove and update", cmdObj["update"].eoo());
|
||||
db.remove(ns, QUERY("_id" << out["_id"]), 1);
|
||||
|
||||
BSONObj gle = db.getLastErrorDetailed();
|
||||
BSONObj gle = db.getLastErrorDetailed(dbname);
|
||||
result.append("lastErrorObject", gle);
|
||||
if (gle["err"].type() == String) {
|
||||
errmsg = gle["err"].String();
|
||||
@@ -324,7 +324,7 @@ namespace mongo {
|
||||
uassert(12516, "must specify remove or update", !update.eoo());
|
||||
db.update(ns, q, update.embeddedObjectUserCheck());
|
||||
|
||||
BSONObj gle = db.getLastErrorDetailed();
|
||||
BSONObj gle = db.getLastErrorDetailed(dbname);
|
||||
result.append("lastErrorObject", gle);
|
||||
if (gle["err"].type() == String) {
|
||||
errmsg = gle["err"].String();
|
||||
|
||||
@@ -76,7 +76,8 @@ namespace mongo {
|
||||
extern bool checkNsFilesOnLoad;
|
||||
extern string repairpath;
|
||||
|
||||
void setupSignals( bool inFork );
|
||||
void setupQuittingSignals();
|
||||
void setupSignals( bool ignored );
|
||||
void startReplication();
|
||||
void exitCleanly( ExitCode code );
|
||||
|
||||
@@ -743,7 +744,7 @@ static int mongoDbMain(int argc, char* argv[]) {
|
||||
|
||||
|
||||
setupCoreSignals();
|
||||
setupSignals( false );
|
||||
setupQuittingSignals();
|
||||
|
||||
dbExecCommand = argv[0];
|
||||
|
||||
@@ -1229,7 +1230,7 @@ namespace mongo {
|
||||
|
||||
void setupSignals_ignoreHelper( int signal ) {}
|
||||
|
||||
void setupSignals( bool inFork ) {
|
||||
void setupQuittingSignals() {
|
||||
struct sigaction addrSignals;
|
||||
memset( &addrSignals, 0, sizeof( struct sigaction ) );
|
||||
addrSignals.sa_sigaction = abruptQuitWithAddrSignal;
|
||||
@@ -1247,20 +1248,16 @@ namespace mongo {
|
||||
|
||||
setupSIGTRAPforGDB();
|
||||
|
||||
set_terminate( myterminate );
|
||||
set_new_handler( my_new_handler );
|
||||
}
|
||||
|
||||
void setupSignals( bool ignored ) {
|
||||
sigemptyset( &asyncSignals );
|
||||
|
||||
if ( inFork )
|
||||
verify( signal( SIGHUP , setupSignals_ignoreHelper ) != SIG_ERR );
|
||||
else
|
||||
sigaddset( &asyncSignals, SIGHUP );
|
||||
|
||||
sigaddset( &asyncSignals, SIGINT );
|
||||
sigaddset( &asyncSignals, SIGTERM );
|
||||
verify( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 );
|
||||
boost::thread it( interruptThread );
|
||||
|
||||
set_terminate( myterminate );
|
||||
set_new_handler( my_new_handler );
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
@@ -1414,7 +1411,9 @@ namespace mongo {
|
||||
mongoAbort("pure virtual");
|
||||
}
|
||||
|
||||
void setupSignals( bool inFork ) {
|
||||
void setupSignals( bool inFork ) { }
|
||||
|
||||
void setupQuittingSignals() {
|
||||
reportEventToSystem = reportEventToSystemImpl;
|
||||
filtLast = SetUnhandledExceptionFilter(exceptionFilter);
|
||||
massert(10297 , "Couldn't register Windows Ctrl-C handler", SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE));
|
||||
|
||||
@@ -458,349 +458,23 @@ namespace mongo {
|
||||
double _errorSphere;
|
||||
};
|
||||
|
||||
class Box {
|
||||
public:
|
||||
|
||||
Box( const Geo2dType * g , const GeoHash& hash )
|
||||
: _min( g , hash ) ,
|
||||
_max( _min._x + g->sizeEdge( hash ) , _min._y + g->sizeEdge( hash ) ) {
|
||||
}
|
||||
|
||||
Box( double x , double y , double size )
|
||||
: _min( x , y ) ,
|
||||
_max( x + size , y + size ) {
|
||||
}
|
||||
|
||||
Box( Point min , Point max )
|
||||
: _min( min ) , _max( max ) {
|
||||
}
|
||||
|
||||
Box() {}
|
||||
|
||||
BSONArray toBSON() const {
|
||||
return BSON_ARRAY( BSON_ARRAY( _min._x << _min._y ) << BSON_ARRAY( _max._x << _max._y ) );
|
||||
}
|
||||
|
||||
string toString() const {
|
||||
StringBuilder buf;
|
||||
buf << _min.toString() << " -->> " << _max.toString();
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
bool between( double min , double max , double val , double fudge=0) const {
|
||||
return val + fudge >= min && val <= max + fudge;
|
||||
}
|
||||
|
||||
bool onBoundary( double bound, double val, double fudge = 0 ) const {
|
||||
return ( val >= bound - fudge && val <= bound + fudge );
|
||||
}
|
||||
|
||||
bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const {
|
||||
verify( amin <= amax );
|
||||
verify( bmin <= bmax );
|
||||
|
||||
if ( amin < bmin ) {
|
||||
if ( amax < bmin )
|
||||
return false;
|
||||
res = min ? bmin : amax;
|
||||
return true;
|
||||
}
|
||||
if ( amin > bmax )
|
||||
return false;
|
||||
res = min ? amin : bmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
double intersects( const Box& other ) const {
|
||||
|
||||
Point boundMin(0,0);
|
||||
Point boundMax(0,0);
|
||||
|
||||
if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false ||
|
||||
mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false ||
|
||||
mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false ||
|
||||
mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false )
|
||||
return 0;
|
||||
|
||||
Box intersection( boundMin , boundMax );
|
||||
|
||||
return intersection.area() / area();
|
||||
}
|
||||
|
||||
double area() const {
|
||||
return ( _max._x - _min._x ) * ( _max._y - _min._y );
|
||||
}
|
||||
|
||||
double maxDim() const {
|
||||
return max( _max._x - _min._x, _max._y - _min._y );
|
||||
}
|
||||
|
||||
Point center() const {
|
||||
return Point( ( _min._x + _max._x ) / 2 ,
|
||||
( _min._y + _max._y ) / 2 );
|
||||
}
|
||||
|
||||
void truncate( const Geo2dType* g ) {
|
||||
if( _min._x < g->_min ) _min._x = g->_min;
|
||||
if( _min._y < g->_min ) _min._y = g->_min;
|
||||
if( _max._x > g->_max ) _max._x = g->_max;
|
||||
if( _max._y > g->_max ) _max._y = g->_max;
|
||||
}
|
||||
|
||||
void fudge( const Geo2dType* g ) {
|
||||
_min._x -= g->_error;
|
||||
_min._y -= g->_error;
|
||||
_max._x += g->_error;
|
||||
_max._y += g->_error;
|
||||
}
|
||||
|
||||
bool onBoundary( Point p, double fudge = 0 ) {
|
||||
return onBoundary( _min._x, p._x, fudge ) ||
|
||||
onBoundary( _max._x, p._x, fudge ) ||
|
||||
onBoundary( _min._y, p._y, fudge ) ||
|
||||
onBoundary( _max._y, p._y, fudge );
|
||||
}
|
||||
|
||||
bool inside( Point p , double fudge = 0 ) {
|
||||
bool res = inside( p._x , p._y , fudge );
|
||||
//cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool inside( double x , double y , double fudge = 0 ) {
|
||||
return
|
||||
between( _min._x , _max._x , x , fudge ) &&
|
||||
between( _min._y , _max._y , y , fudge );
|
||||
}
|
||||
|
||||
bool contains(const Box& other, double fudge=0) {
|
||||
return inside(other._min, fudge) && inside(other._max, fudge);
|
||||
}
|
||||
|
||||
Point _min;
|
||||
Point _max;
|
||||
};
|
||||
|
||||
|
||||
class Polygon {
|
||||
public:
|
||||
|
||||
Polygon( void ) : _centroidCalculated( false ) {}
|
||||
|
||||
Polygon( vector<Point> points ) : _centroidCalculated( false ),
|
||||
_points( points ) { }
|
||||
|
||||
void add( Point p ) {
|
||||
_centroidCalculated = false;
|
||||
_points.push_back( p );
|
||||
}
|
||||
|
||||
int size( void ) const {
|
||||
return _points.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the point supplied is contained by the current polygon.
|
||||
*
|
||||
* The algorithm uses a ray casting method.
|
||||
*/
|
||||
bool contains( const Point& p ) const {
|
||||
return contains( p, 0 ) > 0;
|
||||
}
|
||||
|
||||
int contains( const Point &p, double fudge ) const {
|
||||
|
||||
Box fudgeBox( Point( p._x - fudge, p._y - fudge ), Point( p._x + fudge, p._y + fudge ) );
|
||||
|
||||
int counter = 0;
|
||||
Point p1 = _points[0];
|
||||
for ( int i = 1; i <= size(); i++ ) {
|
||||
Point p2 = _points[i % size()];
|
||||
|
||||
GEODEBUG( "Doing intersection check of " << fudgeBox.toString() << " with seg " << p1.toString() << " to " << p2.toString() );
|
||||
|
||||
// We need to check whether or not this segment intersects our error box
|
||||
if( fudge > 0 &&
|
||||
// Points not too far below box
|
||||
fudgeBox._min._y <= std::max( p1._y, p2._y ) &&
|
||||
// Points not too far above box
|
||||
fudgeBox._max._y >= std::min( p1._y, p2._y ) &&
|
||||
// Points not too far to left of box
|
||||
fudgeBox._min._x <= std::max( p1._x, p2._x ) &&
|
||||
// Points not too far to right of box
|
||||
fudgeBox._max._x >= std::min( p1._x, p2._x ) ) {
|
||||
|
||||
GEODEBUG( "Doing detailed check" );
|
||||
|
||||
// If our box contains one or more of these points, we need to do an exact check.
|
||||
if( fudgeBox.inside(p1) ) {
|
||||
GEODEBUG( "Point 1 inside" );
|
||||
return 0;
|
||||
}
|
||||
if( fudgeBox.inside(p2) ) {
|
||||
GEODEBUG( "Point 2 inside" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do intersection check for vertical sides
|
||||
if ( p1._y != p2._y ) {
|
||||
|
||||
double invSlope = ( p2._x - p1._x ) / ( p2._y - p1._y );
|
||||
|
||||
double xintersT = ( fudgeBox._max._y - p1._y ) * invSlope + p1._x;
|
||||
if( fudgeBox._min._x <= xintersT && fudgeBox._max._x >= xintersT ) {
|
||||
GEODEBUG( "Top intersection @ " << xintersT );
|
||||
return 0;
|
||||
}
|
||||
|
||||
double xintersB = ( fudgeBox._min._y - p1._y ) * invSlope + p1._x;
|
||||
if( fudgeBox._min._x <= xintersB && fudgeBox._max._x >= xintersB ) {
|
||||
GEODEBUG( "Bottom intersection @ " << xintersB );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Do intersection check for horizontal sides
|
||||
if( p1._x != p2._x ) {
|
||||
|
||||
double slope = ( p2._y - p1._y ) / ( p2._x - p1._x );
|
||||
|
||||
double yintersR = ( p1._x - fudgeBox._max._x ) * slope + p1._y;
|
||||
if( fudgeBox._min._y <= yintersR && fudgeBox._max._y >= yintersR ) {
|
||||
GEODEBUG( "Right intersection @ " << yintersR );
|
||||
return 0;
|
||||
}
|
||||
|
||||
double yintersL = ( p1._x - fudgeBox._min._x ) * slope + p1._y;
|
||||
if( fudgeBox._min._y <= yintersL && fudgeBox._max._y >= yintersL ) {
|
||||
GEODEBUG( "Left intersection @ " << yintersL );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if( fudge == 0 ){
|
||||
|
||||
// If this is an exact vertex, we won't intersect, so check this
|
||||
if( p._y == p1._y && p._x == p1._x ) return 1;
|
||||
else if( p._y == p2._y && p._x == p2._x ) return 1;
|
||||
|
||||
// If this is a horizontal line we won't intersect, so check this
|
||||
if( p1._y == p2._y && p._y == p1._y ){
|
||||
// Check that the x-coord lies in the line
|
||||
if( p._x >= std::min( p1._x, p2._x ) && p._x <= std::max( p1._x, p2._x ) ) return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Normal intersection test.
|
||||
// TODO: Invert these for clearer logic?
|
||||
if ( p._y > std::min( p1._y, p2._y ) ) {
|
||||
if ( p._y <= std::max( p1._y, p2._y ) ) {
|
||||
if ( p._x <= std::max( p1._x, p2._x ) ) {
|
||||
if ( p1._y != p2._y ) {
|
||||
double xinters = (p._y-p1._y)*(p2._x-p1._x)/(p2._y-p1._y)+p1._x;
|
||||
// Special case of point on vertical line
|
||||
if ( p1._x == p2._x && p._x == p1._x ){
|
||||
|
||||
// Need special case for the vertical edges, for example:
|
||||
// 1) \e pe/----->
|
||||
// vs.
|
||||
// 2) \ep---e/----->
|
||||
//
|
||||
// if we count exact as intersection, then 1 is in but 2 is out
|
||||
// if we count exact as no-int then 1 is out but 2 is in.
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if( p1._x == p2._x || p._x <= xinters ) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p1 = p2;
|
||||
}
|
||||
|
||||
if ( counter % 2 == 0 ) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the centroid, or center of mass of the polygon object.
|
||||
*/
|
||||
Point centroid( void ) {
|
||||
|
||||
/* Centroid is cached, it won't change betwen points */
|
||||
if ( _centroidCalculated ) {
|
||||
return _centroid;
|
||||
}
|
||||
|
||||
Point cent;
|
||||
double signedArea = 0.0;
|
||||
double area = 0.0; // Partial signed area
|
||||
|
||||
/// For all vertices except last
|
||||
int i = 0;
|
||||
for ( i = 0; i < size() - 1; ++i ) {
|
||||
area = _points[i]._x * _points[i+1]._y - _points[i+1]._x * _points[i]._y ;
|
||||
signedArea += area;
|
||||
cent._x += ( _points[i]._x + _points[i+1]._x ) * area;
|
||||
cent._y += ( _points[i]._y + _points[i+1]._y ) * area;
|
||||
}
|
||||
|
||||
// Do last vertex
|
||||
area = _points[i]._x * _points[0]._y - _points[0]._x * _points[i]._y;
|
||||
cent._x += ( _points[i]._x + _points[0]._x ) * area;
|
||||
cent._y += ( _points[i]._y + _points[0]._y ) * area;
|
||||
signedArea += area;
|
||||
signedArea *= 0.5;
|
||||
cent._x /= ( 6 * signedArea );
|
||||
cent._y /= ( 6 * signedArea );
|
||||
|
||||
_centroidCalculated = true;
|
||||
_centroid = cent;
|
||||
|
||||
return cent;
|
||||
}
|
||||
|
||||
Box bounds( void ) {
|
||||
|
||||
// TODO: Cache this
|
||||
|
||||
_bounds._max = _points[0];
|
||||
_bounds._min = _points[0];
|
||||
|
||||
for ( int i = 1; i < size(); i++ ) {
|
||||
|
||||
_bounds._max._x = max( _bounds._max._x, _points[i]._x );
|
||||
_bounds._max._y = max( _bounds._max._y, _points[i]._y );
|
||||
_bounds._min._x = min( _bounds._min._x, _points[i]._x );
|
||||
_bounds._min._y = min( _bounds._min._y, _points[i]._y );
|
||||
|
||||
}
|
||||
|
||||
return _bounds;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool _centroidCalculated;
|
||||
Point _centroid;
|
||||
|
||||
Box _bounds;
|
||||
|
||||
vector<Point> _points;
|
||||
};
|
||||
// Equal to Point::Point(args)
|
||||
inline Point pointFromHash(const GeoConvert* g, const GeoHash& hash) {
|
||||
double x, y;
|
||||
g->unhash(hash, x, y);
|
||||
return Point(x, y);
|
||||
}
|
||||
// Point::hash
|
||||
inline GeoHash pointToHash(Point p, const GeoConvert* g) {
|
||||
return g->hash(p._x, p._y);
|
||||
}
|
||||
// Box::Box(...)
|
||||
inline Box boxFromHash(const Geo2dType* g, const GeoHash& hash) {
|
||||
Point min = pointFromHash(g, hash);
|
||||
double edgeSize = g->sizeEdge(hash);
|
||||
Point max = Point(min._x + edgeSize, min._y + edgeSize);
|
||||
return Box(min, max);
|
||||
}
|
||||
|
||||
class Geo2dPlugin : public IndexPlugin {
|
||||
public:
|
||||
@@ -1024,7 +698,7 @@ namespace mongo {
|
||||
// Approximate distance check using key data
|
||||
////
|
||||
double keyD = 0;
|
||||
Point keyP( _g, GeoHash( node._key.firstElement(), _g->_bits ) );
|
||||
Point keyP(pointFromHash(_g, GeoHash( node._key.firstElement(), _g->_bits )));
|
||||
KeyResult keyOk = approxKeyCheck( keyP, keyD );
|
||||
if ( keyOk == BAD ) {
|
||||
GEODEBUG( "\t\t\t\t bad distance : " << node.recordLoc.obj() << "\t" << keyD );
|
||||
@@ -1664,7 +1338,7 @@ namespace mongo {
|
||||
|
||||
if( ! isNeighbor ) {
|
||||
_centerPrefix = _prefix;
|
||||
_centerBox = Box( _g, _centerPrefix );
|
||||
_centerBox = boxFromHash( _g, _centerPrefix );
|
||||
isNeighbor = true;
|
||||
}
|
||||
|
||||
@@ -1702,7 +1376,7 @@ namespace mongo {
|
||||
while( _fringe.size() > 0 ) {
|
||||
|
||||
_prefix = _neighborPrefix + _fringe.back();
|
||||
Box cur( _g , _prefix );
|
||||
Box cur(boxFromHash( _g , _prefix ));
|
||||
|
||||
PREFIXDEBUG( _prefix, _g );
|
||||
|
||||
@@ -1874,7 +1548,7 @@ namespace mongo {
|
||||
BSONObjBuilder bob;
|
||||
BSONArrayBuilder bab;
|
||||
for( i = _expPrefixes.begin(); i != _expPrefixes.end(); ++i ){
|
||||
bab << Box( _g, *i ).toBSON();
|
||||
bab << Box(boxFromHash( _g, *i )).toBSON();
|
||||
}
|
||||
bob << _g->_geo << bab.arr();
|
||||
|
||||
@@ -2626,7 +2300,7 @@ namespace mongo {
|
||||
_want._max = Point( i.next() );
|
||||
|
||||
_wantRegion = _want;
|
||||
_wantRegion.fudge( g ); // Need to make sure we're checking regions within error bounds of where we want
|
||||
_wantRegion.fudge( g->_error ); // Need to make sure we're checking regions within error bounds of where we want
|
||||
fixBox( g, _wantRegion );
|
||||
fixBox( g, _want );
|
||||
|
||||
@@ -2716,8 +2390,8 @@ namespace mongo {
|
||||
uassert( 14030, "polygon must be defined by three points or more", _poly.size() >= 3 );
|
||||
|
||||
_bounds = _poly.bounds();
|
||||
_bounds.fudge( g ); // We need to check regions within the error bounds of these bounds
|
||||
_bounds.truncate( g ); // We don't need to look anywhere outside the space
|
||||
_bounds.fudge( g->_error ); // We need to check regions within the error bounds of these bounds
|
||||
_bounds.truncate( g->_min, g->_max ); // We don't need to look anywhere outside the space
|
||||
|
||||
_maxDim = _g->_error + _bounds.maxDim() / 2;
|
||||
|
||||
@@ -2767,6 +2441,9 @@ namespace mongo {
|
||||
else if ( numWanted == 0 )
|
||||
numWanted = 100;
|
||||
|
||||
// false means we want to filter OUT geoFieldsToNuke, not filter to include only that.
|
||||
BSONObj filteredQuery = query.filterFieldsUndotted(BSON(_geo << ""), false);
|
||||
|
||||
BSONObjIterator i(query);
|
||||
while ( i.more() ) {
|
||||
BSONElement e = i.next();
|
||||
@@ -2776,14 +2453,10 @@ namespace mongo {
|
||||
|
||||
if ( e.type() == Array ) {
|
||||
// If we get an array query, assume it is a location, and do a $within { $center : [[x, y], 0] } search
|
||||
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), query.filterFieldsUndotted( BSON( _geo << "" ), false ), "$center", true ) );
|
||||
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), filteredQuery, "$center", true ) );
|
||||
return c;
|
||||
}
|
||||
else if ( e.type() == Object ) {
|
||||
|
||||
// TODO: Filter out _geo : { $special... } field so it doesn't get matched accidentally,
|
||||
// if matcher changes
|
||||
|
||||
switch ( e.embeddedObject().firstElement().getGtLtOp() ) {
|
||||
case BSONObj::opNEAR: {
|
||||
BSONObj n = e.embeddedObject();
|
||||
@@ -2820,7 +2493,8 @@ namespace mongo {
|
||||
bool uniqueDocs = false;
|
||||
if( ! n["$uniqueDocs"].eoo() ) uniqueDocs = n["$uniqueDocs"].trueValue();
|
||||
|
||||
shared_ptr<GeoSearch> s( new GeoSearch( this , Point( e ) , numWanted , query , maxDistance, type, uniqueDocs ) );
|
||||
shared_ptr<GeoSearch> s( new GeoSearch( this , Point( e ) , numWanted ,
|
||||
filteredQuery , maxDistance, type, uniqueDocs ) );
|
||||
s->exec();
|
||||
shared_ptr<Cursor> c;
|
||||
c.reset( new GeoSearchCursor( s ) );
|
||||
@@ -2840,24 +2514,27 @@ namespace mongo {
|
||||
|
||||
if ( startsWith(type, "$center") ) {
|
||||
uassert( 13059 , "$center has to take an object or array" , e.isABSONObj() );
|
||||
shared_ptr<Cursor> c( new GeoCircleBrowse( this , e.embeddedObjectUserCheck() , query , type, uniqueDocs ) );
|
||||
shared_ptr<Cursor> c(new GeoCircleBrowse(this, e.embeddedObjectUserCheck(),
|
||||
filteredQuery, type, uniqueDocs));
|
||||
return c;
|
||||
}
|
||||
else if ( type == "$box" ) {
|
||||
uassert( 13065 , "$box has to take an object or array" , e.isABSONObj() );
|
||||
shared_ptr<Cursor> c( new GeoBoxBrowse( this , e.embeddedObjectUserCheck() , query, uniqueDocs ) );
|
||||
shared_ptr<Cursor> c(new GeoBoxBrowse(this, e.embeddedObjectUserCheck(),
|
||||
filteredQuery, uniqueDocs));
|
||||
return c;
|
||||
}
|
||||
else if ( startsWith( type, "$poly" ) ) {
|
||||
uassert( 14029 , "$polygon has to take an object or array" , e.isABSONObj() );
|
||||
shared_ptr<Cursor> c( new GeoPolygonBrowse( this , e.embeddedObjectUserCheck() , query, uniqueDocs ) );
|
||||
shared_ptr<Cursor> c(new GeoPolygonBrowse(this, e.embeddedObjectUserCheck(),
|
||||
filteredQuery, uniqueDocs));
|
||||
return c;
|
||||
}
|
||||
throw UserException( 13058 , str::stream() << "unknown $within information : " << context << ", a shape must be specified." );
|
||||
}
|
||||
default:
|
||||
// Otherwise... assume the object defines a point, and we want to do a zero-radius $within $center
|
||||
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), query.filterFieldsUndotted( BSON( _geo << "" ), false ) ) );
|
||||
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), filteredQuery));
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "mongo/pch.h"
|
||||
#include "../jsobj.h"
|
||||
#include "shapes.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -401,106 +402,6 @@ namespace mongo {
|
||||
virtual GeoHash hash( double x , double y ) const = 0;
|
||||
};
|
||||
|
||||
class Point {
|
||||
public:
|
||||
|
||||
Point( const GeoConvert * g , const GeoHash& hash ) {
|
||||
g->unhash( hash , _x , _y );
|
||||
}
|
||||
|
||||
explicit Point( const BSONElement& e ) {
|
||||
BSONObjIterator i(e.Obj());
|
||||
_x = i.next().number();
|
||||
_y = i.next().number();
|
||||
}
|
||||
|
||||
explicit Point( const BSONObj& o ) {
|
||||
BSONObjIterator i(o);
|
||||
_x = i.next().number();
|
||||
_y = i.next().number();
|
||||
}
|
||||
|
||||
Point( double x , double y )
|
||||
: _x( x ) , _y( y ) {
|
||||
}
|
||||
|
||||
Point() : _x(0),_y(0) {
|
||||
}
|
||||
|
||||
GeoHash hash( const GeoConvert * g ) {
|
||||
return g->hash( _x , _y );
|
||||
}
|
||||
|
||||
double distance( const Point& p ) const {
|
||||
double a = _x - p._x;
|
||||
double b = _y - p._y;
|
||||
|
||||
// Avoid numerical error if possible...
|
||||
if( a == 0 ) return abs( _y - p._y );
|
||||
if( b == 0 ) return abs( _x - p._x );
|
||||
|
||||
return sqrt( ( a * a ) + ( b * b ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance method that compares x or y coords when other direction is zero,
|
||||
* avoids numerical error when distances are very close to radius but axis-aligned.
|
||||
*
|
||||
* An example of the problem is:
|
||||
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
|
||||
* but:
|
||||
* 51.9999 + 0.0001 <= 52.0
|
||||
*
|
||||
* This avoids some (but not all!) suprising results in $center queries where points are
|
||||
* ( radius + center.x, center.y ) or vice-versa.
|
||||
*/
|
||||
bool distanceWithin( const Point& p, double radius ) const {
|
||||
double a = _x - p._x;
|
||||
double b = _y - p._y;
|
||||
|
||||
if( a == 0 ) {
|
||||
//
|
||||
// Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
|
||||
// calculated imprecisely. We need to force the compiler to always evaluate it correctly,
|
||||
// hence the weirdness.
|
||||
//
|
||||
// On some 32-bit linux machines, removing the volatile keyword or calculating the sum inline
|
||||
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
|
||||
// not just affected systems.
|
||||
if( sizeof(void*) <= 4 ){
|
||||
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
|
||||
return _y > p._y ? sum >= _y : sum >= p._y;
|
||||
}
|
||||
else {
|
||||
// Original math, correct for most systems
|
||||
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
|
||||
}
|
||||
}
|
||||
if( b == 0 ) {
|
||||
if( sizeof(void*) <= 4 ){
|
||||
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
|
||||
return _x > p._x ? sum >= _x : sum >= p._x;
|
||||
}
|
||||
else {
|
||||
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
|
||||
}
|
||||
}
|
||||
|
||||
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
|
||||
}
|
||||
|
||||
string toString() const {
|
||||
StringBuilder buf;
|
||||
buf << "(" << _x << "," << _y << ")";
|
||||
return buf.str();
|
||||
|
||||
}
|
||||
|
||||
double _x;
|
||||
double _y;
|
||||
};
|
||||
|
||||
|
||||
extern const double EARTH_RADIUS_KM;
|
||||
extern const double EARTH_RADIUS_MILES;
|
||||
|
||||
|
||||
455
src/mongo/db/geo/shapes.h
Normal file
455
src/mongo/db/geo/shapes.h
Normal file
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* Copyright (C) 2012 10gen 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef GEODEBUG
|
||||
#define GEODEBUG(x)
|
||||
#endif
|
||||
|
||||
namespace mongo {
|
||||
class Point {
|
||||
public:
|
||||
explicit Point( const BSONElement& e ) {
|
||||
BSONObjIterator i(e.Obj());
|
||||
_x = i.next().number();
|
||||
_y = i.next().number();
|
||||
}
|
||||
|
||||
explicit Point( const BSONObj& o ) {
|
||||
BSONObjIterator i(o);
|
||||
_x = i.next().number();
|
||||
_y = i.next().number();
|
||||
}
|
||||
|
||||
Point( double x , double y )
|
||||
: _x( x ) , _y( y ) {
|
||||
}
|
||||
|
||||
Point() : _x(0),_y(0) {
|
||||
}
|
||||
|
||||
double distance( const Point& p ) const {
|
||||
double a = _x - p._x;
|
||||
double b = _y - p._y;
|
||||
|
||||
// Avoid numerical error if possible...
|
||||
if( a == 0 ) return abs( _y - p._y );
|
||||
if( b == 0 ) return abs( _x - p._x );
|
||||
|
||||
return sqrt( ( a * a ) + ( b * b ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance method that compares x or y coords when other direction is zero,
|
||||
* avoids numerical error when distances are very close to radius but axis-aligned.
|
||||
*
|
||||
* An example of the problem is:
|
||||
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
|
||||
* but:
|
||||
* 51.9999 + 0.0001 <= 52.0
|
||||
*
|
||||
* This avoids some (but not all!) suprising results in $center queries where points are
|
||||
* ( radius + center.x, center.y ) or vice-versa.
|
||||
*/
|
||||
bool distanceWithin( const Point& p, double radius ) const {
|
||||
double a = _x - p._x;
|
||||
double b = _y - p._y;
|
||||
|
||||
if( a == 0 ) {
|
||||
//
|
||||
// Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
|
||||
// calculated imprecisely. We need to force the compiler to always evaluate it correctly,
|
||||
// hence the weirdness.
|
||||
//
|
||||
// On some 32-bit linux machines, removing the volatile keyword or calculating the sum inline
|
||||
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
|
||||
// not just affected systems.
|
||||
if( sizeof(void*) <= 4 ){
|
||||
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
|
||||
return _y > p._y ? sum >= _y : sum >= p._y;
|
||||
}
|
||||
else {
|
||||
// Original math, correct for most systems
|
||||
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
|
||||
}
|
||||
}
|
||||
if( b == 0 ) {
|
||||
if( sizeof(void*) <= 4 ){
|
||||
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
|
||||
return _x > p._x ? sum >= _x : sum >= p._x;
|
||||
}
|
||||
else {
|
||||
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
|
||||
}
|
||||
}
|
||||
|
||||
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
|
||||
}
|
||||
|
||||
string toString() const {
|
||||
StringBuilder buf;
|
||||
buf << "(" << _x << "," << _y << ")";
|
||||
return buf.str();
|
||||
|
||||
}
|
||||
|
||||
double _x;
|
||||
double _y;
|
||||
};
|
||||
|
||||
|
||||
class Box {
|
||||
public:
|
||||
Box( double x , double y , double size )
|
||||
: _min( x , y ) ,
|
||||
_max( x + size , y + size ) {
|
||||
}
|
||||
|
||||
Box( Point min , Point max )
|
||||
: _min( min ) , _max( max ) {
|
||||
}
|
||||
|
||||
Box() {}
|
||||
|
||||
BSONArray toBSON() const {
|
||||
return BSON_ARRAY( BSON_ARRAY( _min._x << _min._y ) << BSON_ARRAY( _max._x << _max._y ) );
|
||||
}
|
||||
|
||||
string toString() const {
|
||||
StringBuilder buf;
|
||||
buf << _min.toString() << " -->> " << _max.toString();
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
bool between( double min , double max , double val , double fudge=0) const {
|
||||
return val + fudge >= min && val <= max + fudge;
|
||||
}
|
||||
|
||||
bool onBoundary( double bound, double val, double fudge = 0 ) const {
|
||||
return ( val >= bound - fudge && val <= bound + fudge );
|
||||
}
|
||||
|
||||
bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const {
|
||||
verify( amin <= amax );
|
||||
verify( bmin <= bmax );
|
||||
|
||||
if ( amin < bmin ) {
|
||||
if ( amax < bmin )
|
||||
return false;
|
||||
res = min ? bmin : amax;
|
||||
return true;
|
||||
}
|
||||
if ( amin > bmax )
|
||||
return false;
|
||||
res = min ? amin : bmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
double intersects( const Box& other ) const {
|
||||
|
||||
Point boundMin(0,0);
|
||||
Point boundMax(0,0);
|
||||
|
||||
if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false ||
|
||||
mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false ||
|
||||
mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false ||
|
||||
mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false )
|
||||
return 0;
|
||||
|
||||
Box intersection( boundMin , boundMax );
|
||||
|
||||
return intersection.area() / area();
|
||||
}
|
||||
|
||||
double area() const {
|
||||
return ( _max._x - _min._x ) * ( _max._y - _min._y );
|
||||
}
|
||||
|
||||
double maxDim() const {
|
||||
return max( _max._x - _min._x, _max._y - _min._y );
|
||||
}
|
||||
// pointFromHash
|
||||
// pointToHash
|
||||
// boxFromHash
|
||||
|
||||
Point center() const {
|
||||
return Point( ( _min._x + _max._x ) / 2 ,
|
||||
( _min._y + _max._y ) / 2 );
|
||||
}
|
||||
|
||||
void truncate(double min, double max) {
|
||||
if( _min._x < min ) _min._x = min;
|
||||
if( _min._y < min ) _min._y = min;
|
||||
if( _max._x > max ) _max._x = max;
|
||||
if( _max._y > max ) _max._y = max;
|
||||
}
|
||||
|
||||
void fudge(double error) {
|
||||
_min._x -= error;
|
||||
_min._y -= error;
|
||||
_max._x += error;
|
||||
_max._y += error;
|
||||
}
|
||||
|
||||
bool onBoundary( Point p, double fudge = 0 ) {
|
||||
return onBoundary( _min._x, p._x, fudge ) ||
|
||||
onBoundary( _max._x, p._x, fudge ) ||
|
||||
onBoundary( _min._y, p._y, fudge ) ||
|
||||
onBoundary( _max._y, p._y, fudge );
|
||||
}
|
||||
|
||||
bool inside( Point p , double fudge = 0 ) const {
|
||||
bool res = inside( p._x , p._y , fudge );
|
||||
//cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool inside( double x , double y , double fudge = 0 ) const {
|
||||
return
|
||||
between( _min._x , _max._x , x , fudge ) &&
|
||||
between( _min._y , _max._y , y , fudge );
|
||||
}
|
||||
|
||||
bool contains(const Box& other, double fudge=0) {
|
||||
return inside(other._min, fudge) && inside(other._max, fudge);
|
||||
}
|
||||
|
||||
Point _min;
|
||||
Point _max;
|
||||
};
|
||||
|
||||
|
||||
class Polygon {
|
||||
public:
|
||||
|
||||
Polygon( void ) : _centroidCalculated( false ) {}
|
||||
|
||||
Polygon( vector<Point> points ) : _centroidCalculated( false ),
|
||||
_points( points ) { }
|
||||
|
||||
void add( Point p ) {
|
||||
_centroidCalculated = false;
|
||||
_points.push_back( p );
|
||||
}
|
||||
|
||||
int size( void ) const {
|
||||
return _points.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the point supplied is contained by the current polygon.
|
||||
*
|
||||
* The algorithm uses a ray casting method.
|
||||
*/
|
||||
bool contains( const Point& p ) const {
|
||||
return contains( p, 0 ) > 0;
|
||||
}
|
||||
|
||||
int contains( const Point &p, double fudge ) const {
|
||||
|
||||
Box fudgeBox( Point( p._x - fudge, p._y - fudge ), Point( p._x + fudge, p._y + fudge ) );
|
||||
|
||||
int counter = 0;
|
||||
Point p1 = _points[0];
|
||||
for ( int i = 1; i <= size(); i++ ) {
|
||||
Point p2 = _points[i % size()];
|
||||
|
||||
GEODEBUG( "Doing intersection check of " << fudgeBox.toString() << " with seg " << p1.toString() << " to " << p2.toString() );
|
||||
|
||||
// We need to check whether or not this segment intersects our error box
|
||||
if( fudge > 0 &&
|
||||
// Points not too far below box
|
||||
fudgeBox._min._y <= std::max( p1._y, p2._y ) &&
|
||||
// Points not too far above box
|
||||
fudgeBox._max._y >= std::min( p1._y, p2._y ) &&
|
||||
// Points not too far to left of box
|
||||
fudgeBox._min._x <= std::max( p1._x, p2._x ) &&
|
||||
// Points not too far to right of box
|
||||
fudgeBox._max._x >= std::min( p1._x, p2._x ) ) {
|
||||
|
||||
GEODEBUG( "Doing detailed check" );
|
||||
|
||||
// If our box contains one or more of these points, we need to do an exact check.
|
||||
if( fudgeBox.inside(p1) ) {
|
||||
GEODEBUG( "Point 1 inside" );
|
||||
return 0;
|
||||
}
|
||||
if( fudgeBox.inside(p2) ) {
|
||||
GEODEBUG( "Point 2 inside" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do intersection check for vertical sides
|
||||
if ( p1._y != p2._y ) {
|
||||
|
||||
double invSlope = ( p2._x - p1._x ) / ( p2._y - p1._y );
|
||||
|
||||
double xintersT = ( fudgeBox._max._y - p1._y ) * invSlope + p1._x;
|
||||
if( fudgeBox._min._x <= xintersT && fudgeBox._max._x >= xintersT ) {
|
||||
GEODEBUG( "Top intersection @ " << xintersT );
|
||||
return 0;
|
||||
}
|
||||
|
||||
double xintersB = ( fudgeBox._min._y - p1._y ) * invSlope + p1._x;
|
||||
if( fudgeBox._min._x <= xintersB && fudgeBox._max._x >= xintersB ) {
|
||||
GEODEBUG( "Bottom intersection @ " << xintersB );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Do intersection check for horizontal sides
|
||||
if( p1._x != p2._x ) {
|
||||
|
||||
double slope = ( p2._y - p1._y ) / ( p2._x - p1._x );
|
||||
|
||||
double yintersR = ( p1._x - fudgeBox._max._x ) * slope + p1._y;
|
||||
if( fudgeBox._min._y <= yintersR && fudgeBox._max._y >= yintersR ) {
|
||||
GEODEBUG( "Right intersection @ " << yintersR );
|
||||
return 0;
|
||||
}
|
||||
|
||||
double yintersL = ( p1._x - fudgeBox._min._x ) * slope + p1._y;
|
||||
if( fudgeBox._min._y <= yintersL && fudgeBox._max._y >= yintersL ) {
|
||||
GEODEBUG( "Left intersection @ " << yintersL );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if( fudge == 0 ){
|
||||
|
||||
// If this is an exact vertex, we won't intersect, so check this
|
||||
if( p._y == p1._y && p._x == p1._x ) return 1;
|
||||
else if( p._y == p2._y && p._x == p2._x ) return 1;
|
||||
|
||||
// If this is a horizontal line we won't intersect, so check this
|
||||
if( p1._y == p2._y && p._y == p1._y ){
|
||||
// Check that the x-coord lies in the line
|
||||
if( p._x >= std::min( p1._x, p2._x ) && p._x <= std::max( p1._x, p2._x ) ) return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Normal intersection test.
|
||||
// TODO: Invert these for clearer logic?
|
||||
if ( p._y > std::min( p1._y, p2._y ) ) {
|
||||
if ( p._y <= std::max( p1._y, p2._y ) ) {
|
||||
if ( p._x <= std::max( p1._x, p2._x ) ) {
|
||||
if ( p1._y != p2._y ) {
|
||||
double xinters = (p._y-p1._y)*(p2._x-p1._x)/(p2._y-p1._y)+p1._x;
|
||||
// Special case of point on vertical line
|
||||
if ( p1._x == p2._x && p._x == p1._x ){
|
||||
|
||||
// Need special case for the vertical edges, for example:
|
||||
// 1) \e pe/----->
|
||||
// vs.
|
||||
// 2) \ep---e/----->
|
||||
//
|
||||
// if we count exact as intersection, then 1 is in but 2 is out
|
||||
// if we count exact as no-int then 1 is out but 2 is in.
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if( p1._x == p2._x || p._x <= xinters ) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p1 = p2;
|
||||
}
|
||||
|
||||
if ( counter % 2 == 0 ) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the centroid, or center of mass of the polygon object.
|
||||
*/
|
||||
Point centroid( void ) {
|
||||
|
||||
/* Centroid is cached, it won't change betwen points */
|
||||
if ( _centroidCalculated ) {
|
||||
return _centroid;
|
||||
}
|
||||
|
||||
Point cent;
|
||||
double signedArea = 0.0;
|
||||
double area = 0.0; // Partial signed area
|
||||
|
||||
/// For all vertices except last
|
||||
int i = 0;
|
||||
for ( i = 0; i < size() - 1; ++i ) {
|
||||
area = _points[i]._x * _points[i+1]._y - _points[i+1]._x * _points[i]._y ;
|
||||
signedArea += area;
|
||||
cent._x += ( _points[i]._x + _points[i+1]._x ) * area;
|
||||
cent._y += ( _points[i]._y + _points[i+1]._y ) * area;
|
||||
}
|
||||
|
||||
// Do last vertex
|
||||
area = _points[i]._x * _points[0]._y - _points[0]._x * _points[i]._y;
|
||||
cent._x += ( _points[i]._x + _points[0]._x ) * area;
|
||||
cent._y += ( _points[i]._y + _points[0]._y ) * area;
|
||||
signedArea += area;
|
||||
signedArea *= 0.5;
|
||||
cent._x /= ( 6 * signedArea );
|
||||
cent._y /= ( 6 * signedArea );
|
||||
|
||||
_centroidCalculated = true;
|
||||
_centroid = cent;
|
||||
|
||||
return cent;
|
||||
}
|
||||
|
||||
Box bounds( void ) {
|
||||
|
||||
// TODO: Cache this
|
||||
|
||||
_bounds._max = _points[0];
|
||||
_bounds._min = _points[0];
|
||||
|
||||
for ( int i = 1; i < size(); i++ ) {
|
||||
|
||||
_bounds._max._x = max( _bounds._max._x, _points[i]._x );
|
||||
_bounds._max._y = max( _bounds._max._y, _points[i]._y );
|
||||
_bounds._min._x = min( _bounds._min._x, _points[i]._x );
|
||||
_bounds._min._y = min( _bounds._min._y, _points[i]._y );
|
||||
|
||||
}
|
||||
|
||||
return _bounds;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool _centroidCalculated;
|
||||
Point _centroid;
|
||||
|
||||
Box _bounds;
|
||||
|
||||
vector<Point> _points;
|
||||
};
|
||||
} // namespace mongo
|
||||
@@ -48,6 +48,7 @@ namespace mongo {
|
||||
void LastError::appendSelfStatus( BSONObjBuilder &b ) {
|
||||
if ( writebackId.isSet() ) {
|
||||
b.append( "writeback" , writebackId );
|
||||
b.append( "writebackSince", writebackSince );
|
||||
b.append( "instanceIdent" , prettyHostName() ); // this can be any unique string
|
||||
}
|
||||
}
|
||||
@@ -55,7 +56,7 @@ namespace mongo {
|
||||
bool LastError::appendSelf( BSONObjBuilder &b , bool blankErr ) {
|
||||
|
||||
appendSelfStatus( b );
|
||||
|
||||
|
||||
if ( !valid ) {
|
||||
if ( blankErr )
|
||||
b.appendNull( "err" );
|
||||
@@ -93,6 +94,7 @@ namespace mongo {
|
||||
uassert(13649, "no operation yet", le);
|
||||
le->disabled = true;
|
||||
le->nPrev--; // caller is a command that shouldn't count as an operation
|
||||
le->writebackSince--; // same as above
|
||||
return le;
|
||||
}
|
||||
|
||||
@@ -135,6 +137,7 @@ namespace mongo {
|
||||
else {
|
||||
err->disabled = false;
|
||||
err->nPrev++;
|
||||
err->writebackSince++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace mongo {
|
||||
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting;
|
||||
OID upsertedId;
|
||||
OID writebackId; // this shouldn't get reset so that old GLE are handled
|
||||
int writebackSince;
|
||||
long long nObjects;
|
||||
int nPrev;
|
||||
bool valid;
|
||||
@@ -36,6 +37,7 @@ namespace mongo {
|
||||
void writeback( OID& oid ) {
|
||||
reset( true );
|
||||
writebackId = oid;
|
||||
writebackSince = 0;
|
||||
}
|
||||
void raiseError(int _code , const char *_msg) {
|
||||
reset( true );
|
||||
@@ -56,6 +58,7 @@ namespace mongo {
|
||||
}
|
||||
LastError() {
|
||||
reset();
|
||||
writebackSince = 0;
|
||||
}
|
||||
void reset( bool _valid = false ) {
|
||||
code = 0;
|
||||
|
||||
65
src/mongo/db/matcher.cpp
Executable file → Normal file
65
src/mongo/db/matcher.cpp
Executable file → Normal file
@@ -256,7 +256,7 @@ namespace mongo {
|
||||
ss << "elemMatchKey: " << ( _elemMatchKeyFound ? _elemMatchKey : "NONE" ) << " ";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
void Matcher::addRegex(const char *fieldName, const char *regex, const char *flags, bool isNot) {
|
||||
|
||||
RegexMatcher rm;
|
||||
@@ -363,8 +363,52 @@ namespace mongo {
|
||||
flags = fe.valuestrsafe();
|
||||
break;
|
||||
}
|
||||
case BSONObj::opWITHIN: {
|
||||
BSONObj shapeObj = fe.embeddedObject();
|
||||
BSONObjIterator argIt(shapeObj);
|
||||
uassert(16481, "Empty obj for $within: " + shapeObj.toString(), argIt.more());
|
||||
|
||||
BSONElement elt = argIt.next();
|
||||
uassert(16466, "Within must be provided a BSONObj: " + elt.toString(),
|
||||
elt.isABSONObj());
|
||||
BSONObj obj = elt.Obj();
|
||||
BSONObjIterator coordIt(elt.Obj());
|
||||
uassert(16482, "Malformed $within: ", coordIt.more());
|
||||
|
||||
if (str::equals(elt.fieldName(), "$box")) {
|
||||
BSONElement minE = coordIt.next();
|
||||
uassert(16467, "Malformed $box: " + obj.toString(), minE.isABSONObj());
|
||||
uassert(16468, "Malformed $box: " + obj.toString(), coordIt.more());
|
||||
BSONElement maxE = coordIt.next();
|
||||
uassert(16469, "Malformed $box: " + obj.toString(), minE.isABSONObj());
|
||||
_geo.push_back(GeoMatcher::makeBox(e.fieldName(), minE.Obj(), maxE.Obj()));
|
||||
} else if (str::equals(elt.fieldName(), "$center")) {
|
||||
BSONElement center = coordIt.next();
|
||||
uassert(16473, "Malformed $center: " + obj.toString(), center.isABSONObj());
|
||||
uassert(16474, "Malformed $center: " + obj.toString(), coordIt.more());
|
||||
BSONElement radius = coordIt.next();
|
||||
uassert(16475, "Malformed $center: " + obj.toString(), radius.isNumber());
|
||||
_geo.push_back(
|
||||
GeoMatcher::makeCircle(e.fieldName(), center.Obj(), radius.number()));
|
||||
} else if (str::equals(elt.fieldName(), "$polygon")) {
|
||||
while (coordIt.more()) {
|
||||
BSONElement coord = coordIt.next();
|
||||
uassert(16476, "Malformed $polygon: " + obj.toString(), coord.isABSONObj());
|
||||
BSONObjIterator numIt(coord.Obj());
|
||||
uassert(16477, "Malformed $polygon: " + obj.toString(), numIt.more());
|
||||
BSONElement x = numIt.next();
|
||||
uassert(16478, "Malformed $polygon: " + obj.toString(), x.isNumber());
|
||||
uassert(16484, "Malformed $polygon: " + obj.toString(), numIt.more());
|
||||
BSONElement y = numIt.next();
|
||||
uassert(16479, "Malformed $polygon: " + obj.toString(), y.isNumber());
|
||||
}
|
||||
_geo.push_back(GeoMatcher::makePolygon(e.fieldName(), elt.Obj()));
|
||||
} else {
|
||||
uasserted(16483, "Couldn't pull any geometry out of $within query: " + obj.toString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BSONObj::opNEAR:
|
||||
case BSONObj::opWITHIN:
|
||||
case BSONObj::opMAX_DISTANCE:
|
||||
break;
|
||||
default:
|
||||
@@ -936,6 +980,20 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
for (vector<GeoMatcher>::const_iterator it = _geo.begin(); it != _geo.end(); ++it) {
|
||||
verify(_constrainIndexKey.isEmpty());
|
||||
BSONElementSet s;
|
||||
jsobj.getFieldsDotted(it->getFieldName().c_str(), s, false);
|
||||
int matches = 0;
|
||||
for (BSONElementSet::const_iterator i = s.begin(); i != s.end(); ++i) {
|
||||
if (!i->isABSONObj()) { continue; }
|
||||
Point p;
|
||||
if (!GeoMatcher::pointFrom(i->Obj(), &p)) { continue; }
|
||||
if (it->containsPoint(p)) { ++matches; break; }
|
||||
}
|
||||
if (0 == matches) { return false; }
|
||||
}
|
||||
|
||||
for (vector<RegexMatcher>::const_iterator it = _regexs.begin();
|
||||
it != _regexs.end();
|
||||
++it) {
|
||||
@@ -1177,6 +1235,9 @@ namespace mongo {
|
||||
if ( !( _basics.size() == docMatcher._basics.size() && _regexs.size() == docMatcher._regexs.size() && !docMatcher._where ) ) {
|
||||
return false;
|
||||
}
|
||||
if (_geo.size() != docMatcher._geo.size()) {
|
||||
return false;
|
||||
}
|
||||
if ( _andMatchers.size() != docMatcher._andMatchers.size() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "jsobj.h"
|
||||
#include "pcrecpp.h"
|
||||
#include "geo/shapes.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
@@ -42,6 +43,94 @@ namespace mongo {
|
||||
RegexMatcher() : _isNot() {}
|
||||
};
|
||||
|
||||
class GeoMatcher {
|
||||
private:
|
||||
GeoMatcher(const string& field) : _isBox(false), _isCircle(false), _isPolygon(false),
|
||||
_fieldName(field) {}
|
||||
bool _isBox;
|
||||
Box _box;
|
||||
|
||||
bool _isCircle;
|
||||
Point _center;
|
||||
double _radius;
|
||||
|
||||
bool _isPolygon;
|
||||
Polygon _polygon;
|
||||
|
||||
string _fieldName;
|
||||
public:
|
||||
const string& getFieldName() const { return _fieldName; }
|
||||
|
||||
static GeoMatcher makeBox(const string& field, const BSONObj &min, const BSONObj &max) {
|
||||
GeoMatcher m(field);
|
||||
m._isBox = true;
|
||||
uassert(16470, "Malformed coord: " + min.toString(), pointFrom(min, &m._box._min));
|
||||
uassert(16471, "Malformed coord: " + max.toString(), pointFrom(max, &m._box._max));
|
||||
return m;
|
||||
}
|
||||
|
||||
static GeoMatcher makeCircle(const string& field, const BSONObj ¢er, double rad) {
|
||||
GeoMatcher m(field);
|
||||
m._isCircle = true;
|
||||
uassert(16472, "Malformed coord: " + center.toString(), pointFrom(center, &m._center));
|
||||
m._radius = rad;
|
||||
return m;
|
||||
}
|
||||
|
||||
static GeoMatcher makePolygon(const string& field, const BSONObj &poly) {
|
||||
GeoMatcher m(field);
|
||||
vector<Point> points;
|
||||
|
||||
m._isPolygon = true;
|
||||
BSONObjIterator coordIt(poly);
|
||||
while (coordIt.more()) {
|
||||
BSONElement coord = coordIt.next();
|
||||
const BSONObj& obj = coord.Obj();
|
||||
Point p;
|
||||
uassert(16480, "Malformed coord: " + obj.toString(), pointFrom(obj, &p));
|
||||
points.push_back(p);
|
||||
}
|
||||
m._polygon = Polygon(points);
|
||||
return m;
|
||||
}
|
||||
|
||||
bool containsPoint(Point p) const {
|
||||
if (_isBox) {
|
||||
return _box.inside(p, 0);
|
||||
} else if (_isCircle) {
|
||||
return _center.distance(p) <= _radius;
|
||||
} else if (_isPolygon) {
|
||||
return _polygon.contains(p);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string toString() const {
|
||||
stringstream ss;
|
||||
if (_isBox) {
|
||||
ss << "GeoMatcher Box: " << _box.toString();
|
||||
} else if (_isCircle) {
|
||||
ss << "GeoMatcher Circle @ " << _center.toString() << " r = " << _radius;
|
||||
} else {
|
||||
ss << "GeoMatcher UNKNOWN";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static bool pointFrom(const BSONObj o, Point *p) {
|
||||
BSONObjIterator i(o);
|
||||
if (!i.more()) { return false; }
|
||||
BSONElement xe = i.next();
|
||||
if (!i.more()) { return false; }
|
||||
BSONElement ye = i.next();
|
||||
if (!xe.isNumber() || !ye.isNumber()) { return false; }
|
||||
p->_x = xe.number();
|
||||
p->_y = ye.number();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct element_lt {
|
||||
bool operator()(const BSONElement& l, const BSONElement& r) const {
|
||||
int x = (int) l.canonicalType() - (int) r.canonicalType();
|
||||
@@ -266,6 +355,7 @@ namespace mongo {
|
||||
bool _atomic;
|
||||
|
||||
vector<RegexMatcher> _regexs;
|
||||
vector<GeoMatcher> _geo;
|
||||
|
||||
// so we delete the mem when we're done:
|
||||
vector< shared_ptr< BSONObjBuilder > > _builders;
|
||||
|
||||
@@ -1113,4 +1113,54 @@ namespace mongo {
|
||||
updateIsIndexed( i->second, idxKeys , backgroundKeys );
|
||||
}
|
||||
|
||||
bool getCanonicalIndexField( const string& fullName, string* out ) {
|
||||
// check if fieldName contains ".$" or ".###" substrings (#=digit) and skip them
|
||||
if ( fullName.find( '.' ) == string::npos )
|
||||
return false;
|
||||
|
||||
bool modified = false;
|
||||
|
||||
StringBuilder buf;
|
||||
for ( size_t i=0; i<fullName.size(); i++ ) {
|
||||
|
||||
char c = fullName[i];
|
||||
|
||||
if ( c != '.' ) {
|
||||
buf << c;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for ".$", skip if present
|
||||
if ( fullName[i+1] == '$' ) {
|
||||
i++;
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for ".###" for any number of digits (no letters)
|
||||
if ( isdigit( fullName[i+1] ) ) {
|
||||
size_t j = i;
|
||||
// skip digits
|
||||
while ( j+1 < fullName.size() && isdigit( fullName[j+1] ) )
|
||||
j++;
|
||||
|
||||
if ( j+1 == fullName.size() || fullName[j+1] == '.' ) {
|
||||
// only digits found, skip forward
|
||||
i = j;
|
||||
modified = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
buf << c;
|
||||
}
|
||||
|
||||
if ( !modified )
|
||||
return false;
|
||||
|
||||
*out = buf.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
@@ -30,6 +30,12 @@ namespace mongo {
|
||||
class ModState;
|
||||
class ModSetState;
|
||||
|
||||
/**
|
||||
* a.$ -> a
|
||||
* @return true if out is set and we made a change
|
||||
*/
|
||||
bool getCanonicalIndexField( const string& fullName, string* out );
|
||||
|
||||
/* Used for modifiers such as $inc, $set, $push, ...
|
||||
* stores the info about a single operation
|
||||
* once created should never be modified
|
||||
@@ -143,6 +149,7 @@ namespace mongo {
|
||||
// check if there is an index key equal to mod
|
||||
if ( idxKeys.count(fullName) )
|
||||
return true;
|
||||
|
||||
// check if there is an index key that is a child of mod
|
||||
set< string >::const_iterator j = idxKeys.upper_bound( fullName );
|
||||
if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[fullName.size()] == '.' )
|
||||
@@ -151,51 +158,22 @@ namespace mongo {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if mod is in the index by inspecting fieldName, and removing
|
||||
* .$ or .### substrings (#=digit) with any number of digits.
|
||||
*
|
||||
* @return true iff the mod is indexed
|
||||
*/
|
||||
bool isIndexed( const set<string>& idxKeys ) const {
|
||||
string fullName = fieldName;
|
||||
string myFieldName = fieldName;
|
||||
|
||||
if ( isIndexed( fullName , idxKeys ) )
|
||||
// first, check if full name is in idxKeys
|
||||
if ( isIndexed( myFieldName , idxKeys ) )
|
||||
return true;
|
||||
|
||||
if ( strstr( fieldName , "." ) ) {
|
||||
// check for a.0.1
|
||||
StringBuilder buf;
|
||||
for ( size_t i=0; i<fullName.size(); i++ ) {
|
||||
char c = fullName[i];
|
||||
|
||||
if ( c == '$' &&
|
||||
i > 0 && fullName[i-1] == '.' &&
|
||||
i+1<fullName.size() &&
|
||||
fullName[i+1] == '.' ) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
buf << c;
|
||||
|
||||
if ( c != '.' )
|
||||
continue;
|
||||
|
||||
if ( ! isdigit( fullName[i+1] ) )
|
||||
continue;
|
||||
|
||||
bool possible = true;
|
||||
size_t j=i+2;
|
||||
for ( ; j<fullName.size(); j++ ) {
|
||||
char d = fullName[j];
|
||||
if ( d == '.' )
|
||||
break;
|
||||
if ( isdigit( d ) )
|
||||
continue;
|
||||
possible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( possible )
|
||||
i = j;
|
||||
}
|
||||
string x = buf.str();
|
||||
if ( isIndexed( x , idxKeys ) )
|
||||
string x;
|
||||
if ( getCanonicalIndexField( myFieldName, &x ) ) {
|
||||
if ( isIndexed( x, idxKeys ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -642,6 +642,7 @@ doneCheckOrder:
|
||||
|
||||
bool QueryPlanGenerator::addSpecialPlan( NamespaceDetails *d ) {
|
||||
DEBUGQO( "\t special : " << _qps.frsp().getSpecial() );
|
||||
// If an index exists for the special query component, use it.
|
||||
if ( _qps.frsp().getSpecial().size() ) {
|
||||
string special = _qps.frsp().getSpecial();
|
||||
NamespaceDetails::IndexIterator i = d->ii();
|
||||
@@ -656,6 +657,12 @@ doneCheckOrder:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no index exists but the index is not mandatory (Matcher has functionality to
|
||||
// support it), have the caller fall through to using a normal query plan.
|
||||
if (!_qps.frsp().hasSpecialThatNeedsIndex()) { return false; }
|
||||
|
||||
// Otherwise, error.
|
||||
uassert( 13038, (string)"can't find special index: " + special +
|
||||
" for: " + _qps.originalQuery().toString(), false );
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace mongo {
|
||||
|
||||
|
||||
FieldRange::FieldRange( const BSONElement &e, bool isNot, bool optimize ) :
|
||||
_exactMatchRepresentation() {
|
||||
_specialNeedsIndex(true), _exactMatchRepresentation() {
|
||||
int op = e.getGtLtOp();
|
||||
|
||||
// NOTE with $not, we could potentially form a complementary set of intervals.
|
||||
@@ -428,8 +428,12 @@ namespace mongo {
|
||||
log() << "warning: shouldn't get here?" << endl;
|
||||
break;
|
||||
}
|
||||
case BSONObj::opNEAR:
|
||||
case BSONObj::opWITHIN:
|
||||
_specialNeedsIndex = false;
|
||||
_special = "2d";
|
||||
break;
|
||||
case BSONObj::opNEAR:
|
||||
_specialNeedsIndex = true;
|
||||
_special = "2d";
|
||||
break;
|
||||
case BSONObj::opEXISTS: {
|
||||
@@ -465,8 +469,10 @@ namespace mongo {
|
||||
_intervals = newIntervals;
|
||||
for( vector<BSONObj>::const_iterator i = other._objData.begin(); i != other._objData.end(); ++i )
|
||||
_objData.push_back( *i );
|
||||
if ( _special.size() == 0 && other._special.size() )
|
||||
if ( _special.size() == 0 && other._special.size() ) {
|
||||
_special = other._special;
|
||||
_specialNeedsIndex = other._specialNeedsIndex;
|
||||
}
|
||||
_exactMatchRepresentation = exactMatchRepresentation;
|
||||
}
|
||||
|
||||
@@ -767,6 +773,17 @@ namespace mongo {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool FieldRangeSet::hasSpecialThatNeedsIndex() const {
|
||||
for ( map<string,FieldRange>::const_iterator i=_ranges.begin(); i!=_ranges.end(); i++ ) {
|
||||
if ( i->second.getSpecial().size() == 0 )
|
||||
continue;
|
||||
if ( i->second.hasSpecialThatNeedsIndex() )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Btree scanning for a multidimentional key range will yield a
|
||||
* multidimensional box. The idea here is that if an 'other'
|
||||
|
||||
@@ -359,6 +359,8 @@ namespace mongo {
|
||||
*/
|
||||
void reverse( FieldRange &ret ) const;
|
||||
|
||||
bool hasSpecialThatNeedsIndex() const { verify( _special.size() ); return _specialNeedsIndex; }
|
||||
|
||||
string toString() const;
|
||||
private:
|
||||
BSONObj addObj( const BSONObj &o );
|
||||
@@ -368,6 +370,7 @@ namespace mongo {
|
||||
// Owns memory for our BSONElements.
|
||||
vector<BSONObj> _objData;
|
||||
string _special;
|
||||
bool _specialNeedsIndex;
|
||||
bool _exactMatchRepresentation;
|
||||
};
|
||||
|
||||
@@ -457,6 +460,7 @@ namespace mongo {
|
||||
|
||||
QueryPattern pattern( const BSONObj &sort = BSONObj() ) const;
|
||||
string getSpecial() const;
|
||||
bool hasSpecialThatNeedsIndex() const;
|
||||
|
||||
/**
|
||||
* @return a FieldRangeSet approximation of the documents in 'this' but
|
||||
@@ -578,6 +582,7 @@ namespace mongo {
|
||||
const char *ns() const { return _singleKey.ns(); }
|
||||
|
||||
string getSpecial() const { return _singleKey.getSpecial(); }
|
||||
bool hasSpecialThatNeedsIndex() const { return _singleKey.hasSpecialThatNeedsIndex(); }
|
||||
|
||||
/** Intersect with another FieldRangeSetPair. */
|
||||
FieldRangeSetPair &operator&=( const FieldRangeSetPair &other );
|
||||
|
||||
@@ -1066,8 +1066,7 @@ namespace mongo {
|
||||
else {
|
||||
BSONObj user;
|
||||
{
|
||||
Lock::GlobalWrite lk;
|
||||
Client::Context ctxt("local.");
|
||||
Client::ReadContext ctxt("local.");
|
||||
if( !Helpers::findOne("local.system.users", userReplQuery, user) ||
|
||||
// try the first user in local
|
||||
!Helpers::getSingleton("local.system.users", user) ) {
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace mongo {
|
||||
|
||||
virtual bool slaveOk() const { return true; }
|
||||
virtual bool adminOnly() const { return true; }
|
||||
virtual LockType locktype() const { return READ; }
|
||||
virtual LockType locktype() const { return NONE; }
|
||||
virtual void help( stringstream& help ) const { help << "usage by collection, in micros "; }
|
||||
|
||||
virtual bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
|
||||
|
||||
@@ -132,6 +132,44 @@ namespace MatcherTests {
|
||||
}
|
||||
};
|
||||
|
||||
class WithinBox {
|
||||
public:
|
||||
void run() {
|
||||
Matcher m(fromjson("{loc:{$within:{$box:[{x:4,y:4},[6,6]]}}}"));
|
||||
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [5,5]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [5,5.1]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: {x: 5, y:5.1}}")));
|
||||
}
|
||||
};
|
||||
|
||||
class WithinPolygon {
|
||||
public:
|
||||
void run() {
|
||||
Matcher m(fromjson("{loc:{$within:{$polygon:[[0,0],[0,5],[5,5],[5,0]]}}}"));
|
||||
ASSERT(m.matches(fromjson("{loc: [3,4]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: {x:5,y:5}}")));
|
||||
ASSERT(!m.matches(fromjson("{loc: [5,5.1]}")));
|
||||
ASSERT(!m.matches(fromjson("{loc: {}}")));
|
||||
}
|
||||
};
|
||||
|
||||
class WithinCenter {
|
||||
public:
|
||||
void run() {
|
||||
Matcher m(fromjson("{loc:{$within:{$center:[{x:30,y:30},10]}}}"));
|
||||
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: {x:30,y:30}}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [20,30]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [30,20]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [40,30]}")));
|
||||
ASSERT(m.matches(fromjson("{loc: [30,40]}")));
|
||||
ASSERT(!m.matches(fromjson("{loc: [31,40]}")));
|
||||
}
|
||||
};
|
||||
|
||||
/** Test that MatchDetails::elemMatchKey() is set correctly after a match. */
|
||||
class ElemMatchKey {
|
||||
public:
|
||||
@@ -371,6 +409,9 @@ namespace MatcherTests {
|
||||
add<Covered::ElemMatchKeyIndexedSingleKey>();
|
||||
add<AllTiming>();
|
||||
add<Visit>();
|
||||
add<WithinBox>();
|
||||
add<WithinCenter>();
|
||||
add<WithinPolygon>();
|
||||
}
|
||||
} dball;
|
||||
|
||||
|
||||
@@ -1295,6 +1295,36 @@ namespace UpdateTests {
|
||||
|
||||
};
|
||||
|
||||
class IndexFieldNameTest {
|
||||
public:
|
||||
void run() {
|
||||
string x;
|
||||
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a", &x ) );
|
||||
ASSERT_FALSE( getCanonicalIndexField( "aaa", &x ) );
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a.b", &x ) );
|
||||
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.$", &x ) );
|
||||
ASSERT_EQUALS( x, "a" );
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.0", &x ) );
|
||||
ASSERT_EQUALS( x, "a" );
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.123", &x ) );
|
||||
ASSERT_EQUALS( x, "a" );
|
||||
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.$.b", &x ) );
|
||||
ASSERT_EQUALS( x, "a.b" );
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.0.b", &x ) );
|
||||
ASSERT_EQUALS( x, "a.b" );
|
||||
ASSERT_TRUE( getCanonicalIndexField( "a.123.b", &x ) );
|
||||
ASSERT_EQUALS( x, "a.b" );
|
||||
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a.123a", &x ) );
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a.a123", &x ) );
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a.123a.b", &x ) );
|
||||
ASSERT_FALSE( getCanonicalIndexField( "a.a123.b", &x ) );
|
||||
}
|
||||
};
|
||||
|
||||
class All : public Suite {
|
||||
public:
|
||||
All() : Suite( "update" ) {
|
||||
@@ -1401,6 +1431,8 @@ namespace UpdateTests {
|
||||
add< basic::bit1 >();
|
||||
add< basic::unset >();
|
||||
add< basic::setswitchint >();
|
||||
|
||||
add< IndexFieldNameTest >();
|
||||
}
|
||||
} myall;
|
||||
|
||||
|
||||
@@ -212,8 +212,8 @@ namespace mongo {
|
||||
int balancedLastTime ) {
|
||||
|
||||
|
||||
// 1) check for shards that policy require to us to move off of
|
||||
// draining, maxSize
|
||||
// 1) check for shards that policy require to us to move off of:
|
||||
// draining only
|
||||
// 2) check tag policy violations
|
||||
// 3) then we make sure chunks are balanced for each tag
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace mongo {
|
||||
string shard = *z;
|
||||
const ShardInfo& info = distribution.shardInfo( shard );
|
||||
|
||||
if ( ! info.isSizeMaxed() && ! info.isDraining() )
|
||||
if ( ! info.isDraining() )
|
||||
continue;
|
||||
|
||||
if ( distribution.numberOfChunksInShard( shard ) == 0 )
|
||||
|
||||
@@ -80,6 +80,10 @@ namespace mongo {
|
||||
*/
|
||||
bool hasOpsQueued() const { return _hasOpsQueued; }
|
||||
|
||||
long long getMaxSize() const { return _maxSize; }
|
||||
|
||||
long long getCurrSize() const { return _currSize; }
|
||||
|
||||
string toString() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -291,5 +291,221 @@ namespace mongo {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Idea for this test is to set up three shards, one of which is overloaded (too much data).
|
||||
*
|
||||
* Even though the overloaded shard has less chunks, we shouldn't move chunks to that shard.
|
||||
*/
|
||||
TEST( BalancerPolicyTests, MaxSizeRespect ) {
|
||||
|
||||
ShardToChunksMap chunks;
|
||||
addShard( chunks, 3 , false );
|
||||
addShard( chunks, 4 , false );
|
||||
addShard( chunks, 6 , true );
|
||||
|
||||
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 3.
|
||||
// Other shards have maxSize = 0 = unset.
|
||||
|
||||
ShardInfoMap shards;
|
||||
// ShardInfo(maxSize, currSize, draining, opsQueued)
|
||||
shards["shard0"] = ShardInfo( 1, 3, false, false );
|
||||
shards["shard1"] = ShardInfo( 0, 4, false, false );
|
||||
shards["shard2"] = ShardInfo( 0, 6, false, false );
|
||||
|
||||
DistributionStatus d( shards, chunks );
|
||||
MigrateInfo* m = BalancerPolicy::balance( "ns", d, 0 );
|
||||
ASSERT( m );
|
||||
ASSERT_EQUALS( "shard2" , m->from );
|
||||
ASSERT_EQUALS( "shard1" , m->to );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we check that being over the maxSize is *not* equivalent to draining, we don't want
|
||||
* to empty shards for no other reason than they are over this limit.
|
||||
*/
|
||||
TEST( BalancerPolicyTests, MaxSizeNoDrain ) {
|
||||
|
||||
ShardToChunksMap chunks;
|
||||
// Shard0 will be overloaded
|
||||
addShard( chunks, 4 , false );
|
||||
addShard( chunks, 4 , false );
|
||||
addShard( chunks, 4 , true );
|
||||
|
||||
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 4.
|
||||
// Other shards have maxSize = 0 = unset.
|
||||
|
||||
ShardInfoMap shards;
|
||||
// ShardInfo(maxSize, currSize, draining, opsQueued)
|
||||
shards["shard0"] = ShardInfo( 1, 4, false, false );
|
||||
shards["shard1"] = ShardInfo( 0, 4, false, false );
|
||||
shards["shard2"] = ShardInfo( 0, 4, false, false );
|
||||
|
||||
DistributionStatus d( shards, chunks );
|
||||
MigrateInfo* m = BalancerPolicy::balance( "ns", d, 0 );
|
||||
ASSERT( !m );
|
||||
}
|
||||
|
||||
// Note: Only in 2.2, 2.4 has utility class
|
||||
class PseudoRandom {
|
||||
public:
|
||||
|
||||
PseudoRandom(unsigned int seed) {
|
||||
_seed = seed;
|
||||
}
|
||||
|
||||
int nextInt32( int max = -1 ){
|
||||
|
||||
#if !defined(_WIN32)
|
||||
int r = rand_r( &_seed ) ;
|
||||
#else
|
||||
int r = ::rand(); // seed not used in this case
|
||||
#endif
|
||||
return max > 0 ? r % max : r;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int _seed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Idea behind this test is that we set up several shards, the first two of which are
|
||||
* draining and the second two of which have a data size limit. We also simulate a random
|
||||
* number of chunks on each shard.
|
||||
*
|
||||
* Once the shards are setup, we virtually migrate numChunks times, or until there are no
|
||||
* more migrations to run. Each chunk is assumed to have a size of 1 unit, and we increment
|
||||
* our currSize for each shard as the chunks move.
|
||||
*
|
||||
* Finally, we ensure that the drained shards are drained, the data-limited shards aren't
|
||||
* overloaded, and that all shards (including the data limited shard if the baseline isn't
|
||||
* over the limit are balanced to within 1 unit of some baseline.
|
||||
*
|
||||
*/
|
||||
TEST( BalancerPolicyTests, Simulation ) {
|
||||
|
||||
// Hardcode seed here, make test deterministic.
|
||||
int64_t seed = 1337;
|
||||
PseudoRandom rng(seed);
|
||||
|
||||
// Run test 10 times
|
||||
for (int test = 0; test < 10; test++) {
|
||||
|
||||
//
|
||||
// Setup our shards as draining, with maxSize, and normal
|
||||
//
|
||||
|
||||
int numShards = 7;
|
||||
int numChunks = 0;
|
||||
|
||||
ShardToChunksMap chunks;
|
||||
ShardInfoMap shards;
|
||||
|
||||
map<string,int> expected;
|
||||
|
||||
for (int i = 0; i < numShards; i++) {
|
||||
|
||||
int numShardChunks = rng.nextInt32(100);
|
||||
bool draining = i < 2;
|
||||
bool maxed = i >= 2 && i < 4;
|
||||
|
||||
if (draining) expected[str::stream() << "shard" << i] = 0;
|
||||
if (maxed) expected[str::stream() << "shard" << i] = numShardChunks + 1;
|
||||
|
||||
addShard(chunks, numShardChunks, false);
|
||||
numChunks += numShardChunks;
|
||||
|
||||
shards[str::stream() << "shard" << i] =
|
||||
ShardInfo(maxed ? numShardChunks + 1 : 0,
|
||||
numShardChunks, draining, false);
|
||||
}
|
||||
|
||||
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
|
||||
log() << it->first << " : " << it->second.toString() << endl;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform migrations and increment data size as chunks move
|
||||
//
|
||||
|
||||
for (int i = 0; i < numChunks; i++) {
|
||||
|
||||
DistributionStatus d( shards, chunks );
|
||||
MigrateInfo* m = BalancerPolicy::balance( "ns", d, i != 0 );
|
||||
|
||||
if (!m) {
|
||||
log() << "Finished with test moves." << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
moveChunk(chunks, m);
|
||||
|
||||
{
|
||||
ShardInfo& info = shards[m->from];
|
||||
shards[m->from] = ShardInfo(info.getMaxSize(),
|
||||
info.getCurrSize() - 1,
|
||||
info.isDraining(),
|
||||
info.hasOpsQueued());
|
||||
}
|
||||
|
||||
{
|
||||
ShardInfo& info = shards[m->to];
|
||||
shards[m->to] = ShardInfo(info.getMaxSize(),
|
||||
info.getCurrSize() + 1,
|
||||
info.isDraining(),
|
||||
info.hasOpsQueued());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure our balance is correct and our data size is low.
|
||||
//
|
||||
|
||||
// The balanced value is the count on the last shard, since it's not draining or
|
||||
// limited
|
||||
int balancedSize = (--shards.end())->second.getCurrSize();
|
||||
|
||||
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
|
||||
log() << it->first << " : " << it->second.toString() << endl;
|
||||
}
|
||||
|
||||
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
|
||||
|
||||
log() << it->first << " : " << it->second.toString() << endl;
|
||||
|
||||
map<string,int>::iterator expectedIt = expected.find(it->first);
|
||||
|
||||
if (expectedIt == expected.end()) {
|
||||
bool isInRange = it->second.getCurrSize() >= balancedSize - 1 &&
|
||||
it->second.getCurrSize() <= balancedSize + 1;
|
||||
|
||||
if (!isInRange) {
|
||||
warning() << "non-limited and non-draining shard had "
|
||||
<< it->second.getCurrSize() << " chunks, expected near "
|
||||
<< balancedSize << endl;
|
||||
}
|
||||
|
||||
ASSERT(isInRange);
|
||||
}
|
||||
else {
|
||||
int expectedSize = expectedIt->second;
|
||||
bool isInRange = it->second.getCurrSize() <= expectedSize;
|
||||
if (isInRange && expectedSize >= balancedSize) {
|
||||
isInRange = it->second.getCurrSize() >= balancedSize - 1 &&
|
||||
it->second.getCurrSize() <= balancedSize + 1;
|
||||
}
|
||||
|
||||
if (!isInRange) {
|
||||
warning() << "limited or draining shard had "
|
||||
<< it->second.getCurrSize() << " chunks, expected less than "
|
||||
<< expectedSize << " and (if less than expected) near "
|
||||
<< balancedSize << endl;
|
||||
}
|
||||
|
||||
ASSERT(isInRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../util/startup_test.h"
|
||||
#include "../util/timer.h"
|
||||
#include "mongo/client/dbclientcursor.h"
|
||||
#include "mongo/db/namespacestring.h"
|
||||
|
||||
#include "chunk.h"
|
||||
#include "chunk_diff.h"
|
||||
@@ -364,7 +365,7 @@ namespace mongo {
|
||||
// this does mean mongos has more back pressure than mongod alone
|
||||
// since it nots 100% tcp queue bound
|
||||
// this was implicit before since we did a splitVector on the same socket
|
||||
ShardConnection::sync();
|
||||
ShardConnection::sync( NamespaceString(getManager()->getns()).db );
|
||||
|
||||
LOG(1) << "about to initiate autosplit: " << *this << " dataWritten: " << _dataWritten << " splitThreshold: " << splitThreshold << endl;
|
||||
|
||||
|
||||
@@ -284,7 +284,16 @@ namespace mongo {
|
||||
|
||||
BSONObj query = queryB.obj();
|
||||
|
||||
// log() << "major version query from " << *_maxVersion << " and over " << _maxShardVersions->size() << " shards is " << query << endl;
|
||||
LOG(2) << "major version query from " << *_maxVersion << " and over "
|
||||
<< _maxShardVersions->size() << " shards is " << query << endl;
|
||||
|
||||
//
|
||||
// NOTE: IT IS IMPORTANT FOR CONSISTENCY THAT WE SORT BY ASC VERSION, TO HANDLE
|
||||
// CURSOR YIELDING BETWEEN CHUNKS BEING MIGRATED.
|
||||
//
|
||||
|
||||
Query queryObj(query);
|
||||
queryObj.sort(BSON( "lastmod" << 1 ));
|
||||
|
||||
return Query( query );
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace mongo {
|
||||
_lastAccess = 0;
|
||||
}
|
||||
|
||||
void ClientInfo::_addWriteBack( vector<WBInfo>& all , const BSONObj& gle ) {
|
||||
void ClientInfo::_addWriteBack( vector<WBInfo>& all, const BSONObj& gle, bool fromLastOperation ) {
|
||||
BSONElement w = gle["writeback"];
|
||||
|
||||
if ( w.type() != jstOID )
|
||||
@@ -109,10 +109,12 @@ namespace mongo {
|
||||
if ( gle["instanceIdent"].type() == String )
|
||||
ident = gle["instanceIdent"].String();
|
||||
|
||||
all.push_back( WBInfo( WriteBackListener::ConnectionIdent( ident , cid.numberLong() ) , w.OID() ) );
|
||||
all.push_back( WBInfo( WriteBackListener::ConnectionIdent( ident , cid.numberLong() ),
|
||||
w.OID(),
|
||||
fromLastOperation ) );
|
||||
}
|
||||
|
||||
vector<BSONObj> ClientInfo::_handleWriteBacks( vector<WBInfo>& all , bool fromWriteBackListener ) {
|
||||
vector<BSONObj> ClientInfo::_handleWriteBacks( const vector<WBInfo>& all , bool fromWriteBackListener ) {
|
||||
vector<BSONObj> res;
|
||||
|
||||
if ( all.size() == 0 )
|
||||
@@ -190,7 +192,7 @@ namespace mongo {
|
||||
conn.done();
|
||||
}
|
||||
|
||||
_addWriteBack( writebacks , res );
|
||||
_addWriteBack( writebacks, res, true );
|
||||
|
||||
LOG(4) << "gathering writebacks from " << sinceLastGetError().size() << " hosts for"
|
||||
<< " gle (" << theShard << ")" << endl;
|
||||
@@ -206,7 +208,7 @@ namespace mongo {
|
||||
try {
|
||||
ShardConnection conn( temp , "" );
|
||||
ON_BLOCK_EXIT_OBJ( conn, &ShardConnection::done );
|
||||
_addWriteBack( writebacks , conn->getLastErrorDetailed() );
|
||||
_addWriteBack( writebacks, conn->getLastErrorDetailed(), false );
|
||||
|
||||
}
|
||||
catch( std::exception &e ){
|
||||
@@ -232,10 +234,25 @@ namespace mongo {
|
||||
// since that's the current write
|
||||
// but we block for all
|
||||
verify( v.size() >= 1 );
|
||||
result.appendElements( v[0] );
|
||||
result.appendElementsUnique( res );
|
||||
result.append( "writebackGLE" , v[0] );
|
||||
result.append( "initialGLEHost" , theShard );
|
||||
|
||||
if ( res["writebackSince"].numberInt() > 0 ) {
|
||||
// got writeback from older op
|
||||
// ignore the result from it, just needed to wait
|
||||
result.appendElements( res );
|
||||
}
|
||||
else if ( writebacks[0].fromLastOperation ) {
|
||||
result.appendElements( v[0] );
|
||||
result.appendElementsUnique( res );
|
||||
result.append( "writebackGLE" , v[0] );
|
||||
result.append( "initialGLEHost" , theShard );
|
||||
result.append( "initialGLE", res );
|
||||
}
|
||||
else {
|
||||
// there was a writeback
|
||||
// but its from an old operations
|
||||
// so all that's important is that we block, not that we return stats
|
||||
result.appendElements( res );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -288,7 +305,7 @@ namespace mongo {
|
||||
return false;
|
||||
}
|
||||
|
||||
_addWriteBack( writebacks, res );
|
||||
_addWriteBack( writebacks, res, true );
|
||||
|
||||
string temp = DBClientWithCommands::getLastErrorString( res );
|
||||
if ( (*conn)->type() != ConnectionString::SYNC && ( ok == false || temp.size() ) ) {
|
||||
@@ -327,7 +344,7 @@ namespace mongo {
|
||||
|
||||
ShardConnection conn( temp , "" );
|
||||
try {
|
||||
_addWriteBack( writebacks, conn->getLastErrorDetailed() );
|
||||
_addWriteBack( writebacks, conn->getLastErrorDetailed(), false );
|
||||
}
|
||||
catch( std::exception &e ){
|
||||
warning() << "could not clear last error from a shard " << temp << causedBy( e ) << endl;
|
||||
|
||||
@@ -107,14 +107,16 @@ namespace mongo {
|
||||
private:
|
||||
AuthenticationInfo _ai;
|
||||
struct WBInfo {
|
||||
WBInfo( const WriteBackListener::ConnectionIdent& c , OID o ) : ident( c ) , id( o ) {}
|
||||
WBInfo( const WriteBackListener::ConnectionIdent& c, OID o, bool fromLastOperation )
|
||||
: ident( c ), id( o ), fromLastOperation( fromLastOperation ) {}
|
||||
WriteBackListener::ConnectionIdent ident;
|
||||
OID id;
|
||||
bool fromLastOperation;
|
||||
};
|
||||
|
||||
// for getLastError
|
||||
void _addWriteBack( vector<WBInfo>& all , const BSONObj& o );
|
||||
vector<BSONObj> _handleWriteBacks( vector<WBInfo>& all , bool fromWriteBackListener );
|
||||
void _addWriteBack( vector<WBInfo>& all , const BSONObj& o, bool fromLastOperation );
|
||||
vector<BSONObj> _handleWriteBacks( const vector<WBInfo>& all , bool fromWriteBackListener );
|
||||
|
||||
|
||||
int _id; // unique client id
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "../client/connpool.h"
|
||||
#include "mongo/client/dbclientcursor.h"
|
||||
#include "mongo/db/namespacestring.h"
|
||||
|
||||
#include "../db/dbmessage.h"
|
||||
#include "../db/commands.h"
|
||||
@@ -712,14 +713,14 @@ namespace mongo {
|
||||
if ( ! okForConfigChanges( errmsg ) )
|
||||
return false;
|
||||
|
||||
ShardConnection::sync();
|
||||
|
||||
string ns = cmdObj.firstElement().valuestrsafe();
|
||||
if ( ns.size() == 0 ) {
|
||||
errmsg = "no ns";
|
||||
return false;
|
||||
}
|
||||
|
||||
ShardConnection::sync( NamespaceString(ns).db );
|
||||
|
||||
DBConfigPtr config = grid.getDBConfig( ns );
|
||||
if ( ! config->isSharded( ns ) ) {
|
||||
config->reload();
|
||||
@@ -794,15 +795,16 @@ namespace mongo {
|
||||
if ( ! okForConfigChanges( errmsg ) )
|
||||
return false;
|
||||
|
||||
ShardConnection::sync();
|
||||
|
||||
Timer t;
|
||||
string ns = cmdObj.firstElement().valuestrsafe();
|
||||
if ( ns.size() == 0 ) {
|
||||
errmsg = "no ns";
|
||||
return false;
|
||||
}
|
||||
|
||||
ShardConnection::sync( NamespaceString(ns).db );
|
||||
|
||||
Timer t;
|
||||
|
||||
DBConfigPtr config = grid.getDBConfig( ns );
|
||||
if ( ! config->isSharded( ns ) ) {
|
||||
config->reload();
|
||||
@@ -1281,6 +1283,11 @@ namespace mongo {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( name == "config" || name == "admin" ) {
|
||||
//always get this from the config servers
|
||||
continue;
|
||||
}
|
||||
|
||||
long long size = i->second;
|
||||
totalSize += size;
|
||||
|
||||
@@ -1293,7 +1300,7 @@ namespace mongo {
|
||||
bb.append( temp.obj() );
|
||||
}
|
||||
|
||||
if ( sizes.find( "config" ) == sizes.end() ){
|
||||
{ // get config db from the config servers (first one)
|
||||
scoped_ptr<ScopedDbConnection> conn(
|
||||
ScopedDbConnection::getInternalScopedDbConnection( configServer.getPrimary()
|
||||
.getConnString() ) );
|
||||
@@ -1314,6 +1321,27 @@ namespace mongo {
|
||||
conn->done();
|
||||
}
|
||||
|
||||
{ // get admin db from the config servers (first one)
|
||||
scoped_ptr<ScopedDbConnection> conn(
|
||||
ScopedDbConnection::getInternalScopedDbConnection(
|
||||
configServer.getPrimary().getConnString(), 30));
|
||||
BSONObj x;
|
||||
if ( conn->get()->simpleCommand( "admin" , &x , "dbstats" ) ){
|
||||
BSONObjBuilder b;
|
||||
b.append( "name" , "admin" );
|
||||
b.appendBool( "empty" , false );
|
||||
if ( x["fileSize"].type() )
|
||||
b.appendAs( x["fileSize"] , "sizeOnDisk" );
|
||||
else
|
||||
b.append( "sizeOnDisk" , 1 );
|
||||
bb.append( b.obj() );
|
||||
}
|
||||
else {
|
||||
bb.append( BSON( "name" << "admin" ) );
|
||||
}
|
||||
conn->done();
|
||||
}
|
||||
|
||||
bb.done();
|
||||
|
||||
result.appendNumber( "totalSize" , totalSize );
|
||||
|
||||
@@ -1033,15 +1033,16 @@ namespace mongo {
|
||||
log(1) << "replicaSetChange: shard not found for set: " << monitor->getServerAddress() << endl;
|
||||
return;
|
||||
}
|
||||
scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getScopedDbConnection(
|
||||
scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getInternalScopedDbConnection(
|
||||
configServer.getConnectionString().toString(), 30.0 ) );
|
||||
conn->get()->update( ShardNS::shard,
|
||||
BSON( "_id" << s.getName() ),
|
||||
BSON( "$set" << BSON( "host" << monitor->getServerAddress() ) ) );
|
||||
conn->done();
|
||||
}
|
||||
catch ( DBException & ) {
|
||||
error() << "RSChangeWatcher: could not update config db for set: " << monitor->getName() << " to: " << monitor->getServerAddress() << endl;
|
||||
catch (DBException& e) {
|
||||
error() << "RSChangeWatcher: could not update config db for set: " << monitor->getName()
|
||||
<< " to: " << monitor->getServerAddress() << causedBy(e) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,10 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void done() {
|
||||
Lock::DBRead lk( _ns );
|
||||
log() << "MigrateFromStatus::done About to acquire global write lock to exit critical "
|
||||
"section" << endl;
|
||||
Lock::GlobalWrite lk;
|
||||
log() << "MigrateFromStatus::done Global lock acquired" << endl;
|
||||
|
||||
{
|
||||
scoped_spinlock lk( _trackerLocks );
|
||||
@@ -913,6 +916,7 @@ namespace mongo {
|
||||
configServer.logChange( "moveChunk.start" , ns , chunkInfo );
|
||||
|
||||
ShardChunkVersion maxVersion;
|
||||
ShardChunkVersion startingVersion;
|
||||
string myOldShard;
|
||||
{
|
||||
scoped_ptr<ScopedDbConnection> conn(
|
||||
@@ -981,10 +985,16 @@ namespace mongo {
|
||||
// it's possible this shard will be *at* zero version from a previous migrate and
|
||||
// no refresh will be done
|
||||
// TODO: Make this less fragile
|
||||
ShardChunkVersion shardVersion = maxVersion;
|
||||
shardingState.trySetVersion( ns , shardVersion /* will return updated */ );
|
||||
startingVersion = maxVersion;
|
||||
shardingState.trySetVersion( ns , startingVersion /* will return updated */ );
|
||||
|
||||
log() << "moveChunk request accepted at version " << shardVersion << migrateLog;
|
||||
if (startingVersion.majorVersion() == 0) {
|
||||
// It makes no sense to migrate if our version is zero and we have no chunks, so return
|
||||
warning() << "moveChunk cannot start migration with zero version" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log() << "moveChunk request accepted at version " << startingVersion << migrateLog;
|
||||
}
|
||||
|
||||
timing.done(2);
|
||||
@@ -1099,8 +1109,7 @@ namespace mongo {
|
||||
// 5.a
|
||||
// we're under the collection lock here, so no other migrate can change maxVersion or ShardChunkManager state
|
||||
migrateFromStatus.setInCriticalSection( true );
|
||||
ShardChunkVersion currVersion = maxVersion;
|
||||
ShardChunkVersion myVersion = currVersion;
|
||||
ShardChunkVersion myVersion = maxVersion;
|
||||
myVersion.incMajor();
|
||||
|
||||
{
|
||||
@@ -1120,7 +1129,8 @@ namespace mongo {
|
||||
{
|
||||
BSONObj res;
|
||||
scoped_ptr<ScopedDbConnection> connTo(
|
||||
ScopedDbConnection::getScopedDbConnection( toShard.getConnString() ) );
|
||||
ScopedDbConnection::getScopedDbConnection( toShard.getConnString(),
|
||||
35.0 ) );
|
||||
|
||||
bool ok;
|
||||
|
||||
@@ -1132,21 +1142,24 @@ namespace mongo {
|
||||
catch( DBException& e ){
|
||||
errmsg = str::stream() << "moveChunk could not contact to: shard " << toShard.getConnString() << " to commit transfer" << causedBy( e );
|
||||
warning() << errmsg << endl;
|
||||
return false;
|
||||
ok = false;
|
||||
}
|
||||
|
||||
connTo->done();
|
||||
|
||||
if ( ! ok ) {
|
||||
log() << "moveChunk migrate commit not accepted by TO-shard: " << res
|
||||
<< " resetting shard version to: " << startingVersion << migrateLog;
|
||||
{
|
||||
Lock::DBWrite lk( ns );
|
||||
Lock::GlobalWrite lk;
|
||||
log() << "moveChunk global lock acquired to reset shard version from "
|
||||
"failed migration" << endl;
|
||||
|
||||
// revert the chunk manager back to the state before "forgetting" about the chunk
|
||||
shardingState.undoDonateChunk( ns , min , max , currVersion );
|
||||
shardingState.undoDonateChunk( ns , min , max , startingVersion );
|
||||
}
|
||||
|
||||
log() << "moveChunk migrate commit not accepted by TO-shard: " << res
|
||||
<< " resetting shard version to: " << currVersion << migrateLog;
|
||||
log() << "Shard version successfully reset to clean up failed migration"
|
||||
<< endl;
|
||||
|
||||
errmsg = "_recvChunkCommit failed!";
|
||||
result.append( "cause" , res );
|
||||
@@ -1425,6 +1438,10 @@ namespace mongo {
|
||||
|
||||
slaveCount = ( getSlaveCount() / 2 ) + 1;
|
||||
|
||||
log() << "starting receiving-end of migration of chunk " << min << " -> " << max <<
|
||||
" for collection " << ns << " from " << from <<
|
||||
" (" << getSlaveCount() << " slaves detected)" << endl;
|
||||
|
||||
string errmsg;
|
||||
MoveTimingHelper timing( "to" , ns , min , max , 5 /* steps */ , errmsg );
|
||||
|
||||
@@ -1597,6 +1614,8 @@ namespace mongo {
|
||||
// this will prevent us from going into critical section until we're ready
|
||||
Timer t;
|
||||
while ( t.minutes() < 600 ) {
|
||||
log() << "Waiting for replication to catch up before entering critical section"
|
||||
<< endl;
|
||||
if ( flushPendingWrites( lastOpApplied ) )
|
||||
break;
|
||||
sleepsecs(1);
|
||||
@@ -1786,7 +1805,8 @@ namespace mongo {
|
||||
|
||||
Timer t;
|
||||
// we wait for the commit to succeed before giving up
|
||||
while ( t.minutes() <= 5 ) {
|
||||
while ( t.seconds() <= 30 ) {
|
||||
log() << "Waiting for commit to finish" << endl;
|
||||
sleepmillis(1);
|
||||
if ( state == DONE )
|
||||
return true;
|
||||
|
||||
@@ -162,6 +162,7 @@ namespace mongo {
|
||||
|
||||
void ShardingState::undoDonateChunk( const string& ns , const BSONObj& min , const BSONObj& max , ShardChunkVersion version ) {
|
||||
scoped_lock lk( _mutex );
|
||||
log() << "ShardingState::undoDonateChunk acquired _mutex" << endl;
|
||||
|
||||
ChunkManagersMap::const_iterator it = _chunks.find( ns );
|
||||
verify( it != _chunks.end() ) ;
|
||||
@@ -634,7 +635,7 @@ namespace mongo {
|
||||
if ( version < globalVersion && version.hasCompatibleEpoch( globalVersion ) ) {
|
||||
while ( shardingState.inCriticalMigrateSection() ) {
|
||||
dbtemprelease r;
|
||||
sleepmillis(2);
|
||||
sleepmillis(20);
|
||||
OCCASIONALLY log() << "waiting till out of critical section" << endl;
|
||||
}
|
||||
errmsg = "shard global version for collection is higher than trying to set to '" + ns + "'";
|
||||
@@ -819,7 +820,7 @@ namespace mongo {
|
||||
|
||||
}
|
||||
|
||||
void ShardingConnectionHook::onHandedOut( DBClientBase * conn ) {
|
||||
// no-op for mongod
|
||||
void usingAShardConnection( const string& addr ) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,10 +28,16 @@
|
||||
*/
|
||||
namespace mongo {
|
||||
|
||||
void ShardingConnectionHook::onHandedOut( DBClientBase * conn ) {
|
||||
if( _shardedConnections ){
|
||||
ClientInfo::get()->addShard( conn->getServerAddress() );
|
||||
}
|
||||
void* remapPrivateView(void *oldPrivateAddr) {
|
||||
log() << "remapPrivateView called in mongos, aborting" << endl;
|
||||
fassertFailed(16462);
|
||||
}
|
||||
|
||||
/** When this callback is run, we record a shard that we've used for useful work
|
||||
* in an operation to be read later by getLastError()
|
||||
*/
|
||||
void usingAShardConnection( const string& addr ) {
|
||||
ClientInfo::get()->addShard( addr );
|
||||
}
|
||||
|
||||
TSP_DEFINE(Client,currentClient)
|
||||
|
||||
@@ -79,7 +79,9 @@ namespace mongo {
|
||||
DBConfigPtr config = grid.getDBConfig( adminNs );
|
||||
Shard s = config->getShard( adminNs );
|
||||
|
||||
ShardConnection conn( s, adminNs );
|
||||
scoped_ptr<ScopedDbConnection> connPtr(
|
||||
ScopedDbConnection::getInternalScopedDbConnection(s.getConnString(), 30.0));
|
||||
ScopedDbConnection& conn = *connPtr;
|
||||
BSONObj result = conn->findOne("admin.system.users", Query());
|
||||
if( result.isEmpty() ) {
|
||||
if( ! _warned ) {
|
||||
|
||||
@@ -99,20 +99,15 @@ namespace mongo {
|
||||
}
|
||||
|
||||
bool operator==( const Shard& s ) const {
|
||||
bool n = _name == s._name;
|
||||
bool a = _addr == s._addr;
|
||||
|
||||
verify( n == a ); // names and address are 1 to 1
|
||||
return n;
|
||||
if ( _name != s._name )
|
||||
return false;
|
||||
return _cs.sameLogicalEndpoint( s._cs );
|
||||
}
|
||||
|
||||
bool operator!=( const Shard& s ) const {
|
||||
bool n = _name == s._name;
|
||||
bool a = _addr == s._addr;
|
||||
return ! ( n && a );
|
||||
return ! ( *this == s );
|
||||
}
|
||||
|
||||
|
||||
bool operator==( const string& s ) const {
|
||||
return _name == s || _addr == s;
|
||||
}
|
||||
@@ -272,7 +267,7 @@ namespace mongo {
|
||||
return _setVersion;
|
||||
}
|
||||
|
||||
static void sync();
|
||||
static void sync( const string& db );
|
||||
|
||||
void donotCheckVersion() {
|
||||
_setVersion = false;
|
||||
@@ -314,7 +309,6 @@ namespace mongo {
|
||||
}
|
||||
|
||||
virtual void onCreate( DBClientBase * conn );
|
||||
virtual void onHandedOut( DBClientBase * conn );
|
||||
virtual void onDestroy( DBClientBase * conn );
|
||||
|
||||
bool _shardedConnections;
|
||||
|
||||
69
src/mongo/s/shard_test.cpp
Normal file
69
src/mongo/s/shard_test.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// shard_test.cpp
|
||||
|
||||
/**
|
||||
* Copyright (C) 2008 10gen 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/>.
|
||||
*/
|
||||
|
||||
#include "mongo/s/shard.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
// Note: these are all crutch and hopefully will eventually go away
|
||||
CmdLine cmdLine;
|
||||
|
||||
bool inShutdown() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setupSignals(bool inFork) {}
|
||||
|
||||
DBClientBase *createDirectClient() { return NULL; }
|
||||
|
||||
void dbexit(ExitCode rc, const char *why){
|
||||
::_exit(-1);
|
||||
}
|
||||
|
||||
bool haveLocalShardingInfo(const string& ns) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
|
||||
TEST( Shard, EqualityRs ) {
|
||||
Shard a( "foo", "bar/a,b" );
|
||||
Shard b( "foo", "bar/a,b" );
|
||||
ASSERT_EQUALS( a, b );
|
||||
|
||||
b = Shard( "foo", "bar/b,a" );
|
||||
ASSERT_EQUALS( a, b );
|
||||
}
|
||||
|
||||
TEST( Shard, EqualitySingle ) {
|
||||
ASSERT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "b.foo.com:123") );
|
||||
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "a.foo.com:123") );
|
||||
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "b.foo.com:124") );
|
||||
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foa", "b.foo.com:123") );
|
||||
}
|
||||
|
||||
TEST( Shard, EqualitySync ) {
|
||||
ConnectionString cs( ConnectionString::SYNC, "a,b,c" );
|
||||
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "a,b,c" ) ) );
|
||||
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "c,b,a" ) ) );
|
||||
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "c,a,b" ) ) );
|
||||
|
||||
ASSERT( ! cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "d,a,b" ) ) );
|
||||
}
|
||||
}
|
||||
@@ -98,12 +98,12 @@ namespace mongo {
|
||||
s->avail = conn;
|
||||
}
|
||||
|
||||
void sync() {
|
||||
void sync( const string& db ) {
|
||||
for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
|
||||
string addr = i->first;
|
||||
Status* ss = i->second;
|
||||
if ( ss->avail )
|
||||
ss->avail->getLastError();
|
||||
ss->avail->getLastError(db);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -177,10 +177,13 @@ namespace mongo {
|
||||
_init();
|
||||
}
|
||||
|
||||
void usingAShardConnection( const string& addr );
|
||||
|
||||
void ShardConnection::_init() {
|
||||
verify( _addr.size() );
|
||||
_conn = ClientConnections::threadInstance()->get( _addr , _ns );
|
||||
_finishedInit = false;
|
||||
usingAShardConnection( _addr );
|
||||
}
|
||||
|
||||
void ShardConnection::_finishInit() {
|
||||
@@ -218,8 +221,8 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void ShardConnection::sync() {
|
||||
ClientConnections::threadInstance()->sync();
|
||||
void ShardConnection::sync( const string& db ) {
|
||||
ClientConnections::threadInstance()->sync(db);
|
||||
}
|
||||
|
||||
bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {
|
||||
|
||||
@@ -57,12 +57,14 @@ namespace mongo {
|
||||
return;
|
||||
}
|
||||
|
||||
const string addr = conn.getServerAddress();
|
||||
|
||||
{
|
||||
scoped_lock lk( _cacheLock );
|
||||
if ( _seenSets.count( conn.getServerAddress() ) )
|
||||
if ( _seenSets.count( addr ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we want to do writebacks on all rs nodes
|
||||
string errmsg;
|
||||
ConnectionString cs = ConnectionString::parse( conn.getServerAddress() , errmsg );
|
||||
@@ -73,6 +75,9 @@ namespace mongo {
|
||||
for ( unsigned i=0; i<hosts.size(); i++ )
|
||||
init( hosts[i].toString() );
|
||||
|
||||
scoped_lock lk( _cacheLock );
|
||||
_seenSets.insert( addr );
|
||||
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
||||
@@ -238,7 +238,7 @@ size_t copyString32to8counted( UChar8* dest8, const UChar32* source32, size_t ou
|
||||
size_t outputUTF8ByteCount = 0;
|
||||
if ( outputBufferSizeInBytes ) {
|
||||
size_t reducedBufferSize = outputBufferSizeInBytes - 4;
|
||||
while ( *source32 && charCount-- && outputUTF8ByteCount < reducedBufferSize ) {
|
||||
while ( charCount-- && *source32 && outputUTF8ByteCount < reducedBufferSize ) {
|
||||
UChar32 c = *source32++;
|
||||
if ( c <= 0x7F ) {
|
||||
*dest8++ = c;
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace mongo {
|
||||
catch (po::error &e) {
|
||||
cerr << "ERROR: " << e.what() << endl << endl;
|
||||
printHelp(cerr);
|
||||
return EXIT_BADOPTIONS;
|
||||
::_exit(EXIT_BADOPTIONS);
|
||||
}
|
||||
|
||||
// hide password from ps output
|
||||
@@ -160,12 +160,12 @@ namespace mongo {
|
||||
|
||||
if ( _params.count( "help" ) ) {
|
||||
printHelp(cout);
|
||||
return 0;
|
||||
::_exit(0);
|
||||
}
|
||||
|
||||
if ( _params.count( "version" ) ) {
|
||||
printVersion(cout);
|
||||
return 0;
|
||||
::_exit(0);
|
||||
}
|
||||
|
||||
if ( _params.count( "verbose" ) ) {
|
||||
@@ -206,13 +206,13 @@ namespace mongo {
|
||||
ConnectionString cs = ConnectionString::parse( _host , errmsg );
|
||||
if ( ! cs.isValid() ) {
|
||||
cerr << "invalid hostname [" << _host << "] " << errmsg << endl;
|
||||
return -1;
|
||||
::_exit(-1);
|
||||
}
|
||||
|
||||
_conn = cs.connect( errmsg );
|
||||
if ( ! _conn ) {
|
||||
cerr << "couldn't connect to [" << _host << "] " << errmsg << endl;
|
||||
return -1;
|
||||
::_exit(-1);
|
||||
}
|
||||
|
||||
(_usesstdout ? cout : cerr ) << "connected to: " << _host << endl;
|
||||
@@ -242,7 +242,7 @@ namespace mongo {
|
||||
"path you should connect to that instead of direct data "
|
||||
"file access" << endl << endl;
|
||||
dbexit( EXIT_CLEAN );
|
||||
return -1;
|
||||
::_exit(-1);
|
||||
}
|
||||
|
||||
FileAllocator::get()->start();
|
||||
@@ -311,7 +311,7 @@ namespace mongo {
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
return ret;
|
||||
::_exit(ret);
|
||||
}
|
||||
|
||||
DBClientBase& Tool::conn( bool slaveIfPaired ) {
|
||||
|
||||
@@ -90,10 +90,13 @@ namespace mongo {
|
||||
string s = ss.str();
|
||||
|
||||
if ( ! rename( lp.c_str() , s.c_str() ) ) {
|
||||
cout << "log file [" << lp << "] exists; copied to temporary file [" << s << "]" << endl;
|
||||
cout << "log file [" << lp
|
||||
<< "] exists; copied to temporary file [" << s << "]" << endl;
|
||||
} else {
|
||||
cout << "log file [" << lp << "] exists and couldn't make backup; run with --logappend or manually remove file (" << strerror(errno) << ")" << endl;
|
||||
|
||||
cout << "log file [" << lp
|
||||
<< "] exists and couldn't make backup [" << s
|
||||
<< "]; run with --logappend or manually remove file: "
|
||||
<< errnoWithDescription() << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +140,9 @@ namespace mongo {
|
||||
ss << _path << "." << terseCurrentTime( false );
|
||||
string s = ss.str();
|
||||
if (0 != rename(_path.c_str(), s.c_str())) {
|
||||
error() << "Failed to rename " << _path << " to " << s;
|
||||
error() << "Failed to rename '" << _path
|
||||
<< "' to '" << s
|
||||
<< "': " << errnoWithDescription() << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sock.h"
|
||||
#include "../../db/cmdline.h"
|
||||
#include "../mongoutils/str.h"
|
||||
#include "mongo/db/cmdline.h"
|
||||
#include "mongo/util/mongoutils/str.h"
|
||||
#include "mongo/util/net/sock.h"
|
||||
#include "mongo/bson/util/builder.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
@@ -72,6 +73,8 @@ namespace mongo {
|
||||
|
||||
operator string() const { return toString(); }
|
||||
|
||||
void append( StringBuilder& ss ) const;
|
||||
|
||||
bool empty() const {
|
||||
return _host.empty() && _port < 0;
|
||||
}
|
||||
@@ -122,14 +125,19 @@ namespace mongo {
|
||||
}
|
||||
|
||||
inline string HostAndPort::toString( bool includePort ) const {
|
||||
string h = host();
|
||||
if ( ! includePort )
|
||||
return host();
|
||||
|
||||
StringBuilder ss;
|
||||
append( ss );
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline void HostAndPort::append( StringBuilder& ss ) const {
|
||||
ss << host();
|
||||
|
||||
int p = port();
|
||||
|
||||
if ( ! includePort )
|
||||
return h;
|
||||
|
||||
stringstream ss;
|
||||
ss << h;
|
||||
if ( p != -1 ) {
|
||||
ss << ':';
|
||||
#if defined(_DEBUG)
|
||||
@@ -143,9 +151,10 @@ namespace mongo {
|
||||
ss << p;
|
||||
#endif
|
||||
}
|
||||
return ss.str();
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline bool HostAndPort::isLocalHost() const {
|
||||
string _host = host();
|
||||
return ( _host == "localhost"
|
||||
|
||||
@@ -502,6 +502,17 @@ namespace mongo {
|
||||
_timeout = timeout;
|
||||
_init();
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
close();
|
||||
#ifdef MONGO_SSL
|
||||
if ( _ssl ) {
|
||||
SSL_shutdown( _ssl );
|
||||
SSL_free( _ssl );
|
||||
_ssl = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Socket::_init() {
|
||||
_bytesOut = 0;
|
||||
@@ -513,13 +524,6 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void Socket::close() {
|
||||
#ifdef MONGO_SSL
|
||||
if ( _ssl ) {
|
||||
SSL_shutdown( _ssl );
|
||||
SSL_free( _ssl );
|
||||
_ssl = 0;
|
||||
}
|
||||
#endif
|
||||
if ( _fd >= 0 ) {
|
||||
closesocket( _fd );
|
||||
_fd = -1;
|
||||
|
||||
@@ -204,9 +204,7 @@ namespace mongo {
|
||||
*/
|
||||
Socket(double so_timeout = 0, int logLevel = 0 );
|
||||
|
||||
~Socket() {
|
||||
close();
|
||||
}
|
||||
~Socket();
|
||||
|
||||
bool connect(SockAddr& farEnd);
|
||||
void close();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/smart_ptr/scoped_array.hpp>
|
||||
#include "mongo/platform/windows_basic.h"
|
||||
#include <DbgHelp.h>
|
||||
@@ -53,6 +54,26 @@ namespace mongo {
|
||||
|
||||
namespace mongo {
|
||||
|
||||
/**
|
||||
* Get the path string to be used when searching for PDB files.
|
||||
*
|
||||
* @param process Process handle
|
||||
* @return searchPath Returned search path string
|
||||
*/
|
||||
static const char* getSymbolSearchPath(HANDLE process) {
|
||||
static std::string symbolSearchPath;
|
||||
|
||||
if (symbolSearchPath.empty()) {
|
||||
static const size_t bufferSize = 1024;
|
||||
boost::scoped_array<char> pathBuffer(new char[bufferSize]);
|
||||
GetModuleFileNameA(NULL, pathBuffer.get(), bufferSize);
|
||||
boost::filesystem::path exePath(pathBuffer.get());
|
||||
symbolSearchPath = exePath.parent_path().string();
|
||||
symbolSearchPath += ";C:\\Windows\\System32;C:\\Windows";
|
||||
}
|
||||
return symbolSearchPath.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name of the executable module containing the specified address.
|
||||
*
|
||||
@@ -172,7 +193,7 @@ namespace mongo {
|
||||
void printWindowsStackTrace( CONTEXT& context, std::ostream& os ) {
|
||||
SimpleMutex::scoped_lock lk(_stackTraceMutex);
|
||||
HANDLE process = GetCurrentProcess();
|
||||
BOOL ret = SymInitialize( process, NULL, TRUE );
|
||||
BOOL ret = SymInitialize(process, getSymbolSearchPath(process), TRUE);
|
||||
if ( ret == FALSE ) {
|
||||
DWORD dosError = GetLastError();
|
||||
log() << "Stack trace failed, SymInitialize failed with error " <<
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace mongo {
|
||||
* 1.2.3-rc4-pre-
|
||||
* If you really need to do something else you'll need to fix _versionArray()
|
||||
*/
|
||||
const char versionString[] = "2.2.2-rc0";
|
||||
const char versionString[] = "2.2.3";
|
||||
|
||||
// See unit test for example outputs
|
||||
static BSONArray _versionArray(const char* version){
|
||||
|
||||
Reference in New Issue
Block a user