summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-12-16 06:20:56 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-12-16 11:59:13 +0000
commitb83bf99b6e48e2f41ea3b1e7ed6aeb4cf1eb0d31 (patch)
tree01332eac7b0c156b8bf220c366683e5f122b7de6
parentc99a57f74a55dbb5509f19f5724f2a5697163f44 (diff)
downloadmongo-b83bf99b6e48e2f41ea3b1e7ed6aeb4cf1eb0d31.tar.gz
SERVER-52545 Define listIndexes with IDL
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--jstests/core/geo_s2index.js5
-rw-r--r--jstests/core/list_indexes_invalidation.js10
-rw-r--r--jstests/noPassthrough/index_build_restart_secondary.js4
-rw-r--r--jstests/replsets/id_index_replication.js3
-rw-r--r--jstests/replsets/initial_sync_invalid_index_spec.js65
-rw-r--r--src/mongo/bson/bsonelement.h13
-rw-r--r--src/mongo/db/SConscript14
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp20
-rw-r--r--src/mongo/db/commands/list_indexes.cpp54
-rw-r--r--src/mongo/db/create_indexes.idl55
-rw-r--r--src/mongo/db/list_indexes.idl167
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_config_initialization_test.cpp5
-rw-r--r--src/mongo/db/timeseries/timeseries.idl2
-rw-r--r--src/mongo/idl/basic_types.idl10
-rw-r--r--src/mongo/s/commands/cluster_list_indexes_cmd.cpp8
-rw-r--r--src/mongo/scripting/mozjs/bson.cpp29
-rw-r--r--src/mongo/scripting/mozjs/bson.h3
18 files changed, 349 insertions, 119 deletions
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
index f0563fb5aba..22b4d8a55d1 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
@@ -203,6 +203,7 @@ selector:
- jstests/core/kill_cursors.js
- jstests/core/list_collections1.js
- jstests/core/list_indexes.js
+ - jstests/core/list_indexes_invalidation.js
- jstests/core/list_namespaces_invalidation.js
- jstests/core/oro.js
diff --git a/jstests/core/geo_s2index.js b/jstests/core/geo_s2index.js
index 328b43d04ab..2afd3053cea 100644
--- a/jstests/core/geo_s2index.js
+++ b/jstests/core/geo_s2index.js
@@ -143,6 +143,9 @@ res = t.createIndex({loc: "2dsphere"}, {finestIndexedLevel: 30, coarsestIndexedL
assert.commandWorked(res);
// Ensure the index actually works at a basic level
assert.neq(null, t.findOne({loc: {$geoNear: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}));
+let created = t.getIndexes().filter((idx) => idx.hasOwnProperty("2dsphereIndexVersion"))[0];
+assert.eq(created.finestIndexedLevel, 30, created);
+assert.eq(created.coarsestIndexedLevel, 0, created);
t.drop();
t.save({loc: [0, 0]});
@@ -169,4 +172,4 @@ t.insert({
}
});
res = t.createIndex({loc: "2dsphere"});
-assert.commandWorked(res); \ No newline at end of file
+assert.commandWorked(res);
diff --git a/jstests/core/list_indexes_invalidation.js b/jstests/core/list_indexes_invalidation.js
index 85ab71eec42..74afd3327bd 100644
--- a/jstests/core/list_indexes_invalidation.js
+++ b/jstests/core/list_indexes_invalidation.js
@@ -1,6 +1,11 @@
// Cannot implicitly shard accessed collections because renameCollection command not supported
// on sharded collections.
-// @tags: [assumes_unsharded_collection, requires_non_retryable_commands, requires_fastcount]
+// @tags: [
+// assumes_unsharded_collection,
+// requires_non_retryable_commands,
+// requires_fastcount,
+// requires_getmore
+// ]
(function() {
'use strict';
@@ -16,7 +21,7 @@ function testIndexInvalidation(isRename) {
// Get the first two indexes.
let cmd = {listIndexes: collName};
- Object.extend(cmd, {batchSize: 2});
+ Object.extend(cmd, {cursor: {batchSize: 2}});
let res = db.runCommand(cmd);
assert.commandWorked(res, 'could not run ' + tojson(cmd));
printjson(res);
@@ -25,6 +30,7 @@ function testIndexInvalidation(isRename) {
let cursor = new DBCommandCursor(db, res);
let errMsg = 'expected more data from command ' + tojson(cmd) + ', with result ' + tojson(res);
assert(cursor.hasNext(), errMsg);
+ assert(res.cursor.id !== NumberLong("0"), errMsg);
if (isRename) {
assert.commandWorked(coll.renameCollection(collNameRenamed));
} else {
diff --git a/jstests/noPassthrough/index_build_restart_secondary.js b/jstests/noPassthrough/index_build_restart_secondary.js
index a454f7d9965..94c5c0c7498 100644
--- a/jstests/noPassthrough/index_build_restart_secondary.js
+++ b/jstests/noPassthrough/index_build_restart_secondary.js
@@ -75,7 +75,7 @@ assert.soonNoExcept(() => {
4,
["_id_"],
["i_1", "x_1", "y_1"],
- {includeBuildUUIDS: true});
+ {includeBuildUUIDs: true});
return true;
});
@@ -90,4 +90,4 @@ assert.soon(function() {
}, "Index build did not complete after restart");
replTest.stopSet();
-}()); \ No newline at end of file
+}());
diff --git a/jstests/replsets/id_index_replication.js b/jstests/replsets/id_index_replication.js
index bd693104104..e53054f6b76 100644
--- a/jstests/replsets/id_index_replication.js
+++ b/jstests/replsets/id_index_replication.js
@@ -23,7 +23,8 @@ function testOplogEntryIdIndexSpec(collectionName, idIndexSpec) {
if (idIndexSpec === null) {
assert(!oplogEntry.o.hasOwnProperty("idIndex"), tojson(oplogEntry));
} else {
- assert.eq(0, bsonWoCompare(idIndexSpec, oplogEntry.o.idIndex), tojson(oplogEntry));
+ assert.eq(
+ 0, bsonUnorderedFieldsCompare(idIndexSpec, oplogEntry.o.idIndex), tojson(oplogEntry));
}
}
diff --git a/jstests/replsets/initial_sync_invalid_index_spec.js b/jstests/replsets/initial_sync_invalid_index_spec.js
deleted file mode 100644
index 82bc2dcf050..00000000000
--- a/jstests/replsets/initial_sync_invalid_index_spec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Confirm that replica members undergoing initial sync fail if an invalid index specification is
- * encountered (where index version is >= 2).
- * @tags: [live_record_incompatible]
- */
-load("jstests/libs/logv2_helpers.js");
-
-(function() {
-"use strict";
-
-// Skip db hash check because of invalid index spec.
-TestData.skipCheckDBHashes = true;
-
-load("jstests/replsets/rslib.js");
-
-const testName = "initial_sync_invalid_index_spec";
-const replTest = new ReplSetTest({nodes: 1});
-replTest.startSet();
-replTest.initiate();
-
-let primaryDB = replTest.getPrimary().getDB(testName);
-
-// Create a V2 index with invalid spec field.
-primaryDB.adminCommand(
- {configureFailPoint: "skipIndexCreateFieldNameValidation", mode: "alwaysOn"});
-assert.commandWorked(primaryDB.runCommand(
- {createIndexes: "test", indexes: [{v: 2, name: "x_1", key: {x: 1}, invalidOption: 1}]}));
-
-// Add another node to the replica set to allow an initial sync to occur.
-var initSyncNode = replTest.add({rsConfig: {votes: 0, priority: 0}});
-var initSyncNodeAdminDB = initSyncNode.getDB("admin");
-
-clearRawMongoProgramOutput();
-reInitiateWithoutThrowingOnAbortedMember(replTest);
-
-assert.soon(
- function() {
- try {
- initSyncNodeAdminDB.runCommand({ping: 1});
- } catch (e) {
- return true;
- }
- return false;
- },
- "Node did not terminate due to invalid index spec during initial sync",
- ReplSetTest.kDefaultTimeoutMS);
-
-replTest.stop(initSyncNode, undefined, {allowedExitCode: MongoRunner.EXIT_ABRUPT});
-
-const msgInvalidOption = "The field 'invalidOption' is not valid for an index specification";
-const msgInitialSyncFatalAssertion = "Fatal assertion 40088 InitialSyncFailure";
-
-if (isJsonLogNoConn()) {
- assert(
- rawMongoProgramOutput().search(
- /Fatal assertion*40088.*InitialSyncFailure.*The field 'invalidOption' is not valid for an index specification/),
- "Replication should have aborted on invalid index specification");
-} else {
- assert(rawMongoProgramOutput().match(msgInvalidOption) &&
- rawMongoProgramOutput().match(msgInitialSyncFatalAssertion),
- "Initial sync should have aborted on invalid index specification");
-}
-
-replTest.stopSet();
-})();
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h
index 7c6b86071c7..b3a39c04ce3 100644
--- a/src/mongo/bson/bsonelement.h
+++ b/src/mongo/bson/bsonelement.h
@@ -29,6 +29,7 @@
#pragma once
+#include <algorithm>
#include <cmath>
#include <cstdint>
#include <fmt/format.h>
@@ -377,6 +378,13 @@ public:
*/
int numberInt() const;
+ /** Like numberInt() but with well-defined behavior for doubles that
+ * are NaNs, or too large/small to be represented as int.
+ * NaNs -> 0
+ * very large doubles -> INT_MAX
+ * very small doubles -> INT_MIN */
+ int safeNumberInt() const;
+
/**
* Retrieves the value of this element as a 64 bit integer. If the BSON type is non-numeric,
* returns zero. If the element holds a double, truncates the fractional part.
@@ -950,6 +958,11 @@ inline int BSONElement::numberInt() const {
}
}
+inline int BSONElement::safeNumberInt() const {
+ return static_cast<int>(std::clamp<long long>(
+ safeNumberLong(), std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
+}
+
inline long long BSONElement::numberLong() const {
switch (type()) {
case NumberDouble:
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 09e32294938..af1e5d8a47c 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -575,10 +575,12 @@ env.Library(
'drop.idl',
'drop_database.idl',
'list_collections.idl',
+ 'list_indexes.idl',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'api_parameters',
+ 'create_indexes_idl',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
@@ -1371,15 +1373,25 @@ env.Library(
)
env.Library(
+ target='create_indexes_idl',
+ source=[
+ 'create_indexes.idl',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/idl/idl_parser',
+ ],
+)
+
+env.Library(
target='sessions_collection',
source=[
'sessions_collection.cpp',
- 'create_indexes.idl',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/client/clientdriver_minimal',
'$BUILD_DIR/mongo/s/write_ops/batch_write_types',
+ 'create_indexes_idl',
'logical_session_id',
],
)
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp
index 0019f1b18ad..8ee6c5c9ca6 100644
--- a/src/mongo/db/catalog/rename_collection.cpp
+++ b/src/mongo/db/catalog/rename_collection.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/catalog/rename_collection.h"
+#include "mongo/bson/unordered_fields_bsonobj_comparator.h"
#include "mongo/db/catalog/collection_catalog.h"
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/document_validation.h"
@@ -750,14 +751,17 @@ void doLocalRenameIfOptionsAndIndexesHaveNotChanged(OperationContext* opCtx,
auto currentIndexes =
listIndexesEmptyListIfMissing(opCtx, targetNs, false /* includeBuildUUIDs */);
- uassert(ErrorCodes::CommandFailed,
- str::stream() << "indexes of target collection " << targetNs.ns()
- << " changed during processing.",
- originalIndexes.size() == currentIndexes.size() &&
- std::equal(originalIndexes.begin(),
- originalIndexes.end(),
- currentIndexes.begin(),
- SimpleBSONObjComparator::kInstance.makeEqualTo()));
+
+ UnorderedFieldsBSONObjComparator comparator;
+ uassert(
+ ErrorCodes::CommandFailed,
+ str::stream() << "indexes of target collection " << targetNs.ns()
+ << " changed during processing.",
+ originalIndexes.size() == currentIndexes.size() &&
+ std::equal(originalIndexes.begin(),
+ originalIndexes.end(),
+ currentIndexes.begin(),
+ [&](auto& lhs, auto& rhs) { return comparator.compare(lhs, rhs) == 0; }));
validateAndRunRenameCollection(opCtx, sourceNs, targetNs, options);
}
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index 1573dfccd36..f366615b0e6 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -27,6 +27,8 @@
* it in the license file.
*/
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
#include "mongo/platform/basic.h"
#include <memory>
@@ -44,6 +46,7 @@
#include "mongo/db/exec/queued_data_stage.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/list_indexes_gen.h"
#include "mongo/db/query/cursor_request.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/find_common.h"
@@ -51,6 +54,7 @@
#include "mongo/db/service_context.h"
#include "mongo/db/storage/durable_catalog.h"
#include "mongo/db/storage/storage_engine.h"
+#include "mongo/logv2/log.h"
#include "mongo/util/uuid.h"
namespace mongo {
@@ -62,6 +66,15 @@ using std::vector;
namespace {
+void appendListIndexesCursorReply(CursorId cursorId,
+ const NamespaceString& cursorNss,
+ std::vector<mongo::ListIndexesReplyItem>&& firstBatch,
+ BSONObjBuilder& result) {
+ auto reply =
+ ListIndexesReply(ListIndexesReplyCursor(cursorId, cursorNss, std::move(firstBatch)));
+ reply.serialize(&result);
+}
+
/**
* Lists the indexes for a given collection.
* If 'includeBuildUUIDs' is true, then the index build uuid is also returned alongside the index
@@ -146,19 +159,17 @@ public:
const BSONObj& cmdObj,
BSONObjBuilder& result) {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
- const long long defaultBatchSize = std::numeric_limits<long long>::max();
- long long batchSize;
- uassertStatusOK(
- CursorRequest::parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize));
-
- auto includeBuildUUIDs = cmdObj["includeBuildUUIDs"].trueValue();
+ const auto parsed = ListIndexes::parse({"listIndexes"}, cmdObj);
+ long long batchSize = std::numeric_limits<long long>::max();
+ if (parsed.getCursor() && parsed.getCursor()->getBatchSize()) {
+ batchSize = *parsed.getCursor()->getBatchSize();
+ }
NamespaceString nss;
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> exec;
- BSONArrayBuilder firstBatch;
+ std::vector<mongo::ListIndexesReplyItem> firstBatch;
{
- AutoGetCollectionForReadCommand collection(
- opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj));
+ AutoGetCollectionForReadCommand collection(opCtx, parsed.getNamespaceOrUUID());
uassert(ErrorCodes::NamespaceNotFound,
str::stream() << "ns does not exist: " << collection.getNss().ns(),
collection);
@@ -167,8 +178,8 @@ public:
auto expCtx = make_intrusive<ExpressionContext>(
opCtx, std::unique_ptr<CollatorInterface>(nullptr), nss);
- auto indexList =
- listIndexesInLock(opCtx, collection.getCollection(), nss, includeBuildUUIDs);
+ auto indexList = listIndexesInLock(
+ opCtx, collection.getCollection(), nss, parsed.getIncludeBuildUUIDs());
auto ws = std::make_unique<WorkingSet>();
auto root = std::make_unique<QueuedDataStage>(expCtx.get(), ws.get());
@@ -190,6 +201,7 @@ public:
PlanYieldPolicy::YieldPolicy::NO_YIELD,
nss));
+ int bytesBuffered = 0;
for (long long objCount = 0; objCount < batchSize; objCount++) {
BSONObj nextDoc;
PlanExecutor::ExecState state = exec->getNext(&nextDoc, nullptr);
@@ -199,16 +211,26 @@ public:
invariant(state == PlanExecutor::ADVANCED);
// If we can't fit this result inside the current batch, then we stash it for later.
- if (!FindCommon::haveSpaceForNext(nextDoc, objCount, firstBatch.len())) {
+ if (!FindCommon::haveSpaceForNext(nextDoc, objCount, bytesBuffered)) {
exec->enqueue(nextDoc);
break;
}
- firstBatch.append(nextDoc);
+ try {
+ firstBatch.push_back(ListIndexesReplyItem::parse(
+ IDLParserErrorContext("ListIndexesReplyItem"), nextDoc));
+ } catch (const DBException& exc) {
+ LOGV2_ERROR(5254500,
+ "Could not parse catalog entry while replying to listIndexes",
+ "entry"_attr = nextDoc,
+ "error"_attr = exc);
+ fassertFailed(5254501);
+ }
+ bytesBuffered += nextDoc.objsize();
}
if (exec->isEOF()) {
- appendCursorResponseObject(0LL, nss.ns(), firstBatch.arr(), &result);
+ appendListIndexesCursorReply(0 /* cursorId */, nss, std::move(firstBatch), result);
return true;
}
@@ -228,8 +250,8 @@ public:
cmdObj,
{Privilege(ResourcePattern::forExactNamespace(nss), ActionType::listIndexes)}});
- appendCursorResponseObject(
- pinnedCursor.getCursor()->cursorid(), nss.ns(), firstBatch.arr(), &result);
+ appendListIndexesCursorReply(
+ pinnedCursor.getCursor()->cursorid(), nss, std::move(firstBatch), result);
return true;
}
diff --git a/src/mongo/db/create_indexes.idl b/src/mongo/db/create_indexes.idl
index 20b589660ca..d9a58ba026e 100644
--- a/src/mongo/db/create_indexes.idl
+++ b/src/mongo/db/create_indexes.idl
@@ -38,35 +38,45 @@ imports:
structs:
+ # A built index appears with these fields, which must be the same as ListIndexReplyItem's fields
+ # in list_indexes.idl.
+ # TODO (SERVER-52539): Write a test that asserts they're the same.
NewIndexSpec:
description: "A type representing a spec for a new index"
strict: true
fields:
- key: object
+ v:
+ type: safeInt
+ optional: true
+ key: object_owned
name: string
+ ns:
+ # MongoDB 4.2 and older generate this field, see SERVER-41696.
+ type: string
+ ignore: true
background:
- type: bool
+ type: safeBool
optional: true
unique:
- type: bool
+ type: safeBool
optional: true
hidden:
- type: bool
+ type: safeBool
optional: true
partialFilterExpression:
- type: object
+ type: object_owned
optional: true
sparse:
- type: bool
+ type: safeBool
optional: true
expireAfterSeconds:
- type: int
+ type: safeInt
optional: true
storageEngine:
- type: object
+ type: object_owned
optional: true
weights:
- type: object
+ type: object_owned
optional: true
default_language:
type: string
@@ -75,26 +85,39 @@ structs:
type: string
optional: true
textIndexVersion:
- type: int
+ type: safeInt
optional: true
2dsphereIndexVersion:
- type: int
+ type: safeInt
optional: true
bits:
- type: int
+ type: safeInt
optional: true
min:
- type: double
+ type: safeDouble
optional: true
max:
- type: double
+ type: safeDouble
optional: true
bucketSize:
- type: double
+ type: safeDouble
optional: true
collation:
- type: object
+ type: object_owned
+ optional: true
+ wildcardProjection:
+ type: object_owned
+ optional: true
+ coarsestIndexedLevel:
+ type: safeInt
+ optional: true
+ finestIndexedLevel:
+ type: safeInt
+ optional: true
+ dropDups:
+ type: safeBool
optional: true
+ unstable: true
CreateIndexesCmd:
description: "A struct representing a createIndexes command"
diff --git a/src/mongo/db/list_indexes.idl b/src/mongo/db/list_indexes.idl
new file mode 100644
index 00000000000..81b592c9a10
--- /dev/null
+++ b/src/mongo/db/list_indexes.idl
@@ -0,0 +1,167 @@
+# Copyright (C) 2020-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.
+#
+
+global:
+ cpp_namespace: "mongo"
+ cpp_includes:
+ - "mongo/util/uuid.h"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+ - "mongo/db/create_indexes.idl"
+
+structs:
+ ListIndexesReplyItem:
+ description: "An item in the listIndexes command's reply"
+ fields:
+ #
+ # A built index appears with these fields, which must be the same as NewIndexSpec's
+ # fields. The "key" and "name" fields are required for built indexes, but marked
+ # optional to allow the in-progress index build format using the fields below.
+ #
+ v:
+ type: safeInt
+ optional: true
+ key:
+ type: object_owned
+ optional: true
+ name:
+ type: string
+ optional: true
+ ns:
+ # MongoDB 4.2 and older generate this field, see SERVER-41696.
+ type: string
+ ignore: true
+ background:
+ type: safeBool
+ optional: true
+ unique:
+ type: safeBool
+ optional: true
+ hidden:
+ type: safeBool
+ optional: true
+ partialFilterExpression:
+ type: object_owned
+ optional: true
+ sparse:
+ type: safeBool
+ optional: true
+ expireAfterSeconds:
+ type: safeInt
+ optional: true
+ storageEngine:
+ type: object_owned
+ optional: true
+ weights:
+ type: object_owned
+ optional: true
+ default_language:
+ type: string
+ optional: true
+ language_override:
+ type: string
+ optional: true
+ textIndexVersion:
+ type: safeInt
+ optional: true
+ 2dsphereIndexVersion:
+ type: safeInt
+ optional: true
+ bits:
+ type: safeInt
+ optional: true
+ min:
+ type: safeDouble
+ optional: true
+ max:
+ type: safeDouble
+ optional: true
+ bucketSize:
+ type: safeDouble
+ optional: true
+ collation:
+ type: object_owned
+ optional: true
+ wildcardProjection:
+ type: object_owned
+ optional: true
+ coarsestIndexedLevel:
+ type: safeInt
+ optional: true
+ finestIndexedLevel:
+ type: safeInt
+ optional: true
+ dropDups:
+ type: safeBool
+ optional: true
+ #
+ # An index build in progress appears with these two fields. They're required, but marked
+ # optional to permit the built index format using the fields above instead.
+ #
+ buildUUID:
+ type: uuid
+ optional: true
+ spec:
+ type: NewIndexSpec
+ optional: true
+
+ ListIndexesReplyCursor:
+ description: "Cursor object"
+ fields:
+ id: long
+ ns: namespacestring
+ firstBatch: array<ListIndexesReplyItem>
+
+ ListIndexesReply:
+ description: "The listIndexes command's reply."
+ fields:
+ cursor: ListIndexesReplyCursor
+ # Included so mongos can parse shards' listIndexes replies.
+ ok:
+ type: safeDouble
+ validator: { gte: 0.0, lte: 1.0 }
+ optional: true
+
+commands:
+ listIndexes:
+ description: "Parser for the listIndexes command"
+ command_name: listIndexes
+ namespace: concatenate_with_db_or_uuid
+ cpp_name: listIndexes
+ strict: true
+ api_version: "1"
+ fields:
+ cursor:
+ type: SimpleCursorOptions
+ optional: true
+ includeBuildUUIDs:
+ type: safeBool
+ unstable: true
+ default: false
+ reply_type: ListIndexesReply
diff --git a/src/mongo/db/s/config/sharding_catalog_manager_config_initialization_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_config_initialization_test.cpp
index 19b0fc0b720..d6ff9610264 100644
--- a/src/mongo/db/s/config/sharding_catalog_manager_config_initialization_test.cpp
+++ b/src/mongo/db/s/config/sharding_catalog_manager_config_initialization_test.cpp
@@ -64,10 +64,13 @@ void assertBSONObjsSame(const std::vector<BSONObj>& expectedBSON,
const std::vector<BSONObj>& foundBSON) {
ASSERT_EQUALS(expectedBSON.size(), foundBSON.size());
+ auto flags =
+ BSONObj::ComparisonRules::kIgnoreFieldOrder | BSONObj::ComparisonRules::kConsiderFieldName;
+
for (const auto& expectedObj : expectedBSON) {
bool wasFound = false;
for (const auto& foundObj : foundBSON) {
- if (expectedObj.woCompare(foundObj) == 0) {
+ if (expectedObj.woCompare(foundObj, {}, flags) == 0) {
wasFound = true;
break;
}
diff --git a/src/mongo/db/timeseries/timeseries.idl b/src/mongo/db/timeseries/timeseries.idl
index c2ecaf9227b..814bb634b65 100644
--- a/src/mongo/db/timeseries/timeseries.idl
+++ b/src/mongo/db/timeseries/timeseries.idl
@@ -50,5 +50,5 @@ structs:
expireAfterSeconds:
description: "The number of seconds after which old time-series data should be
deleted."
- type: long
+ type: safeInt64
optional: true
diff --git a/src/mongo/idl/basic_types.idl b/src/mongo/idl/basic_types.idl
index 6352fa9d4ab..a52fc202fe2 100644
--- a/src/mongo/idl/basic_types.idl
+++ b/src/mongo/idl/basic_types.idl
@@ -47,6 +47,16 @@ types:
cpp_type: "std::int32_t"
deserializer: "mongo::BSONElement::_numberInt"
+ safeInt:
+ bson_serialization_type:
+ - long
+ - int
+ - decimal
+ - double
+ description: "Accepts any numerical type within integer range"
+ cpp_type: "std::int32_t"
+ deserializer: "mongo::BSONElement::safeNumberInt"
+
safeInt64:
bson_serialization_type:
- long
diff --git a/src/mongo/s/commands/cluster_list_indexes_cmd.cpp b/src/mongo/s/commands/cluster_list_indexes_cmd.cpp
index 79bc042a0d7..d0be564ab86 100644
--- a/src/mongo/s/commands/cluster_list_indexes_cmd.cpp
+++ b/src/mongo/s/commands/cluster_list_indexes_cmd.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/commands.h"
+#include "mongo/db/list_indexes_gen.h"
#include "mongo/s/cluster_commands_helpers.h"
#include "mongo/s/query/store_possible_cursor.h"
@@ -65,6 +66,10 @@ bool cursorCommandPassthroughShardWithMinKeyChunk(OperationContext* opCtx,
privileges));
CommandHelpers::filterCommandReplyForPassthrough(transformedResponse, out);
+ if (out->asTempObj()["ok"].trueValue()) {
+ // The reply syntax must conform to its IDL definition.
+ ListIndexesReply::parse({"listIndexes"}, out->asTempObj());
+ }
return true;
}
@@ -119,7 +124,10 @@ public:
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
+ // Check the command syntax before passing to shard.
+ const auto parsed = ListIndexes::parse({"listIndexes"}, cmdObj);
+ // The command's IDL definition permits namespace or UUID, but mongos requires a namespace.
const NamespaceString nss(parseNs(dbName, cmdObj));
const auto cm =
uassertStatusOK(Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(opCtx, nss));
diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp
index 0585022615a..efe78d2feab 100644
--- a/src/mongo/scripting/mozjs/bson.cpp
+++ b/src/mongo/scripting/mozjs/bson.cpp
@@ -31,6 +31,7 @@
#include "mongo/scripting/mozjs/bson.h"
#include <boost/optional.hpp>
+#include <fmt/format.h>
#include <set>
#include "mongo/scripting/mozjs/idwrapper.h"
@@ -44,10 +45,13 @@
namespace mongo {
namespace mozjs {
+using namespace fmt::literals;
+
const char* const BSONInfo::className = "BSON";
-const JSFunctionSpec BSONInfo::freeFunctions[4] = {
+const JSFunctionSpec BSONInfo::freeFunctions[5] = {
MONGO_ATTACH_JS_FUNCTION(bsonWoCompare),
+ MONGO_ATTACH_JS_FUNCTION(bsonUnorderedFieldsCompare),
MONGO_ATTACH_JS_FUNCTION(bsonBinaryEqual),
MONGO_ATTACH_JS_FUNCTION(bsonObjToArray),
JS_FS_END,
@@ -276,9 +280,13 @@ void BSONInfo::Functions::bsonObjToArray::call(JSContext* cx, JS::CallArgs args)
ValueReader(cx, args.rval()).fromBSONArray(obj, nullptr, false);
}
-void BSONInfo::Functions::bsonWoCompare::call(JSContext* cx, JS::CallArgs args) {
+namespace {
+void bsonCompareCommon(JSContext* cx,
+ JS::CallArgs args,
+ StringData funcName,
+ BSONObj::ComparisonRulesSet rules) {
if (args.length() != 2)
- uasserted(ErrorCodes::BadValue, "bsonWoCompare needs 2 arguments");
+ uasserted(ErrorCodes::BadValue, "{} needs 2 arguments"_format(funcName));
// If either argument is not proper BSON, then we wrap both objects.
auto scope = getScope(cx);
@@ -288,7 +296,20 @@ void BSONInfo::Functions::bsonWoCompare::call(JSContext* cx, JS::CallArgs args)
BSONObj bsonObject1 = getBSONFromArg(cx, args.get(0), isBSON);
BSONObj bsonObject2 = getBSONFromArg(cx, args.get(1), isBSON);
- args.rval().setInt32(bsonObject1.woCompare(bsonObject2));
+ args.rval().setInt32(bsonObject1.woCompare(bsonObject2, {}, rules));
+}
+} // namespace
+
+void BSONInfo::Functions::bsonWoCompare::call(JSContext* cx, JS::CallArgs args) {
+ bsonCompareCommon(cx, args, "bsonWoCompare", BSONObj::ComparatorInterface::kConsiderFieldName);
+}
+
+void BSONInfo::Functions::bsonUnorderedFieldsCompare::call(JSContext* cx, JS::CallArgs args) {
+ bsonCompareCommon(cx,
+ args,
+ "bsonWoCompare",
+ BSONObj::ComparatorInterface::kConsiderFieldName |
+ BSONObj::ComparatorInterface::kIgnoreFieldOrder);
}
void BSONInfo::Functions::bsonBinaryEqual::call(JSContext* cx, JS::CallArgs args) {
diff --git a/src/mongo/scripting/mozjs/bson.h b/src/mongo/scripting/mozjs/bson.h
index f515c4fd32a..a2cc5f1d2d3 100644
--- a/src/mongo/scripting/mozjs/bson.h
+++ b/src/mongo/scripting/mozjs/bson.h
@@ -72,11 +72,12 @@ struct BSONInfo : public BaseInfo {
struct Functions {
MONGO_DECLARE_JS_FUNCTION(bsonWoCompare);
+ MONGO_DECLARE_JS_FUNCTION(bsonUnorderedFieldsCompare);
MONGO_DECLARE_JS_FUNCTION(bsonBinaryEqual);
MONGO_DECLARE_JS_FUNCTION(bsonObjToArray);
};
- static const JSFunctionSpec freeFunctions[4];
+ static const JSFunctionSpec freeFunctions[5];
static std::tuple<BSONObj*, bool> originalBSON(JSContext* cx, JS::HandleObject obj);
static void make(