mirror of https://github.com/mongodb/mongo
101 lines
4.1 KiB
JavaScript
101 lines
4.1 KiB
JavaScript
import {
|
|
getCollectionName,
|
|
getCommandName,
|
|
getExplainCommand,
|
|
getInnerCommand,
|
|
isInternalDbName,
|
|
isSystemCollectionName,
|
|
} from "jstests/libs/cmd_object_utils.js";
|
|
import {OverrideHelpers} from "jstests/libs/override_methods/override_helpers.js";
|
|
import {everyWinningPlan, getNestedProperties, isIdhackOrExpress} from "jstests/libs/query/analyze_plan.js";
|
|
import {QuerySettingsIndexHintsTests} from "jstests/libs/query/query_settings_index_hints_tests.js";
|
|
import {QuerySettingsUtils} from "jstests/libs/query/query_settings_utils.js";
|
|
|
|
/**
|
|
* Override which applies 'bad' query settings over supported commands in order to test the fallback
|
|
* mechanism. Asserts that the query plans generated by the fallback should be identical to those
|
|
* generated without any query settings.
|
|
*/
|
|
function runCommandOverride(conn, dbName, _cmdName, cmdObj, clientFunction, makeFuncArgs) {
|
|
let resultWithQuerySettings = undefined;
|
|
const computeAndStoreResult = () => {
|
|
resultWithQuerySettings = clientFunction.apply(conn, makeFuncArgs(cmdObj));
|
|
};
|
|
|
|
const assertFallbackPlanMatchesOriginalPlan = () => {
|
|
// Initialize the 'db' instance.
|
|
const db = (() => {
|
|
if (isInternalDbName(dbName)) {
|
|
// Query settings cannot be set over internal databases.
|
|
return undefined;
|
|
}
|
|
try {
|
|
return conn.getDB(dbName);
|
|
} catch (e) {
|
|
// Bail when faced with 'getDB()' exceptions, such as invalid database names.
|
|
return undefined;
|
|
}
|
|
})();
|
|
if (!db) {
|
|
return;
|
|
}
|
|
|
|
const innerCmd = getInnerCommand(cmdObj);
|
|
if (!QuerySettingsUtils.isSupportedCommand(getCommandName(innerCmd))) {
|
|
return;
|
|
}
|
|
|
|
const explain = db.runCommand(getExplainCommand(innerCmd));
|
|
if (!explain.ok) {
|
|
// Some commands such as $collStats cannot be explained and will lead to failures.
|
|
return;
|
|
}
|
|
|
|
if (explain.hasOwnProperty("warning")) {
|
|
// The explain output exceeded the 16MB BSON size limit and was truncated.
|
|
return;
|
|
}
|
|
|
|
// If the query explain has no 'winningPlan', we can not assert for query settings
|
|
// fallback.
|
|
if (getNestedProperties(explain, "winningPlan").length === 0) {
|
|
return;
|
|
}
|
|
|
|
const isIdHackQuery = everyWinningPlan(explain, (winningPlan) => isIdhackOrExpress(db, winningPlan));
|
|
if (isIdHackQuery) {
|
|
// Query settings cannot be applied over IDHACK or Express queries.
|
|
return;
|
|
}
|
|
|
|
const collectionName = getCollectionName(db, innerCmd);
|
|
if (!collectionName || isSystemCollectionName(collectionName)) {
|
|
// Can't test the fallback on queries not involving any collections or queries targeting
|
|
// the system collection:
|
|
// - Queries not involving any collections will always yield to EOF plans.
|
|
// - Query settings validate against queries targeting system collections.
|
|
return;
|
|
}
|
|
|
|
if (innerCmd.hasOwnProperty("rawData")) {
|
|
// Query settings can't be applied on rawData queries
|
|
return;
|
|
}
|
|
|
|
const ns = {db: dbName, coll: collectionName};
|
|
const qsutils = new QuerySettingsUtils(db, collectionName);
|
|
qsutils.onSetQuerySettings(computeAndStoreResult);
|
|
const qstests = new QuerySettingsIndexHintsTests(qsutils);
|
|
const representativeQuery = qsutils.makeQueryInstance(innerCmd);
|
|
qstests.assertQuerySettingsFallback(representativeQuery, ns, explain);
|
|
};
|
|
OverrideHelpers.withPreOverrideRunCommand(assertFallbackPlanMatchesOriginalPlan);
|
|
return resultWithQuerySettings || clientFunction.apply(conn, makeFuncArgs(cmdObj));
|
|
}
|
|
|
|
// Override the default runCommand with our custom version.
|
|
OverrideHelpers.overrideRunCommand(runCommandOverride);
|
|
|
|
// Always apply the override if a test spawns a parallel shell.
|
|
OverrideHelpers.prependOverrideInParallelShell("jstests/libs/override_methods/implicit_query_settings_fallback.js");
|