SERVER-89953 listIndexes should list the collation if it's simple (#49113)
GitOrigin-RevId: 6577e498b0ecff23de975e98dd7e36d2759f8857
This commit is contained in:
parent
40e73bbe54
commit
e291f89d5c
@ -2701,8 +2701,7 @@ tasks:
|
||||
resmoke_args: >-
|
||||
--mongodSetParameters='{logComponentVerbosity: {command: 2}}'
|
||||
--runNoFeatureFlagTests
|
||||
jstestfuzz_vars: --metaSeed 1726779665485 --jstestfuzzGitRev d4c83dd
|
||||
|
||||
jstestfuzz_vars: --metaSeed 1726779665485 --jstestfuzzGitRev 68fe9b4
|
||||
# jstestfuzz standalone update time-series generational fuzzer ##
|
||||
- <<: *jstestfuzz_template
|
||||
name: update_timeseries_fuzzer_gen
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
// server-3253 Unsharded support for $out
|
||||
import {anyEq, assertErrorCode, collectionExists} from "jstests/aggregation/extras/utils.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const testDb = db.getSiblingDB("unsharded_out");
|
||||
let input = testDb.unsharded_out_in;
|
||||
@ -19,13 +20,19 @@ inputDoesntExist.drop(); // never created
|
||||
output.drop();
|
||||
|
||||
function getOutputIndexes() {
|
||||
return output.getIndexes().sort(function (a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
return output
|
||||
.getIndexes()
|
||||
.map(function (index) {
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
return IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, index);
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function test(input, pipeline, expected) {
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
import {validateClusteredCollectionHint} from "jstests/libs/clustered_collections/clustered_collection_hint_common.js";
|
||||
import {assertDropCollection} from "jstests/libs/collection_drop_recreate.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
import {getWinningPlanFromExplain} from "jstests/libs/query/analyze_plan.js";
|
||||
|
||||
const collatedName = "clustered_collection_with_collation";
|
||||
@ -56,7 +57,7 @@ const expectedCollation = {
|
||||
const indexes = collated.getIndexes();
|
||||
assert.eq(0, bsonWoCompare(indexes[0].collation, expectedCollation), "Default index doesn't match expected collation");
|
||||
|
||||
// No collation spec when it's set to "simple".
|
||||
// Collation spec for "simple" is now explicitly included.
|
||||
assertDropCollection(db, "simpleCollation");
|
||||
assert.commandWorked(
|
||||
db.createCollection("simpleCollation", {
|
||||
@ -64,8 +65,9 @@ assert.commandWorked(
|
||||
collation: {locale: "simple"},
|
||||
}),
|
||||
);
|
||||
const indexSpec = db.simpleCollation.getIndexes()[0];
|
||||
assert(!indexSpec.hasOwnProperty("collation"), 'Default index has collation for "simple" locale');
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
const indexSpec = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, db.simpleCollation.getIndexes()[0]);
|
||||
assert.eq(indexSpec.collation.locale, "simple", "Default index should have simple collation");
|
||||
|
||||
const insertDocuments = function (coll) {
|
||||
assert.commandWorked(coll.insert({_id: 5}));
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
* non-replicated collections.
|
||||
*
|
||||
* @tags: [
|
||||
* requires_fcv_53,
|
||||
* does_not_support_stepdowns,
|
||||
* # Write and read on "local" db with multi clients cannot match the expected response.
|
||||
* multi_clients_incompatible,
|
||||
@ -16,6 +15,7 @@ import {ClusteredCollectionUtil} from "jstests/libs/clustered_collections/cluste
|
||||
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
||||
import {PersistenceProviderUtil} from "jstests/libs/server-rss/persistence_provider_util.js";
|
||||
import {add2dsphereVersionIfNeeded} from "jstests/libs/query/geo_index_version_helpers.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const validateCompoundSecondaryIndexes = function (db, coll, clusterKey) {
|
||||
const clusterKeyField = Object.keys(clusterKey)[0];
|
||||
@ -44,15 +44,23 @@ const overrideIndexType = function (clusterKey, indexType) {
|
||||
const validateCreateIndexOnClusterKey = function (db, collName, fullCreateOptions) {
|
||||
const clusterKey = fullCreateOptions.clusteredIndex.key;
|
||||
|
||||
const listIndexes0 = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
const listIndexesClusteredIndex = listIndexes0.cursor.firstBatch[0];
|
||||
let listIndexes0 = db[collName].getIndexes();
|
||||
const listIndexesClusteredIndex = listIndexes0[0];
|
||||
|
||||
// Expect listIndexes to append the 'clustered' field to it's clusteredIndex output.
|
||||
assert.docEq(listIndexesClusteredIndex.key, clusterKey);
|
||||
assert.eq(listIndexesClusteredIndex.clustered, true);
|
||||
|
||||
// no-op with the 'clustered' option.
|
||||
assert.commandWorked(db[collName].runCommand({createIndexes: collName, indexes: [listIndexesClusteredIndex]}));
|
||||
// createIndexes should be a no-op with the 'clustered' option.
|
||||
|
||||
// Remove the 'collation' field from the clustered index spec, since specifying a collation is
|
||||
// not allowed for clustered indexes. This is because clustered indexes are always getting the
|
||||
// collection default collation.
|
||||
let clusteredIndexSpec = {...listIndexesClusteredIndex};
|
||||
if (clusteredIndexSpec.collation) {
|
||||
delete clusteredIndexSpec.collation;
|
||||
}
|
||||
assert.commandWorked(db[collName].runCommand({createIndexes: collName, indexes: [clusteredIndexSpec]}));
|
||||
|
||||
// no-op without the 'clustered' option.
|
||||
assert.commandWorked(db[collName].createIndex(clusterKey));
|
||||
@ -72,11 +80,14 @@ const validateCreateIndexOnClusterKey = function (db, collName, fullCreateOption
|
||||
);
|
||||
|
||||
// The listIndexes output should be unchanged.
|
||||
const listIndexes1 = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
assert.eq(listIndexes1.cursor.firstBatch.length, listIndexes0.cursor.firstBatch.length);
|
||||
for (let i = 0; i < listIndexes1.cursor.firstBatch.length; ++i) {
|
||||
assert.docEq(listIndexes1.cursor.firstBatch[i], listIndexes0.cursor.firstBatch[i]);
|
||||
}
|
||||
let listIndexes1 = db[collName].getIndexes();
|
||||
|
||||
// Standardize index specs to account for possible differences in listIndexes output across FCVs.
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
listIndexes1 = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, listIndexes1);
|
||||
listIndexes0 = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, listIndexes0);
|
||||
|
||||
assert.sameMembers(listIndexes1, listIndexes0);
|
||||
|
||||
// It's possible to create 'hashed','2d','2dsphere' and 'text' indexes on the cluster key.
|
||||
assert.commandWorked(db[collName].createIndex(overrideIndexType(clusterKey, "hashed")));
|
||||
@ -86,8 +97,8 @@ const validateCreateIndexOnClusterKey = function (db, collName, fullCreateOption
|
||||
);
|
||||
assert.commandWorked(db[collName].createIndex(overrideIndexType(clusterKey, "text")));
|
||||
|
||||
const finalIndexes = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
assert.eq(finalIndexes.cursor.firstBatch.length, 5);
|
||||
const finalIndexes = db[collName].getIndexes();
|
||||
assert.eq(finalIndexes.length, 5);
|
||||
};
|
||||
|
||||
// It is illegal to drop the clusteredIndex. Verify that the various ways of dropping the
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// transitioning_replicaset_incompatible,
|
||||
// ]
|
||||
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const sourceColl = db.irap_cmd;
|
||||
const adminDB = db.getSiblingDB("admin");
|
||||
const destDB = db.getSiblingDB("irap_out_db");
|
||||
@ -41,8 +43,8 @@ let commandObj = {
|
||||
// Destination has an extra index.
|
||||
assert.commandFailedWithCode(adminDB.runCommand(commandObj), ErrorCodes.CommandFailed);
|
||||
|
||||
let destIndexes = assert.commandWorked(destDB.runCommand({"listIndexes": destColl.getName()}));
|
||||
commandObj.indexes = new DBCommandCursor(db, destIndexes).toArray();
|
||||
let destIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(destColl.getIndexes());
|
||||
commandObj.indexes = destIndexes;
|
||||
jsTestLog("Testing against destination collection with indexes: " + tojson(commandObj.indexes));
|
||||
assert.commandWorked(adminDB.runCommand(commandObj));
|
||||
|
||||
@ -55,8 +57,10 @@ assert.commandFailedWithCode(adminDB.runCommand(commandObj), ErrorCodes.CommandF
|
||||
destColl.drop();
|
||||
|
||||
assert.commandWorked(destDB.runCommand({"create": destColl.getName(), capped: true, size: 256, max: 2}));
|
||||
destIndexes = assert.commandWorked(destDB.runCommand({"listIndexes": destColl.getName()}));
|
||||
commandObj.indexes = new DBCommandCursor(db, destIndexes).toArray();
|
||||
destIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(
|
||||
destDB.getCollection(destColl.getName()).getIndexes(),
|
||||
);
|
||||
commandObj.indexes = destIndexes;
|
||||
|
||||
// Source is missing collection options.
|
||||
assert.commandFailedWithCode(adminDB.runCommand(commandObj), ErrorCodes.CommandFailed);
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
import {getTimeseriesCollForRawOps, kRawOperationSpec} from "jstests/core/libs/raw_operation_utils.js";
|
||||
import {isShardedTimeseries} from "jstests/core/timeseries/libs/viewless_timeseries_util.js";
|
||||
import {getPlanStage, getPlanStages, getRejectedPlan, getRejectedPlans} from "jstests/libs/query/analyze_plan.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const coll = db[jsTestName()];
|
||||
const timeField = "time";
|
||||
@ -42,6 +43,7 @@ function resetCollection(collation) {
|
||||
"v": 2,
|
||||
"key": {"control.min.time": 1, "control.max.time": 1},
|
||||
"name": "time_1",
|
||||
"collation": {"locale": "simple"},
|
||||
};
|
||||
extraBucketIndexes.push(addCollation(extraBucketIndexesShardedSpec, collation));
|
||||
}
|
||||
@ -51,15 +53,24 @@ function resetCollection(collation) {
|
||||
"v": 2,
|
||||
"key": {"meta": 1, "control.min.time": 1, "control.max.time": 1},
|
||||
"name": "m_1_time_1",
|
||||
"collation": {"locale": "simple"},
|
||||
};
|
||||
extraBucketIndexes.push(addCollation(extraBucketIndexesSpec, collation));
|
||||
}
|
||||
|
||||
resetCollection();
|
||||
|
||||
// Check that there is no collation in the default index.
|
||||
assert.eq(coll.getIndexes()[0].collation, null);
|
||||
assert.sameMembers(getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec), extraBucketIndexes);
|
||||
let index = coll.getIndexes()[0];
|
||||
let actualIndexes = getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec);
|
||||
|
||||
// Check that the default index has simple collation.
|
||||
|
||||
// TODO (SERVER-122417) Remove these workarounds once v9.0 branches out.
|
||||
index = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, index);
|
||||
actualIndexes = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, actualIndexes);
|
||||
|
||||
assert.eq(index.collation.locale, "simple");
|
||||
assert.sameMembers(actualIndexes, extraBucketIndexes);
|
||||
|
||||
assert.commandWorked(
|
||||
coll.insert([
|
||||
@ -204,9 +215,19 @@ assert.commandFailedWithCode(coll.createIndex({a: 1}, {partialFilterExpression:
|
||||
}
|
||||
|
||||
assert.commandWorked(coll.dropIndex({a: 1}));
|
||||
// Check that there is no collation in the default index.
|
||||
assert.eq(coll.getIndexes()[0].collation, null);
|
||||
assert.sameMembers(getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec), extraBucketIndexes);
|
||||
|
||||
// Check that the default index has simple collation.
|
||||
let index = coll.getIndexes()[0];
|
||||
let actualIndexes = getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec);
|
||||
|
||||
// Set the collation to simple if it is not present, which happens on older versions.
|
||||
// TODO (SERVER-122417) Remove these workarounds once v9.0 branches out.
|
||||
index = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, index);
|
||||
actualIndexes = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, actualIndexes);
|
||||
|
||||
assert.eq(index.collation.locale, "simple");
|
||||
assert.sameMembers(actualIndexes, extraBucketIndexes);
|
||||
|
||||
assert.gt(ixscanInWinningPlan, 0);
|
||||
}
|
||||
|
||||
@ -222,7 +243,12 @@ assert.commandWorked(
|
||||
},
|
||||
),
|
||||
);
|
||||
const actualBucketIndexes = getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec);
|
||||
let actualBucketIndexes = getTimeseriesCollForRawOps(coll).getIndexes(kRawOperationSpec);
|
||||
|
||||
// Set the collation to simple if it is not present, which happens on older versions.
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
actualBucketIndexes = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, actualBucketIndexes);
|
||||
|
||||
assert.sameMembers(
|
||||
actualBucketIndexes,
|
||||
extraBucketIndexes.concat([
|
||||
@ -230,6 +256,7 @@ assert.sameMembers(
|
||||
"v": 2,
|
||||
"key": {"control.min.a": 1, "control.max.a": 1},
|
||||
"name": "a_1",
|
||||
"collation": {"locale": "simple"},
|
||||
|
||||
"partialFilterExpression": {
|
||||
// Meta predicates are pushed down verbatim.
|
||||
@ -257,6 +284,7 @@ assert.sameMembers(
|
||||
{b: {$gte: 0}},
|
||||
],
|
||||
},
|
||||
"collation": {"locale": "simple"},
|
||||
v: 2,
|
||||
},
|
||||
},
|
||||
|
||||
@ -32,9 +32,9 @@ const bucketReopeningsFailedCounters = Object.freeze({
|
||||
export var TimeseriesTest = class {
|
||||
static verifyAndDropIndex(coll, shouldHaveOriginalSpec, indexName) {
|
||||
const checkIndexSpec = function (spec, userIndex) {
|
||||
assert(spec.hasOwnProperty("v"));
|
||||
assert(spec.hasOwnProperty("name"));
|
||||
assert(spec.hasOwnProperty("key"));
|
||||
assert(spec.hasOwnProperty("v"), indexName);
|
||||
assert(spec.hasOwnProperty("name"), indexName);
|
||||
assert(spec.hasOwnProperty("key"), indexName);
|
||||
|
||||
if (userIndex) {
|
||||
assert(!spec.hasOwnProperty("originalSpec"));
|
||||
@ -42,13 +42,13 @@ export var TimeseriesTest = class {
|
||||
}
|
||||
|
||||
if (shouldHaveOriginalSpec) {
|
||||
assert(spec.hasOwnProperty("originalSpec"));
|
||||
assert.eq(spec.v, spec.originalSpec.v);
|
||||
assert.eq(spec.name, spec.originalSpec.name);
|
||||
assert.neq(spec.key, spec.originalSpec.key);
|
||||
assert.eq(spec.collation, spec.originalSpec.collation);
|
||||
assert(spec.hasOwnProperty("originalSpec"), indexName);
|
||||
assert.eq(spec.v, spec.originalSpec.v, indexName);
|
||||
assert.eq(spec.name, spec.originalSpec.name, indexName);
|
||||
assert.neq(spec.key, spec.originalSpec.key, indexName);
|
||||
assert.eq(spec.collation, spec.originalSpec.collation, indexName);
|
||||
} else {
|
||||
assert(!spec.hasOwnProperty("originalSpec"));
|
||||
assert(!spec.hasOwnProperty("originalSpec"), indexName);
|
||||
}
|
||||
};
|
||||
let sawIndex = false;
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
* ]
|
||||
*/
|
||||
import {TimeseriesAggTests} from "jstests/core/timeseries/libs/timeseries_agg_helpers.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
import {
|
||||
runningWithViewlessTimeseriesUpgradeDowngrade,
|
||||
isViewfulTimeseriesOnlySuite,
|
||||
@ -108,7 +109,10 @@ function runOutAndCompareResults({
|
||||
}
|
||||
|
||||
let containsDefaultIndex = false;
|
||||
for (const index of outColl.getIndexes()) {
|
||||
for (let index of outColl.getIndexes()) {
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
index = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(testDB, index);
|
||||
|
||||
if (index == timeseriesDefaultIndex() || bsonUnorderedFieldsCompare(index, timeseriesDefaultIndex()) == 0) {
|
||||
containsDefaultIndex = true;
|
||||
break;
|
||||
@ -179,6 +183,7 @@ function timeseriesDefaultIndex() {
|
||||
[timeField]: 1,
|
||||
},
|
||||
"name": metaField + "_1_" + timeField + "_1",
|
||||
"collation": {"locale": "simple"},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
import {isStableFCVSuite} from "jstests/libs/feature_compatibility_version.js";
|
||||
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
||||
|
||||
let kDbName = db.getName();
|
||||
@ -402,7 +403,11 @@ db.foo.drop();
|
||||
assert.commandWorked(db.createCollection("foo", {collation: {locale: "en_US"}}));
|
||||
assert.commandWorked(db.adminCommand({shardCollection: kDbName + ".foo", key: {a: 1}, collation: {locale: "simple"}}));
|
||||
let indexSpec = getIndexSpecByName(db.foo, "a_1");
|
||||
assert(!indexSpec.hasOwnProperty("collation"));
|
||||
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
indexSpec = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, indexSpec);
|
||||
|
||||
assert.eq(indexSpec.collation.locale, "simple");
|
||||
|
||||
jsTestLog(
|
||||
"shardCollection should succeed for the key pattern {a: 1} if there are two indexes on {a: 1} and one has the simple collation.",
|
||||
@ -448,7 +453,11 @@ assert.commandWorked(db.createCollection("foo"));
|
||||
assert.commandWorked(sh.shardCollection(kDbName + ".foo", {a: 1}));
|
||||
indexSpec = getIndexSpecByName(db.foo, "a_1");
|
||||
assert(!indexSpec.hasOwnProperty("unique"), tojson(indexSpec));
|
||||
assert(!indexSpec.hasOwnProperty("collation"), tojson(indexSpec));
|
||||
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
indexSpec = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, indexSpec);
|
||||
|
||||
assert.eq(indexSpec.collation.locale, "simple", tojson(indexSpec));
|
||||
|
||||
jsTestLog('shardCollection() propagates the value for "unique".');
|
||||
db.foo.drop();
|
||||
@ -477,7 +486,11 @@ assert.commandWorked(db.createCollection("foo"));
|
||||
assert.commandFailed(sh.shardCollection(kDbName + ".foo", {a: 1}, false, {collation: {locale: "en_US"}}));
|
||||
assert.commandWorked(sh.shardCollection(kDbName + ".foo", {a: 1}, false, {collation: {locale: "simple"}}));
|
||||
indexSpec = getIndexSpecByName(db.foo, "a_1");
|
||||
assert(!indexSpec.hasOwnProperty("collation"), tojson(indexSpec));
|
||||
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
indexSpec = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, indexSpec);
|
||||
|
||||
assert.eq(indexSpec.collation.locale, "simple", tojson(indexSpec));
|
||||
|
||||
db.foo.drop();
|
||||
assert.commandWorked(db.createCollection("foo", {collation: {locale: "en_US"}}));
|
||||
@ -485,4 +498,8 @@ assert.commandFailed(sh.shardCollection(kDbName + ".foo", {a: 1}));
|
||||
assert.commandFailed(sh.shardCollection(kDbName + ".foo", {a: 1}, false, {collation: {locale: "en_US"}}));
|
||||
assert.commandWorked(sh.shardCollection(kDbName + ".foo", {a: 1}, false, {collation: {locale: "simple"}}));
|
||||
indexSpec = getIndexSpecByName(db.foo, "a_1");
|
||||
assert(!indexSpec.hasOwnProperty("collation"), tojson(indexSpec));
|
||||
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
indexSpec = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, indexSpec);
|
||||
|
||||
assert.eq(indexSpec.collation.locale, "simple", tojson(indexSpec));
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
* ]
|
||||
*/
|
||||
import {getRandomShardName} from "jstests/libs/cluster_helpers/sharded_cluster_fixture_helpers.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const coll = db[jsTestName()];
|
||||
|
||||
@ -22,8 +23,9 @@ coll.drop();
|
||||
// Create a timeseries collection and validate we have the default {m: 1, t: 1} index
|
||||
assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField: "t", metaField: "m"}}));
|
||||
{
|
||||
const index = coll.getIndexByKey({m: 1, t: 1});
|
||||
assert(index && !index.collation, tojson(index));
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
const index = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, coll.getIndexByKey({m: 1, t: 1}));
|
||||
assert(index && index.collation && index.collation.locale === "simple", tojson(index));
|
||||
}
|
||||
|
||||
// Drop the default index and re-create it using a non-default collation
|
||||
|
||||
@ -111,3 +111,6 @@ filters:
|
||||
- "release_memory_util.js":
|
||||
approvers:
|
||||
- 10gen/query-execution
|
||||
- "index_catalog_helpers.js":
|
||||
approvers:
|
||||
- 10gen/server-catalog-and-routing-shard-catalog
|
||||
|
||||
@ -25,6 +25,7 @@ import {
|
||||
getTimeseriesBucketsColl,
|
||||
} from "jstests/core/timeseries/libs/viewless_timeseries_util.js";
|
||||
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
||||
import {getRawOperationSpec} from "jstests/libs/raw_operation_utils.js";
|
||||
import {PersistenceProviderUtil} from "jstests/libs/server-rss/persistence_provider_util.js";
|
||||
@ -303,6 +304,15 @@ function mapListCatalogToListIndexesEntry(listCatalogEntry) {
|
||||
? Math.floor(mdIndexSpec.expireAfterSeconds)
|
||||
: 2147483647,
|
||||
}),
|
||||
// Adding the simple collation to the $listCatalog entry to be able to compare the index specs.
|
||||
// TODO (SERVER-119573) Remove this once listCatalog returns the simple collation explicitly.
|
||||
...(mdIndexSpec.collation === undefined && {collation: {locale: "simple"}}),
|
||||
...(mdIndexSpec.originalSpec !== undefined && {
|
||||
originalSpec: {
|
||||
...mdIndexSpec.originalSpec,
|
||||
...(mdIndexSpec.originalSpec.collation === undefined && {collation: {locale: "simple"}}),
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
@ -324,6 +334,9 @@ function mapListCatalogToListIndexesEntry(listCatalogEntry) {
|
||||
unique: true,
|
||||
}),
|
||||
...(mdOptions.collation !== undefined && {collation: mdOptions.collation}),
|
||||
// Adding the simple collation to the $listCatalog entry to be able to compare the index specs.
|
||||
// TODO (SERVER-119573) Remove this once listCatalog returns the simple collation explicitly.
|
||||
...(mdOptions.collation === undefined && {collation: {locale: "simple"}}),
|
||||
...(mdOptions.expireAfterSeconds !== undefined && {expireAfterSeconds: mdOptions.expireAfterSeconds}),
|
||||
clustered: true,
|
||||
});
|
||||
@ -432,7 +445,7 @@ function isViewListCatalogEntry(listCatalogEntry) {
|
||||
return !listCatalogEntry.md?.options.uuid;
|
||||
}
|
||||
|
||||
function validateListCatalogToListIndexesConsistency(listCatalog, listIndexes, shouldAssert) {
|
||||
function validateListCatalogToListIndexesConsistency(db, listCatalog, listIndexes, shouldAssert) {
|
||||
// Sorting function to ignore irrelevant ordering differences while comparing.
|
||||
function sortCollectionIndexesInPlace(indexList) {
|
||||
indexList.sort((a, b) => a.name.localeCompare(b.name)); // Sort by collection.
|
||||
@ -457,7 +470,8 @@ function validateListCatalogToListIndexesConsistency(listCatalog, listIndexes, s
|
||||
// TODO (SERVER-97749): Don't delete 'background' field once we
|
||||
// handle it properly
|
||||
delete index.background;
|
||||
return index;
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
return IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, index);
|
||||
}),
|
||||
],
|
||||
})),
|
||||
@ -484,6 +498,7 @@ function validateListCatalogToListIndexesConsistency(listCatalog, listIndexes, s
|
||||
}
|
||||
|
||||
function validateCatalogListOperationsConsistency(
|
||||
db,
|
||||
listCatalog,
|
||||
listCollections,
|
||||
listIndexes,
|
||||
@ -498,7 +513,7 @@ function validateCatalogListOperationsConsistency(
|
||||
isDbReadOnly,
|
||||
ignoreRecordIdsReplicatedOption,
|
||||
shouldAssert,
|
||||
) && validateListCatalogToListIndexesConsistency(listCatalog, listIndexes, shouldAssert)
|
||||
) && validateListCatalogToListIndexesConsistency(db, listCatalog, listIndexes, shouldAssert)
|
||||
);
|
||||
}
|
||||
|
||||
@ -567,6 +582,7 @@ export function assertCatalogListOperationsConsistencyForCollection(collection)
|
||||
listCatalog = filterListCatalogEntriesFromShardsWithoutChunks(db, listCatalog);
|
||||
|
||||
validateCatalogListOperationsConsistency(
|
||||
db,
|
||||
listCatalog,
|
||||
listCollections,
|
||||
listIndexes,
|
||||
@ -799,7 +815,7 @@ export function assertCatalogListOperationsConsistencyForDb(db, tenantId) {
|
||||
}
|
||||
if (
|
||||
collIndexes !== null &&
|
||||
!validateListCatalogToListIndexesConsistency(catalogInfoForIndexes, collIndexes, shouldAssert)
|
||||
!validateListCatalogToListIndexesConsistency(db, catalogInfoForIndexes, collIndexes, shouldAssert)
|
||||
) {
|
||||
jsTest.log.info("$listCatalog/listIndexes consistency check failed, retrying...");
|
||||
return false;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
/**
|
||||
* Utilities for testing clustered collections.
|
||||
*/
|
||||
@ -67,7 +69,17 @@ export var ClusteredCollectionUtil = class {
|
||||
// The clusteredIndex should appear in listIndexes with additional "clustered" field.
|
||||
static validateListIndexes(db, collName, createOptions) {
|
||||
const fullCreateOptions = ClusteredCollectionUtil.constructFullCreateOptions(createOptions);
|
||||
const listIndexes = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
|
||||
// TODO (SERVER-119573) Remove this workaround once listIndexes output matches storage format.
|
||||
if (!fullCreateOptions.clusteredIndex.collation) {
|
||||
fullCreateOptions.clusteredIndex.collation = {locale: "simple"};
|
||||
}
|
||||
|
||||
let index = db[collName].getIndexes()[0];
|
||||
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
index = IndexCatalogHelpers.addSimpleCollationToIndexIfMissing(db, index);
|
||||
|
||||
let extraData = {clustered: true};
|
||||
// ttl is not stored on the clusteredIndex but on the collection info. Therefore, we have to
|
||||
// add it back in this check to match the getIndexes output.
|
||||
@ -75,7 +87,7 @@ export var ClusteredCollectionUtil = class {
|
||||
extraData.expireAfterSeconds = createOptions.expireAfterSeconds;
|
||||
}
|
||||
const expectedListIndexesOutput = Object.extend(extraData, fullCreateOptions.clusteredIndex);
|
||||
assert.docEq(expectedListIndexesOutput, listIndexes.cursor.firstBatch[0]);
|
||||
assert.docEq(expectedListIndexesOutput, index);
|
||||
}
|
||||
|
||||
static testBasicClusteredCollection(db, collName, clusterKey) {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import {isStableFCVSuite} from "jstests/libs/feature_compatibility_version.js";
|
||||
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
|
||||
|
||||
/**
|
||||
* Helper functions that help test things to do with the index catalog.
|
||||
*/
|
||||
@ -46,9 +49,10 @@ export var IndexCatalogHelpers = (function () {
|
||||
|
||||
const foundByKeyPatternAndCollation = foundByKeyPattern.filter((spec) => {
|
||||
if (collation.locale === "simple") {
|
||||
// The simple collation is not explicitly stored in the index catalog, so we expect
|
||||
// the "collation" field to be absent.
|
||||
return !spec.hasOwnProperty("collation");
|
||||
// The simple collation is not returned by listIndexes on older versions.
|
||||
// On newer versions, the simple collation is always returned by listIndexes.
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
return !spec.hasOwnProperty("collation") || spec.collation.locale === "simple";
|
||||
}
|
||||
return bsonWoCompare(spec.collation, collation) === 0;
|
||||
});
|
||||
@ -82,10 +86,97 @@ export var IndexCatalogHelpers = (function () {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the index specs returned by listIndexes to the storage index format by stripping out
|
||||
* the 'collation' field if it is a simple collation.
|
||||
*
|
||||
* As of SERVER-89953, every index spec returned by listIndexes includes a 'collation' field,
|
||||
* even when it is just the default "simple" collation. However, the format stored in the catalog
|
||||
* omits the 'collation' field for simple collations.
|
||||
*
|
||||
* TODO (SERVER-119573): Remove this utility once listIndexes output is consistent with storage.
|
||||
*/
|
||||
function convertListIndexesResponseToStorageIndexFormat(indexes) {
|
||||
return indexes.map((index) => {
|
||||
if (index.collation && index.collation.locale === "simple") {
|
||||
const {collation, ...rest} = index;
|
||||
return rest;
|
||||
}
|
||||
return index;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if listIndexes always includes the simple collation in index specs.
|
||||
*
|
||||
* In FCV upgrade/downgrade suites, listIndexes may not include the simple collation if the feature
|
||||
* flag was not enabled in the lastLTS FCV. This function handles both stable and upgrade/downgrade
|
||||
* suite contexts.
|
||||
*
|
||||
* TODO (SERVER-122417): Remove this function once v9.0 branches out.
|
||||
*/
|
||||
function listIndexesIncludesSimpleCollation(db) {
|
||||
if (isStableFCVSuite()) {
|
||||
return FeatureFlagUtil.isPresentAndEnabled(db, "ListIndexesAlwaysIncludesSimpleCollation");
|
||||
}
|
||||
|
||||
// In FCV upgrade/downgrade suite, the flag is reliably enabled only if it has been enabled since
|
||||
// lastLTS (e.g., flag released in FCV 9.0, running binary 9.1 with FCV 9.0 - 9.1
|
||||
// upgrade/downgrade suite --> always includes simple collation).
|
||||
const flagDoc = FeatureFlagUtil.getFeatureFlagDoc(db.getMongo(), "ListIndexesAlwaysIncludesSimpleCollation");
|
||||
return flagDoc && flagDoc.value && MongoRunner.compareBinVersions(lastLTSFCV, flagDoc.version) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures an index spec includes {locale: "simple"} collation if missing from listIndexes output.
|
||||
*
|
||||
* listIndexes may omit the explicit simple collation in downgrade/upgrade FCV suites where the
|
||||
* relevant feature flag was not enabled in the lastLTS FCV. This helper adds it if needed to
|
||||
* standardize index specs for comparison and correctness across test environments.
|
||||
*
|
||||
* TODO (SERVER-122417): Remove this function once v9.0 branches out.
|
||||
*/
|
||||
function addSimpleCollationToIndexIfMissing(db, index) {
|
||||
if (listIndexesIncludesSimpleCollation(db)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
let indexWithSimpleCollation = {...index};
|
||||
if (!index.collation) {
|
||||
indexWithSimpleCollation.collation = {locale: "simple"};
|
||||
}
|
||||
if (index.originalSpec && !index.originalSpec.collation) {
|
||||
indexWithSimpleCollation.originalSpec.collation = {locale: "simple"};
|
||||
}
|
||||
return indexWithSimpleCollation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given list of index specs includes {locale: "simple"} collation if missing from
|
||||
* listIndexes output.
|
||||
*
|
||||
* listIndexes may omit the explicit simple collation in downgrade/upgrade FCV suites where the
|
||||
* relevant feature flag was not enabled in the lastLTS FCV. This helper adds it if needed to
|
||||
* standardize index specs for comparison and correctness across test environments.
|
||||
*
|
||||
* TODO (SERVER-122417): Remove this function once v9.0 branches out.
|
||||
*/
|
||||
function addSimpleCollationToIndexesIfMissing(db, indexes) {
|
||||
if (listIndexesIncludesSimpleCollation(db)) {
|
||||
return indexes;
|
||||
}
|
||||
return indexes.map((index) => addSimpleCollationToIndexIfMissing(db, index));
|
||||
}
|
||||
|
||||
return {
|
||||
findByName: getIndexSpecByName,
|
||||
findByKeyPattern: getIndexSpecByKeyPattern,
|
||||
createSingleIndex: createSingleIndex,
|
||||
createIndexAndVerifyWithDrop: createIndexAndVerifyWithDrop,
|
||||
// TODO (SERVER-119573): Remove this utility once listIndexes output is consistent with storage.
|
||||
convertListIndexesResponseToStorageIndexFormat: convertListIndexesResponseToStorageIndexFormat,
|
||||
// TODO (SERVER-122417): Remove these utilities once v9.0 branches out.
|
||||
addSimpleCollationToIndexIfMissing: addSimpleCollationToIndexIfMissing,
|
||||
addSimpleCollationToIndexesIfMissing: addSimpleCollationToIndexesIfMissing,
|
||||
};
|
||||
})();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
*/
|
||||
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
||||
import {getTimeseriesBucketsColl} from "jstests/core/timeseries/libs/viewless_timeseries_util.js";
|
||||
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
|
||||
|
||||
if (lastLTSFCV != "8.0") {
|
||||
print("Skipping test because last LTS FCV is no longer 8.0");
|
||||
@ -31,6 +32,13 @@ assert.commandWorked(coll.insertOne({t: ISODate(), m: 123}));
|
||||
|
||||
const expectedViewlessMetadata = coll.getMetadata();
|
||||
const expectedIndexes = coll.getIndexes();
|
||||
if (!FeatureFlagUtil.isEnabled(db, "ListIndexesAlwaysIncludesSimpleCollation")) {
|
||||
expectedIndexes.map((index) => {
|
||||
if (!index.collation) {
|
||||
index.collation = {locale: "simple"};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the collection can be converted to viewful timeseries and back to viewless.
|
||||
function assertValidTimeseriesCollection(nodeColl, {expectViewlessFormat}) {
|
||||
@ -55,7 +63,16 @@ function assertValidTimeseriesCollection(nodeColl, {expectViewlessFormat}) {
|
||||
assert.docEq(expectedViewfulMetadata, nodeColl.getMetadata());
|
||||
assert.eq(expectedViewlessMetadata.info.uuid, getTimeseriesBucketsColl(nodeColl).getUUID());
|
||||
}
|
||||
assert.docEq(expectedIndexes, nodeColl.getIndexes());
|
||||
|
||||
let actualIndexes = nodeColl.getIndexes();
|
||||
if (!FeatureFlagUtil.isEnabled(db, "ListIndexesAlwaysIncludesSimpleCollation")) {
|
||||
actualIndexes.map((index) => {
|
||||
if (!index.collation) {
|
||||
index.collation = {locale: "simple"};
|
||||
}
|
||||
});
|
||||
}
|
||||
assert.docEq(expectedIndexes, actualIndexes);
|
||||
|
||||
// The data is still accessible
|
||||
assert.eq(2, nodeColl.countDocuments({}));
|
||||
|
||||
@ -37,15 +37,23 @@ const validateCompoundSecondaryIndexes = function (db, coll, clusterKey) {
|
||||
const validateCreateIndexOnClusterKey = function (db, collName, fullCreateOptions) {
|
||||
const clusterKey = fullCreateOptions.clusteredIndex.key;
|
||||
|
||||
const listIndexes0 = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
const listIndexesClusteredIndex = listIndexes0.cursor.firstBatch[0];
|
||||
const listIndexes0 = db[collName].getIndexes();
|
||||
const listIndexesClusteredIndex = listIndexes0[0];
|
||||
|
||||
// Expect listIndexes to append the 'clustered' field to it's clusteredIndex output.
|
||||
assert.docEq(listIndexesClusteredIndex.key, clusterKey);
|
||||
assert.eq(listIndexesClusteredIndex.clustered, true);
|
||||
|
||||
// no-op with the 'clustered' option.
|
||||
assert.commandWorked(db[collName].runCommand({createIndexes: collName, indexes: [listIndexesClusteredIndex]}));
|
||||
|
||||
// Remove the 'collation' field from the clustered index spec, since specifying a collation is
|
||||
// not allowed for clustered indexes. This is because clustered indexes are always getting the
|
||||
// collection's default collation.
|
||||
let clusteredIndexSpec = {...listIndexesClusteredIndex};
|
||||
if (clusteredIndexSpec.collation) {
|
||||
delete clusteredIndexSpec.collation;
|
||||
}
|
||||
assert.commandWorked(db[collName].runCommand({createIndexes: collName, indexes: [clusteredIndexSpec]}));
|
||||
|
||||
// no-op without the 'clustered' option.
|
||||
assert.commandWorked(db[collName].createIndex(clusterKey));
|
||||
@ -65,9 +73,9 @@ const validateCreateIndexOnClusterKey = function (db, collName, fullCreateOption
|
||||
);
|
||||
|
||||
// The listIndexes output should be unchanged.
|
||||
const listIndexes1 = assert.commandWorked(db[collName].runCommand("listIndexes"));
|
||||
assert.eq(listIndexes1.cursor.firstBatch.length, 1);
|
||||
assert.docEq(listIndexes1.cursor.firstBatch[0], listIndexes0.cursor.firstBatch[0]);
|
||||
const listIndexes1 = db[collName].getIndexes();
|
||||
assert.eq(listIndexes1.length, 1);
|
||||
assert.docEq(listIndexes1[0], listIndexes0[0]);
|
||||
};
|
||||
|
||||
// It is illegal to drop the clusteredIndex. Verify that the various ways of dropping the
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
|
||||
const st = new ShardingTest({shards: 2});
|
||||
|
||||
@ -37,7 +38,7 @@ function makeCorrectCommand(sourceColl, destColl) {
|
||||
internalRenameIfOptionsAndIndexesMatch: 1,
|
||||
from: sourceColl.getFullName(),
|
||||
to: destColl.getFullName(),
|
||||
indexes: destColl.getIndexes(),
|
||||
indexes: IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(destColl.getIndexes()),
|
||||
collectionOptions: collectionOptions,
|
||||
databaseVersion: dbVersion,
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ function testOplogEntryIdIndexSpec(collectionName, idIndexSpec) {
|
||||
|
||||
assert.commandWorked(primaryDB.createCollection("without_version"));
|
||||
let allIndexes = primaryDB.without_version.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
let spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(2, spec.v, "Expected primary to build a v=2 _id index: " + tojson(spec));
|
||||
@ -34,6 +35,7 @@ testOplogEntryIdIndexSpec("without_version", spec);
|
||||
|
||||
assert.commandWorked(primaryDB.createCollection("version_v2", {idIndex: {key: {_id: 1}, name: "_id_", v: 2}}));
|
||||
allIndexes = primaryDB.version_v2.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(2, spec.v, "Expected primary to build a v=2 _id index: " + tojson(spec));
|
||||
@ -41,6 +43,7 @@ testOplogEntryIdIndexSpec("version_v2", spec);
|
||||
|
||||
assert.commandWorked(primaryDB.createCollection("version_v1", {idIndex: {key: {_id: 1}, name: "_id_", v: 1}}));
|
||||
allIndexes = primaryDB.version_v1.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(1, spec.v, "Expected primary to build a v=1 _id index: " + tojson(spec));
|
||||
@ -51,16 +54,19 @@ rst.awaitReplication();
|
||||
// Verify that the secondary built _id indexes with the same version as on the primary.
|
||||
|
||||
allIndexes = secondaryDB.without_version.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(2, spec.v, "Expected secondary to build a v=2 _id index when explicitly requested: " + tojson(spec));
|
||||
|
||||
allIndexes = secondaryDB.version_v2.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(2, spec.v, "Expected secondary to build a v=2 _id index when explicitly requested: " + tojson(spec));
|
||||
|
||||
allIndexes = secondaryDB.version_v1.getIndexes();
|
||||
allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(allIndexes);
|
||||
spec = IndexCatalogHelpers.findByKeyPattern(allIndexes, {_id: 1});
|
||||
assert.neq(null, spec, "_id index not found: " + tojson(allIndexes));
|
||||
assert.eq(1, spec.v, "Expected secondary to implicitly build a v=1 _id index: " + tojson(spec));
|
||||
|
||||
@ -16,7 +16,7 @@ const oplogColl = primary.getDB("local").oplog.rs;
|
||||
|
||||
function testOplogEntryContainsIndexInfoObj(coll, keyPattern, indexOptions) {
|
||||
assert.commandWorked(coll.createIndex(keyPattern, indexOptions));
|
||||
const allIndexes = coll.getIndexes();
|
||||
const allIndexes = IndexCatalogHelpers.convertListIndexesResponseToStorageIndexFormat(coll.getIndexes());
|
||||
const indexSpec = IndexCatalogHelpers.findByKeyPattern(allIndexes, keyPattern);
|
||||
|
||||
assert.neq(null, indexSpec, "Index with key pattern " + tojson(keyPattern) + " not found: " + tojson(allIndexes));
|
||||
|
||||
@ -132,6 +132,10 @@ config.getCollectionInfos().forEach(function (c) {
|
||||
delete i.key;
|
||||
delete i.ns;
|
||||
delete i.v;
|
||||
if (i.clustered) {
|
||||
// Can't specify the collation for a clustered index.
|
||||
delete i.collation;
|
||||
}
|
||||
assert.commandWorked(configCopy.getCollection(c.name).createIndex(key, i));
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
* (SERVER-89744 for more info).
|
||||
*
|
||||
*/
|
||||
import {IndexCatalogHelpers} from "jstests/libs/index_catalog_helpers.js";
|
||||
import {ReshardingTest} from "jstests/sharding/libs/resharding_test_fixture.js";
|
||||
|
||||
const reshardingTest = new ReshardingTest();
|
||||
@ -31,13 +32,16 @@ const collection = reshardingTest.createShardedCollection({
|
||||
chunks: [{min: {oldKey: MinKey}, max: {oldKey: MaxKey}, shard: donorShardNames[0]}],
|
||||
shardCollOptions: {collation: {locale: "simple"}},
|
||||
});
|
||||
const db = collection.getDB();
|
||||
|
||||
const idxSimpleCollationName = "idxSimpleCollation";
|
||||
assert.commandWorked(collection.createIndex({x: 1}, {name: idxSimpleCollationName, collation: {locale: "simple"}}));
|
||||
const idx2Name = "idx2";
|
||||
assert.commandWorked(collection.createIndex({x: 1}, {name: idx2Name}));
|
||||
|
||||
const preReshardingIndexes = collection.getIndexes();
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
const preReshardingIndexes = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, collection.getIndexes());
|
||||
|
||||
const preIdxDict = {};
|
||||
preReshardingIndexes.forEach(function (idx) {
|
||||
preIdxDict[idx.name] = idx;
|
||||
@ -52,11 +56,12 @@ reshardingTest.withReshardingInBackground({
|
||||
const postReshardingIndexes = collection.getIndexes();
|
||||
assert.eq(postReshardingIndexes.length, 5);
|
||||
|
||||
for (const postIdxSpec of postReshardingIndexes) {
|
||||
// TODO (SERVER-122417) Remove this workaround once v9.0 branches out.
|
||||
const normalizedPostIndexes = IndexCatalogHelpers.addSimpleCollationToIndexesIfMissing(db, postReshardingIndexes);
|
||||
for (const postIdxSpec of normalizedPostIndexes) {
|
||||
if ("newKey" in postIdxSpec.key) {
|
||||
// the index collation for post resharding key should be {locale: "simple"}. listIndexes
|
||||
// omits this collation.
|
||||
assert.eq(postIdxSpec.collation, undefined);
|
||||
// the index collation for post resharding key should be {locale: "simple"}.
|
||||
assert.eq(postIdxSpec.collation.locale, "simple");
|
||||
} else {
|
||||
assert.eq(postIdxSpec, preIdxDict[postIdxSpec.name]);
|
||||
}
|
||||
|
||||
@ -204,6 +204,7 @@ function timeseriesDefaultIndex() {
|
||||
[timeField]: 1,
|
||||
},
|
||||
"name": metaField + "_1_" + timeField + "_1",
|
||||
"collation": {"locale": "simple"},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -185,8 +185,6 @@ public:
|
||||
|
||||
/**
|
||||
* Sets a RequestMetadataWriter on this connection.
|
||||
*
|
||||
* TODO: support multiple metadata writers.
|
||||
*/
|
||||
virtual void setRequestMetadataWriter(rpc::RequestMetadataWriter writer);
|
||||
|
||||
@ -198,8 +196,6 @@ public:
|
||||
|
||||
/**
|
||||
* Sets a ReplyMetadataReader on this connection.
|
||||
*
|
||||
* TODO: support multiple metadata readers.
|
||||
*/
|
||||
virtual void setReplyMetadataReader(rpc::ReplyMetadataReader reader);
|
||||
|
||||
@ -465,8 +461,17 @@ public:
|
||||
|
||||
/**
|
||||
* Lists indexes on the collection 'nsOrUuid'.
|
||||
*
|
||||
* Includes in-progress indexes.
|
||||
*
|
||||
* Returned index specifications are not normalized: the 'collation' field is always included,
|
||||
* even when its value is simple. In contrast, in normalized index specifications, the simple
|
||||
* collation is typically omitted.
|
||||
* For cloning purposes, these index specs can be used directly
|
||||
* with a createIndexes-style command, since normalization will occur automatically on the
|
||||
* server. However, they should not be used to create a new index if the normalization step is
|
||||
* bypassed.
|
||||
*
|
||||
* If 'includeBuildUUIDs' is true, in-progress index specs will have the following format:
|
||||
* {
|
||||
* spec: <BSONObj>
|
||||
|
||||
@ -631,7 +631,20 @@ Status DefaultClonerImpl::copyDb(OperationContext* opCtx,
|
||||
auto indexSpecs = uassertStatusOK(
|
||||
getConn()->runExhaustiveCursorCommand(nss.dbName(), listIndexesCmd.toBSON()));
|
||||
|
||||
collectionIndexSpecs[params.collectionName] = indexSpecs;
|
||||
// The listIndexes command always includes a 'collation' field in its output as of
|
||||
// SERVER-89953.
|
||||
// However, here we are expecting a normalized index specification, in which case the
|
||||
// 'collation' field should be omitted when it represents the simple collation. Apply
|
||||
// normalization here to be able to clone the indexes using the expected format.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
std::list<BSONObj> normalizedIndexSpecs;
|
||||
for (const auto& indexSpec : indexSpecs) {
|
||||
normalizedIndexSpecs.emplace_back(
|
||||
IndexCatalog::normalizeIndexSpecFromListIndexes(indexSpec));
|
||||
}
|
||||
|
||||
collectionIndexSpecs[params.collectionName] = normalizedIndexSpecs;
|
||||
|
||||
if (params.idIndexSpec.isEmpty()) {
|
||||
params.idIndexSpec = _getIdIndexSpec(indexSpecs);
|
||||
|
||||
@ -2112,9 +2112,16 @@ OptionsAndIndexes CreateCollectionCoordinator::_getCollectionOptionsAndIndexes(
|
||||
listIndexes.toBSON(),
|
||||
Milliseconds(-1));
|
||||
|
||||
indexes = std::move(uassertStatusOK(swIndexResponse).docs);
|
||||
// The listIndexes command always includes a 'collation' field in its output as of SERVER-89953.
|
||||
// However, the participant shards are expecting a normalized index specification, in which case
|
||||
// the 'collation' field should be omitted when it represents the simple collation. Apply
|
||||
// normalization here to be able to recreate the indexes on the participant shards.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
const auto normalizedIndexes = IndexCatalog::normalizeIndexSpecsFromListIndexes(
|
||||
std::move(uassertStatusOK(swIndexResponse).docs));
|
||||
|
||||
return {optionsBob.obj(), std::move(indexes), idIndex};
|
||||
return {optionsBob.obj(), std::move(normalizedIndexes), idIndex};
|
||||
}
|
||||
|
||||
void CreateCollectionCoordinator::_createCollectionOnParticipants(
|
||||
|
||||
@ -504,7 +504,7 @@ void checkExpectedTargetIndexesMatch(OperationContext* opCtx,
|
||||
const NamespaceString targetNss,
|
||||
const std::vector<BSONObj>& expectedIndexes) {
|
||||
sharding::router::CollectionRouter router(opCtx, targetNss);
|
||||
const auto currentIndexes = router.routeWithRoutingContext(
|
||||
auto currentIndexes = router.routeWithRoutingContext(
|
||||
"checking indexes prerequisites within rename collection coordinator",
|
||||
[&](OperationContext* opCtx, RoutingContext& routingCtx) {
|
||||
const auto response = loadIndexesFromAuthoritativeShard(opCtx, routingCtx, targetNss);
|
||||
@ -514,6 +514,15 @@ void checkExpectedTargetIndexesMatch(OperationContext* opCtx,
|
||||
}
|
||||
return uassertStatusOK(response).docs;
|
||||
});
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in its output as of SERVER-89953.
|
||||
// However, we are expecting a normalized index specification, in which case the 'collation'
|
||||
// field should be omitted when it represents the simple collation. Apply normalization here to
|
||||
// be able to compare the index specs apropiately.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
currentIndexes = IndexCatalog::normalizeIndexSpecsFromListIndexes(currentIndexes);
|
||||
|
||||
uassertStatusOK(checkTargetCollectionIndexesMatch(targetNss, expectedIndexes, currentIndexes));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -441,8 +441,16 @@ void validateShardKeyIsNotEncrypted(OperationContext* opCtx,
|
||||
|
||||
std::vector<BSONObj> ValidationBehaviorsShardCollection::loadIndexes(
|
||||
const NamespaceString& nss) const {
|
||||
return listIndexesEmptyListIfMissing(
|
||||
const auto indexes = listIndexesEmptyListIfMissing(
|
||||
_opCtx, nss, ListIndexesInclude::kNothing, /*isRawDataRequest=*/true);
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in its output as of SERVER-89953.
|
||||
// However, the caller expects a normalized index specification, in which case the 'collation'
|
||||
// field should be omitted when it represents the simple collation. Apply normalization here to
|
||||
// ensure compatibility with storage index specs.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
return IndexCatalog::normalizeIndexSpecsFromListIndexes(indexes);
|
||||
}
|
||||
|
||||
void ValidationBehaviorsShardCollection::verifyUsefulNonMultiKeyIndex(
|
||||
|
||||
@ -62,6 +62,13 @@ class MONGO_MOD_NEEDS_REPLACEMENT ShardKeyValidationBehaviors {
|
||||
public:
|
||||
virtual ~ShardKeyValidationBehaviors() {}
|
||||
|
||||
/*
|
||||
* Returns index specifications for the collection 'nss'.
|
||||
* Returned indexes are in normalized form: if the index uses the simple collation, the
|
||||
* 'collation' field is omitted. Note that because of this normalization, these index specs
|
||||
* cannot be passed directly to a createIndexes user-like command because attempting to
|
||||
* normalize an already-normalized spec is not supported.
|
||||
*/
|
||||
virtual std::vector<BSONObj> loadIndexes(const NamespaceString& nss) const = 0;
|
||||
|
||||
virtual void verifyUsefulNonMultiKeyIndex(const NamespaceString& nss,
|
||||
|
||||
@ -325,30 +325,34 @@ TEST_F(ConfigInitializationTest, BuildsNecessaryIndexes) {
|
||||
->initializeConfigDatabaseIfNeeded(operationContext()));
|
||||
|
||||
std::vector<BSONObj> expectedChunksIndexes = std::vector<BSONObj>{
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("uuid" << 1 << "min" << 1) << "name"
|
||||
<< "uuid_1_min_1"
|
||||
<< "unique" << true),
|
||||
<< "unique" << true << "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("uuid" << 1 << "shard" << 1 << "min" << 1) << "name"
|
||||
<< "uuid_1_shard_1_min_1"
|
||||
<< "unique" << true),
|
||||
<< "unique" << true << "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("uuid" << 1 << "lastmod" << 1) << "name"
|
||||
<< "uuid_1_lastmod_1"
|
||||
<< "unique" << true),
|
||||
<< "unique" << true << "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("uuid" << 1 << "shard" << 1 << "onCurrentShardSince" << 1)
|
||||
<< "name"
|
||||
<< "uuid_1_shard_1_onCurrentShardSince_1")};
|
||||
<< "uuid_1_shard_1_onCurrentShardSince_1" << "collation"
|
||||
<< BSON("locale" << "simple"))};
|
||||
|
||||
auto expectedShardsIndexes = std::vector<BSONObj>{
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "unique" << true << "key" << BSON("host" << 1) << "name"
|
||||
<< "host_1")};
|
||||
<< "host_1" << "collation" << BSON("locale" << "simple"))};
|
||||
auto expectedTagsIndexes = std::vector<BSONObj>{
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "unique" << true << "key" << BSON("ns" << 1 << "min" << 1) << "name"
|
||||
<< "ns_1_min_1"),
|
||||
<< "ns_1_min_1" << "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("ns" << 1 << "tag" << 1) << "name"
|
||||
<< "ns_1_tag_1")};
|
||||
<< "ns_1_tag_1" << "collation" << BSON("locale" << "simple"))};
|
||||
|
||||
auto foundChunksIndexes =
|
||||
assertGet(getIndexes(operationContext(), NamespaceString::kConfigsvrChunksNamespace));
|
||||
@ -362,12 +366,13 @@ TEST_F(ConfigInitializationTest, BuildsNecessaryIndexes) {
|
||||
assertBSONObjsSame(expectedTagsIndexes, foundTagsIndexes);
|
||||
|
||||
auto expectedPlacementHistoryIndexes = std::vector<BSONObj>{
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "unique" << true << "key" << BSON("nss" << 1 << "timestamp" << -1)
|
||||
<< "name"
|
||||
<< "nss_1_timestamp_-1"),
|
||||
<< "nss_1_timestamp_-1" << "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key" << BSON("timestamp" << -1 << "nss" << 1) << "name"
|
||||
<< "timestamp_-1_nss_1")};
|
||||
<< "timestamp_-1_nss_1" << "collation" << BSON("locale" << "simple"))};
|
||||
auto foundPlacementHistoryIndexes = assertGet(
|
||||
getIndexes(operationContext(), NamespaceString::kConfigsvrPlacementHistoryNamespace));
|
||||
assertBSONObjsSame(expectedPlacementHistoryIndexes, foundPlacementHistoryIndexes);
|
||||
|
||||
@ -63,9 +63,10 @@ TEST_F(ConfigIndexTest, CompatibleIndexAlreadyExists) {
|
||||
->initializeConfigDatabaseIfNeeded(operationContext()));
|
||||
|
||||
auto expectedShardsIndexes = std::vector<BSONObj>{
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "unique" << true << "key" << BSON("host" << 1) << "name"
|
||||
<< "host_1")};
|
||||
<< "host_1" << "collation" << BSON("locale" << "simple"))};
|
||||
|
||||
|
||||
auto foundShardsIndexes =
|
||||
|
||||
@ -288,7 +288,13 @@ public:
|
||||
|
||||
/**
|
||||
* Returns the complete set of index specifications for the given namespace.
|
||||
*
|
||||
* For timeseries collections, returns index specs in their raw format.
|
||||
*
|
||||
* Returned indexes are in normalized form: if the index uses the simple collation, the
|
||||
* 'collation' field is omitted. Note that because of this normalization, these index specs
|
||||
* cannot be passed directly to a createIndexes user-like command because attempting to
|
||||
* normalize an already-normalized spec is not supported.
|
||||
*/
|
||||
virtual std::vector<BSONObj> getIndexSpecs(OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
|
||||
@ -112,11 +112,20 @@ std::unique_ptr<Pipeline> NonShardServerProcessInterface::preparePipelineForExec
|
||||
std::vector<BSONObj> NonShardServerProcessInterface::getIndexSpecs(OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
bool includeBuildUUIDs) {
|
||||
return listIndexesEmptyListIfMissing(opCtx,
|
||||
ns,
|
||||
includeBuildUUIDs ? ListIndexesInclude::kBuildUUID
|
||||
: ListIndexesInclude::kNothing,
|
||||
/*isRawDataRequest=*/true);
|
||||
|
||||
const auto indexes = listIndexesEmptyListIfMissing(
|
||||
opCtx,
|
||||
ns,
|
||||
includeBuildUUIDs ? ListIndexesInclude::kBuildUUID : ListIndexesInclude::kNothing,
|
||||
/*isRawDataRequest=*/true);
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in its output as of SERVER-89953.
|
||||
// However, the caller expects a normalized index specification, in which case the 'collation'
|
||||
// field should be omitted when it represents the simple collation. Apply normalization here to
|
||||
// ensure compatibility with storage index specs.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
return IndexCatalog::normalizeIndexSpecsFromListIndexes(indexes);
|
||||
}
|
||||
|
||||
std::vector<FieldPath> NonShardServerProcessInterface::collectDocumentKeyFieldsActingAsRouter(
|
||||
|
||||
@ -433,7 +433,7 @@ std::vector<BSONObj> ShardServerProcessInterface::getIndexSpecs(OperationContext
|
||||
const NamespaceString& ns,
|
||||
bool includeBuildUUIDs) {
|
||||
sharding::router::CollectionRouter router(opCtx, ns);
|
||||
return router.routeWithRoutingContext(
|
||||
const auto indexes = router.routeWithRoutingContext(
|
||||
"ShardServerProcessInterface::getIndexSpecs",
|
||||
[&](OperationContext* opCtx, RoutingContext& routingCtx) -> std::vector<BSONObj> {
|
||||
StatusWith<Shard::QueryResponse> response =
|
||||
@ -444,6 +444,14 @@ std::vector<BSONObj> ShardServerProcessInterface::getIndexSpecs(OperationContext
|
||||
uassertStatusOK(response);
|
||||
return response.getValue().docs;
|
||||
});
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in its output as of SERVER-89953.
|
||||
// However, the caller expects a normalized index specification, in which case the 'collation'
|
||||
// field should be omitted when it represents the simple collation. Apply normalization here to
|
||||
// ensure compatibility with storage index specs.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
return IndexCatalog::normalizeIndexSpecsFromListIndexes(indexes);
|
||||
}
|
||||
|
||||
std::vector<DatabaseName> ShardServerProcessInterface::getAllDatabases(
|
||||
|
||||
@ -294,8 +294,16 @@ BaseCloner::AfterStageBehavior CollectionCloner::listIndexesStage() {
|
||||
const auto storageEngine = getGlobalServiceContext()->getStorageEngine();
|
||||
// Parse the index specs into their respective state, ready or unfinished.
|
||||
for (auto&& spec : indexSpecs) {
|
||||
// Sanitize storage engine options to remove options which might not apply to this node. See
|
||||
// SERVER-68122.
|
||||
// The listIndexes command always includes a 'collation' field in its output as of
|
||||
// SERVER-89953. However, here we are expecting a normalized index specification, in which
|
||||
// case the 'collation' field should be omitted when it represents the simple collation.
|
||||
// Apply normalization here to be able to clone the indexes using the expected format.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs compatible
|
||||
// with the storage format.
|
||||
spec = IndexCatalog::normalizeIndexSpecFromListIndexes(spec);
|
||||
|
||||
// Sanitize storage engine options to remove options which might not apply to this node.
|
||||
// See SERVER-68122.
|
||||
if (auto storageEngineElem = spec.getField(IndexDescriptor::kStorageEngineFieldName)) {
|
||||
auto sanitizedStorageEngineOpts =
|
||||
storageEngine->getSanitizedStorageOptionsForSecondaryReplication(
|
||||
|
||||
@ -446,8 +446,8 @@ public:
|
||||
return _coll->getCompletedIndexCount();
|
||||
}
|
||||
|
||||
BSONObj getIndexSpec(StringData indexName) const override {
|
||||
return _coll->getIndexSpec(indexName);
|
||||
BSONObj getIndexSpec(StringData indexName, bool expandSimpleCollation) const override {
|
||||
return _coll->getIndexSpec(indexName, expandSimpleCollation);
|
||||
}
|
||||
|
||||
void getAllIndexes(std::vector<std::string>* names) const override {
|
||||
|
||||
@ -926,6 +926,15 @@ MigrationDestinationManager::IndexesAndIdIndex MigrationDestinationManager::getC
|
||||
continue;
|
||||
}
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in its output. However,
|
||||
// unless 'expandSimpleCollation' is true, the caller expects a normalized index
|
||||
// specification, in which case the 'collation' field should be omitted when it
|
||||
// represents the simple collation. Apply normalization here to ensure compatibility
|
||||
// with storage index specs.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes returns specs
|
||||
// compatible with the storage format.
|
||||
spec = IndexCatalog::normalizeIndexSpecFromListIndexes(spec);
|
||||
|
||||
if (auto indexNameElem = spec[IndexDescriptor::kIndexNameFieldName];
|
||||
indexNameElem.type() == BSONType::string &&
|
||||
indexNameElem.valueStringData() == IndexConstants::kIdIndexName) {
|
||||
@ -1044,15 +1053,25 @@ namespace {
|
||||
*/
|
||||
void _dropLocalIndexes(OperationContext* opCtx,
|
||||
const NamespaceString& nss,
|
||||
const std::vector<BSONObj>& indexSpecs) {
|
||||
const std::vector<BSONObj>& donorIndexSpecs) {
|
||||
// Determine which indexes exist on the local collection that don't exist on the donor's
|
||||
// collection.
|
||||
DBDirectClient client(opCtx);
|
||||
auto indexes = listIndexesEmptyListIfMissing(
|
||||
opCtx, nss, ListIndexesInclude::kNothing, /*isRawDataRequest=*/true);
|
||||
for (auto&& recipientIndex : indexes) {
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in each index spec as of
|
||||
// SERVER-89953, even if it has a simple collation. In contrast, `donorIndexSpecs` are
|
||||
// normalized, which means that the 'collation' field is omitted when it is a simple collation.
|
||||
// Therefore, we normalize the listIndexes output here to enable direct comparison between both
|
||||
// formats.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes output matches storage
|
||||
// format.
|
||||
const auto& normalizedSpecs = IndexCatalog::normalizeIndexSpecsFromListIndexes(indexes);
|
||||
|
||||
for (auto&& recipientIndex : normalizedSpecs) {
|
||||
bool dropIndex = true;
|
||||
for (auto&& donorIndex : indexSpecs) {
|
||||
for (auto&& donorIndex : donorIndexSpecs) {
|
||||
if (recipientIndex.woCompare(donorIndex) == 0) {
|
||||
dropIndex = false;
|
||||
break;
|
||||
|
||||
@ -1342,7 +1342,7 @@ ReshardingRecipientService::RecipientStateMachine::_buildIndexThenTransitionToAp
|
||||
.then([this, factory](const ReplIndexBuildState::IndexCatalogStats& stats) {
|
||||
if (auto opCtx = _makeOperationContext(factory);
|
||||
resharding::gReshardingIndexVerification.load()) {
|
||||
auto [sourceIdxSpecs, _] = _externalState->getCollectionIndexes(
|
||||
auto [normalizedSourceIdxSpecs, _] = _externalState->getCollectionIndexes(
|
||||
opCtx.get(),
|
||||
_metadata.getSourceNss(),
|
||||
_metadata.getSourceUUID(),
|
||||
@ -1355,10 +1355,22 @@ ReshardingRecipientService::RecipientStateMachine::_buildIndexThenTransitionToAp
|
||||
_metadata.getTempReshardingNss(),
|
||||
ListIndexesInclude::kNothing,
|
||||
/*isRawDataRequest=*/true);
|
||||
resharding::verifyIndexSpecsMatch(sourceIdxSpecs.cbegin(),
|
||||
sourceIdxSpecs.cend(),
|
||||
tempCollIdxSpecs.cbegin(),
|
||||
tempCollIdxSpecs.cend());
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in each index
|
||||
// spec as of SERVER-89953, even if it represents the simple collation. In
|
||||
// contrast, getCollectionIndexes() returns normalized specs where the
|
||||
// 'collation' field is omitted when it is a simple collation. Therefore, we
|
||||
// normalize the listIndexes output here to enable direct comparison between
|
||||
// both formats.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes output
|
||||
// matches storage format.
|
||||
const auto& normalizedTempCollIdxSpecs =
|
||||
IndexCatalog::normalizeIndexSpecsFromListIndexes(tempCollIdxSpecs);
|
||||
|
||||
resharding::verifyIndexSpecsMatch(normalizedSourceIdxSpecs.cbegin(),
|
||||
normalizedSourceIdxSpecs.cend(),
|
||||
normalizedTempCollIdxSpecs.cbegin(),
|
||||
normalizedTempCollIdxSpecs.cend());
|
||||
}
|
||||
_transitionToApplying(factory);
|
||||
});
|
||||
|
||||
@ -156,9 +156,18 @@ protected:
|
||||
BSON("level" << "local"
|
||||
<< "afterClusterTime" << kDefaultFetchTimestamp));
|
||||
|
||||
return BSON(
|
||||
"ok" << 1 << "cursor"
|
||||
<< BSON("id" << 0LL << "ns" << nss.ns_forTest() << "firstBatch" << indexDocs));
|
||||
// Ensure listIndexes always returns a 'collation' field.
|
||||
std::vector<BSONObj> indexDocsWithCollation;
|
||||
for (auto&& indexDoc : indexDocs) {
|
||||
if (!indexDoc.hasField("collation")) {
|
||||
indexDocsWithCollation.emplace_back(
|
||||
indexDoc.addFields(BSON("collation" << BSON("locale" << "simple"))));
|
||||
}
|
||||
}
|
||||
|
||||
return BSON("ok" << 1 << "cursor"
|
||||
<< BSON("id" << 0LL << "ns" << nss.ns_forTest() << "firstBatch"
|
||||
<< indexDocsWithCollation));
|
||||
});
|
||||
}
|
||||
|
||||
@ -354,15 +363,17 @@ TEST_F(RecipientServiceExternalStateTest, CreateLocalReshardingCollectionBasic)
|
||||
kReshardingTimestamp);
|
||||
|
||||
const std::vector<BSONObj> indexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key"
|
||||
<< BSON("a" << 1 << "b"
|
||||
<< "hashed")
|
||||
<< "name"
|
||||
<< "indexOne")};
|
||||
<< "indexOne" << "collation" << BSON("locale" << "simple"))};
|
||||
// When creating collection, only _id index should be created.
|
||||
const std::vector<BSONObj> expectedIndexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName)};
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple"))};
|
||||
auto future = launchAsync([&] {
|
||||
expectRefreshReturnForOriginalColl(
|
||||
kOrigNss, kShardKey, kOrigUUID, kOrigEpoch, kOrigTimestamp);
|
||||
@ -418,15 +429,17 @@ TEST_F(RecipientServiceExternalStateTest,
|
||||
kReshardingTimestamp);
|
||||
|
||||
const std::vector<BSONObj> indexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName),
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple")),
|
||||
BSON("v" << 2 << "key"
|
||||
<< BSON("a" << 1 << "b"
|
||||
<< "hashed")
|
||||
<< "name"
|
||||
<< "indexOne")};
|
||||
<< "indexOne" << "collation" << BSON("locale" << "simple"))};
|
||||
// When creating collection, only _id index should be created.
|
||||
const std::vector<BSONObj> expectedIndexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName)};
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple"))};
|
||||
|
||||
auto future = launchAsync([&] {
|
||||
expectRefreshReturnForOriginalColl(
|
||||
@ -495,9 +508,7 @@ TEST_F(RecipientServiceExternalStateTest,
|
||||
<< "hashed")
|
||||
<< "name"
|
||||
<< "indexOne")};
|
||||
// When creating collection, only _id index should be created.
|
||||
const std::vector<BSONObj> expectedIndexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName)};
|
||||
|
||||
// Create the collection and indexes to simulate retrying after a failover. Only include the id
|
||||
// index, because it is needed to create the collection.
|
||||
CollectionOptionsAndIndexes optionsAndIndexes = {
|
||||
@ -530,6 +541,11 @@ TEST_F(RecipientServiceExternalStateTest,
|
||||
|
||||
future.default_timed_get();
|
||||
|
||||
// When creating collection, only _id index should be created.
|
||||
const std::vector<BSONObj> expectedIndexes = {
|
||||
BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" << IndexConstants::kIdIndexName
|
||||
<< "collation" << BSON("locale" << "simple"))};
|
||||
|
||||
verifyCollectionAndIndexes(kReshardingNss, kReshardingUUID, expectedIndexes);
|
||||
}
|
||||
|
||||
|
||||
@ -301,6 +301,13 @@ feature_flags:
|
||||
cpp_varname: gFeatureFlagQEPrefixSuffixSearch
|
||||
default: false
|
||||
fcv_gated: true
|
||||
# TODO (SERVER-122417): Remove this feature flag once v9.0 branches out.
|
||||
featureFlagListIndexesAlwaysIncludesSimpleCollation:
|
||||
description: "Feature flag to enable the listIndexes command to include the collation field in the index specs, even when it is the simple collation."
|
||||
cpp_varname: feature_flags::gFeatureFlagListIndexesAlwaysIncludesSimpleCollation
|
||||
default: true
|
||||
version: 9.0
|
||||
fcv_gated: true
|
||||
featureFlagIncludeDeltaHeaderForPhylog:
|
||||
description: "Enable including delta headers for phylog pages."
|
||||
cpp_varname: gFeatureFlagIncludeDeltaHeaderForPhylog
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#include "mongo/db/index/index_constants.h"
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/query/collation/collation_spec.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/index_descriptor.h"
|
||||
#include "mongo/idl/idl_parser.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
@ -108,12 +110,16 @@ bool requiresLegacyFormat(const NamespaceString& nss, const CollectionOptions& c
|
||||
|
||||
BSONObj formatClusterKeyForListIndexes(const ClusteredCollectionInfo& collInfo,
|
||||
const BSONObj& collation,
|
||||
const boost::optional<int64_t>& expireAfterSeconds) {
|
||||
const boost::optional<int64_t>& expireAfterSeconds,
|
||||
bool extendSimpleCollation) {
|
||||
BSONObjBuilder bob;
|
||||
collInfo.getIndexSpec().serialize(&bob);
|
||||
if (!collation.isEmpty()) {
|
||||
bob.append("collation", collation);
|
||||
} else if (extendSimpleCollation) {
|
||||
bob.append(IndexDescriptor::kCollationFieldName, CollationSpec::kSimpleSpec);
|
||||
}
|
||||
|
||||
if (expireAfterSeconds) {
|
||||
bob.append("expireAfterSeconds", expireAfterSeconds.value());
|
||||
}
|
||||
|
||||
@ -77,7 +77,8 @@ MONGO_MOD_PUBLIC bool requiresLegacyFormat(const NamespaceString& nss,
|
||||
MONGO_MOD_PUBLIC BSONObj
|
||||
formatClusterKeyForListIndexes(const ClusteredCollectionInfo& collInfo,
|
||||
const BSONObj& collation,
|
||||
const boost::optional<int64_t>& expireAfterSeconds);
|
||||
const boost::optional<int64_t>& expireAfterSeconds,
|
||||
bool extendSimpleCollation = false);
|
||||
|
||||
/**
|
||||
* Returns true if the BSON object matches the collection's cluster key. Caller's should ensure
|
||||
|
||||
@ -632,7 +632,8 @@ public:
|
||||
|
||||
virtual int getCompletedIndexCount() const = 0;
|
||||
|
||||
virtual BSONObj getIndexSpec(StringData indexName) const = 0;
|
||||
virtual BSONObj getIndexSpec(StringData indexName,
|
||||
bool expandSimpleCollation = false) const = 0;
|
||||
|
||||
virtual void getAllIndexes(std::vector<std::string>* names) const = 0;
|
||||
|
||||
|
||||
@ -1965,13 +1965,43 @@ int CollectionImpl::getCompletedIndexCount() const {
|
||||
return num;
|
||||
}
|
||||
|
||||
BSONObj CollectionImpl::getIndexSpec(StringData indexName) const {
|
||||
BSONObj CollectionImpl::getIndexSpec(StringData indexName, bool expandSimpleCollation) const {
|
||||
int offset = _metadata->findIndexOffset(indexName);
|
||||
invariant(offset >= 0,
|
||||
str::stream() << "cannot get index spec for " << indexName << " @ " << getCatalogId()
|
||||
<< " : " << _metadata->toBSON());
|
||||
|
||||
return _metadata->indexes[offset].spec;
|
||||
const auto& spec = _metadata->indexes[offset].spec;
|
||||
if (!expandSimpleCollation) {
|
||||
return spec;
|
||||
}
|
||||
|
||||
// TODO (SERVER-119573): Remove manual addition of the 'collation' field here once index specs
|
||||
// in the catalog always include the 'collation' metadata. At that point, explicit insertion
|
||||
// will no longer be needed.
|
||||
BSONObjBuilder bob;
|
||||
|
||||
// Ensure top-level spec has a simple collation if missing.
|
||||
if (!spec.hasField(IndexDescriptor::kCollationFieldName)) {
|
||||
bob.append(IndexDescriptor::kCollationFieldName, CollationSpec::kSimpleSpec);
|
||||
}
|
||||
|
||||
// If 'originalSpec' is present, ensure it contains the 'collation' field setting it to a simple
|
||||
// collation when missing.
|
||||
if (spec.hasField(IndexDescriptor::kOriginalSpecFieldName)) {
|
||||
BSONObj originalSpec = spec[IndexDescriptor::kOriginalSpecFieldName].Obj();
|
||||
|
||||
if (originalSpec.hasField(IndexDescriptor::kCollationFieldName)) {
|
||||
bob.append(IndexDescriptor::kOriginalSpecFieldName, originalSpec);
|
||||
} else {
|
||||
bob.append(IndexDescriptor::kOriginalSpecFieldName,
|
||||
originalSpec.addFields(BSON(IndexDescriptor::kCollationFieldName
|
||||
<< CollationSpec::kSimpleSpec)));
|
||||
}
|
||||
}
|
||||
|
||||
bob.appendElementsUnique(spec);
|
||||
return bob.obj();
|
||||
}
|
||||
|
||||
void CollectionImpl::getAllIndexes(std::vector<std::string>* names) const {
|
||||
|
||||
@ -385,7 +385,7 @@ public:
|
||||
|
||||
int getCompletedIndexCount() const final;
|
||||
|
||||
BSONObj getIndexSpec(StringData indexName) const final;
|
||||
BSONObj getIndexSpec(StringData indexName, bool expandSimpleCollation) const final;
|
||||
|
||||
void getAllIndexes(std::vector<std::string>* names) const final;
|
||||
|
||||
|
||||
@ -445,7 +445,7 @@ public:
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
|
||||
BSONObj getIndexSpec(StringData indexName) const override {
|
||||
BSONObj getIndexSpec(StringData indexName, bool expandSimpleCollation) const override {
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
#include "mongo/db/shard_role/shard_catalog/index_catalog.h"
|
||||
|
||||
#include "mongo/bson/simple_bsonobj_comparator.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/collection.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
@ -93,6 +94,50 @@ std::vector<BSONObj> IndexCatalog::normalizeIndexSpecs(OperationContext* opCtx,
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<BSONObj> IndexCatalog::normalizeIndexSpecsFromListIndexes(
|
||||
const std::vector<BSONObj>& indexSpecs) {
|
||||
std::vector<BSONObj> results;
|
||||
results.reserve(indexSpecs.size());
|
||||
|
||||
for (const auto& indexSpec : indexSpecs) {
|
||||
results.emplace_back(normalizeIndexSpecFromListIndexes(indexSpec));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
BSONObj IndexCatalog::normalizeIndexSpecFromListIndexes(const BSONObj& indexSpec) {
|
||||
|
||||
auto removeSimpleCollationFromBsonObj = [](BSONObj obj) -> BSONObj {
|
||||
if (obj.hasField(IndexDescriptor::kCollationFieldName) &&
|
||||
SimpleBSONObjComparator::kInstance.evaluate(
|
||||
obj[IndexDescriptor::kCollationFieldName].Obj() == CollationSpec::kSimpleSpec)) {
|
||||
return obj.removeField(IndexDescriptor::kCollationFieldName);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
BSONObjBuilder bob;
|
||||
|
||||
// Remove simple collation under 'spec' field
|
||||
constexpr auto kSpecFieldName = "spec"_sd;
|
||||
if (indexSpec.hasField(kSpecFieldName)) {
|
||||
bob.append(kSpecFieldName,
|
||||
removeSimpleCollationFromBsonObj(indexSpec[kSpecFieldName].Obj()));
|
||||
}
|
||||
|
||||
// Remove simple collation under 'originalSpec' field
|
||||
if (indexSpec.hasField(IndexDescriptor::kOriginalSpecFieldName)) {
|
||||
bob.append(IndexDescriptor::kOriginalSpecFieldName,
|
||||
removeSimpleCollationFromBsonObj(
|
||||
indexSpec[IndexDescriptor::kOriginalSpecFieldName].Obj()));
|
||||
}
|
||||
|
||||
// Remove top-level simple collation if exists
|
||||
bob.appendElementsUnique(removeSimpleCollationFromBsonObj(indexSpec));
|
||||
|
||||
return bob.obj();
|
||||
}
|
||||
|
||||
Status IndexCatalog::canCreateIndex(OperationContext* opCtx,
|
||||
const CollectionPtr& collection,
|
||||
const BSONObj& indexSpec) const {
|
||||
|
||||
@ -559,7 +559,17 @@ public:
|
||||
* collation spec in cases where the index spec specifies a collation, and will add the
|
||||
* collection-default collation, if present, in cases where collation is omitted. If the index
|
||||
* spec omits the collation and the collection does not have a default, the collation field is
|
||||
* omitted from the spec.
|
||||
* omitted from the spec. Additionally, if the resolved collation is {locale: "simple"}, the
|
||||
* returned normalized spec will not include a collation field.
|
||||
*
|
||||
* Clarification: For a non-normalized index spec, if the collation field is missing, it is
|
||||
* treated as the collection's default collation. For a normalized index spec, if the collation
|
||||
* field is missing, it indicates a simple collation.
|
||||
* TODO (SERVER-119573): Review and update the above paragraph if collation normalization logic
|
||||
* changes.
|
||||
*
|
||||
* WARNING: Do not call this function on an already normalized spec: it may incorrectly apply
|
||||
* the collection’s default collation when a simple collation was explicitly requested.
|
||||
*
|
||||
* This function throws on error.
|
||||
*/
|
||||
@ -570,6 +580,24 @@ public:
|
||||
const CollectionPtr& collection,
|
||||
const std::vector<BSONObj>& indexSpecs);
|
||||
|
||||
/**
|
||||
* Helper function that converts index specs returned by listIndexes into normalized index specs
|
||||
* for storage in the catalog. Index specs from listIndexes always include a collation field,
|
||||
* but normalized specs should omit the collation field when it represents the simple collation
|
||||
* (i.e., {locale: "simple"}).
|
||||
* In summary, this function strips the collation field if it is {locale: "simple"}.
|
||||
* TODO (SERVER-119573): Remove this methods once the catalog stores simple collation
|
||||
* explicitly.
|
||||
*
|
||||
* WARNING: Do not call this function on an already normalized spec: it may incorrectly apply
|
||||
* the collection’s default collation when a simple collation was explicitly requested.
|
||||
*/
|
||||
// TODO (SERVER-122859): Remove this helper function once IndexBuildsCoordinator is updated to
|
||||
// always expect an explicit collation in index specs, including when the collation is simple.
|
||||
static std::vector<BSONObj> normalizeIndexSpecsFromListIndexes(
|
||||
const std::vector<BSONObj>& indexSpecs);
|
||||
static BSONObj normalizeIndexSpecFromListIndexes(const BSONObj& indexSpec);
|
||||
|
||||
/**
|
||||
* Checks if the given index spec could be created in this catalog after normalizing it,
|
||||
* returning an error with a reason if it cannot.
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/query/collation/collator_interface.h"
|
||||
#include "mongo/db/server_feature_flags_gen.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/clustered_collection_options_gen.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/clustered_collection_util.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/collection.h"
|
||||
@ -88,6 +89,11 @@ std::vector<BSONObj> listIndexesInLock(OperationContext* opCtx,
|
||||
const bool convertBucketsIndexesToTimeseriesIndexes =
|
||||
collection->isTimeseriesCollection() && !isRawDataRequest;
|
||||
|
||||
const bool expandSimpleCollation =
|
||||
feature_flags::gFeatureFlagListIndexesAlwaysIncludesSimpleCollation.isEnabled(
|
||||
VersionContext::getDecoration(opCtx),
|
||||
serverGlobalParams.featureCompatibility.acquireFCVSnapshot());
|
||||
|
||||
if (collection->isClustered() && !collection->isTimeseriesCollection()) {
|
||||
BSONObj collation;
|
||||
if (auto collator = collection->getDefaultCollator()) {
|
||||
@ -96,7 +102,8 @@ std::vector<BSONObj> listIndexesInLock(OperationContext* opCtx,
|
||||
auto clusteredSpec = clustered_util::formatClusterKeyForListIndexes(
|
||||
collection->getClusteredInfo().value(),
|
||||
collation,
|
||||
collection->getCollectionOptions().expireAfterSeconds);
|
||||
collection->getCollectionOptions().expireAfterSeconds,
|
||||
expandSimpleCollation);
|
||||
if (additionalInclude == ListIndexesInclude::kIndexBuildInfo) {
|
||||
indexSpecs.push_back(BSON("spec"_sd << clusteredSpec));
|
||||
} else {
|
||||
@ -104,7 +111,7 @@ std::vector<BSONObj> listIndexesInLock(OperationContext* opCtx,
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < indexNames.size(); i++) {
|
||||
auto spec = collection->getIndexSpec(indexNames[i]);
|
||||
auto spec = collection->getIndexSpec(indexNames[i], expandSimpleCollation);
|
||||
if (convertBucketsIndexesToTimeseriesIndexes) {
|
||||
auto timeseriesSpec = timeseries::createTimeseriesIndexFromBucketsIndex(
|
||||
*collection->getTimeseriesOptions(), spec);
|
||||
|
||||
@ -48,6 +48,12 @@ enum class MONGO_MOD_NEEDS_REPLACEMENT ListIndexesInclude { kNothing, kBuildUUID
|
||||
/**
|
||||
* Returns the list of indexes for the given collection.
|
||||
*
|
||||
* Returned index specifications are not normalized: the 'collation' field is always included, even
|
||||
* when its value is simple. In contrast, in normalized index specifications, the simple collation
|
||||
* is typically omitted. For cloning purposes, these index specs can be used directly with a
|
||||
* createIndexes-style command, since normalization will occur automatically on the server. However,
|
||||
* they should not be used to create a new index if the normalization step is bypassed.
|
||||
*
|
||||
* If 'isRawDataRequest' is true, indexes on timeseries collections are returned as raw bucket
|
||||
* indexes (i.e. using internal bucket field names such as {control.min.x: 1, control.max.x: 1})
|
||||
* rather than being translated to their user-visible timeseries form (e.g. {x: 1}).
|
||||
@ -64,6 +70,12 @@ MONGO_MOD_PRIVATE std::vector<BSONObj> listIndexesInLock(
|
||||
*
|
||||
* If the collection does not exist, returns an empty list.
|
||||
*
|
||||
* Returned index specifications are not normalized: the 'collation' field is always included, even
|
||||
* when its value is simple. In contrast, in normalized index specifications, the simple collation
|
||||
* is typically omitted. For cloning purposes, these index specs can be used directly with a
|
||||
* createIndexes-style command, since normalization will occur automatically on the server. However,
|
||||
* they should not be used to create a new index if the normalization step is bypassed.
|
||||
*
|
||||
* If 'isRawDataRequest' is true, indexes on timeseries collections are returned as raw bucket
|
||||
* indexes (i.e. using internal bucket field names such as {control.min.x: 1, control.max.x: 1})
|
||||
* rather than being translated to their user-visible timeseries form (e.g. {x: 1}).
|
||||
|
||||
@ -389,8 +389,19 @@ Status renameCollectionWithinDB(OperationContext* opCtx,
|
||||
// Check target collection indexes match expected.
|
||||
const auto currentIndexes = listIndexesEmptyListIfMissing(
|
||||
opCtx, target, ListIndexesInclude::kNothing, /*isRawDataRequest=*/true);
|
||||
|
||||
// The listIndexes command always includes a 'collation' field in each index spec as of
|
||||
// SERVER-89953, even if it has a simple collation. In contrast, `options.originalIndexes`
|
||||
// are normalized specs, which means that the 'collation' field is omitted when it is a
|
||||
// simple collation. Therefore, we normalize the listIndexes output here to enable direct
|
||||
// comparison between both formats.
|
||||
// TODO (SERVER-119573): Remove this normalization once listIndexes output matches storage
|
||||
// format.
|
||||
const auto normalizedCurrentIndexes =
|
||||
IndexCatalog::normalizeIndexSpecsFromListIndexes(currentIndexes);
|
||||
|
||||
status = checkTargetCollectionIndexesMatch(
|
||||
target, options.originalIndexes.get(), currentIndexes);
|
||||
target, options.originalIndexes.get(), normalizedCurrentIndexes);
|
||||
|
||||
if (!status.isOK()) {
|
||||
return status;
|
||||
|
||||
@ -426,7 +426,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
BSONObj getIndexSpec(StringData indexName) const final {
|
||||
BSONObj getIndexSpec(StringData indexName, bool expandSimpleCollation) const final {
|
||||
return BSONObj();
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user