Co-authored-by: Benety Goh <benety@mongodb.com> GitOrigin-RevId: e97b100882e14cca504952d321cedfb135321240
1694 lines
65 KiB
C++
1694 lines
65 KiB
C++
/**
|
|
* Copyright (C) 2018-present MongoDB, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the Server Side Public License, version 1,
|
|
* as published by MongoDB, Inc.
|
|
*
|
|
* 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
|
|
* Server Side Public License for more details.
|
|
*
|
|
* You should have received a copy of the Server Side Public License
|
|
* along with this program. If not, see
|
|
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
|
*
|
|
* 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 Server Side 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 <algorithm>
|
|
#include <boost/cstdint.hpp>
|
|
#include <boost/move/utility_core.hpp>
|
|
#include <boost/none.hpp>
|
|
#include <boost/optional/optional.hpp>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "mongo/base/error_codes.h"
|
|
#include "mongo/base/status.h"
|
|
#include "mongo/base/status_with.h"
|
|
#include "mongo/base/string_data.h"
|
|
#include "mongo/bson/bsonelement.h"
|
|
#include "mongo/bson/bsonmisc.h"
|
|
#include "mongo/bson/bsonobj.h"
|
|
#include "mongo/bson/bsonobjbuilder.h"
|
|
#include "mongo/bson/json.h"
|
|
#include "mongo/bson/simple_bsonobj_comparator.h"
|
|
#include "mongo/bson/timestamp.h"
|
|
#include "mongo/db/basic_types.h"
|
|
#include "mongo/db/basic_types_gen.h"
|
|
#include "mongo/db/cursor_id.h"
|
|
#include "mongo/db/namespace_string.h"
|
|
#include "mongo/db/pipeline/aggregate_command_gen.h"
|
|
#include "mongo/db/pipeline/aggregation_request_helper.h"
|
|
#include "mongo/db/pipeline/legacy_runtime_constants_gen.h"
|
|
#include "mongo/db/pipeline/query_request_conversion.h"
|
|
#include "mongo/db/query/cursor_response_gen.h"
|
|
#include "mongo/db/query/find_command.h"
|
|
#include "mongo/db/query/find_command_gen.h"
|
|
#include "mongo/db/query/max_time_ms_parser.h"
|
|
#include "mongo/db/query/query_request_helper.h"
|
|
#include "mongo/db/query/query_test_service_context.h"
|
|
#include "mongo/db/query/tailable_mode_gen.h"
|
|
#include "mongo/db/repl/replication_coordinator.h"
|
|
#include "mongo/db/repl/replication_coordinator_mock.h"
|
|
#include "mongo/db/service_context_test_fixture.h"
|
|
#include "mongo/db/tenant_id.h"
|
|
#include "mongo/idl/idl_parser.h"
|
|
#include "mongo/idl/server_parameter_test_util.h"
|
|
#include "mongo/rpc/op_msg.h"
|
|
#include "mongo/unittest/assert.h"
|
|
#include "mongo/unittest/bson_test_util.h"
|
|
#include "mongo/unittest/framework.h"
|
|
#include "mongo/util/assert_util.h"
|
|
#include "mongo/util/serialization_context.h"
|
|
#include "mongo/util/time_support.h"
|
|
#include "mongo/util/uuid.h"
|
|
|
|
namespace mongo {
|
|
namespace {
|
|
|
|
using std::unique_ptr;
|
|
|
|
static const NamespaceString testns =
|
|
NamespaceString::createNamespaceString_forTest("testdb.testcoll");
|
|
|
|
TEST(QueryRequestTest, NegativeSkip) {
|
|
FindCommandRequest findCommand(testns);
|
|
ASSERT_THROWS_CODE(findCommand.setSkip(-1), DBException, ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ZeroSkip) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSkip(0);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, PositiveSkip) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSkip(1);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, NegativeLimit) {
|
|
FindCommandRequest findCommand(testns);
|
|
ASSERT_THROWS_CODE(findCommand.setLimit(-1), DBException, ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ZeroLimit) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setLimit(0);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, PositiveLimit) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setLimit(1);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, NegativeBatchSize) {
|
|
FindCommandRequest findCommand(testns);
|
|
ASSERT_THROWS_CODE(findCommand.setBatchSize(-1), DBException, ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ZeroBatchSize) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setBatchSize(0);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, PositiveBatchSize) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setBatchSize(1);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, NegativeMaxTimeMS) {
|
|
FindCommandRequest findCommand(testns);
|
|
ASSERT_THROWS_CODE(findCommand.setMaxTimeMS(-1), DBException, ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ZeroMaxTimeMS) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMaxTimeMS(0);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, PositiveMaxTimeMS) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMaxTimeMS(1);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ValidSortOrder) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSort(fromjson("{a: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, DoesNotErrorOnInvalidSortPattern) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSort(fromjson("{a: \"\"}"));
|
|
// FindCommandRequest isn't responsible for validating the sort pattern, so it is considered
|
|
// valid even though the sort pattern {a: ""} is not well-formed.
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, MinFieldsNotPrefixOfMax) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMin(fromjson("{a: 1}"));
|
|
findCommand.setMax(fromjson("{b: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, MinFieldsMoreThanMax) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMin(fromjson("{a: 1, b: 1}"));
|
|
findCommand.setMax(fromjson("{a: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, MinFieldsLessThanMax) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMin(fromjson("{a: 1}"));
|
|
findCommand.setMax(fromjson("{a: 1, b: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ForbidTailableWithNonNaturalSort) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"tailable: true,"
|
|
"sort: {a: 1}, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ForbidTailableWithSingleBatch) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"tailable: true,"
|
|
"singleBatch: true, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, AllowTailableWithNaturalSort) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"tailable: true,"
|
|
"sort: {$natural: 1}, '$db': 'test'}");
|
|
|
|
auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
ASSERT_TRUE(findCommand->getTailable());
|
|
ASSERT_BSONOBJ_EQ(findCommand->getSort(), BSON("$natural" << 1));
|
|
}
|
|
|
|
//
|
|
// Test compatibility of various projection and sort objects.
|
|
//
|
|
|
|
TEST(QueryRequestTest, ValidSortProj) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setProjection(fromjson("{a: 1}"));
|
|
findCommand.setSort(fromjson("{a: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
|
|
FindCommandRequest metaFC(testns);
|
|
metaFC.setProjection(fromjson("{a: {$meta: \"textScore\"}}"));
|
|
metaFC.setSort(fromjson("{a: {$meta: \"textScore\"}}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(metaFC));
|
|
}
|
|
|
|
TEST(QueryRequestTest, TextScoreMetaSortOnFieldDoesNotRequireMetaProjection) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setProjection(fromjson("{b: 1}"));
|
|
findCommand.setSort(fromjson("{a: {$meta: 'textScore'}}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, TextScoreMetaProjectionDoesNotRequireTextScoreMetaSort) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setProjection(fromjson("{a: {$meta: \"textScore\"}}"));
|
|
findCommand.setSort(fromjson("{b: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, RequestResumeTokenWithHint) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setRequestResumeToken(true);
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setHint(fromjson("{a: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, RequestResumeTokenWithSort) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setRequestResumeToken(true);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setSort(fromjson("{a: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setSort(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, InvalidResumeAfterWrongRecordIdType) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
findCommand.setRequestResumeToken(true);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
resumeAfter = BSON("$recordId" << 1LL);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
ASSERT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
}
|
|
|
|
TEST(QueryRequestTest, InvalidResumeAfterExtraField) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1LL << "$extra" << 1);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
findCommand.setRequestResumeToken(true);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ResumeAfterWithHint) {
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1LL);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
findCommand.setRequestResumeToken(true);
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setHint(fromjson("{a: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ResumeAfterWithSort) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1LL);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
findCommand.setRequestResumeToken(true);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
ASSERT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
findCommand.setSort(fromjson("{a: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setSort(fromjson("{$natural: 1}"));
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ResumeNoSpecifiedRequestResumeToken) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1LL);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setRequestResumeToken(true);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
ASSERT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ExplicitEmptyResumeAfter) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
FindCommandRequest findCommand(NamespaceString::kRsOplogNamespace);
|
|
BSONObj resumeAfter = fromjson("{}");
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setRequestResumeToken(true);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
ASSERT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ResumeAfterMismatchInitialSyncId) {
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
auto replCoord =
|
|
std::make_unique<repl::ReplicationCoordinatorMock>(serviceContext.getServiceContext());
|
|
repl::ReplicationCoordinator::set(serviceContext.getServiceContext(), std::move(replCoord));
|
|
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter =
|
|
BSON("$recordId" << 1LL << "$initialSyncId"
|
|
<< uassertStatusOK(UUID::parse("12345678-1234-9876-1234-000000000000")));
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
// Hint must be explicitly set for the query request to validate.
|
|
findCommand.setHint(fromjson("{$natural: 1}"));
|
|
ASSERT_NOT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
findCommand.setRequestResumeToken(true);
|
|
ASSERT_OK(query_request_helper::validateFindCommandRequest(findCommand));
|
|
ASSERT_NOT_OK(query_request_helper::validateResumeAfter(
|
|
opCtx, findCommand.getResumeAfter(), false /* isClusteredCollection */));
|
|
}
|
|
|
|
//
|
|
// Text meta BSON element validation
|
|
//
|
|
|
|
bool isFirstElementTextScoreMeta(const char* sortStr) {
|
|
BSONObj sortObj = fromjson(sortStr);
|
|
BSONElement elt = sortObj.firstElement();
|
|
bool result = query_request_helper::isTextScoreMeta(elt);
|
|
return result;
|
|
}
|
|
|
|
// Check validation of $meta expressions
|
|
TEST(QueryRequestTest, IsTextScoreMeta) {
|
|
// Valid textScore meta sort
|
|
ASSERT(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\"}}"));
|
|
|
|
// Invalid textScore meta sorts
|
|
ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: 1}}"));
|
|
ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"image\"}}"));
|
|
ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$world: \"textScore\"}}"));
|
|
ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\", b: 1}}"));
|
|
}
|
|
|
|
//
|
|
// Tests for parsing a query request from a command BSON object.
|
|
//
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandBasic) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 3},"
|
|
"sort: {a: 1},"
|
|
"projection: {_id: 0, a: 1}, '$db': 'test'}");
|
|
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandWithOptions) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 3},"
|
|
"sort: {a: 1},"
|
|
"projection: {_id: 0, a: 1},"
|
|
"showRecordId: true, '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
// Make sure the values from the command BSON are reflected in the QR.
|
|
ASSERT(findCommand->getShowRecordId());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandHintAsString) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"hint: 'foo_1', '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
BSONObj hintObj = findCommand->getHint();
|
|
ASSERT_BSONOBJ_EQ(BSON("$hint"
|
|
<< "foo_1"),
|
|
hintObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandValidSortProj) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {a: 1},"
|
|
"sort: {a: 1}, '$db': 'test'}");
|
|
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandValidSortProjMeta) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {a: {$meta: 'textScore'}},"
|
|
"sort: {a: {$meta: 'textScore'}}, '$db': 'test'}");
|
|
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandAllFlagsTrue) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"tailable: true,"
|
|
"noCursorTimeout: true,"
|
|
"awaitData: true,"
|
|
"allowPartialResults: true,"
|
|
"readOnce: true,"
|
|
"includeQueryStatsMetrics: true,"
|
|
"allowSpeculativeMajorityRead: true, '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
// Test that all the flags got set to true.
|
|
ASSERT(findCommand->getTailable());
|
|
ASSERT(findCommand->getNoCursorTimeout());
|
|
ASSERT(findCommand->getTailable() && findCommand->getAwaitData());
|
|
ASSERT(findCommand->getAllowPartialResults());
|
|
ASSERT(findCommand->getReadOnce());
|
|
ASSERT(findCommand->getIncludeQueryStatsMetrics());
|
|
ASSERT(findCommand->getAllowSpeculativeMajorityRead());
|
|
}
|
|
|
|
TEST(QueryRequestTest, OplogReplayFlagIsAllowedButIgnored) {
|
|
auto cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "oplogReplay" << true << "tailable" << true << "$db"
|
|
<< "test");
|
|
const NamespaceString nss = NamespaceString::createNamespaceString_forTest("test.testns");
|
|
auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
|
|
// Verify that the 'oplogReplay' flag does not appear if we reserialize the request.
|
|
auto reserialized = findCommand->toBSON(BSONObj());
|
|
ASSERT_BSONOBJ_EQ(reserialized,
|
|
BSON("find"
|
|
<< "testns"
|
|
<< "tailable" << true));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandReadOnceDefaultsToFalse) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(!findCommand->getReadOnce());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandIncludeQueryStatsMetricsDefaultsToFalse) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(!findCommand->getIncludeQueryStatsMetrics());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandValidMinMax) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"comment: 'the comment',"
|
|
"min: {a: 1},"
|
|
"max: {a: 2}, '$db': 'test'}");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
BSONObj expectedMin = BSON("a" << 1);
|
|
ASSERT_EQUALS(0, expectedMin.woCompare(findCommand->getMin()));
|
|
BSONObj expectedMax = BSON("a" << 2);
|
|
ASSERT_EQUALS(0, expectedMax.woCompare(findCommand->getMax()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandAllNonOptionFields) {
|
|
LegacyRuntimeConstants rtc{Date_t::now(), Timestamp(1, 1)};
|
|
BSONObj rtcObj = BSON("runtimeConstants" << rtc.toBSON());
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"sort: {b: 1},"
|
|
"projection: {c: 1},"
|
|
"hint: {d: 1},"
|
|
"readConcern: {e: 1},"
|
|
"$queryOptions: {$readPreference: 'secondary'},"
|
|
"collation: {f: 1},"
|
|
"limit: 3,"
|
|
"skip: 5,"
|
|
"batchSize: 90,"
|
|
"singleBatch: false, '$db': 'test'}")
|
|
.addField(rtcObj["runtimeConstants"]);
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
// Check the values inside the QR.
|
|
BSONObj expectedQuery = BSON("a" << 1);
|
|
ASSERT_EQUALS(0, expectedQuery.woCompare(findCommand->getFilter()));
|
|
BSONObj expectedSort = BSON("b" << 1);
|
|
ASSERT_EQUALS(0, expectedSort.woCompare(findCommand->getSort()));
|
|
BSONObj expectedProj = BSON("c" << 1);
|
|
ASSERT_EQUALS(0, expectedProj.woCompare(findCommand->getProjection()));
|
|
BSONObj expectedHint = BSON("d" << 1);
|
|
ASSERT_EQUALS(0, expectedHint.woCompare(findCommand->getHint()));
|
|
BSONObj expectedReadConcern = BSON("e" << 1);
|
|
ASSERT(findCommand->getReadConcern());
|
|
ASSERT_BSONOBJ_EQ(expectedReadConcern, *findCommand->getReadConcern());
|
|
BSONObj expectedUnwrappedReadPref = BSON("$readPreference"
|
|
<< "secondary");
|
|
ASSERT_EQUALS(0, expectedUnwrappedReadPref.woCompare(findCommand->getUnwrappedReadPref()));
|
|
BSONObj expectedCollation = BSON("f" << 1);
|
|
ASSERT_EQUALS(0, expectedCollation.woCompare(findCommand->getCollation()));
|
|
ASSERT_EQUALS(3, *findCommand->getLimit());
|
|
ASSERT_EQUALS(5, *findCommand->getSkip());
|
|
ASSERT_EQUALS(90, *findCommand->getBatchSize());
|
|
ASSERT(findCommand->getLegacyRuntimeConstants().has_value());
|
|
ASSERT_EQUALS(findCommand->getLegacyRuntimeConstants()->getLocalNow(), rtc.getLocalNow());
|
|
ASSERT_EQUALS(findCommand->getLegacyRuntimeConstants()->getClusterTime(), rtc.getClusterTime());
|
|
ASSERT(!findCommand->getSingleBatch());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLargeLimit) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"limit: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getLimit());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLargeBatchSize) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"batchSize: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getBatchSize());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLargeSkip) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"skip: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getSkip());
|
|
}
|
|
|
|
//
|
|
// Parsing errors where a field has the wrong type.
|
|
//
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandQueryWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandSortWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"sort: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandProjWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"projection: 'foo', '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandSkipWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"skip: '5',"
|
|
"projection: {a: 1}, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLimitWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"limit: '5',"
|
|
"projection: {a: 1}, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandSingleBatchWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"singleBatch: 'false',"
|
|
"projection: {a: 1}, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandUnwrappedReadPrefWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"$queryOptions: 1, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandMaxTimeMSWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"maxTimeMS: true, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandMaxWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"max: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandMinWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"min: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandReturnKeyWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"returnKey: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandShowRecordIdWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"showRecordId: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandTailableWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"tailable: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandSlaveOkWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"slaveOk: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandOplogReplayWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"oplogReplay: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandNoCursorTimeoutWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"noCursorTimeout: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandAwaitDataWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"tailable: true,"
|
|
"awaitData: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandExhaustWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"exhaust: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField);
|
|
}
|
|
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandPartialWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"allowPartialResults: 3, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandReadConcernWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"readConcern: 'foo', '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandCollationWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"collation: 'foo', '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandReadOnceWrongType) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"readOnce: 1, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLegacyRuntimeConstantsWrongType) {
|
|
BSONObj cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "runtimeConstants"
|
|
<< "shouldNotBeString"
|
|
<< "$db"
|
|
<< "test");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLegacyRuntimeConstantsSubfieldsWrongType) {
|
|
BSONObj cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "runtimeConstants"
|
|
<< BSON("localNow"
|
|
<< "shouldBeDate"
|
|
<< "clusterTime"
|
|
<< "shouldBeTimestamp")
|
|
<< "$db"
|
|
<< "test");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
AssertionException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandIncludeQueryStatsMetricsWrongType) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test', 'includeQueryStatsMetrics': 42}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
//
|
|
// Parsing errors where a field has the right type but a bad value.
|
|
//
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandNegativeSkipError) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"skip: -3,"
|
|
"filter: {a: 3}, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandSkipIsZero) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"skip: 0,"
|
|
"filter: {a: 3}, '$db': 'test'}");
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT_BSONOBJ_EQ(BSON("a" << 3), findCommand->getFilter());
|
|
ASSERT_FALSE(findCommand->getSkip());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandNegativeLimitError) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"limit: -3,"
|
|
"filter: {a: 3}, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandLimitIsZero) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"limit: 0,"
|
|
"filter: {a: 3}, '$db': 'test'}");
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT_BSONOBJ_EQ(BSON("a" << 3), findCommand->getFilter());
|
|
ASSERT_FALSE(findCommand->getLimit());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandNegativeBatchSizeError) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"batchSize: -10,"
|
|
"filter: {a: 3}, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandBatchSizeZero) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', batchSize: 0, '$db': 'test'}");
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT(findCommand->getBatchSize());
|
|
ASSERT_EQ(0, *findCommand->getBatchSize());
|
|
ASSERT(!findCommand->getLimit());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandDefaultBatchSize) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}");
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT(!findCommand->getBatchSize());
|
|
ASSERT(!findCommand->getLimit());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandRequestResumeToken) {
|
|
BSONObj cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "hint" << BSON("$natural" << 1) << "sort" << BSON("$natural" << 1)
|
|
<< "$_requestResumeToken" << true << "$db"
|
|
<< "test");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(findCommand->getRequestResumeToken());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandResumeToken) {
|
|
BSONObj cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "hint" << BSON("$natural" << 1) << "sort" << BSON("$natural" << 1)
|
|
<< "$_requestResumeToken" << true << "$_resumeAfter"
|
|
<< BSON("$recordId" << 1LL) << "$db"
|
|
<< "test");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(!findCommand->getResumeAfter().isEmpty());
|
|
ASSERT(findCommand->getRequestResumeToken());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandEmptyResumeToken) {
|
|
BSONObj resumeAfter = fromjson("{}");
|
|
BSONObj cmdObj =
|
|
BSON("find"
|
|
<< "testns"
|
|
<< "hint" << BSON("$natural" << 1) << "sort" << BSON("$natural" << 1)
|
|
<< "$_requestResumeToken" << true << "$_resumeAfter" << resumeAfter << "$db"
|
|
<< "test");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(findCommand->getRequestResumeToken());
|
|
ASSERT(findCommand->getResumeAfter().isEmpty());
|
|
}
|
|
|
|
//
|
|
// Test FindCommandRequest object ns and uuid variants.
|
|
//
|
|
|
|
TEST(QueryRequestTest, AsFindCommandRequestAllNonOptionFields) {
|
|
BSONObj storage = BSON("runtimeConstants"
|
|
<< (LegacyRuntimeConstants{Date_t::now(), Timestamp(1, 1)}.toBSON()));
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"filter: {a: 1},"
|
|
"projection: {c: 1},"
|
|
"sort: {b: 1},"
|
|
"hint: {d: 1},"
|
|
"collation: {f: 1},"
|
|
"skip: 5,"
|
|
"limit: 3,"
|
|
"batchSize: 90,"
|
|
"singleBatch: true, "
|
|
"readConcern: {e: 1}, '$db': 'test'}")
|
|
.addField(storage["runtimeConstants"]);
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, AsFindCommandRequestWithUuidAllNonOptionFields) {
|
|
BSONObj storage = BSON("runtimeConstants"
|
|
<< (LegacyRuntimeConstants{Date_t::now(), Timestamp(1, 1)}.toBSON()));
|
|
BSONObj cmdObj =
|
|
fromjson(
|
|
// This binary value is UUID("01234567-89ab-cdef-edcb-a98765432101")
|
|
"{find: { \"$binary\" : \"ASNFZ4mrze/ty6mHZUMhAQ==\", \"$type\" : \"04\" },"
|
|
"filter: {a: 1},"
|
|
"projection: {c: 1},"
|
|
"sort: {b: 1},"
|
|
"hint: {d: 1},"
|
|
"collation: {f: 1},"
|
|
"skip: 5,"
|
|
"limit: 3,"
|
|
"batchSize: 90,"
|
|
"singleBatch: true,"
|
|
"readConcern: {e: 1}, '$db': 'test'}")
|
|
.addField(storage["runtimeConstants"]);
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, AsFindCommandRequestWithUuidNoAvailableNamespace) {
|
|
BSONObj cmdObj =
|
|
fromjson("{find: { \"$binary\" : \"ASNFZ4mrze/ty6mHZUMhAQ==\", \"$type\" : \"04\" }}");
|
|
FindCommandRequest findCommand(
|
|
NamespaceStringOrUUID(DatabaseName::createDatabaseName_forTest(boost::none, "test"),
|
|
UUID::parse("01234567-89ab-cdef-edcb-a98765432101").getValue()));
|
|
ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand.toBSON(BSONObj()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, AsFindCommandRequestWithResumeToken) {
|
|
BSONObj cmdObj = BSON("find"
|
|
<< "testns"
|
|
<< "sort" << BSON("$natural" << 1) << "hint" << BSON("$natural" << 1)
|
|
<< "$_requestResumeToken" << true << "$_resumeAfter"
|
|
<< BSON("$recordId" << 1LL) << "$db"
|
|
<< "test");
|
|
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, AsFindCommandRequestWithEmptyResumeToken) {
|
|
BSONObj resumeAfter = fromjson("{}");
|
|
BSONObj cmdObj =
|
|
BSON("find"
|
|
<< "testns"
|
|
<< "hint" << BSON("$natural" << 1) << "sort" << BSON("$natural" << 1)
|
|
<< "$_requestResumeToken" << true << "$_resumeAfter" << resumeAfter << "$db"
|
|
<< "test");
|
|
unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
ASSERT(findCommand->toBSON(BSONObj()).getField("$_resumeAftr").eoo());
|
|
}
|
|
|
|
//
|
|
//
|
|
// Errors checked in query_request_helper::validateFindCommandRequest().
|
|
//
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandMinMaxDifferentFieldsError) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"min: {a: 3},"
|
|
"max: {b: 4}, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 51176);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandAllowNonMetaSortOnFieldWithMetaProject) {
|
|
BSONObj cmdObj;
|
|
|
|
cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {a: {$meta: 'textScore'}},"
|
|
"sort: {a: 1}, '$db': 'test'}");
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
|
|
cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {a: {$meta: 'textScore'}},"
|
|
"sort: {b: 1}, '$db': 'test'}");
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandAllowMetaSortOnFieldWithoutMetaProject) {
|
|
BSONObj cmdObj;
|
|
|
|
cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {a: 1},"
|
|
"sort: {a: {$meta: 'textScore'}}, '$db': 'test'}");
|
|
|
|
auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
|
|
cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"projection: {b: 1},"
|
|
"sort: {a: {$meta: 'textScore'}}, '$db': 'test'}");
|
|
findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandForbidExhaust) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', exhaust: true, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField);
|
|
}
|
|
|
|
// Older versions of the server supported an "ntoreturn" parameter to the find command, which was
|
|
// used for upconversion of legacy OP_QUERY to a find command. Now that OP_QUERY is no longer
|
|
// supported, the "ntoreturn" parameter is not supported either, and should be rejected during
|
|
// parsing.
|
|
TEST(QueryRequestTest, NToReturnParamFailsToParse) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test', ntoreturn: 5}");
|
|
ASSERT_THROWS_CODE_AND_WHAT(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField,
|
|
"BSON field 'FindCommandRequest.ntoreturn' is an unknown field.");
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandAwaitDataButNotTailable) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', awaitData: true, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::FailedToParse);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandFirstFieldNotString) {
|
|
BSONObj cmdObj = fromjson("{find: 1, '$db': 'test'}");
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandIgnoreShardVersionField) {
|
|
BSONObj cmdObj = fromjson("{find: 'test.testns', shardVersion: 'foo', '$db': 'test'}");
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
}
|
|
|
|
TEST(QueryRequestTest, DefaultQueryParametersCorrect) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}");
|
|
|
|
std::unique_ptr<FindCommandRequest> findCommand(
|
|
query_request_helper::makeFromFindCommandForTests(cmdObj));
|
|
|
|
ASSERT_FALSE(findCommand->getSkip());
|
|
ASSERT_FALSE(findCommand->getLimit());
|
|
|
|
ASSERT_FALSE(findCommand->getSingleBatch());
|
|
ASSERT_EQUALS(0, findCommand->getMaxTimeMS().value_or(0));
|
|
ASSERT_EQUALS(false, findCommand->getReturnKey());
|
|
ASSERT_EQUALS(false, findCommand->getShowRecordId());
|
|
ASSERT_EQUALS(false, findCommand->getTailable());
|
|
ASSERT_EQUALS(false, findCommand->getNoCursorTimeout());
|
|
ASSERT_EQUALS(false, findCommand->getTailable() && findCommand->getAwaitData());
|
|
ASSERT_EQUALS(false, findCommand->getAllowPartialResults());
|
|
ASSERT_EQUALS(false, findCommand->getLegacyRuntimeConstants().has_value());
|
|
ASSERT_EQUALS(false, findCommand->getAllowDiskUse());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandAllowDiskUseTrue) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', allowDiskUse: true, '$db': 'test'}");
|
|
|
|
auto result = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
|
|
ASSERT_EQ(true, result->getAllowDiskUse());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseCommandAllowDiskUseFalse) {
|
|
BSONObj cmdObj = fromjson("{find: 'testns', allowDiskUse: false, '$db': 'test'}");
|
|
|
|
auto result = query_request_helper::makeFromFindCommandForTests(cmdObj);
|
|
|
|
ASSERT_EQ(false, result->getAllowDiskUse());
|
|
}
|
|
|
|
//
|
|
// Extra fields cause the parse to fail.
|
|
//
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandForbidExtraField) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"foo: {a: 1}, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseFromCommandForbidExtraOption) {
|
|
BSONObj cmdObj = fromjson(
|
|
"{find: 'testns',"
|
|
"foo: true, '$db': 'test'}");
|
|
|
|
ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj),
|
|
DBException,
|
|
ErrorCodes::IDLUnknownField);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSStringValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << "foo");
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSBoolValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << true);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSNullValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << BSONNULL);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSUndefinedValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << BSONUndefined);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSEmptyObjectValueFails) {
|
|
const auto emptyObj = BSONObjBuilder().obj();
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << emptyObj);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSNonIntegralValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 100.3);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSOutOfRangeDoubleFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 1e200);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSNegativeValueFails) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << -400);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]).getStatus(),
|
|
ErrorCodes::BadValue);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSZeroSucceeds) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 0);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]), 0);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSEmptyElementSucceeds) {
|
|
const auto emptyElem = BSONElement();
|
|
ASSERT_EQ(parseMaxTimeMS(emptyElem), 0);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ParseMaxTimeMSPositiveInRangeSucceeds) {
|
|
BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 300);
|
|
ASSERT_EQ(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]), 300);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(!ar.getExplain());
|
|
ASSERT(!ar.getAllowDiskUse().has_value());
|
|
ASSERT(ar.getPipeline().empty());
|
|
ASSERT_EQ(ar.getCursor().getBatchSize().value_or(aggregation_request_helper::kDefaultBatchSize),
|
|
aggregation_request_helper::kDefaultBatchSize);
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSONObj());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationOmitsExplain) {
|
|
FindCommandRequest findCommand(testns);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT_FALSE(ar.getExplain());
|
|
ASSERT(ar.getPipeline().empty());
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSONObj());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithHintSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setHint(fromjson("{a_1: -1}"));
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT_BSONOBJ_EQ(findCommand.getHint(), ar.getHint().value_or(BSONObj()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithMinFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMin(fromjson("{a: 1}"));
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithMaxFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMax(fromjson("{a: 1}"));
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSingleBatch(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldAndLimitFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSingleBatch(true);
|
|
findCommand.setLimit(7);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldLimitOneSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setSingleBatch(true);
|
|
findCommand.setLimit(1);
|
|
|
|
ASSERT_DOES_NOT_THROW(query_request_conversion::asAggregateCommandRequest(findCommand));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithReturnKeyFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setReturnKey(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithShowRecordIdFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setShowRecordId(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithTailableFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
query_request_helper::setTailableMode(TailableModeEnum::kTailable, &findCommand);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithNoCursorTimeoutFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setNoCursorTimeout(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithAwaitDataFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
query_request_helper::setTailableMode(TailableModeEnum::kTailableAndAwaitData, &findCommand);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithAllowPartialResultsFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowPartialResults(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithRequestResumeTokenSucceeds) {
|
|
RAIIServerParameterControllerForTest featureFlagController("featureFlagReshardingImprovements",
|
|
true);
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setRequestResumeToken(true);
|
|
findCommand.setHint(BSON("$natural" << 1));
|
|
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(ar.getRequestResumeToken());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithResumeAfterFails) {
|
|
RAIIServerParameterControllerForTest featureFlagController("featureFlagReshardingImprovements",
|
|
false);
|
|
FindCommandRequest findCommand(testns);
|
|
BSONObj resumeAfter = BSON("$recordId" << 1LL);
|
|
findCommand.setResumeAfter(resumeAfter);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithPipeline) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setFilter(BSON("x" << 1));
|
|
findCommand.setSort(BSON("y" << -1));
|
|
findCommand.setLimit(3);
|
|
findCommand.setSkip(7);
|
|
findCommand.setProjection(BSON("z" << 0));
|
|
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(!ar.getExplain());
|
|
ASSERT_EQ(ar.getCursor().getBatchSize().value_or(aggregation_request_helper::kDefaultBatchSize),
|
|
aggregation_request_helper::kDefaultBatchSize);
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSONObj());
|
|
|
|
std::vector<BSONObj> expectedPipeline{BSON("$match" << BSON("x" << 1)),
|
|
BSON("$sort" << BSON("y" << -1)),
|
|
BSON("$skip" << 7),
|
|
BSON("$limit" << 3),
|
|
BSON("$project" << BSON("z" << 0))};
|
|
ASSERT(std::equal(expectedPipeline.begin(),
|
|
expectedPipeline.end(),
|
|
ar.getPipeline().begin(),
|
|
SimpleBSONObjComparator::kInstance.makeEqualTo()));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithBatchSize) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setBatchSize(4);
|
|
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(!ar.getExplain());
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_EQ(ar.getCursor().getBatchSize().value_or(aggregation_request_helper::kDefaultBatchSize),
|
|
4LL);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSONObj());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithMaxTimeMS) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setMaxTimeMS(9);
|
|
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT_TRUE(ar.getMaxTimeMS().has_value());
|
|
ASSERT_EQ(ar.getMaxTimeMS().get(), 9);
|
|
|
|
ASSERT(!ar.getExplain());
|
|
ASSERT_EQ(ar.getCursor().getBatchSize().value_or(aggregation_request_helper::kDefaultBatchSize),
|
|
aggregation_request_helper::kDefaultBatchSize);
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSONObj());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithCollationSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setCollation(BSON("f" << 1));
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(!ar.getExplain());
|
|
ASSERT(ar.getPipeline().empty());
|
|
ASSERT_EQ(ar.getCursor().getBatchSize().value_or(aggregation_request_helper::kDefaultBatchSize),
|
|
aggregation_request_helper::kDefaultBatchSize);
|
|
ASSERT_EQ(ar.getNamespace(), testns);
|
|
ASSERT_BSONOBJ_EQ(ar.getCollation().value_or(BSONObj()), BSON("f" << 1));
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithReadOnceFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setReadOnce(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithAllowSpeculativeMajorityReadFails) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowSpeculativeMajorityRead(true);
|
|
ASSERT_THROWS_CODE(query_request_conversion::asAggregateCommandRequest(findCommand),
|
|
DBException,
|
|
ErrorCodes::InvalidPipelineOperator);
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithLegacyRuntimeConstantsSucceeds) {
|
|
LegacyRuntimeConstants rtc{Date_t::now(), Timestamp(1, 1)};
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setLegacyRuntimeConstants(rtc);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(ar.getLegacyRuntimeConstants().has_value());
|
|
ASSERT_EQ(ar.getLegacyRuntimeConstants()->getLocalNow(), rtc.getLocalNow());
|
|
ASSERT_EQ(ar.getLegacyRuntimeConstants()->getClusterTime(), rtc.getClusterTime());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseTrueSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowDiskUse(true);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(ar.getAllowDiskUse().has_value());
|
|
ASSERT_EQ(true, ar.getAllowDiskUse());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseFalseSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowDiskUse(false);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT(ar.getAllowDiskUse().has_value());
|
|
ASSERT_EQ(false, ar.getAllowDiskUse());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithIncludeQueryStatsMetricsTrueSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setIncludeQueryStatsMetrics(true);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT_TRUE(ar.getIncludeQueryStatsMetrics());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToAggregationWithIncludeQueryStatsMetricsFalseSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setIncludeQueryStatsMetrics(false);
|
|
auto ar = query_request_conversion::asAggregateCommandRequest(findCommand);
|
|
|
|
ASSERT_FALSE(ar.getIncludeQueryStatsMetrics());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToFindWithAllowDiskUseTrueSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowDiskUse(true);
|
|
const auto findCmd = findCommand.toBSON(BSONObj());
|
|
|
|
BSONElement elem = findCmd[FindCommandRequest::kAllowDiskUseFieldName];
|
|
ASSERT_EQ(true, elem.isBoolean());
|
|
ASSERT_EQ(true, elem.Bool());
|
|
}
|
|
|
|
TEST(QueryRequestTest, ConvertToFindWithAllowDiskUseFalseSucceeds) {
|
|
FindCommandRequest findCommand(testns);
|
|
findCommand.setAllowDiskUse(false);
|
|
const auto findCmd = findCommand.toBSON(BSONObj());
|
|
|
|
ASSERT_FALSE(findCmd[FindCommandRequest::kAllowDiskUseFieldName].booleanSafe());
|
|
}
|
|
|
|
TEST(QueryRequestHelperTest, ValidateResponseMissingFields) {
|
|
BSONObjBuilder builder;
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
ASSERT_THROWS_CODE(
|
|
query_request_helper::validateCursorResponse(builder.asTempObj(),
|
|
auth::ValidatedTenancyScope::get(opCtx),
|
|
boost::none,
|
|
SerializationContext()),
|
|
DBException,
|
|
6253507);
|
|
}
|
|
|
|
TEST(QueryRequestHelperTest, ValidateResponseWrongDataType) {
|
|
BSONObjBuilder builder;
|
|
builder.append("cursor", 1);
|
|
QueryTestServiceContext serviceContext;
|
|
auto uniqueTxn = serviceContext.makeOperationContext();
|
|
OperationContext* opCtx = uniqueTxn.get();
|
|
ASSERT_THROWS_CODE(
|
|
query_request_helper::validateCursorResponse(builder.asTempObj(),
|
|
auth::ValidatedTenancyScope::get(opCtx),
|
|
boost::none,
|
|
SerializationContext()),
|
|
DBException,
|
|
ErrorCodes::TypeMismatch);
|
|
}
|
|
|
|
TEST(QueryRequestHelperTest, ParsedCursorRemainsValidAfterBSONDestroyed) {
|
|
std::vector<BSONObj> batch = {BSON("_id" << 1), BSON("_id" << 2)};
|
|
CursorInitialReply cir;
|
|
{
|
|
BSONObj cursorObj =
|
|
BSON("cursor" << BSON("id" << CursorId(123) << "ns"
|
|
<< "testdb.testcoll"
|
|
<< "firstBatch"
|
|
<< BSON_ARRAY(BSON("_id" << 1) << BSON("_id" << 2))));
|
|
cir = CursorInitialReply::parseOwned(
|
|
IDLParserContext("QueryRequestHelperTest::ParsedCursorRemainsValidAFterBSONDestroyed"),
|
|
std::move(cursorObj));
|
|
cursorObj = BSONObj();
|
|
}
|
|
ASSERT_EQ(cir.getCursor()->getFirstBatch().size(), batch.size());
|
|
for (std::vector<BSONObj>::size_type i = 0; i < batch.size(); ++i) {
|
|
ASSERT_BSONOBJ_EQ(batch[i], cir.getCursor()->getFirstBatch()[i]);
|
|
}
|
|
}
|
|
|
|
class QueryRequestTest : public ServiceContextTest {};
|
|
|
|
TEST_F(QueryRequestTest, ParseFromUUID) {
|
|
const UUID uuid = UUID::gen();
|
|
NamespaceStringOrUUID nssOrUUID(DatabaseName::createDatabaseName_forTest(boost::none, "test"),
|
|
uuid);
|
|
FindCommandRequest findCommand(nssOrUUID);
|
|
const NamespaceString nss = NamespaceString::createNamespaceString_forTest("test.testns");
|
|
findCommand.setNss(nss);
|
|
ASSERT_EQ(nss, findCommand.getNamespaceOrUUID().nss());
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace mongo
|