SERVER-110519: Audit invariant calls in db/query/compiler (#42288)

Co-authored-by: Serhii Lysenko <serhiilysenko@mongodb.com>
GitOrigin-RevId: 2bfc1147af1f580e1faeb378f7d30d8d13548085
This commit is contained in:
Serhii Lysenko 2025-10-09 10:47:34 +01:00 committed by MongoDB Bot
parent f8ab79670b
commit a4a462e03a
21 changed files with 147 additions and 107 deletions

View File

@ -98,40 +98,38 @@ public:
*/
class ProjectionAnalysisVisitor final : public ProjectionASTConstVisitor {
public:
ProjectionAnalysisVisitor(ProjectionDependencies* deps) : _deps(deps) {
invariant(_deps);
}
ProjectionAnalysisVisitor(ProjectionDependencies& deps) : _deps(deps) {}
void visit(const ProjectionPathASTNode* node) final {
if (node->parent()) {
_deps->hasDottedPath = true;
_deps.hasDottedPath = true;
}
}
void visit(const ProjectionPositionalASTNode* node) final {
_deps->requiresMatchDetails = true;
_deps->requiresDocument = true;
_deps.requiresMatchDetails = true;
_deps.requiresDocument = true;
}
void visit(const ProjectionSliceASTNode* node) final {
_deps->requiresDocument = true;
_deps->hasExpressions = true;
_deps.requiresDocument = true;
_deps.hasExpressions = true;
}
void visit(const ProjectionElemMatchASTNode* node) final {
_deps->requiresDocument = true;
_deps->hasExpressions = true;
_deps->containsElemMatch = true;
_deps.requiresDocument = true;
_deps.hasExpressions = true;
_deps.containsElemMatch = true;
}
void visit(const ExpressionASTNode* node) final {
_deps->hasExpressions = true;
_deps.hasExpressions = true;
}
void visit(const BooleanConstantASTNode* node) final {}
void visit(const MatchExpressionASTNode* node) final {}
private:
ProjectionDependencies* _deps;
ProjectionDependencies& _deps;
};
/**
@ -143,13 +141,12 @@ private:
*/
class DepsAnalysisVisitor final : public ProjectionASTConstVisitor {
public:
DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>* context) : _context{context} {
invariant(_context);
}
DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>& context)
: _context{context} {}
void visit(const MatchExpressionASTNode* node) final {
dependency_analysis::addDependencies(&(*node->matchExpression()),
&_context->data().fieldDependencyTracker);
&_context.data().fieldDependencyTracker);
}
void visit(const ProjectionPositionalASTNode* node) final {
@ -170,11 +167,10 @@ public:
void visit(const ExpressionASTNode* node) final {
// The output of an expression on a dotted path depends on whether that field is an array.
invariant(node->parent());
expression::addDependencies(node->expressionRaw(),
&_context->data().fieldDependencyTracker);
tassert(11051954, "Projection AST node is missing parent", node->parent());
expression::addDependencies(node->expressionRaw(), &_context.data().fieldDependencyTracker);
if (_context->fullPath().getPathLength() > 1) {
if (_context.fullPath().getPathLength() > 1) {
// If assigning to a top-level field, the value of that field is not actually required.
// Otherwise, any assignment of an expression to a field requires the first component
// of that field. e.g. {a.b.c: <expression>} will require all of 'a' since it may be an
@ -188,7 +184,7 @@ public:
if (node->value()) {
addFullPathAsDependency();
} else {
_context->data().addExcludedPath(_context->fullPath().fullPath());
_context.data().addExcludedPath(_context.fullPath().fullPath());
}
}
@ -196,25 +192,25 @@ public:
private:
void addTopLevelPathAsDependency() {
const auto& path = _context->fullPath();
const auto& path = _context.fullPath();
_context->data().addRequiredField(std::string{path.front()});
_context.data().addRequiredField(std::string{path.front()});
}
void addFullPathAsDependency() {
const auto& path = _context->fullPath();
const auto& path = _context.fullPath();
_context->data().addRequiredField(path.fullPath());
_context.data().addRequiredField(path.fullPath());
}
PathTrackingVisitorContext<DepsAnalysisData>* _context;
PathTrackingVisitorContext<DepsAnalysisData>& _context;
};
auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) {
ProjectionDependencies deps;
PathTrackingVisitorContext<DepsAnalysisData> context;
DepsAnalysisVisitor depsAnalysisVisitor{&context};
ProjectionAnalysisVisitor projectionAnalysisVisitor{&deps};
DepsAnalysisVisitor depsAnalysisVisitor{context};
ProjectionAnalysisVisitor projectionAnalysisVisitor{deps};
PathTrackingWalker walker{&context, {&depsAnalysisVisitor, &projectionAnalysisVisitor}, {}};
tree_walker::walk<true, projection_ast::ASTNode>(root, &walker);
@ -224,8 +220,7 @@ auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) {
if (type == ProjectType::kInclusion || type == ProjectType::kAddition) {
deps.paths = userData.requiredFields();
} else {
invariant(type == ProjectType::kExclusion);
} else { // type == ProjectType::kExclusion
deps.requiresDocument = true;
deps.paths = std::move(userData.excludedPaths);
}
@ -349,7 +344,7 @@ using MetaFieldVisitorContext = PathTrackingVisitorContext<MetaFieldData>;
*/
class MetaFieldVisitor final : public ProjectionASTConstVisitor {
public:
MetaFieldVisitor(MetaFieldVisitorContext* context) : _context(context) {}
MetaFieldVisitor(MetaFieldVisitorContext& context) : _context(context) {}
void visit(const ExpressionASTNode* node) final {
const auto* metaExpr = dynamic_cast<const ExpressionMeta*>(node->expressionRaw());
@ -357,7 +352,7 @@ public:
return;
}
_context->data().metaPaths.push_back(_context->fullPath());
_context.data().metaPaths.push_back(_context.fullPath());
}
void visit(const ProjectionPositionalASTNode* node) final {}
@ -368,13 +363,13 @@ public:
void visit(const MatchExpressionASTNode* node) final {}
private:
MetaFieldVisitorContext* _context;
MetaFieldVisitorContext& _context;
};
} // namespace
std::vector<FieldPath> Projection::extractSortKeyMetaFields() const {
MetaFieldVisitorContext ctx;
MetaFieldVisitor visitor(&ctx);
MetaFieldVisitor visitor(ctx);
PathTrackingConstWalker<MetaFieldData> walker{&ctx, {&visitor}, {}};
tree_walker::walk<true, ASTNode>(root(), &walker);

View File

@ -111,7 +111,9 @@ public:
}
const OrderedPathSet& getExcludedPaths() const {
invariant(_type == ProjectType::kExclusion);
tassert(11051953,
"Expecting projection to be of exclusion type",
_type == ProjectType::kExclusion);
return *_deps.paths;
}

