/**
* 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/db/json.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/query_test_service_context.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/s/chunk_manager.h"
#include "mongo/stdx/memory.h"
namespace mongo {
using std::set;
using std::string;
using std::vector;
class TestableChunkManager : public ChunkManager {
public:
TestableChunkManager(const string& ns,
const ShardKeyPattern& keyPattern,
std::unique_ptr defaultCollator,
bool unique)
: ChunkManager(ns, keyPattern, std::move(defaultCollator), unique) {}
void setSingleChunkForShards(const vector& splitPoints) {
vector mySplitPoints(splitPoints);
mySplitPoints.insert(mySplitPoints.begin(), _keyPattern.getKeyPattern().globalMin());
mySplitPoints.push_back(_keyPattern.getKeyPattern().globalMax());
for (unsigned i = 1; i < mySplitPoints.size(); ++i) {
const string shardId = str::stream() << (i - 1);
_shardIds.insert(shardId);
std::shared_ptr chunk(new Chunk(this,
mySplitPoints[i - 1],
mySplitPoints[i],
shardId,
ChunkVersion(0, 0, OID()),
0));
_chunkMap[mySplitPoints[i]] = chunk;
}
_chunkRangeMap = _constructRanges(_chunkMap);
}
};
namespace {
class Base {
public:
Base() = default;
virtual ~Base() = default;
void run() {
QueryTestServiceContext serviceContext;
auto opCtx = serviceContext.makeOperationContext();
ShardKeyPattern shardKeyPattern(shardKey());
TestableChunkManager chunkManager("", shardKeyPattern, defaultCollator(), false);
chunkManager.setSingleChunkForShards(splitPointsVector());
set shardIds;
chunkManager.getShardIdsForQuery(opCtx.get(), query(), queryCollation(), &shardIds);
BSONArrayBuilder b;
for (const ShardId& shardId : shardIds) {
b << shardId;
}
ASSERT_EQUALS(expectedShardNames(), b.arr());
}
protected:
virtual BSONObj shardKey() const {
return BSON("a" << 1);
}
virtual std::unique_ptr defaultCollator() const {
return {nullptr};
}
virtual BSONArray splitPoints() const {
return BSONArray();
}
virtual BSONObj query() const {
return BSONObj();
}
virtual BSONObj queryCollation() const {
return BSONObj();
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0");
}
virtual vector splitPointsVector() const {
vector ret;
BSONArray a = splitPoints();
BSONObjIterator i(a);
while (i.more()) {
ret.push_back(i.next().Obj().getOwned());
}
return ret;
}
};
class EmptyQuerySingleShard : public Base {};
class MultiShardBase : public Base {
virtual BSONArray splitPoints() const {
return BSON_ARRAY(BSON("a"
<< "x")
<< BSON("a"
<< "y")
<< BSON("a"
<< "z"));
}
};
class EmptyQueryMultiShard : public MultiShardBase {
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2"
<< "3");
}
};
class UniversalRangeMultiShard : public EmptyQueryMultiShard {
virtual BSONObj query() const {
return BSON("b" << 1);
}
};
class EqualityRangeSingleShard : public EmptyQuerySingleShard {
virtual BSONObj query() const {
return BSON("a"
<< "x");
}
};
class EqualityRangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a"
<< "y");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("2");
}
};
class SetRangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return fromjson("{a:{$in:['u','y']}}");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "2");
}
};
class GTRangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << GT << "x");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("1"
<< "2"
<< "3");
}
};
class GTERangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << GTE << "x");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("1"
<< "2"
<< "3");
}
};
class LTRangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << LT << "y");
}
/**
* It isn't actually necessary to return shard 2 because its lowest key is "y", which
* is excluded from the query. SERVER-4791
*/
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2");
}
};
class LTERangeMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << LTE << "y");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2");
}
};
class OrEqualities : public MultiShardBase {
virtual BSONObj query() const {
return fromjson("{$or:[{a:'u'},{a:'y'}]}");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "2");
}
};
class OrEqualityInequality : public MultiShardBase {
virtual BSONObj query() const {
return fromjson("{$or:[{a:'u'},{a:{$gte:'y'}}]}");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "2"
<< "3");
}
};
class OrEqualityInequalityUnhelpful : public MultiShardBase {
virtual BSONObj query() const {
return fromjson("{$or:[{a:'u'},{a:{$gte:'zz'}},{}]}");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2"
<< "3");
}
};
template
class Unsatisfiable : public BASE {
/**
* SERVER-4914 For now the first shard is returned for unsatisfiable queries, as some
* clients of getShardIdsForQuery() expect at least one shard.
*/
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0");
}
};
class UnsatisfiableRangeSingleShard : public Unsatisfiable {
virtual BSONObj query() const {
return BSON("a" << GT << "x" << LT << "x");
}
};
class UnsatisfiableRangeMultiShard : public Unsatisfiable {
virtual BSONObj query() const {
return BSON("a" << GT << "x" << LT << "x");
}
};
class EqualityThenUnsatisfiable : public Unsatisfiable {
virtual BSONObj shardKey() const {
return BSON("a" << 1 << "b" << 1);
}
virtual BSONObj query() const {
return BSON("a" << 1 << "b" << GT << 4 << LT << 4);
}
};
class InequalityThenUnsatisfiable : public Unsatisfiable {
virtual BSONObj shardKey() const {
return BSON("a" << 1 << "b" << 1);
}
virtual BSONObj query() const {
return BSON("a" << GT << 1 << "b" << GT << 4 << LT << 4);
}
};
class OrEqualityUnsatisfiableInequality : public MultiShardBase {
virtual BSONObj query() const {
return fromjson("{$or:[{a:'x'},{a:{$gt:'u',$lt:'u'}},{a:{$gte:'y'}}]}");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("1"
<< "2"
<< "3");
}
};
class CompoundKeyBase : public Base {
virtual BSONObj shardKey() const {
return BSON("a" << 1 << "b" << 1);
}
virtual BSONArray splitPoints() const {
return BSON_ARRAY(BSON("a" << 5 << "b" << 10) << BSON("a" << 5 << "b" << 20));
}
};
class InMultiShard : public CompoundKeyBase {
virtual BSONObj query() const {
return BSON("a" << BSON("$in" << BSON_ARRAY(0 << 5 << 10)) << "b"
<< BSON("$in" << BSON_ARRAY(0 << 5 << 25)));
}
// If we were to send this query to just the shards it actually needed to hit, it would
// only hit shards 0 and 2. Because of the optimization from SERVER-4745, however, we'll
// also hit shard 1.
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2");
}
};
class CollationStringsMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a"
<< "y");
}
virtual BSONObj queryCollation() const {
return BSON("locale"
<< "mock_reverse_string");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2"
<< "3");
}
};
class DefaultCollationStringsMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a"
<< "y");
}
virtual std::unique_ptr defaultCollator() const {
auto collator = stdx::make_unique(
CollatorInterfaceMock::MockType::kReverseString);
return {std::move(collator)};
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0"
<< "1"
<< "2"
<< "3");
}
};
class SimpleCollationStringsMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a"
<< "y");
}
virtual std::unique_ptr defaultCollator() const {
auto collator = stdx::make_unique(
CollatorInterfaceMock::MockType::kReverseString);
return {std::move(collator)};
}
virtual BSONObj queryCollation() const {
return BSON("locale"
<< "simple");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("2");
}
};
class CollationNumbersMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << 5);
}
virtual BSONObj queryCollation() const {
return BSON("locale"
<< "mock_reverse_string");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0");
}
};
class DefaultCollationNumbersMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << 5);
}
virtual std::unique_ptr defaultCollator() const {
auto collator = stdx::make_unique(
CollatorInterfaceMock::MockType::kReverseString);
return {std::move(collator)};
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0");
}
};
class SimpleCollationNumbersMultiShard : public MultiShardBase {
virtual BSONObj query() const {
return BSON("a" << 5);
}
virtual std::unique_ptr defaultCollator() const {
auto collator = stdx::make_unique(
CollatorInterfaceMock::MockType::kReverseString);
return {std::move(collator)};
}
virtual BSONObj queryCollation() const {
return BSON("locale"
<< "simple");
}
virtual BSONArray expectedShardNames() const {
return BSON_ARRAY("0");
}
};
class All : public Suite {
public:
All() : Suite("chunk") {}
void setupTests() {
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
add();
}
};
SuiteInstance myAll;
} // namespace
} // namespace mongo