SERVER-83102: Make existing jstest work with the new CQF explain (core/aggregation) (3)

This commit is contained in:
Matt Olma 2023-11-30 00:30:53 +00:00 committed by Evergreen Agent
parent 101e6b16ab
commit 418bc67e60
6 changed files with 81 additions and 39 deletions

View File

@ -5,11 +5,13 @@
//
// Relies on the pipeline stages to be collapsed into a single $cursor stage, so pipelines cannot be
// wrapped into a facet stage to not prevent this optimization. Also, this test is not prepared to
// handle explain output for sharded collections.
// This test makes assumptions about how the explain output will be formatted, so cannot be run when
// pipeline optimization is disabled.
// handle explain output for sharded collections. The assertions made in this test are irrelevant
// for CQF, since it has no concept of a "query layer" or "aggregation layer". This test makes
// assumptions about how the explain output will be formatted, so cannot be run when pipeline
// optimization is disabled.
// @tags: [
// assumes_unsharded_collection,
// cqf_incompatible,
// do_not_wrap_aggregations_in_facets,
// requires_pipeline_optimization,
// requires_profiling,

View File

@ -6,6 +6,7 @@
// ]
import {orderedArrayEq} from "jstests/aggregation/extras/utils.js";
import {
getOptimizer,
getWinningPlan,
isAggregationPlan,
isQueryPlan,
@ -56,6 +57,10 @@ function assertResultsMatch({
result = getWinningPlan(explain.stages[0].$cursor.queryPlanner);
}
let optimizer = getOptimizer(explain);
switch (optimizer) {
case "classic": {
// Check that $project uses the query system.
assert.eq(expectProjectToCoalesce,
planHasStage(db, result, "PROJECTION_DEFAULT") ||
@ -70,6 +75,14 @@ function assertResultsMatch({
assert.neq(removedProjectStage, stage["$project"], explain);
});
}
break;
}
case "CQF": {
// TODO SERVER-77719: Implement the assertion for projection optimization rules for
// CQF.
break;
}
}
}
// Again without an index.

View File

@ -1,7 +1,10 @@
/**
* Test that $unionWith's pipeline argument returns the same explain as an equivalent normal
* pipeline.
* pipeline. The assertions in this test assume that the optimizer for $unionWith queries is
* the same as the optimizer for the "normal" pipeline. This assumption is not strictly true when
* CQF is enabled.
* @tags: [
* cqf_incompatible,
* do_not_wrap_aggregations_in_facets,
* ]
*/

View File

@ -9,6 +9,7 @@
// ]
import {
aggPlanHasStage,
getOptimizer,
hasRejectedPlans,
isAggregationPlan,
isQueryPlan,
@ -24,36 +25,55 @@ for (let i = 0; i < 100; ++i) {
}
assert.commandWorked(bulk.execute());
function assertQueryCoversProjection(
{pipeline = [], pipelineOptimizedAway = true, options = {}} = {}) {
function assertQueryCoversProjection({pipeline = [], options = {}} = {}) {
const explainOutput = coll.explain().aggregate(pipeline, options);
const optimizer = getOptimizer(explainOutput);
if (pipelineOptimizedAway) {
assert(isQueryPlan(explainOutput), explainOutput);
assert(!planHasStage(db, explainOutput, "FETCH"), explainOutput);
assert(planHasStage(db, explainOutput, "IXSCAN"), explainOutput);
} else {
assert(isAggregationPlan(explainOutput), explainOutput);
assert(!aggPlanHasStage(explainOutput, "FETCH"), explainOutput);
assert(aggPlanHasStage(explainOutput, "IXSCAN"), explainOutput);
// TODO SERVER-77719: Ensure that all stages are defined for all optimizers.
let stage = {"classic": "FETCH"};
if (stage[optimizer]) {
assert(!planHasStage(db, explainOutput, stage[optimizer]), explainOutput);
}
// TODO SERVER-77719: Ensure that all stages are defined for all optimizers.
stage = {"classic": "IXSCAN"};
if (stage[optimizer]) {
assert(planHasStage(db, explainOutput, stage[optimizer]), explainOutput);
}
switch (optimizer) {
case "classic":
assert(!hasRejectedPlans(explainOutput), explainOutput);
break;
case "CQF":
// TODO SERVER-77719: Address the existence of rejected plans in CQF.
break;
}
return explainOutput;
}
function assertQueryDoesNotCoverProjection({pipeline = [], pipelineOptimizedAway = true} = {}) {
function assertQueryDoesNotCoverProjection({pipeline = []} = {}) {
const explainOutput = coll.explain().aggregate(pipeline);
const optimizer = getOptimizer(explainOutput);
if (pipelineOptimizedAway) {
assert(isQueryPlan(explainOutput), explainOutput);
assert(planHasStage(db, explainOutput, "FETCH") || aggPlanHasStage("COLLSCAN"),
explainOutput);
} else {
assert(isAggregationPlan(explainOutput), explainOutput);
assert(aggPlanHasStage(explainOutput, "FETCH") || aggPlanHasStage("COLLSCAN"),
// TODO SERVER-77719: Ensure that all stages are defined for all optimizers.
let stage1 = {"classic": "FETCH"};
let stage2 = {"classic": "COLLSCAN"};
if (stage1[optimizer] && stage2[optimizer]) {
assert(planHasStage(db, explainOutput, stage1[optimizer]) ||
aggPlanHasStage(stage2[optimizer]),
explainOutput);
}
switch (optimizer) {
case "classic":
assert(!hasRejectedPlans(explainOutput), explainOutput);
break;
case "CQF":
// TODO SERVER-77719: Address the existence of rejected plans in CQF.
break;
}
return explainOutput;
}

View File

@ -193,6 +193,7 @@ export function getAllPlanStages(root) {
* Asserts that no more than one stage is a match.
*/
export function getPlanStage(root, stage) {
assert(stage, "Stage was not defined in getPlanStage.")
var planStageList = getPlanStages(root, stage);
if (planStageList.length === 0) {
@ -341,6 +342,7 @@ export function getShardQueryPlans(root) {
* structure matches expected format.
*/
export function getAggPlanStages(root, stage, useQueryPlannerSection = false) {
assert(stage, "Stage was not defined in getAggPlanStages.");
let results = [];
function getDocumentSources(docSourceArray) {
@ -434,6 +436,7 @@ export function getAggPlanStages(root, stage, useQueryPlannerSection = false) {
* will be used to lookup the given 'stage', even if 'executionStats' section is available.
*/
export function getAggPlanStage(root, stage, useQueryPlannerSection = false) {
assert(stage, "Stage was not defined in getAggPlanStage.")
let planStageList = getAggPlanStages(root, stage, useQueryPlannerSection);
if (planStageList.length === 0) {
@ -465,6 +468,7 @@ export function aggPlanHasStage(root, stage) {
* on one node's query plan, an error will be thrown.
*/
export function planHasStage(db, root, stage) {
assert(stage, "Stage was not defined in planHasStage.")
const matchingStages = getPlanStages(root, stage);
// If we are executing against a mongos, we may get more than one occurrence of the stage.