View File

@ -91,7 +91,10 @@ public:
}
ASTNode* child(size_t index) const {
invariant(index < _children.size());
tassert(11051952,
str::stream() << "Expect child index to be less than " << _children.size()
<< " but got " << index,
index < _children.size());
return _children[index].get();
}
@ -290,7 +293,9 @@ private:
class ProjectionPositionalASTNode final : public ASTNode {
public:
ProjectionPositionalASTNode(std::unique_ptr<MatchExpressionASTNode> child) {
invariant(child);
tassert(11051951,
"Projection positional AST node is missing MatchExpression child node",
child);
addChildToInternalVector(std::move(child));
}
@ -339,7 +344,9 @@ private:
class ProjectionElemMatchASTNode final : public ASTNode {
public:
ProjectionElemMatchASTNode(std::unique_ptr<MatchExpressionASTNode> child) {
invariant(child);
tassert(11051950,
"Projection element match AST node is missing MatchExpression child node",
child);
addChildToInternalVector(std::move(child));
}

View File

@ -53,8 +53,10 @@ public:
PathTrackingVisitorContext(UserData data) : _data{std::move(data)} {}
auto fullPath() const {
invariant(!_fieldNames.empty());
invariant(!_fieldNames.top().empty());
tassert(11051949, "Empty stack of field names", !_fieldNames.empty());
tassert(11051948,
"Empty list of field names to visit at the top of the stack",
!_fieldNames.top().empty());
if (!_basePath) {
return FieldPath(_fieldNames.top().front());
@ -80,7 +82,9 @@ public:
}
void popFieldNames() {
invariant(_fieldNames.top().empty());
tassert(11051947,
"List of field names to visit at the top of the stack is not empty",
_fieldNames.top().empty());
_fieldNames.pop();
}
@ -114,7 +118,7 @@ template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
class PathTrackingPreVisitor final : public ProjectionASTVisitor<IsConst> {
public:
PathTrackingPreVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
invariant(_context);
tassert(11051946, "PathTrackingPreVisitor is missing the context", _context);
}
void visit(tree_walker::MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {
@ -147,7 +151,7 @@ template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
class PathTrackingPostVisitor final : public ProjectionASTVisitor<IsConst> {
public:
PathTrackingPostVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
invariant(_context);
tassert(11051945, "PathTrackingPostVisitor is missing the context", _context);
}
void visit(tree_walker::MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {

View File

@ -212,7 +212,9 @@ BSONObj astToDebugBSON(const ASTNode* root) {
tree_walker::walk<true, projection_ast::ASTNode>(root, &walker);
invariant(context.data().builders.size() == 1);
tassert(11051944,
"Stack of BSONObjBuilders doesn't contain a single element",
context.data().builders.size() == 1);
return context.data().builders.top().obj();
}
@ -223,7 +225,9 @@ BSONObj serialize(const ProjectionPathASTNode& root, const SerializationOptions&
PathTrackingWalker walker{&context, {&preVisitor}, {&postVisitor}};
tree_walker::walk<true, projection_ast::ASTNode>(&root, &walker);
invariant(context.data().builders.size() == 1);
tassert(11051943,
"Stack of BSONObjBuilders doesn't contain a single element",
context.data().builders.size() == 1);
return context.data().builders.top().obj();
}
} // namespace mongo::projection_ast

View File

@ -156,8 +156,10 @@ bool isInclusionOrExclusionType(BSONType type) {
ProjectionPathASTNode* findLastInnerNodeOnPath(ProjectionPathASTNode* root,
const FieldPath& path,
size_t componentIndex) {
invariant(root);
invariant(path.getPathLength() > componentIndex);
tassert(11051942, "Missing root node", root);
tassert(11051941,
"Field path is longer than the size of componentIndex",
path.getPathLength() > componentIndex);
auto child = exact_pointer_cast<ProjectionPathASTNode*>(
root->getChild(path.getFieldName(componentIndex)));
@ -174,8 +176,11 @@ void addNodeAtPathHelper(ProjectionPathASTNode* root,
const FieldPath& path,
size_t componentIndex,
std::unique_ptr<ASTNode> newChild) {
invariant(root);
invariant(path.getPathLength() > componentIndex);
tassert(11051940, "Missing root node", root);
tassert(11051939,
"Field path is longer than the size of componentIndex",
path.getPathLength() > componentIndex);
const auto nextComponent = path.getFieldName(componentIndex);
ASTNode* child = root->getChild(nextComponent);
@ -366,7 +371,6 @@ bool parseSubObjectAsExpression(ParseContext* parseCtx,
// Create a MatchExpression for the elemMatch.
BSONObj elemMatchObj = BSON(path.fullPath() << subObj);
invariant(elemMatchObj.isOwned());
auto matcher = CopyableMatchExpression{elemMatchObj,
parseCtx->expCtx,
@ -443,7 +447,7 @@ void parseInclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode*
// attached to the tree.
CopyableMatchExpression matcher{ctx->queryObj, ctx->query->clone()};
invariant(ctx->query);
tassert(11051938, "ParseContext is missing MatchExpression", ctx->query);
addNodeAtPath(parent,
path,
std::make_unique<ProjectionPositionalASTNode>(
@ -465,7 +469,9 @@ void parseInclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode*
* Treats the given element as an exclusion projection and updates the tree as necessary.
*/
void parseExclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode* parent) {
invariant(!elem.trueValue());
tassert(11051937,
str::stream() << " field " << elem.fieldNameStringData() << " is missing value",
!elem.trueValue());
FieldPath path(elem.fieldNameStringData());
addNodeAtPath(parent, path, std::make_unique<BooleanConstantASTNode>(false));
@ -669,7 +675,6 @@ Projection parseAndAnalyze(boost::intrusive_ptr<ExpressionContext> expCtx,
ctx.idIncludedEntirely,
ctx.idExcludedEntirely);
}
invariant(ctx.type);
if (!ctx.idSpecified) {
if (policies.idPolicy == ProjectionPolicies::DefaultIdPolicy::kIncludeId &&
@ -686,7 +691,7 @@ Projection parseAndAnalyze(boost::intrusive_ptr<ExpressionContext> expCtx,
if (*ctx.type == ProjectType::kExclusion && ctx.idSpecified && ctx.idIncludedEntirely) {
// The user explicitly included _id in an exclusion projection. This is legal syntax, but
// the node indicating that _id is included doesn't need to be in the tree.
invariant(root.removeChild("_id"));
tassert(11051936, "_id field is missing in the projection AST", root.removeChild("_id"));
}
// Optimize the projection expression if requested and as long as not explicitly disabled

View File

@ -89,7 +89,7 @@ BSONElement IndexEntry::getWildcardField() const {
BSONObjIterator it(keyPattern);
BSONElement wildcardElt = it.next();
for (size_t i = 0; i < wildcardFieldPos; ++i) {
invariant(it.more());
tassert(11051935, "the wildcardFieldPos is larger than the keyPattern length", it.more());
wildcardElt = it.next();
}

View File

@ -84,9 +84,10 @@ struct CoreIndexInfo {
collator(ci),
indexPathProjection(indexPathProj),
indexCatalogEntryStorage(std::move(iceStorage)) {
// If a projection executor exists, we always expect a $** index
if (indexPathProjection != nullptr)
invariant(type == IndexType::INDEX_WILDCARD);
tassert(11051934,
"Wildcard index is expected if index path projection is provided",
type == IndexType::INDEX_WILDCARD);
}
virtual ~CoreIndexInfo() = default;
@ -209,8 +210,9 @@ struct MONGO_MOD_NEEDS_REPLACEMENT IndexEntry : CoreIndexInfo {
multikeyPathSet(std::move(multikeyPathSet)),
infoObj(io),
wildcardFieldPos(wildcardPos) {
// The caller must not supply multikey metadata in two different formats.
invariant(this->multikeyPaths.empty() || this->multikeyPathSet.empty());
tassert(11051933,
"Multikey data must not be supplied in two different formats",
this->multikeyPaths.empty() || this->multikeyPathSet.empty());
}
IndexEntry(const IndexEntry&) = default;

View File

@ -81,15 +81,17 @@ void assertOILIsAscendingLocally(const vector<Interval>& intervals, size_t idx)
// Each individual interval being examined should be ascending or none.
const auto dir = intervals[idx].getDirection();
// Should be either ascending, or have no direction (be a point/null/empty interval).
invariant(dir == Interval::Direction::kDirectionAscending ||
dir == Interval::Direction::kDirectionNone);
tassert(11051932,
"Expect interval direction to be either ascending, or have no direction",
dir == Interval::Direction::kDirectionAscending ||
dir == Interval::Direction::kDirectionNone /* point/null/empty interval */);
// The previous OIL's end value should be <= the next OIL's start value.
if (idx > 0) {
// Pass 'false' to avoid comparing the field names.
const int res = intervals[idx - 1].end.woCompare(intervals[idx].start, false);
invariant(res <= 0);
tassert(11051931,
"Expect previous OIL's end value to be not more than the next OIL's start value",
res <= 0);
}
}
@ -190,8 +192,10 @@ IndexBoundsBuilder::BoundsTightness translateWildcardIndexBoundsAndTightness(
// planning, *before* finishWildcardIndexScanNode has been invoked. The IndexEntry should thus
// only have a single keyPattern field and multikeyPath entry, but this is sufficient to
// determine whether it will be necessary to adjust the tightness.
invariant(index.type == IndexType::INDEX_WILDCARD);
invariant(oil);
tassert(11051930,
str::stream() << "Expecting Wildcard index type, got " << index.type,
index.type == IndexType::INDEX_WILDCARD);
tassert(11051929, "Missing OrderedIntervalList parameter", oil);
// If 'oil' was not filled the filter type may not be supported, but we can still use this
// wildcard index for queries on prefix fields. The index bounds for the wildcard field will be
@ -392,9 +396,11 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
namespace {
IndexBoundsBuilder::BoundsTightness computeTightnessForTypeSet(const MatcherTypeSet& typeSet,
const IndexEntry& index) {
// The Array case will not be handled because a typeSet with Array should not reach this
// function
invariant(!typeSet.hasType(BSONType::array));
if (typeSet.hasType(BSONType::array)) {
// The Array case will not be handled because a typeSet with Array should not reach this
// function
MONGO_UNREACHABLE_TASSERT(11051928);
}
// The String and Object types with collation require an inexact fetch.
if (index.collator != nullptr &&
@ -587,8 +593,7 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
OrderedIntervalList* oilOut,
BoundsTightness* tightnessOut,
interval_evaluation_tree::Builder* ietBuilder) {
// We expect that the OIL we are constructing starts out empty.
invariant(oilOut->intervals.empty());
tassert(11051927, "Expect the OIL to start out empty", oilOut->intervals.empty());
oilOut->name = elt.fieldName();
@ -597,8 +602,9 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
isHashed = true;
}
// We should never be asked to translate an unsupported predicate for a hashed index.
invariant(!isHashed || Indexability::nodeIsSupportedByHashedIndex(expr));
tassert(11051926,
"Translating unsupported predicate for a hashed index",
!isHashed || Indexability::nodeIsSupportedByHashedIndex(expr));
if (MatchExpression::ELEM_MATCH_VALUE == expr->matchType()) {
_translatePredicate(expr->getChild(0), elt, index, oilOut, tightnessOut, ietBuilder);
@ -627,8 +633,7 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
// If we have a NOT -> EXISTS, we must handle separately.
if (MatchExpression::EXISTS == child->matchType()) {
// We should never try to use a sparse index for $exists:false.
invariant(!index.sparse);
tassert(11051925, "Expect dense index for $exists:false", !index.sparse);
// {$exists:false} is a point-interval on [null,null] that requires a fetch.
oilOut->intervals.push_back(makeNullPointInterval(isHashed));
*tightnessOut = IndexBoundsBuilder::INEXACT_FETCH;
@ -1182,8 +1187,6 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
ime = clonedInMatchExpr;
}
invariant(ime->isBSONOwned());
// Because we own the BSON buffer for the $in array, this allows us to create Interval
// objects which point directly to this BSON (without having to make copies just to strip
// out the field name as is usually done in IndexBoundsBuilder::translateEquality()).
@ -1287,8 +1290,8 @@ Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, BoundInclusio
// static
void IndexBoundsBuilder::intersectize(const OrderedIntervalList& oilA, OrderedIntervalList* oilB) {
invariant(oilB);
invariant(oilA.name == oilB->name);
tassert(11051924, "missing oilB parameter", oilB);
tassert(11051923, "Expect oilA and oilB to have the same name", oilA.name == oilB->name);
size_t oilAIdx = 0;
const vector<Interval>& oilAIntervals = oilA.intervals;
@ -1611,8 +1614,8 @@ void IndexBoundsBuilder::appendTrailingAllValuesInterval(const Interval& interva
bool endKeyInclusive,
BSONObjBuilder* startBob,
BSONObjBuilder* endBob) {
invariant(startBob);
invariant(endBob);
tassert(11051922, "missing startBob parameter", startBob);
tassert(11051921, "missing endBob parameter", endBob);
// Must be min->max or max->min.
if (interval.isMinToMax()) {

View File

@ -196,10 +196,12 @@ public:
return algebra::walk(child, *this);
}();
invariant(node.index() < static_cast<int>(childOil.intervals.size()));
tassert(11051920,
"Expect the node index to be less than the number of childOil intervals",
node.index() < static_cast<int>(childOil.intervals.size()));
childOil.intervals[0] = childOil.intervals[node.index()];
childOil.intervals.resize(1);
invariant(childOil.isPoint());
tassert(11051919, "Expect the childOil to be a point interval", childOil.isPoint());
return childOil;
}

View File

@ -1336,7 +1336,9 @@ StatusWithMatchExpression parseGeo(boost::optional<StringData> name,
return {std::make_unique<GeoMatchExpression>(
name, gq.release(), section, createAnnotation(expCtx, operatorName, name, section))};
} else {
invariant(PathAcceptingKeyword::GEO_NEAR == type);
tassert(11051918,
"Expect PathAcceptingKeyword to be $geoNear",
PathAcceptingKeyword::GEO_NEAR == type);
if ((allowedFeatures & MatchExpressionParser::AllowedFeatures::kGeoNear) == 0u) {
return {Status(ErrorCodes::Error(5626500),
@ -1418,7 +1420,9 @@ StatusWithMatchExpression parseElemMatch(boost::optional<StringData> name,
bool isElemMatchValue = false;
if (isExpressionDocument(e, true)) {
auto elt = obj.firstElement();
invariant(elt);
if (!elt) {
return {Status(ErrorCodes::BadValue, "$elemMatch needs a non-empty Object")};
}
isElemMatchValue = !retrievePathlessParser(elt.fieldNameStringData().substr(1));
}
@ -1714,7 +1718,7 @@ StatusWithMatchExpression parseSubField(const BSONObj& context,
const ExtensionsCallback* extensionsCallback,
MatchExpressionParser::AllowedFeatureSet allowedFeatures,
DocumentParseLevel currentLevel) {
invariant(e);
tassert(11051917, "Missing match expression parameter", e);
if ("$not"_sd == e.fieldNameStringData()) {
return parseNot(name, e, expCtx, extensionsCallback, allowedFeatures, currentLevel);

View File

@ -148,7 +148,9 @@ std::unique_ptr<MatchExpression> makeRestriction(
boost::optional<StringData> path,
std::unique_ptr<MatchExpression> restrictionExpr,
InternalSchemaTypeExpression* statedType) {
invariant(restrictionType.isSingleType());
tassert(11051916,
"Expecting restriction type set to contain a single type",
restrictionType.isSingleType());
if (statedType && statedType->typeSet().isSingleType()) {
// Use NumberInt in the "number" case as a stand-in.
@ -866,7 +868,8 @@ StatusWithMatchExpression translateSchemaDependency(
BSONElement dependency,
AllowedFeatureSet allowedFeatures,
bool ignoreUnknownKeywords) {
invariant(dependency.type() == BSONType::object);
tassert(
11051915, "Expect dependency bson to be an object", dependency.type() == BSONType::object);
auto nestedSchemaMatch =
_parse(expCtx, path, dependency.embeddedObject(), allowedFeatures, ignoreUnknownKeywords);
@ -898,7 +901,8 @@ StatusWithMatchExpression translatePropertyDependency(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
boost::optional<StringData> path,
BSONElement dependency) {
invariant(dependency.type() == BSONType::array);
tassert(
11051914, "Expect dependency bson to be an array", dependency.type() == BSONType::array);
if (dependency.embeddedObject().isEmpty()) {
return {ErrorCodes::FailedToParse,

View File

@ -915,7 +915,9 @@ IndexBoundsChecker::Location IndexBoundsChecker::findIntervalForField(
// Additional check to determine if interval contains key.
Location where = intervalCmp(*i, elt, expectedDirection);
invariant(BEHIND == where || WITHIN == where);
tassert(11051913,
"Expect the element to be either behind, or within the interval",
BEHIND == where || WITHIN == where);
return where;
}

View File

@ -89,7 +89,7 @@ Interval::Direction Interval::getDirection() const {
// 'false' to not consider the field name.
const int res = start.woCompare(end, false);
invariant(res != 0);
tassert(11051912, "Calling getDirection on a point interval", res != 0);
return res < 0 ? Direction::kDirectionAscending : Direction::kDirectionDescending;
}
@ -121,7 +121,7 @@ bool Interval::equals(const Interval& other) const {
}
bool Interval::intersects(const Interval& other) const {
if (kDebugBuild) {
if constexpr (kDebugBuild) {
// This function assumes that both intervals are ascending (or are empty/point intervals).
// Determining this may be expensive, so we only do these checks when in a debug build.
const auto thisDir = getDirection();

View File

@ -845,9 +845,10 @@ std::set<StringData> IndexScanNode::getFieldsWithStringBounds(const IndexBounds&
}
std::set<StringData> ret;
invariant(bounds.fields.size() == static_cast<size_t>(indexKeyPattern.nFields()));
tassert(11051911,
"Expect the number of input bounds to match the number of fields in index key pattern",
bounds.fields.size() == static_cast<size_t>(indexKeyPattern.nFields()));
for (const auto& oil : bounds.fields) {
invariant(keyPatternIterator.more());
BSONElement el = keyPatternIterator.next();
OrderedIntervalList intersection = buildStringBoundsOil(el.fieldName());
IndexBoundsBuilder::intersectize(oil, &intersection);
@ -924,7 +925,9 @@ bool confirmBoundsProvideSortComponentGivenMultikeyness(
// This is because if they are equal and do not have [minKey, maxKey] bounds, we would
// already have bailed out of the function. If they do have [minKey, maxKey] bounds,
// they will be skipped in the check for [minKey, maxKey] bounds above.
invariant(refName != boundsPath);
if (refName == boundsPath) {
MONGO_UNREACHABLE_TASSERT(11051910);
}
// Checks if there's a common prefix between the interval list name and the sort pattern
// name.
if (commonPrefixSize > 0) {
@ -1360,7 +1363,7 @@ void ProjectionNode::appendToString(str::stream* ss, int indent) const {
}
void ProjectionNode::computeProperties() {
invariant(children.size() == 1U);
tassert(11051909, "Expect projection to have 1 query solution", children.size() == 1U);
children[0]->computeProperties();
// Our input sort is not necessarily maintained if we project some fields that are part of the

View File

@ -684,7 +684,7 @@ std::unique_ptr<MatchExpression> inOptimizer(std::unique_ptr<MatchExpression> ex
if (regexes.size() == 1 && ime.equalitiesIsEmpty()) {
// Simplify IN of exactly one regex to be a regex match.
auto& childRe = regexes.front();
invariant(!childRe->getTag());
tassert(11051908, "Expect RegexMatchExpression to be untagged", !childRe->getTag());
auto simplifiedExpression = std::make_unique<RegexMatchExpression>(
expression->path(), childRe->getString(), childRe->getFlags());

View File

@ -275,7 +275,8 @@ class MatchExpressionParameterizationVisitor final : public MatchExpressionMutab
public:
MatchExpressionParameterizationVisitor(MatchExpressionParameterizationVisitorContext* context)
: _context{context} {
invariant(_context);
tassert(
11051907, "MatchExpressionParameterizationVisitor is missing the context", _context);
}
void visit(AlwaysFalseMatchExpression* expr) final {}
@ -356,7 +357,7 @@ class MatchExpressionParameterizationWalker {
public:
MatchExpressionParameterizationWalker(MatchExpressionParameterizationVisitor* visitor)
: _visitor{visitor} {
invariant(_visitor);
tassert(11051906, "MatchExpressionParameterizationWalker is missing the visitor", _visitor);
}
void preVisit(MatchExpression* expr) {

View File

@ -177,7 +177,7 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
}
const auto& operandList = expr->getOperandList();
invariant(operandList.size() == 2);
tassert(11051905, "Expect comparison expression to have 2 operands", operandList.size() == 2);
ExpressionFieldPath* lhs{nullptr};
ExpressionConstant* rhs{nullptr};
@ -186,11 +186,14 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
// Extract left-hand side and right-hand side MatchExpression components.
if ((lhs = dynamic_cast<ExpressionFieldPath*>(operandList[0].get()))) {
rhs = dynamic_cast<ExpressionConstant*>(operandList[1].get());
invariant(rhs);
tassert(11051904, "Expect rhs operand to be of type ExpressionConstant", rhs);
} else {
lhs = dynamic_cast<ExpressionFieldPath*>(operandList[1].get());
rhs = dynamic_cast<ExpressionConstant*>(operandList[0].get());
invariant(lhs && rhs);
tassert(11051903,
"Expect lhs and rhs operands to be of types ExpressionFieldPath and "
"ExpressionConstant respectively",
lhs && rhs);
// The MatchExpression is normalized so that the field path expression is on the left. For
// cases like {$gt: [1, "$x"]} where the order of the child expressions matter, we also
@ -334,7 +337,7 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteInExpression(
const boost::intrusive_ptr<ExpressionIn>& expr) {
const auto& operandList = expr->getOperandList();
invariant(operandList.size() == 2);
tassert(11051902, "Expect InExpression to have 2 operands", operandList.size() == 2);
auto lhs = operandList[0].get();
auto rhs = operandList[1].get();

View File

@ -62,7 +62,7 @@ StatsCache::LookupResult StatsCache::_lookupStats(OperationContext* opCtx,
const StatsCacheValueHandle& stats) {
try {
invariant(_statsCacheLoader);
tassert(11051901, "Expecting stats cache loader to be provided", _statsCacheLoader);
auto newStats = _statsCacheLoader->getStats(opCtx, statsPath).get();
return LookupResult(std::move(newStats));
} catch (const DBException& ex) {

View File

@ -66,7 +66,7 @@ public:
* Returns statsCacheLoader currently used for testing only.
*/
StatsCacheLoader* getStatsCacheLoader() {
invariant(_statsCacheLoader);
tassert(11051900, "Expecting stats cache loader to be provided", _statsCacheLoader);
return _statsCacheLoader.get();
}

View File

@ -573,7 +573,6 @@ absl::flat_hash_map<sbe::value::TypeTags, sbe::value::TypeTags> nextTypeTagsMap(
// Skips the last one as there is no next type.
sbe::value::TypeTags nextTag = kTypeTagsSorted[kTypeTagsSorted.size() - 1];
invariant(!kTypeTagsSorted.empty());
for (int32_t index = kTypeTagsSorted.size() - 2; index >= 0; --index) {
auto tag = kTypeTagsSorted[index];
nextTypeTagsMap[tag] = nextTag;