From 976cf4d33fe21df2cfe7fd0a3e4d40429cbe1acc Mon Sep 17 00:00:00 2001 From: Militsa Sotirova Date: Fri, 21 Nov 2025 11:37:57 -0500 Subject: [PATCH] SERVER-113897 Report existence of invalid compound wildcard index (#44001) GitOrigin-RevId: bdbef6a80cd18e4aac5a77d738b1d2ed21a63092 --- evergreen/multiversion_selection.sh | 2 +- .../invalid_wildcard_index_log_at_startup.js | 95 +++++++++++++++++++ .../db/local_catalog/index_catalog_impl.cpp | 18 ++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 jstests/multiVersion/genericBinVersion/query-optimization/invalid_wildcard_index_log_at_startup.js diff --git a/evergreen/multiversion_selection.sh b/evergreen/multiversion_selection.sh index 93566f6321b..0366cfa30c1 100644 --- a/evergreen/multiversion_selection.sh +++ b/evergreen/multiversion_selection.sh @@ -42,7 +42,7 @@ local_args="--edition $edition \ --debug \ --fallbackToMaster \ ${last_lts_arg} \ - ${last_continuous_arg} 6.0 7.0" + ${last_continuous_arg} 6.0 7.0 8.0.16" remote_invocation="${base_command} ${evergreen_args} ${local_args}" eval "${remote_invocation}" diff --git a/jstests/multiVersion/genericBinVersion/query-optimization/invalid_wildcard_index_log_at_startup.js b/jstests/multiVersion/genericBinVersion/query-optimization/invalid_wildcard_index_log_at_startup.js new file mode 100644 index 00000000000..639a27f0f18 --- /dev/null +++ b/jstests/multiVersion/genericBinVersion/query-optimization/invalid_wildcard_index_log_at_startup.js @@ -0,0 +1,95 @@ +/** + * Tests that a server containing an invalid wildcard index will log a warning on startup. + * + * @tags: [ + * requires_persistence, + * requires_replication, + * ] + */ +import {ReplSetTest} from "jstests/libs/replsettest.js"; + +// This is a version that allows the bad index to be created. +const oldVersion = "8.0.16"; + +// Standalone mongod +{ + const testName = "invalid_wildcard_index_log_at_startup"; + const dbpath = MongoRunner.dataPath + testName; + const collName = "collectionWithInvalidWildcardIndex"; + + { + // Startup mongod version where we are allowed to create the invalid index. + const conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: oldVersion}); + assert.neq(null, conn, "mongod was unable to start up"); + + const testDB = conn.getDB("test"); + assert.commandWorked(testDB[collName].insert({a: 1})); + + // Invalid index + assert.commandWorked(testDB[collName].createIndex({"a": 1, "$**": 1}, {wildcardProjection: {"_id": 0}})); + + MongoRunner.stopMongod(conn); + } + { + const conn = MongoRunner.runMongod({dbpath: dbpath, noCleanData: true}); + assert.neq(null, conn, "mongod was unable to start up"); + const testDB = conn.getDB("test"); + + const cmdRes = assert.commandWorked(testDB.adminCommand({getLog: "startupWarnings"})); + assert( + /Found a compound wildcard index with an invalid wildcardProjection. Such indexes can no longer be created./.test( + cmdRes.log, + ), + ); + + // Be sure that inserting to the collection with the invalid index succeeds. + assert.commandWorked(testDB[collName].insert({a: 2})); + + // Inserting to another collection should succeed. + assert.commandWorked(testDB.someOtherCollection.insert({a: 1})); + assert.eq(testDB.someOtherCollection.find().itcount(), 1); + + MongoRunner.stopMongod(conn); + } +} + +// Replica set +{ + let nodes = { + n1: {binVersion: oldVersion}, + n2: {binVersion: oldVersion}, + }; + + const rst = new ReplSetTest({nodes: nodes}); + rst.startSet(); + rst.initiate(); + + let primary = rst.getPrimary(); + const db = primary.getDB("test"); + const coll = db.t; + assert.commandWorked(coll.insert({a: 1})); + + assert.commandWorked(coll.createIndex({"a": 1, "$**": 1}, {wildcardProjection: {"_id": 0}})); + + // Force checkpoint in storage engine to ensure index is part of the catalog in + // in finished state at startup. + rst.awaitReplication(); + let secondary = rst.getSecondary(); + assert.commandWorked(secondary.adminCommand({fsync: 1})); + + // Check that initial sync works, this node would not allow the index to be created + // (since it is on a version with the new validation logic) but should not fail on startup. + const initialSyncNode = rst.add({rsConfig: {priority: 0}}); + rst.reInitiate(); + rst.awaitSecondaryNodes(null, [initialSyncNode]); + + // Restart the new node and check for the startup warning in the logs. + rst.restart(initialSyncNode); + rst.awaitSecondaryNodes(null, [initialSyncNode]); + + checkLog.containsJson(initialSyncNode, 11389700, { + ns: coll.getFullName(), + }); + + rst.stopSet(); +} diff --git a/src/mongo/db/local_catalog/index_catalog_impl.cpp b/src/mongo/db/local_catalog/index_catalog_impl.cpp index f31c7aeaa72..6091e46fcc3 100644 --- a/src/mongo/db/local_catalog/index_catalog_impl.cpp +++ b/src/mongo/db/local_catalog/index_catalog_impl.cpp @@ -49,6 +49,7 @@ #include "mongo/db/index/index_constants.h" #include "mongo/db/index/s2_access_method.h" #include "mongo/db/index/s2_bucket_access_method.h" +#include "mongo/db/index/wildcard_validation.h" #include "mongo/db/index_builds/index_build_block.h" #include "mongo/db/index_builds/index_builds_common.h" #include "mongo/db/index_names.h" @@ -272,6 +273,23 @@ void IndexCatalogImpl::init(OperationContext* opCtx, "spec"_attr = spec); } + // Look for an invalid compound wildcard index. + if (IndexNames::findPluginName(keyPattern) == IndexNames::WILDCARD && + keyPattern.nFields() > 1 && spec.hasField("wildcardProjection")) { + auto validationStatus = + validateWildcardProjection(keyPattern, spec.getObjectField("wildcardProjection")); + if (!validationStatus.isOK()) { + LOGV2_OPTIONS(11389700, + {logv2::LogTag::kStartupWarnings}, + "Found a compound wildcard index with an invalid wildcardProjection. " + "Such indexes can no longer be created.", + "ns"_attr = collection->ns(), + "uuid"_attr = collection->uuid(), + "index"_attr = indexName, + "spec"_attr = spec); + } + } + auto descriptor = IndexDescriptor(_getAccessMethodName(keyPattern), spec); if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) {