summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2022-11-09 15:45:20 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-11-09 16:27:00 +0000
commit805f480d33180f92e6f6d5fe77bec3d0da10b6df (patch)
tree24ac8a1cb97eb97efd246a66459b43c105c70b1c
parent7981ec64d28c49dd2f41d466f7b6bfaccad66fa9 (diff)
downloadmongo-805f480d33180f92e6f6d5fe77bec3d0da10b6df.tar.gz
SERVER-70965 [CQF] Strong alias for ProjectionNames and FieldNames
-rw-r--r--src/mongo/db/exec/sbe/abt/abt_lower.cpp10
-rw-r--r--src/mongo/db/exec/sbe/abt/abt_lower.h2
-rw-r--r--src/mongo/db/pipeline/abt/abt_translate_cq_bm.cpp2
-rw-r--r--src/mongo/db/pipeline/abt/abt_translate_pipeline_bm.cpp2
-rw-r--r--src/mongo/db/pipeline/abt/agg_expression_visitor.cpp27
-rw-r--r--src/mongo/db/pipeline/abt/agg_expression_visitor.h4
-rw-r--r--src/mongo/db/pipeline/abt/algebrizer_context.h4
-rw-r--r--src/mongo/db/pipeline/abt/collation_translation.cpp3
-rw-r--r--src/mongo/db/pipeline/abt/document_source_visitor.cpp65
-rw-r--r--src/mongo/db/pipeline/abt/expr_algebrizer_context.cpp22
-rw-r--r--src/mongo/db/pipeline/abt/expr_algebrizer_context.h16
-rw-r--r--src/mongo/db/pipeline/abt/field_map_builder.cpp24
-rw-r--r--src/mongo/db/pipeline/abt/field_map_builder.h8
-rw-r--r--src/mongo/db/pipeline/abt/match_expression_visitor.cpp14
-rw-r--r--src/mongo/db/pipeline/abt/match_expression_visitor.h4
-rw-r--r--src/mongo/db/pipeline/abt/transformer_visitor.cpp14
-rw-r--r--src/mongo/db/pipeline/abt/transformer_visitor.h2
-rw-r--r--src/mongo/db/pipeline/abt/utils.cpp6
-rw-r--r--src/mongo/db/pipeline/abt/utils.h2
-rw-r--r--src/mongo/db/query/ce/ce_dataflow_nodes_test.cpp2
-rw-r--r--src/mongo/db/query/ce/ce_heuristic.cpp2
-rw-r--r--src/mongo/db/query/ce/ce_histogram_test.cpp11
-rw-r--r--src/mongo/db/query/cqf_get_executor.cpp2
-rw-r--r--src/mongo/db/query/optimizer/cascades/enforcers.cpp2
-rw-r--r--src/mongo/db/query/optimizer/cascades/implementers.cpp29
-rw-r--r--src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp20
-rw-r--r--src/mongo/db/query/optimizer/cascades/logical_rewriter.h2
-rw-r--r--src/mongo/db/query/optimizer/defs.cpp6
-rw-r--r--src/mongo/db/query/optimizer/defs.h35
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp84
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.cpp24
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.h4
-rw-r--r--src/mongo/db/query/optimizer/metadata.h2
-rw-r--r--src/mongo/db/query/optimizer/metadata_factory.cpp2
-rw-r--r--src/mongo/db/query/optimizer/node.cpp13
-rw-r--r--src/mongo/db/query/optimizer/node.h5
-rw-r--r--src/mongo/db/query/optimizer/opt_phase_manager.cpp13
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp6
-rw-r--r--src/mongo/db/query/optimizer/props.cpp2
-rw-r--r--src/mongo/db/query/optimizer/reference_tracker.cpp17
-rw-r--r--src/mongo/db/query/optimizer/reference_tracker.h8
-rw-r--r--src/mongo/db/query/optimizer/rewrites/path.cpp4
-rw-r--r--src/mongo/db/query/optimizer/rewrites/path_lower.cpp68
-rw-r--r--src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp18
-rw-r--r--src/mongo/db/query/optimizer/syntax/expr.cpp2
-rw-r--r--src/mongo/db/query/optimizer/syntax/expr.h16
-rw-r--r--src/mongo/db/query/optimizer/syntax/path.h25
-rw-r--r--src/mongo/db/query/optimizer/syntax/syntax.h11
-rw-r--r--src/mongo/db/query/optimizer/utils/abt_compare.cpp13
-rw-r--r--src/mongo/db/query/optimizer/utils/abt_hash.cpp37
-rw-r--r--src/mongo/db/query/optimizer/utils/reftracker_utils.cpp112
-rw-r--r--src/mongo/db/query/optimizer/utils/reftracker_utils.h3
-rw-r--r--src/mongo/db/query/optimizer/utils/strong_string_alias.h116
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.cpp48
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.h18
-rw-r--r--src/mongo/db/query/sbe_stage_builder.cpp2
56 files changed, 617 insertions, 398 deletions
diff --git a/src/mongo/db/exec/sbe/abt/abt_lower.cpp b/src/mongo/db/exec/sbe/abt/abt_lower.cpp
index deeedabe492..75eb70d21e5 100644
--- a/src/mongo/db/exec/sbe/abt/abt_lower.cpp
+++ b/src/mongo/db/exec/sbe/abt/abt_lower.cpp
@@ -849,18 +849,18 @@ void SBENodeLowering::generateSlots(const FieldProjectionMap& fieldProjectionMap
boost::optional<sbe::value::SlotId>& rootSlot,
std::vector<std::string>& fields,
sbe::value::SlotVector& vars) {
- if (!fieldProjectionMap._ridProjection.empty()) {
+ if (const auto& projName = fieldProjectionMap._ridProjection) {
ridSlot = _slotIdGenerator.generate();
- _slotMap.emplace(fieldProjectionMap._ridProjection, ridSlot.value());
+ _slotMap.emplace(*projName, ridSlot.value());
}
- if (!fieldProjectionMap._rootProjection.empty()) {
+ if (const auto& projName = fieldProjectionMap._rootProjection) {
rootSlot = _slotIdGenerator.generate();
- _slotMap.emplace(fieldProjectionMap._rootProjection, rootSlot.value());
+ _slotMap.emplace(*projName, rootSlot.value());
}
for (const auto& [fieldName, projectionName] : fieldProjectionMap._fieldProjections) {
vars.push_back(_slotIdGenerator.generate());
_slotMap.emplace(projectionName, vars.back());
- fields.push_back(fieldName);
+ fields.push_back(fieldName.value().toString());
}
}
diff --git a/src/mongo/db/exec/sbe/abt/abt_lower.h b/src/mongo/db/exec/sbe/abt/abt_lower.h
index 819bb8321ca..dfb95048598 100644
--- a/src/mongo/db/exec/sbe/abt/abt_lower.h
+++ b/src/mongo/db/exec/sbe/abt/abt_lower.h
@@ -36,7 +36,7 @@
#include "mongo/db/query/optimizer/utils/utils.h"
namespace mongo::optimizer {
-using SlotVarMap = stdx::unordered_map<std::string, sbe::value::SlotId>;
+using SlotVarMap = stdx::unordered_map<ProjectionName, sbe::value::SlotId, ProjectionName::Hasher>;
class SBEExpressionLowering {
public:
diff --git a/src/mongo/db/pipeline/abt/abt_translate_cq_bm.cpp b/src/mongo/db/pipeline/abt/abt_translate_cq_bm.cpp
index 8603ea2d7c0..3a5d0de8df7 100644
--- a/src/mongo/db/pipeline/abt/abt_translate_cq_bm.cpp
+++ b/src/mongo/db/pipeline/abt/abt_translate_cq_bm.cpp
@@ -59,7 +59,7 @@ public:
Metadata metadata{{}};
PrefixId prefixId;
- std::string scanProjName = prefixId.getNextId("scan");
+ ProjectionName scanProjName{prefixId.getNextId("scan")};
auto findCommand = std::make_unique<FindCommandRequest>(nss);
findCommand->setFilter(matchSpec);
diff --git a/src/mongo/db/pipeline/abt/abt_translate_pipeline_bm.cpp b/src/mongo/db/pipeline/abt/abt_translate_pipeline_bm.cpp
index f878c494c13..842122740bb 100644
--- a/src/mongo/db/pipeline/abt/abt_translate_pipeline_bm.cpp
+++ b/src/mongo/db/pipeline/abt/abt_translate_pipeline_bm.cpp
@@ -65,7 +65,7 @@ public:
Metadata metadata{{}};
PrefixId prefixId;
- std::string scanProjName = prefixId.getNextId("scan");
+ ProjectionName scanProjName{prefixId.getNextId("scan")};
std::unique_ptr<Pipeline, PipelineDeleter> parsedPipeline =
Pipeline::parse(pipeline, expCtx);
diff --git a/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp b/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp
index 7074915dc16..737671c4c7f 100644
--- a/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp
+++ b/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp
@@ -256,11 +256,11 @@ public:
ABT path = translateFieldPath(
fieldPath,
make<PathIdentity>(),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
if (!isLastElement) {
input = make<PathTraverse>(std::move(input), PathTraverse::kUnlimited);
}
- return make<PathGet>(fieldName, std::move(input));
+ return make<PathGet>(std::move(fieldName), std::move(input));
},
1ul);
@@ -272,7 +272,7 @@ public:
uassert(6624427,
"Filter variable must be user-defined.",
Variables::isUserDefinedVariable(varId));
- const std::string& varName = generateVariableName(varId);
+ const ProjectionName varName{generateVariableName(varId)};
_ctx.ensureArity(2);
ABT filter = _ctx.pop();
@@ -856,10 +856,13 @@ private:
_ctx.push(std::move(current));
}
- std::string generateVariableName(const Variables::Id varId) {
- std::ostringstream os;
- os << _ctx.getUniqueIdPrefix() << "_var_" << varId;
- return os.str();
+ ProjectionName generateVariableName(const Variables::Id varId) {
+ str::stream os;
+ if (const auto& projName = _ctx.getUniqueIdPrefix()) {
+ os << *projName << "_";
+ }
+ os << "var_" << varId;
+ return ProjectionName{os};
}
void unsupportedExpression(const char* op) const {
@@ -884,10 +887,12 @@ private:
};
ABT generateAggExpression(const Expression* expr,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix) {
- ExpressionAlgebrizerContext ctx(
- true /*assertExprSort*/, false /*assertPathSort*/, rootProjection, uniqueIdPrefix);
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix) {
+ ExpressionAlgebrizerContext ctx(true /*assertExprSort*/,
+ false /*assertPathSort*/,
+ rootProjection,
+ std::move(uniqueIdPrefix));
ABTAggExpressionVisitor visitor(ctx);
AggExpressionWalker walker(&visitor);
diff --git a/src/mongo/db/pipeline/abt/agg_expression_visitor.h b/src/mongo/db/pipeline/abt/agg_expression_visitor.h
index b0300dceb63..2971e5d22b1 100644
--- a/src/mongo/db/pipeline/abt/agg_expression_visitor.h
+++ b/src/mongo/db/pipeline/abt/agg_expression_visitor.h
@@ -36,7 +36,7 @@
namespace mongo::optimizer {
ABT generateAggExpression(const Expression* expr,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix);
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix);
} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/algebrizer_context.h b/src/mongo/db/pipeline/abt/algebrizer_context.h
index c85c7dde0c7..da4034104f2 100644
--- a/src/mongo/db/pipeline/abt/algebrizer_context.h
+++ b/src/mongo/db/pipeline/abt/algebrizer_context.h
@@ -70,8 +70,8 @@ public:
return _node;
}
- std::string getNextId(const std::string& key) {
- return _prefixId.getNextId(key);
+ ProjectionName getNextId(const std::string& prefix) {
+ return _prefixId.getNextId(prefix);
}
PrefixId& getPrefixId() {
diff --git a/src/mongo/db/pipeline/abt/collation_translation.cpp b/src/mongo/db/pipeline/abt/collation_translation.cpp
index 56bc3345fe7..aa804830324 100644
--- a/src/mongo/db/pipeline/abt/collation_translation.cpp
+++ b/src/mongo/db/pipeline/abt/collation_translation.cpp
@@ -45,7 +45,8 @@ void generateCollationNode(AlgebrizerContext& ctx, const SortPattern& sortPatter
const FieldPath& fieldPath = part.fieldPath.value();
ABT sortPath = make<PathIdentity>();
for (size_t j = 0; j < fieldPath.getPathLength(); j++) {
- sortPath = make<PathGet>(fieldPath.getFieldName(j).toString(), std::move(sortPath));
+ sortPath = make<PathGet>(FieldNameType{fieldPath.getFieldName(j).toString()},
+ std::move(sortPath));
}
ctx.setNode<EvaluationNode>(
diff --git a/src/mongo/db/pipeline/abt/document_source_visitor.cpp b/src/mongo/db/pipeline/abt/document_source_visitor.cpp
index fef377ca443..32727852f3f 100644
--- a/src/mongo/db/pipeline/abt/document_source_visitor.cpp
+++ b/src/mongo/db/pipeline/abt/document_source_visitor.cpp
@@ -132,7 +132,7 @@ public:
std::vector<FieldNameType> groupByFieldNames;
for (const auto& [fieldName, expr] : idFields) {
- groupByFieldNames.push_back(fieldName);
+ groupByFieldNames.push_back(FieldNameType{fieldName});
}
const bool isSingleIdField =
groupByFieldNames.size() == 1 && groupByFieldNames.front() == "_id";
@@ -147,7 +147,7 @@ public:
groupByProjNames.push_back(groupByProjName);
ABT groupByExpr = generateAggExpression(
- idFields.at(fieldName).get(), entry._rootProjection, groupByProjName);
+ idFields.at(fieldName.value()).get(), entry._rootProjection, groupByProjName);
_ctx.setNode<EvaluationNode>(entry._rootProjection,
groupByProjName,
@@ -157,7 +157,7 @@ public:
}
// Fields corresponding to each accumulator
- ProjectionNameVector aggProjFieldNames;
+ std::vector<FieldNameType> aggProjFieldNames;
// Projection names corresponding to each high-level accumulator ($avg can be broken down
// into sum and count.).
ProjectionNameVector aggOutputProjNames;
@@ -177,10 +177,10 @@ public:
std::vector<AvgProjNames> avgProjNames;
for (const AccumulationStatement& stmt : accumulatedFields) {
- const FieldNameType& fieldName = stmt.fieldName;
+ const FieldNameType fieldName{stmt.fieldName};
aggProjFieldNames.push_back(fieldName);
- ProjectionName aggOutputProjName = _ctx.getNextId(fieldName + "_agg");
+ ProjectionName aggOutputProjName{_ctx.getNextId(str::stream() << fieldName << "_agg")};
ABT aggInputExpr = generateAggExpression(
stmt.expr.argument.get(), entry._rootProjection, aggOutputProjName);
@@ -199,9 +199,11 @@ public:
aggOutputProjNames.push_back(aggOutputProjName);
if (stmt.makeAccumulator()->getOpName() == "$avg"_sd) {
// Express $avg as sum / count.
- ProjectionName sumProjName = _ctx.getNextId(fieldName + "_sum_agg");
+ ProjectionName sumProjName{
+ _ctx.getNextId(str::stream() << fieldName << "_sum_agg")};
aggLowLevelOutputProjNames.push_back(sumProjName);
- ProjectionName countProjName = _ctx.getNextId(fieldName + "_count_agg");
+ ProjectionName countProjName{
+ _ctx.getNextId(str::stream() << fieldName << "_count_agg")};
aggLowLevelOutputProjNames.push_back(countProjName);
avgProjNames.emplace_back(AvgProjNames{std::move(aggOutputProjName),
std::move(sumProjName),
@@ -237,14 +239,14 @@ public:
ABT integrationPath = make<PathIdentity>();
for (size_t i = 0; i < groupByFieldNames.size(); i++) {
- std::string fieldName = std::move(groupByFieldNames.at(i));
+ std::string fieldName = groupByFieldNames.at(i).value().toString();
if (!isSingleIdField) {
// Erase '_id.' prefix.
fieldName = fieldName.substr(strlen("_id."));
}
maybeComposePath(integrationPath,
- make<PathField>(std::move(fieldName),
+ make<PathField>(FieldNameType{std::move(fieldName)},
make<PathConstant>(make<Variable>(
std::move(groupByProjNames.at(i))))));
}
@@ -260,7 +262,7 @@ public:
}
entry = _ctx.getNode();
- const std::string& mergeProject = _ctx.getNextId("agg_project");
+ const ProjectionName mergeProject{_ctx.getNextId("agg_project")};
_ctx.setNode<EvaluationNode>(
mergeProject,
mergeProject,
@@ -331,9 +333,9 @@ public:
ABT localPathGet = translateFieldPath(
*localPath,
make<PathIdentity>(),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
return make<PathGet>(
- fieldName,
+ std::move(fieldName),
isLastElement ? std::move(input)
: make<PathTraverse>(std::move(input), PathTraverse::kUnlimited));
});
@@ -360,9 +362,10 @@ public:
ABT foreignSimplePath = translateFieldPath(
*foreignPath,
make<PathCompare>(Operations::EqMember, make<Variable>(localProjName)),
- [](const std::string& fieldName, const bool /*isLastElement*/, ABT input) {
+ [](FieldNameType fieldName, const bool /*isLastElement*/, ABT input) {
return make<PathGet>(
- fieldName, make<PathTraverse>(std::move(input), PathTraverse::kSingleLevel));
+ std::move(fieldName),
+ make<PathTraverse>(std::move(input), PathTraverse::kSingleLevel));
});
// Retain only the top-level get into foreignSimplePath.
@@ -398,11 +401,11 @@ public:
ABT resultPath = translateFieldPath(
source->getAsField(),
make<PathConstant>(make<Variable>(foreignFoldedProjName)),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
if (!isLastElement) {
input = make<PathTraverse>(std::move(input), PathTraverse::kUnlimited);
}
- return make<PathField>(fieldName, std::move(input));
+ return make<PathField>(std::move(fieldName), std::move(input));
});
const ProjectionName& resultProjName = _ctx.getNextId("result");
@@ -579,8 +582,8 @@ public:
const FieldPath& unwindFieldPath = source->getUnwindPath();
const bool preserveNullAndEmpty = source->preserveNullAndEmptyArrays();
- const std::string pidProjName = _ctx.getNextId("unwoundPid");
- const std::string unwoundProjName = _ctx.getNextId("unwoundProj");
+ const ProjectionName pidProjName{_ctx.getNextId("unwoundPid")};
+ const ProjectionName unwoundProjName{_ctx.getNextId("unwoundProj")};
const auto generatePidGteZeroTest = [&pidProjName](ABT thenCond, ABT elseCond) {
return make<If>(
@@ -591,7 +594,7 @@ public:
ABT embedPath = make<Variable>(unwoundProjName);
if (preserveNullAndEmpty) {
- const std::string unwindLambdaVarName = _ctx.getNextId("unwoundLambdaVarName");
+ const ProjectionName unwindLambdaVarName{_ctx.getNextId("unwoundLambdaVarName")};
embedPath = make<PathLambda>(make<LambdaAbstraction>(
unwindLambdaVarName,
generatePidGteZeroTest(std::move(embedPath), make<Variable>(unwindLambdaVarName))));
@@ -601,19 +604,19 @@ public:
embedPath = translateFieldPath(
unwindFieldPath,
std::move(embedPath),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
return make<PathField>(
- fieldName,
+ std::move(fieldName),
isLastElement ? std::move(input)
: make<PathTraverse>(std::move(input), PathTraverse::kUnlimited));
});
- ABT unwoundPath = translateFieldPath(
- unwindFieldPath,
- make<PathIdentity>(),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
- return make<PathGet>(fieldName, std::move(input));
- });
+ ABT unwoundPath =
+ translateFieldPath(unwindFieldPath,
+ make<PathIdentity>(),
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
+ return make<PathGet>(std::move(fieldName), std::move(input));
+ });
auto entry = _ctx.getNode();
_ctx.setNode<EvaluationNode>(
@@ -630,7 +633,7 @@ public:
std::move(entry._node));
entry = _ctx.getNode();
- const std::string embedProjName = _ctx.getNextId("embedProj");
+ const ProjectionName embedProjName{_ctx.getNextId("embedProj")};
_ctx.setNode<EvaluationNode>(
embedProjName,
embedProjName,
@@ -644,12 +647,12 @@ public:
indexFieldPath,
make<PathConstant>(
generatePidGteZeroTest(make<Variable>(pidProjName), Constant::null())),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
- return make<PathField>(fieldName, std::move(input));
+ [](FieldNameType fieldName, const bool /*isLastElement*/, ABT input) {
+ return make<PathField>(std::move(fieldName), std::move(input));
});
entry = _ctx.getNode();
- const std::string embedPidProjName = _ctx.getNextId("embedPidProj");
+ const ProjectionName embedPidProjName{_ctx.getNextId("embedPidProj")};
_ctx.setNode<EvaluationNode>(
embedPidProjName,
embedPidProjName,
diff --git a/src/mongo/db/pipeline/abt/expr_algebrizer_context.cpp b/src/mongo/db/pipeline/abt/expr_algebrizer_context.cpp
index ef7e7fab292..045007529a3 100644
--- a/src/mongo/db/pipeline/abt/expr_algebrizer_context.cpp
+++ b/src/mongo/db/pipeline/abt/expr_algebrizer_context.cpp
@@ -31,15 +31,16 @@
namespace mongo::optimizer {
-ExpressionAlgebrizerContext::ExpressionAlgebrizerContext(const bool assertExprSort,
- const bool assertPathSort,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix)
+ExpressionAlgebrizerContext::ExpressionAlgebrizerContext(
+ const bool assertExprSort,
+ const bool assertPathSort,
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix)
: _assertExprSort(assertExprSort),
_assertPathSort(assertPathSort),
_rootProjection(rootProjection),
_rootProjVar(make<Variable>(_rootProjection)),
- _uniqueIdPrefix(uniqueIdPrefix),
+ _uniqueIdPrefix(std::move(uniqueIdPrefix)),
_prefixId() {}
void ExpressionAlgebrizerContext::push(ABT node) {
@@ -64,7 +65,7 @@ void ExpressionAlgebrizerContext::ensureArity(const size_t arity) {
uassert(6624429, "Arity violation", _stack.size() >= arity);
}
-const std::string& ExpressionAlgebrizerContext::getRootProjection() const {
+const ProjectionName& ExpressionAlgebrizerContext::getRootProjection() const {
return _rootProjection;
}
@@ -72,12 +73,15 @@ const ABT& ExpressionAlgebrizerContext::getRootProjVar() const {
return _rootProjVar;
}
-const std::string& ExpressionAlgebrizerContext::getUniqueIdPrefix() const {
+const boost::optional<ProjectionName>& ExpressionAlgebrizerContext::getUniqueIdPrefix() const {
return _uniqueIdPrefix;
}
-std::string ExpressionAlgebrizerContext::getNextId(const std::string& key) {
- return getUniqueIdPrefix() + "_" + _prefixId.getNextId(key);
+ProjectionName ExpressionAlgebrizerContext::getNextId(const std::string& prefix) {
+ if (const auto& projName = _uniqueIdPrefix) {
+ return _prefixId.getNextId(str::stream() << *projName << "_" << prefix);
+ }
+ return _prefixId.getNextId(prefix);
}
} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/expr_algebrizer_context.h b/src/mongo/db/pipeline/abt/expr_algebrizer_context.h
index 7c852281078..f36c2954dd6 100644
--- a/src/mongo/db/pipeline/abt/expr_algebrizer_context.h
+++ b/src/mongo/db/pipeline/abt/expr_algebrizer_context.h
@@ -41,8 +41,8 @@ class ExpressionAlgebrizerContext {
public:
ExpressionAlgebrizerContext(bool assertExprSort,
bool assertPathSort,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix);
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix);
/**
* Push an ABT onto the stack. Optionally perform a check on the type of the ABT based on
@@ -64,15 +64,15 @@ public:
*/
void ensureArity(size_t arity);
- const std::string& getRootProjection() const;
+ const ProjectionName& getRootProjection() const;
const ABT& getRootProjVar() const;
- const std::string& getUniqueIdPrefix() const;
+ const boost::optional<ProjectionName>& getUniqueIdPrefix() const;
/**
- * Returns a unique string for a new projection name. It will be prefixed by 'uniqueIdPrefix'.
+ * Returns a unique projection. It will be prefixed by 'uniqueIdPrefix'.
*/
- std::string getNextId(const std::string& key);
+ ProjectionName getNextId(const std::string& prefix);
void enterElemMatch(const MatchExpression::MatchType matchType) {
_elemMatchStack.push_back(matchType);
@@ -112,11 +112,11 @@ private:
const bool _assertPathSort;
// The name of the input projection on which the top-level expression will be evaluated.
- const std::string _rootProjection;
+ const ProjectionName _rootProjection;
const ABT _rootProjVar;
// Used to vend out unique strings for projection names.
- const std::string _uniqueIdPrefix;
+ const boost::optional<ProjectionName> _uniqueIdPrefix;
PrefixId _prefixId;
// Used to track the parts of the expression tree that have so far been translated to ABT.
diff --git a/src/mongo/db/pipeline/abt/field_map_builder.cpp b/src/mongo/db/pipeline/abt/field_map_builder.cpp
index 3bb8485b043..e0e86480459 100644
--- a/src/mongo/db/pipeline/abt/field_map_builder.cpp
+++ b/src/mongo/db/pipeline/abt/field_map_builder.cpp
@@ -34,7 +34,7 @@ namespace mongo::optimizer {
void FieldMapBuilder::integrateFieldPath(
const FieldPath& fieldPath, const std::function<void(const bool, FieldMapEntry&)>& fn) {
std::string path = kRootElement;
- auto it = _fieldMap.emplace(path, kRootElement);
+ auto it = _fieldMap.emplace(path, FieldNameType{kRootElement});
const size_t fieldPathLength = fieldPath.getPathLength();
for (size_t i = 0; i < fieldPathLength; i++) {
@@ -42,7 +42,7 @@ void FieldMapBuilder::integrateFieldPath(
path += '.' + fieldName;
it.first->second._childPaths.insert(path);
- it = _fieldMap.emplace(path, fieldName);
+ it = _fieldMap.emplace(path, FieldNameType{fieldName});
fn(i == fieldPathLength - 1, it.first->second);
}
}
@@ -56,23 +56,23 @@ boost::optional<ABT> FieldMapBuilder::generateABT() const {
}
ABT FieldMapBuilder::generateABTForField(const FieldMapEntry& entry) const {
- const bool isRootEntry = entry._fieldName == kRootElement;
+ const bool isRootEntry = entry._fieldName.value() == kRootElement;
bool hasLeadingObj = false;
bool hasTrailingDefault = false;
- std::set<std::string> keepSet;
- std::set<std::string> dropSet;
- std::map<std::string, std::string> varMap;
+ FieldNameOrderedSet keepSet;
+ FieldNameOrderedSet dropSet;
+ std::map<FieldNameType, ProjectionName> varMap;
for (const std::string& childField : entry._childPaths) {
const FieldMapEntry& childEntry = _fieldMap.at(childField);
- const std::string& childFieldName = childEntry._fieldName;
+ const FieldNameType childFieldName = childEntry._fieldName;
if (childEntry._hasKeep) {
- keepSet.insert(childFieldName);
+ keepSet.insert(FieldNameType{childFieldName});
}
if (childEntry._hasDrop) {
- dropSet.insert(childFieldName);
+ dropSet.insert(FieldNameType{childFieldName});
}
if (childEntry._hasLeadingObj) {
hasLeadingObj = true;
@@ -80,8 +80,8 @@ ABT FieldMapBuilder::generateABTForField(const FieldMapEntry& entry) const {
if (childEntry._hasTrailingDefault) {
hasTrailingDefault = true;
}
- if (!childEntry._constVarName.empty()) {
- varMap.emplace(childFieldName, childEntry._constVarName);
+ if (const auto& constVarName = childEntry._constVarName) {
+ varMap.emplace(childFieldName, *constVarName);
}
}
@@ -114,7 +114,7 @@ ABT FieldMapBuilder::generateABTForField(const FieldMapEntry& entry) const {
ABT childResult = generateABTForField(childEntry);
if (!childResult.is<PathIdentity>()) {
maybeComposePath(result,
- make<PathField>(childEntry._fieldName,
+ make<PathField>(FieldNameType{childEntry._fieldName},
make<PathTraverse>(std::move(childResult),
PathTraverse::kUnlimited)));
}
diff --git a/src/mongo/db/pipeline/abt/field_map_builder.h b/src/mongo/db/pipeline/abt/field_map_builder.h
index c8af6181286..67bf68f907a 100644
--- a/src/mongo/db/pipeline/abt/field_map_builder.h
+++ b/src/mongo/db/pipeline/abt/field_map_builder.h
@@ -46,16 +46,16 @@ namespace mongo::optimizer {
* Obj * Keep "b", "c"
*/
struct FieldMapEntry {
- FieldMapEntry(std::string fieldName) : _fieldName(std::move(fieldName)) {
- uassert(6624200, "Empty field name", !_fieldName.empty());
+ FieldMapEntry(FieldNameType fieldName) : _fieldName(std::move(fieldName)) {
+ uassert(6624200, "Empty field name", !_fieldName.value().empty());
}
- std::string _fieldName;
+ FieldNameType _fieldName;
bool _hasKeep = false;
bool _hasLeadingObj = false;
bool _hasTrailingDefault = false;
bool _hasDrop = false;
- std::string _constVarName;
+ boost::optional<ProjectionName> _constVarName;
// TODO SERVER-68516: Consider maintaining children as a vector of FieldMapEntry's. Then we can
// remove the _fieldMap member of FieldMapBuilder.
diff --git a/src/mongo/db/pipeline/abt/match_expression_visitor.cpp b/src/mongo/db/pipeline/abt/match_expression_visitor.cpp
index 70be6fd3651..0acce0d1a2a 100644
--- a/src/mongo/db/pipeline/abt/match_expression_visitor.cpp
+++ b/src/mongo/db/pipeline/abt/match_expression_visitor.cpp
@@ -418,7 +418,7 @@ public:
void visit(const SizeMatchExpression* expr) override {
assertSupportedPathExpression(expr);
- const std::string lambdaProjName = _ctx.getNextId("lambda_sizeMatch");
+ const ProjectionName lambdaProjName{_ctx.getNextId("lambda_sizeMatch")};
ABT result = make<PathLambda>(make<LambdaAbstraction>(
lambdaProjName,
make<BinaryOp>(
@@ -447,7 +447,7 @@ public:
void visit(const TypeMatchExpression* expr) override {
assertSupportedPathExpression(expr);
- const std::string lambdaProjName = _ctx.getNextId("lambda_typeMatch");
+ const ProjectionName lambdaProjName{_ctx.getNextId("lambda_typeMatch")};
ABT result = make<PathLambda>(make<LambdaAbstraction>(
lambdaProjName,
make<FunctionCall>("typeMatch",
@@ -656,10 +656,12 @@ private:
ABT generateMatchExpression(const MatchExpression* expr,
const bool allowAggExpressions,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix) {
- ExpressionAlgebrizerContext ctx(
- false /*assertExprSort*/, true /*assertPathSort*/, rootProjection, uniqueIdPrefix);
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix) {
+ ExpressionAlgebrizerContext ctx(false /*assertExprSort*/,
+ true /*assertPathSort*/,
+ rootProjection,
+ std::move(uniqueIdPrefix));
ABTMatchExpressionPreVisitor preVisitor(ctx);
ABTMatchExpressionVisitor postVisitor(ctx, allowAggExpressions);
MatchExpressionWalker walker(&preVisitor, nullptr /*inVisitor*/, &postVisitor);
diff --git a/src/mongo/db/pipeline/abt/match_expression_visitor.h b/src/mongo/db/pipeline/abt/match_expression_visitor.h
index d971ffd5ec6..c23e0f772c8 100644
--- a/src/mongo/db/pipeline/abt/match_expression_visitor.h
+++ b/src/mongo/db/pipeline/abt/match_expression_visitor.h
@@ -39,7 +39,7 @@ namespace mongo::optimizer {
*/
ABT generateMatchExpression(const MatchExpression* expr,
bool allowAggExpressions,
- const std::string& rootProjection,
- const std::string& uniqueIdPrefix);
+ const ProjectionName& rootProjection,
+ boost::optional<ProjectionName> uniqueIdPrefix);
} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/transformer_visitor.cpp b/src/mongo/db/pipeline/abt/transformer_visitor.cpp
index ccfbeb56e31..101e2f24d6e 100644
--- a/src/mongo/db/pipeline/abt/transformer_visitor.cpp
+++ b/src/mongo/db/pipeline/abt/transformer_visitor.cpp
@@ -58,7 +58,7 @@ void ABTTransformerVisitor::visit(const GroupFromFirstDocumentTransformation* tr
void ABTTransformerVisitor::visit(const ReplaceRootTransformation* transformer) {
auto entry = _ctx.getNode();
- const std::string& projName = _ctx.getNextId("newRoot");
+ const ProjectionName projName{_ctx.getNextId("newRoot")};
ABT expr =
generateAggExpression(transformer->getExpression().get(), entry._rootProjection, projName);
@@ -112,7 +112,7 @@ void ABTTransformerVisitor::processProjectedPaths(const projection_executor::Inc
* Handles renamed fields and computed projections.
*/
void ABTTransformerVisitor::processComputedPaths(const projection_executor::InclusionNode& node,
- const std::string& rootProjection,
+ const ProjectionName& rootProjection,
const bool isAddingFields) {
OrderedPathSet computedPaths;
StringMap<std::string> renamedPaths;
@@ -123,15 +123,15 @@ void ABTTransformerVisitor::processComputedPaths(const projection_executor::Incl
ABT path = translateFieldPath(
FieldPath(renamedPathEntry.second),
make<PathIdentity>(),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ [](FieldNameType fieldName, const bool isLastElement, ABT input) {
return make<PathGet>(
- fieldName,
+ std::move(fieldName),
isLastElement ? std::move(input)
: make<PathTraverse>(std::move(input), PathTraverse::kUnlimited));
});
auto entry = _ctx.getNode();
- const std::string& renamedProjName = _ctx.getNextId("projRenamedPath");
+ const ProjectionName renamedProjName{_ctx.getNextId("projRenamedPath")};
_ctx.setNode<EvaluationNode>(
entry._rootProjection,
renamedProjName,
@@ -158,7 +158,7 @@ void ABTTransformerVisitor::processComputedPaths(const projection_executor::Incl
const FieldPath computedPath(computedPathStr);
auto entry = _ctx.getNode();
- const std::string& getProjName = _ctx.getNextId("projGetPath");
+ const ProjectionName getProjName{_ctx.getNextId("projGetPath")};
ABT getExpr = generateAggExpression(
node.getExpressionForPath(computedPath).get(), rootProjection, getProjName);
@@ -184,7 +184,7 @@ void ABTTransformerVisitor::processComputedPaths(const projection_executor::Incl
void ABTTransformerVisitor::visitInclusionNode(const projection_executor::InclusionNode& node,
const bool isAddingFields) {
auto entry = _ctx.getNode();
- const std::string rootProjection = entry._rootProjection;
+ const ProjectionName rootProjection{entry._rootProjection};
processProjectedPaths(node);
processComputedPaths(node, rootProjection, isAddingFields);
diff --git a/src/mongo/db/pipeline/abt/transformer_visitor.h b/src/mongo/db/pipeline/abt/transformer_visitor.h
index 8313454d540..0ebc00e3308 100644
--- a/src/mongo/db/pipeline/abt/transformer_visitor.h
+++ b/src/mongo/db/pipeline/abt/transformer_visitor.h
@@ -76,7 +76,7 @@ private:
* Handles renamed fields and computed projections.
*/
void processComputedPaths(const projection_executor::InclusionNode& node,
- const std::string& rootProjection,
+ const ProjectionName& rootProjection,
bool isAddingFields);
void visitInclusionNode(const projection_executor::InclusionNode& node, bool isAddingFields);
diff --git a/src/mongo/db/pipeline/abt/utils.cpp b/src/mongo/db/pipeline/abt/utils.cpp
index 9911e090305..e8be71b900a 100644
--- a/src/mongo/db/pipeline/abt/utils.cpp
+++ b/src/mongo/db/pipeline/abt/utils.cpp
@@ -73,8 +73,8 @@ ABT translateFieldPath(const FieldPath& fieldPath,
const size_t fieldPathLength = fieldPath.getPathLength();
bool isLastElement = true;
for (size_t i = fieldPathLength; i-- > skipFromStart;) {
- result =
- fieldNameFn(fieldPath.getFieldName(i).toString(), isLastElement, std::move(result));
+ result = fieldNameFn(
+ FieldNameType{fieldPath.getFieldName(i).toString()}, isLastElement, std::move(result));
isLastElement = false;
}
@@ -108,7 +108,7 @@ ABT translateFieldRef(const FieldRef& fieldRef, ABT initial) {
result = make<PathTraverse>(std::move(result), PathTraverse::kSingleLevel);
}
}
- result = make<PathGet>(fieldRef[i].toString(), std::move(result));
+ result = make<PathGet>(FieldNameType{fieldRef[i].toString()}, std::move(result));
}
return result;
diff --git a/src/mongo/db/pipeline/abt/utils.h b/src/mongo/db/pipeline/abt/utils.h
index 54a255adff7..23ce980662f 100644
--- a/src/mongo/db/pipeline/abt/utils.h
+++ b/src/mongo/db/pipeline/abt/utils.h
@@ -39,7 +39,7 @@ namespace mongo::optimizer {
std::pair<sbe::value::TypeTags, sbe::value::Value> convertFrom(Value val);
using ABTFieldNameFn =
- std::function<ABT(const std::string& fieldName, const bool isLastElement, ABT input)>;
+ std::function<ABT(FieldNameType fieldName, const bool isLastElement, ABT input)>;
/**
* Translates an aggregation FieldPath by invoking the `fieldNameFn` for each path component.
diff --git a/src/mongo/db/query/ce/ce_dataflow_nodes_test.cpp b/src/mongo/db/query/ce/ce_dataflow_nodes_test.cpp
index 9a59257e4bb..1f11472c811 100644
--- a/src/mongo/db/query/ce/ce_dataflow_nodes_test.cpp
+++ b/src/mongo/db/query/ce/ce_dataflow_nodes_test.cpp
@@ -81,7 +81,7 @@ TEST(CEDataflowTest, EstimateTrivialNodes) {
TEST(CEDataflowTest, EstimateUnionNode) {
auto makeUnionBranch = [](const std::string& collName) {
- auto scanVar = "scan_" + collName;
+ ProjectionName scanVar{"scan_" + collName};
auto scanNode = make<ScanNode>(scanVar, collName);
auto evalPath =
make<EvalPath>(make<PathGet>("a", make<PathIdentity>()), make<Variable>(scanVar));
diff --git a/src/mongo/db/query/ce/ce_heuristic.cpp b/src/mongo/db/query/ce/ce_heuristic.cpp
index 2c2ab13fb86..5e5712064ba 100644
--- a/src/mongo/db/query/ce/ce_heuristic.cpp
+++ b/src/mongo/db/query/ce/ce_heuristic.cpp
@@ -260,7 +260,7 @@ public:
// Each item represents a field in a dotted path.
// Collected while traversing a path expression.
// Used for deciding whether a conjunction of comparisons is an interval or not.
- std::vector<std::string> path;
+ FieldPathType path;
// When handling a PathComposeM, we need to access its child comparisons which might be
// hidden under path expressions.
const PathCompare* compare;
diff --git a/src/mongo/db/query/ce/ce_histogram_test.cpp b/src/mongo/db/query/ce/ce_histogram_test.cpp
index a51b054bdf6..7c9e8fcc402 100644
--- a/src/mongo/db/query/ce/ce_histogram_test.cpp
+++ b/src/mongo/db/query/ce/ce_histogram_test.cpp
@@ -730,12 +730,13 @@ TEST(CEHistogramTest, TestArrayHistogramOnCompositePredicates) {
ASSERT_MATCH_CE_NODE(t, "{mixed: {$elemMatch: {}}}", 88.0, isSargable);
// Take into account both empty and non-empty arrays.
- auto makePathArrABT = [&](const std::string& path) {
- const auto scanProjection = "scan_0";
+ auto makePathArrABT = [&](const FieldNameType& fieldName) {
+ const ProjectionName scanProjection{"scan_0"};
auto scanNode = make<ScanNode>(scanProjection, collName);
- auto filterNode = make<FilterNode>(
- make<EvalFilter>(make<PathGet>(path, make<PathArr>()), make<Variable>(scanProjection)),
- std::move(scanNode));
+ auto filterNode =
+ make<FilterNode>(make<EvalFilter>(make<PathGet>(std::move(fieldName), make<PathArr>()),
+ make<Variable>(scanProjection)),
+ std::move(scanNode));
return make<RootNode>(
properties::ProjectionRequirement{ProjectionNameVector{scanProjection}},
std::move(filterNode));
diff --git a/src/mongo/db/query/cqf_get_executor.cpp b/src/mongo/db/query/cqf_get_executor.cpp
index 2aed4f9304c..c16bb715af5 100644
--- a/src/mongo/db/query/cqf_get_executor.cpp
+++ b/src/mongo/db/query/cqf_get_executor.cpp
@@ -220,7 +220,7 @@ static opt::unordered_map<std::string, optimizer::IndexDefinition> buildIndexSpe
ABT exprABT = generateMatchExpression(expr.get(),
false /*allowAggExpression*/,
"<root>" /*rootProjection*/,
- "" /*uniquePrefix*/);
+ boost::none /*uniquePrefix*/);
exprABT = make<EvalFilter>(std::move(exprABT), make<Variable>(scanProjName));
// TODO SERVER-70315: simplify partial filter expression.
diff --git a/src/mongo/db/query/optimizer/cascades/enforcers.cpp b/src/mongo/db/query/optimizer/cascades/enforcers.cpp
index 801ae716202..33a57d32424 100644
--- a/src/mongo/db/query/optimizer/cascades/enforcers.cpp
+++ b/src/mongo/db/query/optimizer/cascades/enforcers.cpp
@@ -190,7 +190,7 @@ public:
const ProjectionNameSet& availableProjections =
getPropertyConst<ProjectionAvailability>(_logicalProps).getProjections();
- ProjectionName ridProjName;
+ boost::optional<ProjectionName> ridProjName;
if (hasProperty<IndexingAvailability>(_logicalProps)) {
const auto& scanDefName =
getPropertyConst<IndexingAvailability>(_logicalProps).getScanDefName();
diff --git a/src/mongo/db/query/optimizer/cascades/implementers.cpp b/src/mongo/db/query/optimizer/cascades/implementers.cpp
index 835045a2611..e5e2635c1fb 100644
--- a/src/mongo/db/query/optimizer/cascades/implementers.cpp
+++ b/src/mongo/db/query/optimizer/cascades/implementers.cpp
@@ -172,12 +172,12 @@ public:
const auto& requiredProjections =
getPropertyConst<ProjectionRequirement>(_physProps).getProjections();
- ProjectionName ridProjName;
+ boost::optional<ProjectionName> ridProjName;
bool needsRID = false;
if (hasProperty<IndexingAvailability>(_logicalProps)) {
ridProjName = _ridProjections.at(
getPropertyConst<IndexingAvailability>(_logicalProps).getScanDefName());
- needsRID = requiredProjections.find(ridProjName).second;
+ needsRID = requiredProjections.find(*ridProjName).second;
}
if (needsRID && !node.getHasRID()) {
// We cannot provide RID.
@@ -201,7 +201,7 @@ public:
}
if (needsRID) {
physNode = make<EvaluationNode>(
- std::move(ridProjName), Constant::nothing(), std::move(physNode));
+ std::move(*ridProjName), Constant::nothing(), std::move(physNode));
nodeCEMap.emplace(physNode.cast<Node>(), 0.0);
}
} else {
@@ -210,7 +210,7 @@ public:
physNode = make<LimitSkipNode>(LimitSkipRequirement{1, 0}, std::move(physNode));
nodeCEMap.emplace(physNode.cast<Node>(), 1.0);
- const ProjectionName valueScanProj = _prefixId.getNextId("valueScan");
+ const ProjectionName valueScanProj{_prefixId.getNextId("valueScan")};
physNode =
make<EvaluationNode>(valueScanProj, node.getValueArray(), std::move(physNode));
nodeCEMap.emplace(physNode.cast<Node>(), 1.0);
@@ -245,7 +245,7 @@ public:
if (needsRID) {
// Obtain row id from first element of the array.
physNode = make<EvaluationNode>(
- std::move(ridProjName), getElementFn(0), std::move(physNode));
+ std::move(*ridProjName), getElementFn(0), std::move(physNode));
nodeCEMap.emplace(physNode.cast<Node>(), node.getArraySize());
}
}
@@ -269,7 +269,7 @@ public:
return;
}
- VariableNameSetType references = collectVariableReferences(n);
+ ProjectionNameSet references = collectVariableReferences(n);
if (checkIntroducesScanProjectionUnderIndexOnly(references)) {
// Reject if under indexing requirements and now we introduce dependence on scan
// projection.
@@ -349,7 +349,7 @@ public:
// requirement.
PhysProps newProps = _physProps;
- VariableNameSetType references = collectVariableReferences(n);
+ ProjectionNameSet references = collectVariableReferences(n);
if (checkIntroducesScanProjectionUnderIndexOnly(references)) {
// Reject if under indexing requirements and now we introduce dependence on scan
// projection.
@@ -572,8 +572,9 @@ public:
!areCompoundIntervalsEqualities(*singularInterval) && indexDef.isMultiKey() &&
requirements.getDedupRID();
- indexProjectionMap._ridProjection =
- (needsRID || needsUniqueStage) ? ridProjName : "";
+ if (needsRID || needsUniqueStage) {
+ indexProjectionMap._ridProjection = ridProjName;
+ }
if (singularInterval) {
physNode =
make<IndexScanNode>(std::move(indexProjectionMap),
@@ -943,7 +944,7 @@ public:
getPropertyConst<ProjectionRequirement>(_physProps).getProjections();
// Add expression references to requirements.
- VariableNameSetType references = collectVariableReferences(n);
+ ProjectionNameSet references = collectVariableReferences(n);
for (const auto& varName : references) {
reqProjections.emplace_back(varName);
}
@@ -978,7 +979,7 @@ public:
// Split collation between inner and outer side.
const CollationSplitResult& collationSplit = splitCollationSpec(
- "" /*ridProjName*/, collationSpec, leftProjections, rightProjections);
+ boost::none /*ridProjName*/, collationSpec, leftProjections, rightProjections);
if (!collationSplit._validSplit) {
return;
}
@@ -1102,7 +1103,7 @@ public:
// Iterate over the aggregation expressions and only add those required.
ABTVector aggregationProjections;
ProjectionNameVector aggregationProjectionNames;
- VariableNameSetType projectionsToAdd;
+ ProjectionNameSet projectionsToAdd;
for (const ProjectionName& groupByProjName : groupByProjections) {
projectionsToAdd.insert(groupByProjName);
}
@@ -1310,7 +1311,7 @@ private:
const bool needsCollation =
candidateIndexEntry._fieldsToCollate.count(indexField) > 0;
- auto it = fieldProjections.find(encodeIndexKeyName(indexField));
+ auto it = fieldProjections.find(FieldNameType{encodeIndexKeyName(indexField)});
if (it == fieldProjections.cend()) {
// No bound projection for this index field.
if (needsCollation) {
@@ -1369,7 +1370,7 @@ private:
* Check if we are under index-only requirements and expression introduces dependency on scan
* projection.
*/
- bool checkIntroducesScanProjectionUnderIndexOnly(const VariableNameSetType& references) {
+ bool checkIntroducesScanProjectionUnderIndexOnly(const ProjectionNameSet& references) {
return hasProperty<IndexingAvailability>(_logicalProps) &&
getPropertyConst<IndexingRequirement>(_physProps).getIndexReqTarget() ==
IndexReqTarget::Index &&
diff --git a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
index 0c9a923b2ed..77d9fd71c53 100644
--- a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
+++ b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp
@@ -319,7 +319,7 @@ ReorderDependencies computeDependencies(ABT::reference_type aboveNodeRef,
env.hasDefinitions(belowChild) ? env.getDefinitions(belowChild) : DefinitionsMap{};
ReorderDependencies dependencies;
- for (const std::string& varName : aboveNodeVarNames) {
+ for (const ProjectionName& varName : aboveNodeVarNames) {
auto it = belowNodeDefs.find(varName);
// Variable is exclusively defined in the below node.
const bool refersToNode = it != belowNodeDefs.cend() && it->second.definedBy == belowNode;
@@ -696,7 +696,7 @@ static void convertFilterToSargableNode(ABT::reference_type node,
for (auto it = conversion->_reqMap.cbegin(); it != conversion->_reqMap.cend();) {
uassert(6624111,
"Filter partial schema requirement must contain a variable name.",
- !it->first._projectionName.empty());
+ it->first._projectionName);
uassert(6624112,
"Filter partial schema requirement cannot bind.",
!it->second.getBoundProjectionName());
@@ -844,12 +844,12 @@ struct SubstituteConvert<EvaluationNode> {
ABT result = evalNode.getChild();
ABT keepPath = make<PathIdentity>();
- std::set<std::string> orderedSet;
- for (const std::string& fieldName : pathKeepPtr->getNames()) {
+ FieldNameOrderedSet orderedSet;
+ for (const FieldNameType& fieldName : pathKeepPtr->getNames()) {
orderedSet.insert(fieldName);
}
- for (const std::string& fieldName : orderedSet) {
- ProjectionName projName = ctx.getPrefixId().getNextId("fieldProj");
+ for (const FieldNameType& fieldName : orderedSet) {
+ ProjectionName projName{ctx.getPrefixId().getNextId("fieldProj")};
result = make<EvaluationNode>(
projName,
make<EvalPath>(make<PathGet>(fieldName, make<PathIdentity>()),
@@ -892,7 +892,7 @@ struct SubstituteConvert<EvaluationNode> {
uassert(6624114,
"Eval partial schema requirement must contain a variable name.",
- !key._projectionName.empty());
+ key._projectionName);
uassert(6624115,
"Eval partial schema requirement cannot have a range",
isIntervalReqFullyOpenDNF(req.getIntervals()));
@@ -960,7 +960,7 @@ static SplitRequirementsResult splitRequirements(
const bool disableYieldingTolerantPlans,
const std::vector<bool>& isFullyOpen,
const std::vector<bool>& mayReturnNull,
- const boost::optional<opt::unordered_set<FieldNameType>>& indexFieldPrefixMapForScanDef,
+ const boost::optional<FieldNameSet>& indexFieldPrefixMapForScanDef,
const PartialSchemaRequirements& reqMap) {
SplitRequirementsResult result;
auto& leftReqs = result._leftReqs;
@@ -1071,7 +1071,7 @@ struct ExploreConvert<SargableNode> {
}
const ProjectionName& scanProjectionName = indexingAvailability.getScanProjection();
- if (collectVariableReferences(node) != VariableNameSetType{scanProjectionName}) {
+ if (collectVariableReferences(node) != ProjectionNameSet{scanProjectionName}) {
// Rewrite not applicable if we refer projections other than the scan projection.
return;
}
@@ -1079,7 +1079,7 @@ struct ExploreConvert<SargableNode> {
const bool isIndex = target == IndexReqTarget::Index;
const auto& indexFieldPrefixMap = ctx.getIndexFieldPrefixMap();
- boost::optional<opt::unordered_set<FieldNameType>> indexFieldPrefixMapForScanDef;
+ boost::optional<FieldNameSet> indexFieldPrefixMapForScanDef;
if (auto it = indexFieldPrefixMap.find(scanDefName);
it != indexFieldPrefixMap.cend() && !isIndex) {
indexFieldPrefixMapForScanDef = it->second;
diff --git a/src/mongo/db/query/optimizer/cascades/logical_rewriter.h b/src/mongo/db/query/optimizer/cascades/logical_rewriter.h
index 9ffa602da23..0af43abae90 100644
--- a/src/mongo/db/query/optimizer/cascades/logical_rewriter.h
+++ b/src/mongo/db/query/optimizer/cascades/logical_rewriter.h
@@ -136,7 +136,7 @@ private:
// Contains the set of top-level index fields for a given scanDef. For example "a.b" is encoded
// as "a". This is used to constrain the possible splits of a sargable node.
- opt::unordered_map<std::string, opt::unordered_set<FieldNameType>> _indexFieldPrefixMap;
+ opt::unordered_map<std::string, FieldNameSet> _indexFieldPrefixMap;
// Track number of times a SargableNode at a given position in the memo has been split.
opt::unordered_map<MemoLogicalNodeId, size_t, NodeIdHash> _sargableSplitCountMap;
diff --git a/src/mongo/db/query/optimizer/defs.cpp b/src/mongo/db/query/optimizer/defs.cpp
index d3de6784107..80f51efb8c9 100644
--- a/src/mongo/db/query/optimizer/defs.cpp
+++ b/src/mongo/db/query/optimizer/defs.cpp
@@ -33,9 +33,9 @@
namespace mongo::optimizer {
-static opt::unordered_map<ProjectionName, size_t> createMapFromVector(
+static opt::unordered_map<ProjectionName, size_t, ProjectionName::Hasher> createMapFromVector(
const ProjectionNameVector& v) {
- opt::unordered_map<ProjectionName, size_t> result;
+ opt::unordered_map<ProjectionName, size_t, ProjectionName::Hasher> result;
for (size_t i = 0; i < v.size(); i++) {
result.emplace(v.at(i), i);
}
@@ -95,7 +95,7 @@ bool ProjectionNameOrderPreservingSet::erase(const ProjectionName& projectionNam
}
_map.erase(projectionName);
- _vector.resize(_vector.size() - 1);
+ _vector.pop_back();
return true;
}
diff --git a/src/mongo/db/query/optimizer/defs.h b/src/mongo/db/query/optimizer/defs.h
index 2cf6dbccd10..c3d63967750 100644
--- a/src/mongo/db/query/optimizer/defs.h
+++ b/src/mongo/db/query/optimizer/defs.h
@@ -29,6 +29,7 @@
#pragma once
+#include <boost/optional.hpp>
#include <set>
#include <sstream>
#include <string>
@@ -36,20 +37,36 @@
#include "mongo/db/query/optimizer/containers.h"
#include "mongo/db/query/optimizer/utils/printable_enum.h"
+#include "mongo/db/query/optimizer/utils/strong_string_alias.h"
namespace mongo::optimizer {
-using FieldNameType = std::string;
+/**
+ * Representation of a field name. Can be empty.
+ */
+struct FieldNameAliasTag {
+ static constexpr bool kAllowEmpty = true;
+};
+using FieldNameType = StrongStringAlias<FieldNameAliasTag>;
+
using FieldPathType = std::vector<FieldNameType>;
+using FieldNameOrderedSet = std::set<FieldNameType>;
+using FieldNameSet = opt::unordered_set<FieldNameType, FieldNameType::Hasher>;
-using CollectionNameType = std::string;
+/**
+ * Representation of a variable name. Cannot be empty.
+ */
+struct ProjectionNameAliasTag {
+ static constexpr bool kAllowEmpty = false;
+};
+using ProjectionName = StrongStringAlias<ProjectionNameAliasTag>;
-using ProjectionName = std::string;
-using ProjectionNameSet = opt::unordered_set<ProjectionName>;
+using ProjectionNameSet = opt::unordered_set<ProjectionName, ProjectionName::Hasher>;
using ProjectionNameOrderedSet = std::set<ProjectionName>;
using ProjectionNameVector = std::vector<ProjectionName>;
-using ProjectionRenames = opt::unordered_map<ProjectionName, ProjectionName>;
+using ProjectionRenames =
+ opt::unordered_map<ProjectionName, ProjectionName, ProjectionName::Hasher>;
// Map from scanDefName to rid projection name.
using RIDProjectionsMap = opt::unordered_map<std::string, ProjectionName>;
@@ -73,7 +90,7 @@ public:
const ProjectionNameVector& getVector() const;
private:
- opt::unordered_map<ProjectionName, size_t> _map;
+ opt::unordered_map<ProjectionName, size_t, ProjectionName::Hasher> _map;
ProjectionNameVector _vector;
};
@@ -101,9 +118,9 @@ MAKE_PRINTABLE_ENUM_STRING_ARRAY(DistributionTypeEnum, DistributionType, DISTRIB
// In case of covering scan, index, or fetch, specify names of bound projections for each field.
// Also optionally specify if applicable the rid and record (root) projections.
struct FieldProjectionMap {
- ProjectionName _ridProjection;
- ProjectionName _rootProjection;
- opt::unordered_map<FieldNameType, ProjectionName> _fieldProjections;
+ boost::optional<ProjectionName> _ridProjection;
+ boost::optional<ProjectionName> _rootProjection;
+ opt::unordered_map<FieldNameType, ProjectionName, FieldNameType::Hasher> _fieldProjections;
bool operator==(const FieldProjectionMap& other) const;
};
diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp
index 5e9df5df3bb..6432ae3e9b3 100644
--- a/src/mongo/db/query/optimizer/explain.cpp
+++ b/src/mongo/db/query/optimizer/explain.cpp
@@ -124,6 +124,12 @@ public:
return *this;
}
+ template <class TagType>
+ ExplainPrinterImpl& print(const StrongStringAlias<TagType>& t) {
+ print(t.value().empty() ? "<empty>" : t.value());
+ return *this;
+ }
+
/**
* Here and below: "other" printer(s) may be siphoned out.
*/
@@ -414,8 +420,13 @@ public:
}
ExplainPrinterImpl& print(const std::string& s) {
- auto [tag, val] = sbe::value::makeNewString(s);
- addValue(tag, val);
+ printStringInternal(s);
+ return *this;
+ }
+
+ template <class TagType>
+ ExplainPrinterImpl& print(const StrongStringAlias<TagType>& s) {
+ printStringInternal(s.value().toString());
return *this;
}
@@ -458,12 +469,26 @@ public:
return *this;
}
+ template <size_t N>
+ ExplainPrinterImpl& fieldName(const char (&name)[N],
+ const ExplainVersion minVersion = ExplainVersion::V1,
+ const ExplainVersion maxVersion = ExplainVersion::Vmax) {
+ fieldNameInternal(name, minVersion, maxVersion);
+ return *this;
+ }
+
ExplainPrinterImpl& fieldName(const std::string& name,
const ExplainVersion minVersion = ExplainVersion::V1,
const ExplainVersion maxVersion = ExplainVersion::Vmax) {
- if (minVersion <= version && maxVersion >= version) {
- _nextFieldName = name;
- }
+ fieldNameInternal(name, minVersion, maxVersion);
+ return *this;
+ }
+
+ template <class TagType>
+ ExplainPrinterImpl& fieldName(const StrongStringAlias<TagType>& name,
+ const ExplainVersion minVersion = ExplainVersion::V1,
+ const ExplainVersion maxVersion = ExplainVersion::Vmax) {
+ fieldNameInternal(name.value().toString(), minVersion, maxVersion);
return *this;
}
@@ -473,6 +498,21 @@ public:
}
private:
+ ExplainPrinterImpl& printStringInternal(const std::string& s) {
+ auto [tag, val] = sbe::value::makeNewString(s);
+ addValue(tag, val);
+ return *this;
+ }
+
+ ExplainPrinterImpl& fieldNameInternal(const std::string& name,
+ const ExplainVersion minVersion,
+ const ExplainVersion maxVersion) {
+ if (minVersion <= version && maxVersion >= version) {
+ _nextFieldName = name;
+ }
+ return *this;
+ }
+
ExplainPrinterImpl& print(ExplainPrinterImpl& other, const bool append) {
auto [tag, val] = other.moveValue();
addValue(tag, val, append);
@@ -674,7 +714,7 @@ public:
ExplainPrinter transport(const ABT& /*n*/,
const ExpressionBinder& binders,
std::vector<ExplainPrinter> inResults) {
- std::map<std::string, ExplainPrinter> ordered;
+ std::map<ProjectionName, ExplainPrinter> ordered;
for (size_t idx = 0; idx < inResults.size(); ++idx) {
ordered.emplace(binders.names()[idx], std::move(inResults[idx]));
}
@@ -699,11 +739,11 @@ public:
static void printFieldProjectionMap(ExplainPrinter& printer, const FieldProjectionMap& map) {
std::map<FieldNameType, ProjectionName> ordered;
- if (!map._ridProjection.empty()) {
- ordered["<rid>"] = map._ridProjection;
+ if (const auto& projName = map._ridProjection) {
+ ordered.emplace("<rid>", *projName);
}
- if (!map._rootProjection.empty()) {
- ordered["<root>"] = map._rootProjection;
+ if (const auto& projName = map._rootProjection) {
+ ordered.emplace("<root>", *projName);
}
for (const auto& entry : map._fieldProjections) {
ordered.insert(entry);
@@ -1085,7 +1125,9 @@ public:
for (const auto& [key, req] : reqMap) {
ExplainPrinter local;
- local.fieldName("refProjection").print(key._projectionName).separator(", ");
+ if (const auto& projName = key._projectionName) {
+ local.fieldName("refProjection").print(*projName).separator(", ");
+ }
ExplainPrinter pathPrinter = generate(key._path);
local.fieldName("path").separator("'").printSingleLevel(pathPrinter).separator("', ");
@@ -1113,7 +1155,9 @@ public:
for (const auto& [key, req, entryIndex] : residualReqs) {
ExplainPrinter local;
- local.fieldName("refProjection").print(key._projectionName).separator(", ");
+ if (const auto& projName = key._projectionName) {
+ local.fieldName("refProjection").print(*projName).separator(", ");
+ }
ExplainPrinter pathPrinter = generate(key._path);
local.fieldName("path").separator("'").printSingleLevel(pathPrinter).separator("', ");
@@ -1733,10 +1777,10 @@ public:
ExplainPrinter pathPrinter = gen.generate(key._path);
ExplainPrinter local;
- local.fieldName("refProjection")
- .print(key._projectionName)
- .separator(", ")
- .fieldName("path")
+ if (const auto& projName = key._projectionName) {
+ local.fieldName("refProjection").print(*projName).separator(", ");
+ }
+ local.fieldName("path")
.separator("'")
.printSingleLevel(pathPrinter)
.separator("', ")
@@ -2172,10 +2216,10 @@ public:
return printer;
}
- static void printPathProjections(ExplainPrinter& printer, const std::set<std::string>& names) {
+ static void printPathProjections(ExplainPrinter& printer, const FieldNameOrderedSet& names) {
if constexpr (version < ExplainVersion::V3) {
bool first = true;
- for (const std::string& s : names) {
+ for (const FieldNameType& s : names) {
if (first) {
first = false;
} else {
@@ -2185,7 +2229,7 @@ public:
}
} else if constexpr (version == ExplainVersion::V3) {
std::vector<ExplainPrinter> printers;
- for (const std::string& s : names) {
+ for (const FieldNameType& s : names) {
ExplainPrinter local;
local.print(s);
printers.push_back(std::move(local));
@@ -2263,7 +2307,7 @@ public:
ExplainPrinter printer("PathGet");
printer.separator(" [")
.fieldName("path", ExplainVersion::V3)
- .print(path.name().empty() ? "<empty>" : path.name())
+ .print(path.name())
.separator("]")
.setChildCount(1)
.fieldName("input", ExplainVersion::V3)
diff --git a/src/mongo/db/query/optimizer/index_bounds.cpp b/src/mongo/db/query/optimizer/index_bounds.cpp
index 918158a25a6..3a7161e1510 100644
--- a/src/mongo/db/query/optimizer/index_bounds.cpp
+++ b/src/mongo/db/query/optimizer/index_bounds.cpp
@@ -105,7 +105,13 @@ void IntervalRequirement::reverse() {
std::swap(_lowBound, _highBound);
}
+PartialSchemaKey::PartialSchemaKey(ABT path) : PartialSchemaKey(boost::none, std::move(path)) {}
+
PartialSchemaKey::PartialSchemaKey(ProjectionName projectionName, ABT path)
+ : PartialSchemaKey(boost::optional<ProjectionName>{std::move(projectionName)},
+ std::move(path)) {}
+
+PartialSchemaKey::PartialSchemaKey(boost::optional<ProjectionName> projectionName, ABT path)
: _projectionName(std::move(projectionName)), _path(std::move(path)) {
assertPathSort(_path);
}
@@ -131,8 +137,6 @@ PartialSchemaRequirement::PartialSchemaRequirement(
tassert(6624154,
"Cannot have perf only requirement which also binds",
!_isPerfOnly || !_boundProjectionName);
- tassert(
- 6624155, "Empty projection name", !_boundProjectionName || !_boundProjectionName->empty());
}
bool PartialSchemaRequirement::operator==(const PartialSchemaRequirement& other) const {
@@ -162,10 +166,20 @@ bool IndexPath3WComparator::operator()(const ABT& path1, const ABT& path2) const
bool PartialSchemaKeyLessComparator::operator()(const PartialSchemaKey& k1,
const PartialSchemaKey& k2) const {
- const int projCmp = k1._projectionName.compare(k2._projectionName);
- if (projCmp != 0) {
- return projCmp < 0;
+ if (const auto& p1 = k1._projectionName) {
+ if (const auto& p2 = k2._projectionName) {
+ const int projCmp = p1->compare(*p2);
+ if (projCmp != 0) {
+ return projCmp < 0;
+ }
+ // Fallthrough to comparison below.
+ } else {
+ return false;
+ }
+ } else if (k2._projectionName) {
+ return false;
}
+
return compareExprAndPaths(k1._path, k2._path) < 0;
}
diff --git a/src/mongo/db/query/optimizer/index_bounds.h b/src/mongo/db/query/optimizer/index_bounds.h
index 685a26b045f..05599a87395 100644
--- a/src/mongo/db/query/optimizer/index_bounds.h
+++ b/src/mongo/db/query/optimizer/index_bounds.h
@@ -80,12 +80,14 @@ private:
};
struct PartialSchemaKey {
+ PartialSchemaKey(ABT path);
PartialSchemaKey(ProjectionName projectionName, ABT path);
+ PartialSchemaKey(boost::optional<ProjectionName> projectionName, ABT path);
bool operator==(const PartialSchemaKey& other) const;
// Referred, or input projection name.
- ProjectionName _projectionName;
+ boost::optional<ProjectionName> _projectionName;
// (Partially determined) path.
ABT _path;
diff --git a/src/mongo/db/query/optimizer/metadata.h b/src/mongo/db/query/optimizer/metadata.h
index e67fdd5693c..70e1fd8689c 100644
--- a/src/mongo/db/query/optimizer/metadata.h
+++ b/src/mongo/db/query/optimizer/metadata.h
@@ -88,7 +88,7 @@ struct MultikeynessTrie {
void merge(const MultikeynessTrie& other);
void add(const ABT& path);
- opt::unordered_map<std::string, MultikeynessTrie> children;
+ opt::unordered_map<FieldNameType, MultikeynessTrie, FieldNameType::Hasher> children;
// An empty trie doesn't know whether anything is multikey.
// 'true' means "not sure" while 'false' means "definitely no arrays".
bool isMultiKey = true;
diff --git a/src/mongo/db/query/optimizer/metadata_factory.cpp b/src/mongo/db/query/optimizer/metadata_factory.cpp
index 36989c64129..6eadf67d1cc 100644
--- a/src/mongo/db/query/optimizer/metadata_factory.cpp
+++ b/src/mongo/db/query/optimizer/metadata_factory.cpp
@@ -70,7 +70,7 @@ ScanDefinition createScanDef(ScanDefOptions options,
// Simplify partial filter requirements using the non-multikey paths.
for (auto& [indexDefName, indexDef] : indexDefs) {
[[maybe_unused]] const bool hasEmptyInterval = simplifyPartialSchemaReqPaths(
- "" /*scanProjName*/, multikeynessTrie, indexDef.getPartialReqMap(), constFold);
+ "<root>", multikeynessTrie, indexDef.getPartialReqMap(), constFold);
// If "hasEmptyInterval" is set, we have a partial filter index with an unsatisfiable
// condition, which is thus guaranteed to never contain any documents.
}
diff --git a/src/mongo/db/query/optimizer/node.cpp b/src/mongo/db/query/optimizer/node.cpp
index 78be197b709..901ef3fbfe2 100644
--- a/src/mongo/db/query/optimizer/node.cpp
+++ b/src/mongo/db/query/optimizer/node.cpp
@@ -79,11 +79,11 @@ static ProjectionNameVector extractProjectionNamesForScan(
const FieldProjectionMap& fieldProjectionMap) {
ProjectionNameVector result;
- if (!fieldProjectionMap._ridProjection.empty()) {
- result.push_back(fieldProjectionMap._ridProjection);
+ if (const auto& projName = fieldProjectionMap._ridProjection) {
+ result.push_back(*projName);
}
- if (!fieldProjectionMap._rootProjection.empty()) {
- result.push_back(fieldProjectionMap._rootProjection);
+ if (const auto& projName = fieldProjectionMap._rootProjection) {
+ result.push_back(*projName);
}
for (const auto& entry : fieldProjectionMap._fieldProjections) {
result.push_back(entry.second);
@@ -278,7 +278,6 @@ EvaluationNode::EvaluationNode(ProjectionName projectionName, ProjectionType pro
: Base(std::move(child),
make<ExpressionBinder>(std::move(projectionName), std::move(projection))) {
assertNodeSort(getChild());
- tassert(6684504, "Empty projection name", !getProjectionName().empty());
}
bool EvaluationNode::operator==(const EvaluationNode& other) const {
@@ -331,7 +330,7 @@ static ProjectionNameVector createSargableBindings(const PartialSchemaRequiremen
static ProjectionNameVector createSargableReferences(const PartialSchemaRequirements& reqMap) {
ProjectionNameOrderPreservingSet result;
for (const auto& entry : reqMap) {
- result.emplace_back(entry.first._projectionName);
+ result.emplace_back(*entry.first._projectionName);
}
return result.getVector();
}
@@ -378,7 +377,7 @@ SargableNode::SargableNode(PartialSchemaRequirements reqMap,
for (const auto& [key, req] : _reqMap) {
tassert(6624088,
"SargableNode cannot reference an internally bound projection",
- boundsProjectionNameSet.count(key._projectionName) == 0);
+ boundsProjectionNameSet.count(*key._projectionName) == 0);
}
}
diff --git a/src/mongo/db/query/optimizer/node.h b/src/mongo/db/query/optimizer/node.h
index 894eeaeb353..cded5e129e9 100644
--- a/src/mongo/db/query/optimizer/node.h
+++ b/src/mongo/db/query/optimizer/node.h
@@ -844,11 +844,6 @@ public:
private:
properties::DistributionRequirement _distribution;
-
- /**
- * Defined for hash and range-based partitioning.
- */
- const ProjectionName _projectionName;
};
/**
diff --git a/src/mongo/db/query/optimizer/opt_phase_manager.cpp b/src/mongo/db/query/optimizer/opt_phase_manager.cpp
index fdfd911dd5c..79e6b34201a 100644
--- a/src/mongo/db/query/optimizer/opt_phase_manager.cpp
+++ b/src/mongo/db/query/optimizer/opt_phase_manager.cpp
@@ -81,14 +81,17 @@ OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet,
}
static std::string generateFreeVarsAssertMsg(const VariableEnvironment& env) {
- std::string result;
+ str::stream os;
+ bool first = true;
for (const auto& name : env.freeVariableNames()) {
- if (!result.empty()) {
- result += ", ";
+ if (first) {
+ first = false;
+ } else {
+ os << ", ";
}
- result += name;
+ os << name;
}
- return result;
+ return os;
}
template <OptPhase phase, class C>
diff --git a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
index deea5286501..ff8977310a4 100644
--- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
@@ -1529,7 +1529,7 @@ TEST(PhysRewriter, FilterIndexingStress) {
os << "field" << index;
result = make<FilterNode>(
- make<EvalFilter>(make<PathGet>(os.str(),
+ make<EvalFilter>(make<PathGet>(FieldNameType{os.str()},
make<PathTraverse>(make<PathCompare>(Operations::Eq,
Constant::int64(0)),
PathTraverse::kSingleLevel)),
@@ -1916,12 +1916,12 @@ TEST(PhysRewriter, FilterReorder) {
ProjectionName projName = prefixId.getNextId("field");
hints.emplace(
PartialSchemaKey{"root",
- make<PathGet>(projName,
+ make<PathGet>(FieldNameType{projName.value()},
make<PathTraverse>(make<PathIdentity>(),
PathTraverse::kSingleLevel))},
kDefaultSelectivity * (kFilterCount - i));
result = make<FilterNode>(
- make<EvalFilter>(make<PathGet>(std::move(projName),
+ make<EvalFilter>(make<PathGet>(FieldNameType{projName.value()},
make<PathTraverse>(make<PathCompare>(Operations::Eq,
Constant::int64(i)),
PathTraverse::kSingleLevel)),
diff --git a/src/mongo/db/query/optimizer/props.cpp b/src/mongo/db/query/optimizer/props.cpp
index c0079a78ed2..07fd05ede30 100644
--- a/src/mongo/db/query/optimizer/props.cpp
+++ b/src/mongo/db/query/optimizer/props.cpp
@@ -358,7 +358,7 @@ size_t DistributionHash::operator()(
size_t result = 0;
updateHash(result, std::hash<DistributionType>()(distributionAndProjections._type));
for (const ProjectionName& projectionName : distributionAndProjections._projectionNames) {
- updateHash(result, std::hash<ProjectionName>()(projectionName));
+ updateHash(result, ProjectionName::Hasher()(projectionName));
}
return result;
}
diff --git a/src/mongo/db/query/optimizer/reference_tracker.cpp b/src/mongo/db/query/optimizer/reference_tracker.cpp
index a21aa06b25d..bf2dbbfac7d 100644
--- a/src/mongo/db/query/optimizer/reference_tracker.cpp
+++ b/src/mongo/db/query/optimizer/reference_tracker.cpp
@@ -35,7 +35,9 @@
namespace mongo::optimizer {
struct CollectedInfo {
- using VarRefsMap = opt::unordered_map<std::string, opt::unordered_map<const Variable*, bool>>;
+ using VarRefsMap = opt::unordered_map<ProjectionName,
+ opt::unordered_map<const Variable*, bool>,
+ ProjectionName::Hasher>;
/**
* All resolved variables so far, regardless of visibility in the ABT.
@@ -52,7 +54,10 @@ struct CollectedInfo {
* ABT. Maps from projection name to all Variable instances referencing that name. Variables
* move from 'freeVars' to 'useMap' when they are resolved.
*/
- opt::unordered_map<std::string, std::vector<std::reference_wrapper<const Variable>>> freeVars;
+ opt::unordered_map<ProjectionName,
+ std::vector<std::reference_wrapper<const Variable>>,
+ ProjectionName::Hasher>
+ freeVars;
/**
* Maps from a node to the definitions (projections) available for use in its ancestor nodes.
@@ -172,7 +177,7 @@ struct CollectedInfo {
* Records collected last variable references for a specific variable. Should only be called
* when the variable is guaranteed not to be referenced again in the ABT.
*/
- void finalizeLastRefs(const std::string& name) {
+ void finalizeLastRefs(const ProjectionName& name) {
if (auto it = varLastRefs.find(name); it != varLastRefs.end()) {
for (auto& [var, isLastRef] : it->second) {
if (isLastRef) {
@@ -742,15 +747,15 @@ bool VariableEnvironment::hasFreeVariables() const {
return !_info->freeVars.empty();
}
-opt::unordered_set<std::string> VariableEnvironment::freeVariableNames() const {
- opt::unordered_set<std::string> freeVarNames;
+ProjectionNameSet VariableEnvironment::freeVariableNames() const {
+ ProjectionNameSet freeVarNames;
for (auto&& [name, vars] : _info->freeVars) {
freeVarNames.insert(name);
}
return freeVarNames;
}
-size_t VariableEnvironment::freeOccurences(const std::string& variable) const {
+size_t VariableEnvironment::freeOccurences(const ProjectionName& variable) const {
auto it = _info->freeVars.find(variable);
if (it == _info->freeVars.end()) {
return 0;
diff --git a/src/mongo/db/query/optimizer/reference_tracker.h b/src/mongo/db/query/optimizer/reference_tracker.h
index 0b1425dec31..eb5016e7eec 100644
--- a/src/mongo/db/query/optimizer/reference_tracker.h
+++ b/src/mongo/db/query/optimizer/reference_tracker.h
@@ -53,7 +53,7 @@ struct Definition {
};
struct CollectedInfo;
-using DefinitionsMap = opt::unordered_map<ProjectionName, Definition>;
+using DefinitionsMap = opt::unordered_map<ProjectionName, Definition, ProjectionName::Hasher>;
struct VariableCollectorResult {
// The Variables referenced by the subtree.
@@ -62,7 +62,7 @@ struct VariableCollectorResult {
// variable resolution. Tracking these separately allows us to easily check, for example, which
// variables are referenced in but not defined by the subtree (i.e. variables which should be
// defined elsewhere in the ABT).
- opt::unordered_set<std::string> _definedVars;
+ ProjectionNameSet _definedVars;
};
/**
@@ -119,12 +119,12 @@ public:
bool hasFreeVariables() const;
- opt::unordered_set<std::string> freeVariableNames() const;
+ ProjectionNameSet freeVariableNames() const;
/**
* Returns the number of places in the ABT where there is a free Variable with name 'variable'.
*/
- size_t freeOccurences(const std::string& variable) const;
+ size_t freeOccurences(const ProjectionName& variable) const;
/**
* Returns whether 'var' is guaranteed to be the last access to the projection to which it
diff --git a/src/mongo/db/query/optimizer/rewrites/path.cpp b/src/mongo/db/query/optimizer/rewrites/path.cpp
index 83e5f6c45b5..fa03e569688 100644
--- a/src/mongo/db/query/optimizer/rewrites/path.cpp
+++ b/src/mongo/db/query/optimizer/rewrites/path.cpp
@@ -314,10 +314,10 @@ void PathFusion::tryFuseComposition(ABT& n, ABT& input) {
// TODO: handle Drop elements.
// Latest value per field.
- opt::unordered_map<FieldNameType, ABT> fieldMap;
+ opt::unordered_map<FieldNameType, ABT, FieldNameType::Hasher> fieldMap;
// Used to preserve the relative order in which fields are set on the result.
FieldPathType orderedFieldNames;
- boost::optional<std::set<FieldNameType>> toKeep;
+ boost::optional<FieldNameOrderedSet> toKeep;
Type inputType = Type::any;
if (auto constPtr = input.cast<Constant>(); constPtr != nullptr && constPtr->isObject()) {
diff --git a/src/mongo/db/query/optimizer/rewrites/path_lower.cpp b/src/mongo/db/query/optimizer/rewrites/path_lower.cpp
index d1cdf66c23d..0b21183f388 100644
--- a/src/mongo/db/query/optimizer/rewrites/path_lower.cpp
+++ b/src/mongo/db/query/optimizer/rewrites/path_lower.cpp
@@ -49,7 +49,7 @@ void EvalPathLowering::transport(ABT& n, const PathConstant&, ABT& c) {
}
void EvalPathLowering::transport(ABT& n, const PathIdentity&) {
- const std::string& name = _prefixId.getNextId("x");
+ const ProjectionName name{_prefixId.getNextId("x")};
n = make<LambdaAbstraction>(name, make<Variable>(name));
_changed = true;
@@ -62,7 +62,7 @@ void EvalPathLowering::transport(ABT& n, const PathLambda&, ABT& lam) {
void EvalPathLowering::transport(ABT& n, const PathDefault&, ABT& c) {
// if (exists(x), x, c)
- const std::string& name = _prefixId.getNextId("valDefault");
+ const ProjectionName name{_prefixId.getNextId("valDefault")};
n = make<LambdaAbstraction>(
name,
@@ -77,26 +77,26 @@ void EvalPathLowering::transport(ABT& n, const PathCompare&, ABT& c) {
}
void EvalPathLowering::transport(ABT& n, const PathGet& p, ABT& inner) {
- const std::string& name = _prefixId.getNextId("inputGet");
+ const ProjectionName name{_prefixId.getNextId("inputGet")};
n = make<LambdaAbstraction>(
name,
make<LambdaApplication>(
std::exchange(inner, make<Blackhole>()),
make<FunctionCall>("getField",
- makeSeq(make<Variable>(name), Constant::str(p.name())))));
+ makeSeq(make<Variable>(name), Constant::str(p.name().value())))));
_changed = true;
}
void EvalPathLowering::transport(ABT& n, const PathDrop& drop) {
// if (isObject(x), dropFields(x,...) , x)
// Alternatively, we can implement a special builtin function that does the comparison and drop.
- const std::string& name = _prefixId.getNextId("valDrop");
+ const ProjectionName name{_prefixId.getNextId("valDrop")};
std::vector<ABT> params;
params.emplace_back(make<Variable>(name));
for (const auto& fieldName : drop.getNames()) {
- params.emplace_back(Constant::str(fieldName));
+ params.emplace_back(Constant::str(fieldName.value()));
}
n = make<LambdaAbstraction>(
@@ -110,12 +110,12 @@ void EvalPathLowering::transport(ABT& n, const PathDrop& drop) {
void EvalPathLowering::transport(ABT& n, const PathKeep& keep) {
// if (isObject(x), keepFields(x,...) , x)
// Alternatively, we can implement a special builtin function that does the comparison and drop.
- const std::string& name = _prefixId.getNextId("valKeep");
+ const ProjectionName name{_prefixId.getNextId("valKeep")};
std::vector<ABT> params;
params.emplace_back(make<Variable>(name));
for (const auto& fieldName : keep.getNames()) {
- params.emplace_back(Constant::str(fieldName));
+ params.emplace_back(Constant::str(fieldName.value()));
}
n = make<LambdaAbstraction>(
@@ -128,7 +128,7 @@ void EvalPathLowering::transport(ABT& n, const PathKeep& keep) {
void EvalPathLowering::transport(ABT& n, const PathObj&) {
// if (isObject(x), x, Nothing)
- const std::string& name = _prefixId.getNextId("valObj");
+ const ProjectionName name{_prefixId.getNextId("valObj")};
n = make<LambdaAbstraction>(
name,
@@ -140,7 +140,7 @@ void EvalPathLowering::transport(ABT& n, const PathObj&) {
void EvalPathLowering::transport(ABT& n, const PathArr&) {
// if (isArray(x), x, Nothing)
- const std::string& name = _prefixId.getNextId("valArr");
+ const ProjectionName name{_prefixId.getNextId("valArr")};
n = make<LambdaAbstraction>(
name,
@@ -158,7 +158,7 @@ void EvalPathLowering::transport(ABT& n, const PathTraverse& p, ABT& inner) {
"Currently we allow only multi-level traversal under EvalPath",
p.getMaxDepth() == PathTraverse::kUnlimited);
- const std::string& name = _prefixId.getNextId("valTraverse");
+ const ProjectionName name{_prefixId.getNextId("valTraverse")};
n = make<LambdaAbstraction>(name,
make<FunctionCall>("traverseP",
@@ -169,8 +169,8 @@ void EvalPathLowering::transport(ABT& n, const PathTraverse& p, ABT& inner) {
}
void EvalPathLowering::transport(ABT& n, const PathField& p, ABT& inner) {
- const std::string& name = _prefixId.getNextId("inputField");
- const std::string& val = _prefixId.getNextId("valField");
+ const ProjectionName name{_prefixId.getNextId("inputField")};
+ const ProjectionName val{_prefixId.getNextId("valField")};
n = make<LambdaAbstraction>(
name,
@@ -179,22 +179,22 @@ void EvalPathLowering::transport(ABT& n, const PathField& p, ABT& inner) {
make<LambdaApplication>(
std::exchange(inner, make<Blackhole>()),
make<FunctionCall>("getField",
- makeSeq(make<Variable>(name), Constant::str(p.name())))),
- make<If>(
- make<BinaryOp>(Operations::Or,
- make<FunctionCall>("exists", makeSeq(make<Variable>(val))),
- make<FunctionCall>("isObject", makeSeq(make<Variable>(name)))),
- make<FunctionCall>(
- "setField",
- makeSeq(make<Variable>(name), Constant::str(p.name()), make<Variable>(val))),
- make<Variable>(name))));
+ makeSeq(make<Variable>(name), Constant::str(p.name().value())))),
+ make<If>(make<BinaryOp>(Operations::Or,
+ make<FunctionCall>("exists", makeSeq(make<Variable>(val))),
+ make<FunctionCall>("isObject", makeSeq(make<Variable>(name)))),
+ make<FunctionCall>("setField",
+ makeSeq(make<Variable>(name),
+ Constant::str(p.name().value()),
+ make<Variable>(val))),
+ make<Variable>(name))));
_changed = true;
}
void EvalPathLowering::transport(ABT& n, const PathComposeM&, ABT& p1, ABT& p2) {
// p1 * p2 -> (p2 (p1 input))
- const std::string& name = _prefixId.getNextId("inputComposeM");
+ const ProjectionName name{_prefixId.getNextId("inputComposeM")};
n = make<LambdaAbstraction>(
name,
@@ -251,7 +251,7 @@ void EvalFilterLowering::transport(ABT& n, const PathLambda&, ABT& lam) {
}
void EvalFilterLowering::transport(ABT& n, const PathDefault&, ABT& c) {
- const std::string& name = _prefixId.getNextId("valDefault");
+ const ProjectionName name{_prefixId.getNextId("valDefault")};
n = make<LambdaAbstraction>(
name,
@@ -263,7 +263,7 @@ void EvalFilterLowering::transport(ABT& n, const PathDefault&, ABT& c) {
}
void EvalFilterLowering::transport(ABT& n, const PathCompare& cmp, ABT& c) {
- const std::string& name = _prefixId.getNextId("valCmp");
+ const ProjectionName name{_prefixId.getNextId("valCmp")};
if (cmp.op() == Operations::Eq) {
n = make<LambdaAbstraction>(
@@ -289,16 +289,16 @@ void EvalFilterLowering::transport(ABT& n, const PathCompare& cmp, ABT& c) {
}
void EvalFilterLowering::transport(ABT& n, const PathGet& p, ABT& inner) {
- const std::string& name = _prefixId.getNextId("inputGet");
+ const ProjectionName name{_prefixId.getNextId("inputGet")};
int idx;
- bool isNumber = NumberParser{}(p.name(), &idx).isOK();
+ bool isNumber = NumberParser{}(p.name().value(), &idx).isOK();
n = make<LambdaAbstraction>(
name,
make<LambdaApplication>(
std::exchange(inner, make<Blackhole>()),
make<FunctionCall>(isNumber ? "getFieldOrElement" : "getField",
- makeSeq(make<Variable>(name), Constant::str(p.name())))));
+ makeSeq(make<Variable>(name), Constant::str(p.name().value())))));
_changed = true;
}
@@ -311,14 +311,14 @@ void EvalFilterLowering::transport(ABT& n, const PathKeep& keep) {
}
void EvalFilterLowering::transport(ABT& n, const PathObj&) {
- const std::string& name = _prefixId.getNextId("valObj");
+ const ProjectionName name{_prefixId.getNextId("valObj")};
n = make<LambdaAbstraction>(name,
make<FunctionCall>("isObject", makeSeq(make<Variable>(name))));
_changed = true;
}
void EvalFilterLowering::transport(ABT& n, const PathArr&) {
- const std::string& name = _prefixId.getNextId("valArr");
+ const ProjectionName name{_prefixId.getNextId("valArr")};
n = make<LambdaAbstraction>(name, make<FunctionCall>("isArray", makeSeq(make<Variable>(name))));
_changed = true;
}
@@ -327,7 +327,7 @@ void EvalFilterLowering::prepare(ABT& n, const PathTraverse& t) {
int idx;
// This is a bad hack that detect if a child is number path element
if (auto child = t.getPath().cast<PathGet>();
- child && NumberParser{}(child->name(), &idx).isOK()) {
+ child && NumberParser{}(child->name().value(), &idx).isOK()) {
_traverseStack.emplace_back(n.ref());
}
}
@@ -339,7 +339,7 @@ void EvalFilterLowering::transport(ABT& n, const PathTraverse& p, ABT& inner) {
"Currently we allow only single-level traversal under EvalFilter",
p.getMaxDepth() == PathTraverse::kSingleLevel);
- const std::string& name = _prefixId.getNextId("valTraverse");
+ const ProjectionName name{_prefixId.getNextId("valTraverse")};
ABT numberPath = Constant::boolean(false);
if (!_traverseStack.empty() && _traverseStack.back() == n.ref()) {
@@ -360,7 +360,7 @@ void EvalFilterLowering::transport(ABT& n, const PathField& p, ABT& inner) {
}
void EvalFilterLowering::transport(ABT& n, const PathComposeM&, ABT& p1, ABT& p2) {
- const std::string& name = _prefixId.getNextId("inputComposeM");
+ const ProjectionName name{_prefixId.getNextId("inputComposeM")};
n = make<LambdaAbstraction>(
name,
@@ -376,7 +376,7 @@ void EvalFilterLowering::transport(ABT& n, const PathComposeM&, ABT& p1, ABT& p2
}
void EvalFilterLowering::transport(ABT& n, const PathComposeA&, ABT& p1, ABT& p2) {
- const std::string& name = _prefixId.getNextId("inputComposeA");
+ const ProjectionName name{_prefixId.getNextId("inputComposeA")};
n = make<LambdaAbstraction>(
name,
diff --git a/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp b/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
index 6a04c6648f8..5d5a37b453a 100644
--- a/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
@@ -410,7 +410,7 @@ TEST(Path, Fuse5) {
auto project = make<EvaluationNode>(
"x",
- make<EvalPath>(make<PathKeep>(PathKeep::NameSet{"a", "b", "c"}), make<Variable>("root")),
+ make<EvalPath>(make<PathKeep>(FieldNameOrderedSet{"a", "b", "c"}), make<Variable>("root")),
std::move(scanNode));
// Get "a" Traverse Compare= 2
@@ -494,11 +494,11 @@ TEST(Path, Fuse6) {
auto project = make<EvaluationNode>(
"x",
- make<EvalPath>(
- make<PathComposeM>(make<PathComposeM>(make<PathObj>(),
- make<PathKeep>(PathKeep::NameSet{"a", "b", "c"})),
- make<PathField>("a", make<PathConstant>(Constant::emptyObject()))),
- make<Variable>("root")),
+ make<EvalPath>(make<PathComposeM>(
+ make<PathComposeM>(make<PathObj>(),
+ make<PathKeep>(FieldNameOrderedSet{"a", "b", "c"})),
+ make<PathField>("a", make<PathConstant>(Constant::emptyObject()))),
+ make<Variable>("root")),
std::move(scanNode));
auto tree = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"x"}},
@@ -576,7 +576,7 @@ TEST(Path, Fuse7) {
"py",
make<EvalPath>(
make<PathComposeM>(
- make<PathComposeM>(make<PathKeep>(PathKeep::NameSet{"a"}),
+ make<PathComposeM>(make<PathKeep>(FieldNameOrderedSet{"a"}),
make<PathField>("a", make<PathConstant>(make<Variable>("px")))),
make<PathDefault>(Constant::emptyObject())),
make<Variable>("root")),
@@ -873,9 +873,9 @@ TEST(Path, ProjElim2) {
TEST(Path, ProjElim3) {
auto node = make<ScanNode>("root", "test");
- std::string var = "root";
+ ProjectionName var{"root"};
for (int i = 0; i < 100; ++i) {
- std::string newVar = "p" + std::to_string(i);
+ ProjectionName newVar{"p" + std::to_string(i)};
node = make<EvaluationNode>(
newVar,
// make<FunctionCall>("anyFunctionWillDo", makeSeq(make<Variable>(var))),
diff --git a/src/mongo/db/query/optimizer/syntax/expr.cpp b/src/mongo/db/query/optimizer/syntax/expr.cpp
index 94b8b771ef2..3b340381cb6 100644
--- a/src/mongo/db/query/optimizer/syntax/expr.cpp
+++ b/src/mongo/db/query/optimizer/syntax/expr.cpp
@@ -57,7 +57,7 @@ ABT Constant::createFromCopy(const sbe::value::TypeTags tag, const sbe::value::V
return make<Constant>(copy.first, copy.second);
}
-ABT Constant::str(std::string str) {
+ABT Constant::str(StringData str) {
// Views are non-owning so we have to make a copy.
auto [tag, val] = makeNewString(str);
return make<Constant>(tag, val);
diff --git a/src/mongo/db/query/optimizer/syntax/expr.h b/src/mongo/db/query/optimizer/syntax/expr.h
index 10046f183e0..9d7fd18b181 100644
--- a/src/mongo/db/query/optimizer/syntax/expr.h
+++ b/src/mongo/db/query/optimizer/syntax/expr.h
@@ -51,7 +51,7 @@ public:
static ABT createFromCopy(sbe::value::TypeTags tag, sbe::value::Value val);
- static ABT str(std::string str);
+ static ABT str(StringData str);
static ABT int32(int32_t valueInt32);
static ABT int64(int64_t valueInt64);
@@ -129,12 +129,10 @@ private:
* elsewhere.
*/
class Variable final : public Operator<0>, public ExpressionSyntaxSort {
- std::string _name;
+ ProjectionName _name;
public:
- Variable(std::string inName) : _name(std::move(inName)) {
- tassert(6684503, "Empty variable name", !_name.empty());
- }
+ Variable(ProjectionName inName) : _name(std::move(inName)) {}
bool operator==(const Variable& other) const {
return _name == other._name;
@@ -244,10 +242,10 @@ public:
class Let final : public Operator<2>, public ExpressionSyntaxSort {
using Base = Operator<2>;
- std::string _varName;
+ ProjectionName _varName;
public:
- Let(std::string var, ABT inBind, ABT inExpr)
+ Let(ProjectionName var, ABT inBind, ABT inExpr)
: Base(std::move(inBind), std::move(inExpr)), _varName(std::move(var)) {
assertExprSort(bind());
assertExprSort(in());
@@ -278,10 +276,10 @@ public:
class LambdaAbstraction final : public Operator<1>, public ExpressionSyntaxSort {
using Base = Operator<1>;
- std::string _varName;
+ ProjectionName _varName;
public:
- LambdaAbstraction(std::string var, ABT inBody)
+ LambdaAbstraction(ProjectionName var, ABT inBody)
: Base(std::move(inBody)), _varName(std::move(var)) {
assertExprSort(getBody());
}
diff --git a/src/mongo/db/query/optimizer/syntax/path.h b/src/mongo/db/query/optimizer/syntax/path.h
index 84f219afa19..e949f48216c 100644
--- a/src/mongo/db/query/optimizer/syntax/path.h
+++ b/src/mongo/db/query/optimizer/syntax/path.h
@@ -169,20 +169,18 @@ public:
*/
class PathDrop final : public Operator<0>, public PathSyntaxSort {
public:
- using NameSet = std::set<std::string>;
-
- PathDrop(NameSet inNames) : _names(std::move(inNames)) {}
+ PathDrop(FieldNameOrderedSet inNames) : _names(std::move(inNames)) {}
bool operator==(const PathDrop& other) const {
return _names == other._names;
}
- const NameSet& getNames() const {
+ const auto& getNames() const {
return _names;
}
private:
- const NameSet _names;
+ const FieldNameOrderedSet _names;
};
/**
@@ -193,20 +191,18 @@ private:
*/
class PathKeep final : public Operator<0>, public PathSyntaxSort {
public:
- using NameSet = std::set<std::string>;
-
- PathKeep(NameSet inNames) : _names(std::move(inNames)) {}
+ PathKeep(FieldNameOrderedSet inNames) : _names(std::move(inNames)) {}
bool operator==(const PathKeep other) const {
return _names == other._names;
}
- const NameSet& getNames() const {
+ const auto& getNames() const {
return _names;
}
private:
- const NameSet _names;
+ const FieldNameOrderedSet _names;
};
/**
@@ -290,10 +286,11 @@ private:
*/
class PathField final : public Operator<1>, public PathSyntaxSort {
using Base = Operator<1>;
- std::string _name;
+ FieldNameType _name;
public:
- PathField(std::string inName, ABT inPath) : Base(std::move(inPath)), _name(std::move(inName)) {
+ PathField(FieldNameType inName, ABT inPath)
+ : Base(std::move(inPath)), _name(std::move(inName)) {
assertPathSort(getPath());
}
@@ -323,10 +320,10 @@ public:
*/
class PathGet final : public Operator<1>, public PathSyntaxSort {
using Base = Operator<1>;
- std::string _name;
+ FieldNameType _name;
public:
- PathGet(std::string inName, ABT inPath) : Base(std::move(inPath)), _name(std::move(inName)) {
+ PathGet(FieldNameType inName, ABT inPath) : Base(std::move(inPath)), _name(std::move(inName)) {
assertPathSort(getPath());
}
diff --git a/src/mongo/db/query/optimizer/syntax/syntax.h b/src/mongo/db/query/optimizer/syntax/syntax.h
index 03f35718871..3c1d23d590e 100644
--- a/src/mongo/db/query/optimizer/syntax/syntax.h
+++ b/src/mongo/db/query/optimizer/syntax/syntax.h
@@ -33,6 +33,7 @@
#include "mongo/db/query/optimizer/algebra/operator.h"
#include "mongo/db/query/optimizer/algebra/polyvalue.h"
+#include "mongo/db/query/optimizer/defs.h"
#include "mongo/db/query/optimizer/syntax/syntax_fwd_declare.h"
#include "mongo/db/query/optimizer/utils/printable_enum.h"
#include "mongo/util/assert_util.h"
@@ -236,7 +237,7 @@ public:
/**
* Construct Variable objects out of provided vector of strings.
*/
- References(const std::vector<std::string>& names) : Base(ABTVector{}) {
+ References(const ProjectionNameVector& names) : Base(ABTVector{}) {
// Construct actual Variable objects from names and make them the children of this object.
for (const auto& name : names) {
nodes().emplace_back(make<Variable>(name));
@@ -266,17 +267,17 @@ public:
*/
class ExpressionBinder : public OperatorDynamicHomogenous {
using Base = OperatorDynamicHomogenous;
- std::vector<std::string> _names;
+ ProjectionNameVector _names;
public:
- ExpressionBinder(std::string name, ABT expr) : Base(makeSeq(std::move(expr))) {
+ ExpressionBinder(ProjectionName name, ABT expr) : Base(makeSeq(std::move(expr))) {
_names.emplace_back(std::move(name));
for (const auto& node : nodes()) {
assertExprSort(node);
}
}
- ExpressionBinder(std::vector<std::string> names, ABTVector exprs)
+ ExpressionBinder(ProjectionNameVector names, ABTVector exprs)
: Base(std::move(exprs)), _names(std::move(names)) {
for (const auto& node : nodes()) {
assertExprSort(node);
@@ -287,7 +288,7 @@ public:
return _names == other._names && exprs() == other.exprs();
}
- const std::vector<std::string>& names() const {
+ const ProjectionNameVector& names() const {
return _names;
}
diff --git a/src/mongo/db/query/optimizer/utils/abt_compare.cpp b/src/mongo/db/query/optimizer/utils/abt_compare.cpp
index 180af41c6d0..77151482d2f 100644
--- a/src/mongo/db/query/optimizer/utils/abt_compare.cpp
+++ b/src/mongo/db/query/optimizer/utils/abt_compare.cpp
@@ -56,7 +56,11 @@ int compareContainers(const T& n1, const T& n2, const Fn& fn) {
return 0;
}
-int compareStrings(const std::string& v1, const std::string& v2) {
+/**
+ * Used to compare strings and strong string aliases.
+ */
+template <class T>
+int compareStrings(const T& v1, const T& v2) {
return v1.compare(v2);
}
@@ -220,11 +224,11 @@ private:
}
int compare(const PathDrop& node, const PathDrop& other) {
- return compareContainers(node.getNames(), other.getNames(), compareStrings);
+ return compareContainers(node.getNames(), other.getNames(), compareStrings<FieldNameType>);
}
int compare(const PathKeep& node, const PathKeep& other) {
- return compareContainers(node.getNames(), other.getNames(), compareStrings);
+ return compareContainers(node.getNames(), other.getNames(), compareStrings<FieldNameType>);
}
int compare(const PathObj& node, const PathObj& other) {
@@ -277,7 +281,8 @@ private:
}
int compare(const ExpressionBinder& node, const ExpressionBinder& other) {
- const int cmp = compareContainers(node.names(), other.names(), compareStrings);
+ const int cmp =
+ compareContainers(node.names(), other.names(), compareStrings<ProjectionName>);
if (cmp != 0) {
return cmp;
}
diff --git a/src/mongo/db/query/optimizer/utils/abt_hash.cpp b/src/mongo/db/query/optimizer/utils/abt_hash.cpp
index b9f8d5a2517..c447d4b6458 100644
--- a/src/mongo/db/query/optimizer/utils/abt_hash.cpp
+++ b/src/mongo/db/query/optimizer/utils/abt_hash.cpp
@@ -37,7 +37,7 @@ namespace mongo::optimizer {
static size_t computeCollationHash(const properties::CollationRequirement& prop) {
size_t collationHash = 17;
for (const auto& entry : prop.getCollationSpec()) {
- updateHash(collationHash, std::hash<ProjectionName>()(entry.first));
+ updateHash(collationHash, ProjectionName::Hasher()(entry.first));
updateHash(collationHash, std::hash<CollationOp>()(entry.second));
}
return collationHash;
@@ -53,7 +53,7 @@ static size_t computeLimitSkipHash(const properties::LimitSkipRequirement& prop)
static size_t computePropertyProjectionsHash(const ProjectionNameVector& projections) {
size_t resultHash = 17;
for (const ProjectionName& projection : projections) {
- updateHashUnordered(resultHash, std::hash<ProjectionName>()(projection));
+ updateHashUnordered(resultHash, ProjectionName::Hasher()(projection));
}
return resultHash;
}
@@ -123,9 +123,14 @@ static size_t computePartialSchemaReqHash(const PartialSchemaRequirements& reqMa
IntervalHasher<IntervalReqExpr> intervalHasher;
for (const auto& [key, req] : reqMap) {
- updateHash(result, std::hash<ProjectionName>()(key._projectionName));
+ if (const auto& projName = key._projectionName) {
+ updateHash(result, ProjectionName::Hasher()(*projName));
+ }
updateHash(result, ABTHashGenerator::generate(key._path));
- updateHash(result, std::hash<ProjectionName>()(req.getBoundProjectionName().value_or("")));
+
+ if (const auto& projName = req.getBoundProjectionName()) {
+ updateHash(result, ProjectionName::Hasher()(*projName));
+ }
updateHash(result, intervalHasher.compute(req.getIntervals()));
updateHash(result, std::hash<bool>()(req.getIsPerfOnly()));
}
@@ -152,7 +157,9 @@ public:
}
size_t transport(const ExpressionBinder& binders, std::vector<size_t> inResults) {
- return computeHashSeq<2>(computeVectorHash(binders.names()), computeVectorHash(inResults));
+ return computeHashSeq<2>(
+ computeVectorHash<ProjectionName, ProjectionName::Hasher>(binders.names()),
+ computeVectorHash(inResults));
}
size_t transport(const ScanNode& node, size_t bindResult) {
@@ -194,7 +201,7 @@ public:
size_t leftChildResult,
size_t rightChildResult) {
// Specifically always including children.
- return computeHashSeq<45>(std::hash<ProjectionName>()(node.getScanProjectionName()),
+ return computeHashSeq<45>(ProjectionName::Hasher()(node.getScanProjectionName()),
leftChildResult,
rightChildResult);
}
@@ -267,7 +274,7 @@ public:
}
size_t transport(const Variable& expr) {
- return computeHashSeq<18>(std::hash<std::string>()(expr.name()));
+ return computeHashSeq<18>(ProjectionName::Hasher()(expr.name()));
}
size_t transport(const UnaryOp& expr, size_t inResult) {
@@ -283,11 +290,11 @@ public:
}
size_t transport(const Let& expr, size_t bindResult, size_t exprResult) {
- return computeHashSeq<22>(std::hash<std::string>()(expr.varName()), bindResult, exprResult);
+ return computeHashSeq<22>(ProjectionName::Hasher()(expr.varName()), bindResult, exprResult);
}
size_t transport(const LambdaAbstraction& expr, size_t inResult) {
- return computeHashSeq<23>(std::hash<std::string>()(expr.varName()), inResult);
+ return computeHashSeq<23>(ProjectionName::Hasher()(expr.varName()), inResult);
}
size_t transport(const LambdaApplication& expr, size_t lambdaResult, size_t argumentResult) {
@@ -336,16 +343,16 @@ public:
size_t transport(const PathDrop& path) {
size_t namesHash = 17;
- for (const std::string& name : path.getNames()) {
- updateHash(namesHash, std::hash<std::string>()(name));
+ for (const FieldNameType& name : path.getNames()) {
+ updateHash(namesHash, FieldNameType::Hasher()(name));
}
return computeHashSeq<34>(namesHash);
}
size_t transport(const PathKeep& path) {
size_t namesHash = 17;
- for (const std::string& name : path.getNames()) {
- updateHash(namesHash, std::hash<std::string>()(name));
+ for (const FieldNameType& name : path.getNames()) {
+ updateHash(namesHash, FieldNameType::Hasher()(name));
}
return computeHashSeq<35>(namesHash);
}
@@ -363,11 +370,11 @@ public:
}
size_t transport(const PathField& path, size_t inResult) {
- return computeHashSeq<39>(std::hash<std::string>()(path.name()), inResult);
+ return computeHashSeq<39>(FieldNameType::Hasher()(path.name()), inResult);
}
size_t transport(const PathGet& path, size_t inResult) {
- return computeHashSeq<40>(std::hash<std::string>()(path.name()), inResult);
+ return computeHashSeq<40>(FieldNameType::Hasher()(path.name()), inResult);
}
size_t transport(const PathComposeM& path, size_t leftResult, size_t rightResult) {
diff --git a/src/mongo/db/query/optimizer/utils/reftracker_utils.cpp b/src/mongo/db/query/optimizer/utils/reftracker_utils.cpp
index 2a6fb284dd0..18b0fb020b4 100644
--- a/src/mongo/db/query/optimizer/utils/reftracker_utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/reftracker_utils.cpp
@@ -41,142 +41,140 @@ namespace mongo::optimizer {
class NodeVariableTracker {
public:
template <typename T, typename... Ts>
- VariableNameSetType walk(const T&, Ts&&...) {
+ ProjectionNameSet walk(const T&, Ts&&...) {
static_assert(!std::is_base_of_v<Node, T>, "Nodes must implement variable tracking");
// Default case: no variables.
return {};
}
- VariableNameSetType walk(const ScanNode& /*node*/, const ABT& /*binds*/) {
+ ProjectionNameSet walk(const ScanNode& /*node*/, const ABT& /*binds*/) {
return {};
}
- VariableNameSetType walk(const ValueScanNode& /*node*/, const ABT& /*binds*/) {
+ ProjectionNameSet walk(const ValueScanNode& /*node*/, const ABT& /*binds*/) {
return {};
}
- VariableNameSetType walk(const PhysicalScanNode& /*node*/, const ABT& /*binds*/) {
+ ProjectionNameSet walk(const PhysicalScanNode& /*node*/, const ABT& /*binds*/) {
return {};
}
- VariableNameSetType walk(const CoScanNode& /*node*/) {
+ ProjectionNameSet walk(const CoScanNode& /*node*/) {
return {};
}
- VariableNameSetType walk(const IndexScanNode& /*node*/, const ABT& /*binds*/) {
+ ProjectionNameSet walk(const IndexScanNode& /*node*/, const ABT& /*binds*/) {
return {};
}
- VariableNameSetType walk(const SeekNode& /*node*/, const ABT& /*binds*/, const ABT& refs) {
+ ProjectionNameSet walk(const SeekNode& /*node*/, const ABT& /*binds*/, const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const MemoLogicalDelegatorNode& /*node*/) {
+ ProjectionNameSet walk(const MemoLogicalDelegatorNode& /*node*/) {
return {};
}
- VariableNameSetType walk(const MemoPhysicalDelegatorNode& /*node*/) {
+ ProjectionNameSet walk(const MemoPhysicalDelegatorNode& /*node*/) {
return {};
}
- VariableNameSetType walk(const FilterNode& /*node*/, const ABT& /*child*/, const ABT& expr) {
+ ProjectionNameSet walk(const FilterNode& /*node*/, const ABT& /*child*/, const ABT& expr) {
return extractFromABT(expr);
}
- VariableNameSetType walk(const EvaluationNode& /*node*/,
- const ABT& /*child*/,
- const ABT& expr) {
+ ProjectionNameSet walk(const EvaluationNode& /*node*/, const ABT& /*child*/, const ABT& expr) {
return extractFromABT(expr);
}
- VariableNameSetType walk(const SargableNode& /*node*/,
- const ABT& /*child*/,
- const ABT& /*binds*/,
- const ABT& refs) {
+ ProjectionNameSet walk(const SargableNode& /*node*/,
+ const ABT& /*child*/,
+ const ABT& /*binds*/,
+ const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const RIDIntersectNode& /*node*/,
- const ABT& /*leftChild*/,
- const ABT& /*rightChild*/) {
+ ProjectionNameSet walk(const RIDIntersectNode& /*node*/,
+ const ABT& /*leftChild*/,
+ const ABT& /*rightChild*/) {
return {};
}
- VariableNameSetType walk(const BinaryJoinNode& /*node*/,
- const ABT& /*leftChild*/,
- const ABT& /*rightChild*/,
- const ABT& expr) {
+ ProjectionNameSet walk(const BinaryJoinNode& /*node*/,
+ const ABT& /*leftChild*/,
+ const ABT& /*rightChild*/,
+ const ABT& expr) {
return extractFromABT(expr);
}
- VariableNameSetType walk(const HashJoinNode& /*node*/,
- const ABT& /*leftChild*/,
- const ABT& /*rightChild*/,
- const ABT& refs) {
+ ProjectionNameSet walk(const HashJoinNode& /*node*/,
+ const ABT& /*leftChild*/,
+ const ABT& /*rightChild*/,
+ const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const MergeJoinNode& /*node*/,
- const ABT& /*leftChild*/,
- const ABT& /*rightChild*/,
- const ABT& refs) {
+ ProjectionNameSet walk(const MergeJoinNode& /*node*/,
+ const ABT& /*leftChild*/,
+ const ABT& /*rightChild*/,
+ const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const UnionNode& /*node*/,
- const ABTVector& /*children*/,
- const ABT& /*binder*/,
- const ABT& refs) {
+ ProjectionNameSet walk(const UnionNode& /*node*/,
+ const ABTVector& /*children*/,
+ const ABT& /*binder*/,
+ const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const GroupByNode& /*node*/,
- const ABT& /*child*/,
- const ABT& /*aggBinder*/,
- const ABT& aggRefs,
- const ABT& /*groupbyBinder*/,
- const ABT& groupbyRefs) {
- VariableNameSetType result;
+ ProjectionNameSet walk(const GroupByNode& /*node*/,
+ const ABT& /*child*/,
+ const ABT& /*aggBinder*/,
+ const ABT& aggRefs,
+ const ABT& /*groupbyBinder*/,
+ const ABT& groupbyRefs) {
+ ProjectionNameSet result;
extractFromABT(result, aggRefs);
extractFromABT(result, groupbyRefs);
return result;
}
- VariableNameSetType walk(const UnwindNode& /*node*/,
- const ABT& /*child*/,
- const ABT& /*binds*/,
- const ABT& refs) {
+ ProjectionNameSet walk(const UnwindNode& /*node*/,
+ const ABT& /*child*/,
+ const ABT& /*binds*/,
+ const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const UniqueNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
+ ProjectionNameSet walk(const UniqueNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const CollationNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
+ ProjectionNameSet walk(const CollationNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const LimitSkipNode& /*node*/, const ABT& /*child*/) {
+ ProjectionNameSet walk(const LimitSkipNode& /*node*/, const ABT& /*child*/) {
return {};
}
- VariableNameSetType walk(const ExchangeNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
+ ProjectionNameSet walk(const ExchangeNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
return extractFromABT(refs);
}
- VariableNameSetType walk(const RootNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
+ ProjectionNameSet walk(const RootNode& /*node*/, const ABT& /*child*/, const ABT& refs) {
return extractFromABT(refs);
}
- static VariableNameSetType collect(const ABT& n) {
+ static ProjectionNameSet collect(const ABT& n) {
NodeVariableTracker tracker;
return algebra::walk<false>(n, tracker);
}
private:
- void extractFromABT(VariableNameSetType& vars, const ABT& v) {
+ void extractFromABT(ProjectionNameSet& vars, const ABT& v) {
const auto& result = VariableEnvironment::getVariables(v);
for (const Variable& var : result._variables) {
if (result._definedVars.count(var.name()) == 0) {
@@ -186,14 +184,14 @@ private:
}
}
- VariableNameSetType extractFromABT(const ABT& v) {
- VariableNameSetType result;
+ ProjectionNameSet extractFromABT(const ABT& v) {
+ ProjectionNameSet result;
extractFromABT(result, v);
return result;
}
};
-VariableNameSetType collectVariableReferences(const ABT& n) {
+ProjectionNameSet collectVariableReferences(const ABT& n) {
return NodeVariableTracker::collect(n);
}
diff --git a/src/mongo/db/query/optimizer/utils/reftracker_utils.h b/src/mongo/db/query/optimizer/utils/reftracker_utils.h
index 7e0ab3a64f2..6cf7746a99a 100644
--- a/src/mongo/db/query/optimizer/utils/reftracker_utils.h
+++ b/src/mongo/db/query/optimizer/utils/reftracker_utils.h
@@ -37,7 +37,6 @@ namespace mongo::optimizer {
/**
* Used to extract variable references from a node.
*/
-using VariableNameSetType = opt::unordered_set<std::string>;
-VariableNameSetType collectVariableReferences(const ABT& n);
+ProjectionNameSet collectVariableReferences(const ABT& n);
} // namespace mongo::optimizer
diff --git a/src/mongo/db/query/optimizer/utils/strong_string_alias.h b/src/mongo/db/query/optimizer/utils/strong_string_alias.h
new file mode 100644
index 00000000000..e78dd83e60d
--- /dev/null
+++ b/src/mongo/db/query/optimizer/utils/strong_string_alias.h
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/util/assert_util.h"
+#include "mongo/util/str.h"
+
+
+namespace mongo::optimizer {
+
+/**
+ * Strong string alias. It is used to provide strong type safety between various string types in the
+ * optimizer code. Instances with different tags are not constructable from, assignable to and
+ * comparable to each other. TagType needs to define a constexpr boolean "kAllowEmpty" which
+ * determines if empty strings are allowed.
+ */
+template <class TagType>
+class StrongStringAlias {
+public:
+ struct Hasher {
+ size_t operator()(const StrongStringAlias& v) const {
+ return std::hash<std::string>()(v._value);
+ }
+ };
+
+ // Allow implicit construction from a string literal, but not from a const char*.
+ template <size_t N>
+ StrongStringAlias(const char (&value)[N]) : _value(value){};
+
+ // We disallow empty strings based on the tag's kAllowEmpty field.
+ template <class TagType1 = TagType,
+ class = typename std::enable_if<!TagType1::kAllowEmpty>::type>
+ StrongStringAlias(const char (&value)[1]) = delete;
+
+ // Need to explicitly construct from StringData and const char*.
+ explicit StrongStringAlias(StringData value) : _value(value) {
+ if constexpr (!TagType::kAllowEmpty) {
+ invariant(!_value.empty());
+ }
+ }
+
+ StrongStringAlias(const StrongStringAlias<TagType>& other) = default;
+ StrongStringAlias(StrongStringAlias<TagType>&& other) = default;
+ StrongStringAlias& operator=(const StrongStringAlias& other) = default;
+ StrongStringAlias& operator=(StrongStringAlias&& other) = default;
+
+ bool operator==(const StrongStringAlias& other) const {
+ return _value == other._value;
+ }
+ bool operator!=(const StrongStringAlias& other) const {
+ return _value != other._value;
+ }
+
+ bool operator<(const StrongStringAlias& other) const {
+ return _value < other._value;
+ }
+
+ int compare(const StrongStringAlias& other) const {
+ return _value.compare(other._value);
+ }
+
+ template <size_t N>
+ bool operator==(const char (&value)[N]) const {
+ return _value == value;
+ }
+ template <size_t N>
+ bool operator!=(const char (&value)[N]) const {
+ return _value != value;
+ }
+
+ StringData value() const {
+ return _value;
+ }
+
+private:
+ std::string _value;
+};
+
+/**
+ * Exclude str::stream since it currently leads to ambiguous calls.
+ */
+template <class StreamType,
+ class TagType,
+ class = typename std::enable_if<!std::is_same<StreamType, str::stream>::value>::type>
+StreamType& operator<<(StreamType& stream, const StrongStringAlias<TagType>& t) {
+ return stream << t.value();
+}
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/query/optimizer/utils/utils.cpp b/src/mongo/db/query/optimizer/utils/utils.cpp
index d09aea3cbf4..84c1c4bcbb6 100644
--- a/src/mongo/db/query/optimizer/utils/utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/utils.cpp
@@ -60,10 +60,10 @@ bool isSimplePath(const ABT& node) {
return false;
}
-std::string PrefixId::getNextId(const std::string& key) {
+ProjectionName PrefixId::getNextId(const StringData& prefix) {
std::ostringstream os;
- os << key << "_" << _idCounterPerKey[key]++;
- return os.str();
+ os << prefix << "_" << _idCounterPerPrefix[prefix.toString()]++;
+ return ProjectionName{os.str()};
}
ProjectionNameOrderedSet convertToOrderedSet(ProjectionNameSet unordered) {
@@ -144,7 +144,7 @@ bool areCompoundIntervalsEqualities(const CompoundIntervalRequirement& intervals
return true;
}
-CollationSplitResult splitCollationSpec(const ProjectionName& ridProjName,
+CollationSplitResult splitCollationSpec(const boost::optional<ProjectionName>& ridProjName,
const ProjectionCollationSpec& collationSpec,
const ProjectionNameSet& leftProjections,
const ProjectionNameSet& rightProjections) {
@@ -220,7 +220,7 @@ public:
PartialSchemaRequirements newMap;
for (auto& [key, req] : pathResult->_reqMap) {
- if (!key._projectionName.empty()) {
+ if (key._projectionName) {
return {};
}
newMap.emplace(PartialSchemaKey{boundVarName, key._path}, std::move(req));
@@ -433,7 +433,7 @@ public:
auto intervalExpr = IntervalReqExpr::makeSingularDNF(IntervalRequirement{
{true /*inclusive*/, Constant::null()}, {true /*inclusive*/, Constant::null()}});
return {{PartialSchemaRequirements{
- {PartialSchemaKey{"" /*projectionName*/, make<PathIdentity>()},
+ {PartialSchemaKey{make<PathIdentity>()},
PartialSchemaRequirement{boost::none /*boundProjectionName*/,
std::move(intervalExpr),
false /*isPerfOnly*/}}}}};
@@ -456,7 +456,7 @@ public:
PartialSchemaRequirements newMap;
for (auto& entry : inputResult->_reqMap) {
- if (!entry.first._projectionName.empty()) {
+ if (entry.first._projectionName) {
return {};
}
@@ -467,7 +467,7 @@ public:
std::swap(appendedPath.cast<T>()->getPath(), path);
std::swap(path, appendedPath);
- newMap.emplace(PartialSchemaKey{"", std::move(path)}, std::move(entry.second));
+ newMap.emplace(PartialSchemaKey{std::move(path)}, std::move(entry.second));
}
inputResult->_reqMap = std::move(newMap);
@@ -533,7 +533,7 @@ public:
}
return {{PartialSchemaRequirements{
- {PartialSchemaKey{"" /*projectionName*/, make<PathIdentity>()},
+ {PartialSchemaKey{make<PathIdentity>()},
PartialSchemaRequirement{boost::none /*boundProjectionName*/,
std::move(*unionedInterval),
false /*isPerfOnly*/}}}}};
@@ -583,14 +583,14 @@ public:
auto intervalExpr = IntervalReqExpr::makeSingularDNF(IntervalRequirement{
{lowBoundInclusive, std::move(lowBound)}, {highBoundInclusive, std::move(highBound)}});
return {{PartialSchemaRequirements{
- {PartialSchemaKey{"" /*projectionName*/, make<PathIdentity>()},
+ {PartialSchemaKey{make<PathIdentity>()},
PartialSchemaRequirement{boost::none /*boundProjectionName*/,
std::move(intervalExpr),
false /*isPerfOnly*/}}}}};
}
ResultType transport(const ABT& n, const PathIdentity& pathIdentity) {
- return {{PartialSchemaRequirements{{{"" /*projectionName*/, n},
+ return {{PartialSchemaRequirements{{{n},
{boost::none /*boundProjectionName*/,
IntervalReqExpr::makeSingularDNF(),
false /*isPerfOnly*/}}}}};
@@ -615,7 +615,7 @@ public:
// If we have a path converter, attempt to convert directly into bounds.
if (auto conversion = _pathToInterval(n); conversion) {
return {{PartialSchemaRequirements{
- {PartialSchemaKey{"" /*projectionName*/, make<PathIdentity>()},
+ {PartialSchemaKey{make<PathIdentity>()},
PartialSchemaRequirement{boost::none /*boundProjectionName*/,
std::move(*conversion),
false /*isPerfOnly*/}}}}};
@@ -1147,7 +1147,7 @@ CandidateIndexes computeCandidateIndexes(PrefixId& prefixId,
if (!req.getIsPerfOnly()) {
// Only regular requirements are added to residual predicates.
const ProjectionName& tempProjName = getExistingOrTempProjForFieldName(
- prefixId, encodeIndexKeyName(indexField), fieldProjMap);
+ prefixId, FieldNameType{encodeIndexKeyName(indexField)}, fieldProjMap);
entry._residualRequirements.emplace_back(
PartialSchemaKey{tempProjName, std::move(*fusedPath._suffix)},
req,
@@ -1408,7 +1408,7 @@ void lowerPartialSchemaRequirement(const PartialSchemaKey& key,
if (const auto& boundProjName = req.getBoundProjectionName()) {
node = make<EvaluationNode>(*boundProjName,
- make<EvalPath>(key._path, make<Variable>(key._projectionName)),
+ make<EvalPath>(key._path, make<Variable>(*key._projectionName)),
std::move(node));
visitor(node);
@@ -1425,9 +1425,9 @@ void lowerPartialSchemaRequirement(const PartialSchemaKey& key,
path = key._path;
appender.append(path);
- node =
- make<FilterNode>(make<EvalFilter>(std::move(path), make<Variable>(key._projectionName)),
- std::move(node));
+ node = make<FilterNode>(
+ make<EvalFilter>(std::move(path), make<Variable>(*key._projectionName)),
+ std::move(node));
visitor(node);
}
}
@@ -1522,7 +1522,7 @@ void removeRedundantResidualPredicates(const ProjectionNameOrderPreservingSet& r
}
}
- residualTempProjections.insert(key._projectionName);
+ residualTempProjections.insert(*key._projectionName);
it++;
}
@@ -1769,7 +1769,7 @@ public:
_estimateStack.push_back(childSel);
FieldProjectionMap childMap = _fpmStack.back();
- if (childMap._ridProjection.empty()) {
+ if (!childMap._ridProjection) {
childMap._ridProjection = _ridProjName;
}
if (childCount > 1) {
@@ -1799,7 +1799,7 @@ public:
}
ProjectionNameVector unionProjectionNames;
- unionProjectionNames.push_back(innerMap._ridProjection);
+ unionProjectionNames.push_back(*innerMap._ridProjection);
for (const auto& [fieldName, projectionName] : innerMap._fieldProjections) {
unionProjectionNames.push_back(projectionName);
}
@@ -1815,7 +1815,7 @@ public:
make<FunctionCall>("$first", makeSeq(make<Variable>(projectionName))));
}
- ProjectionName sideSetProjectionName;
+ boost::optional<ProjectionName> sideSetProjectionName;
if constexpr (isIntersect) {
const ProjectionName sideIdProjectionName = _prefixId.getNextId("sideId");
unionProjectionNames.push_back(sideIdProjectionName);
@@ -1831,13 +1831,13 @@ public:
aggExpressions.emplace_back(
make<FunctionCall>("$addToSet", makeSeq(make<Variable>(sideIdProjectionName))));
- aggProjectionNames.push_back(sideSetProjectionName);
+ aggProjectionNames.push_back(*sideSetProjectionName);
}
ABT result = make<UnionNode>(std::move(unionProjectionNames), std::move(inputs));
_nodeCEMap.emplace(result.cast<Node>(), ce);
- result = make<GroupByNode>(ProjectionNameVector{innerMap._ridProjection},
+ result = make<GroupByNode>(ProjectionNameVector{*innerMap._ridProjection},
std::move(aggProjectionNames),
std::move(aggExpressions),
std::move(result));
@@ -1848,7 +1848,7 @@ public:
make<EvalFilter>(
make<PathCompare>(Operations::Eq, Constant::int64(inputSize)),
make<FunctionCall>("getArraySize",
- makeSeq(make<Variable>(sideSetProjectionName)))),
+ makeSeq(make<Variable>(*sideSetProjectionName)))),
std::move(result));
_nodeCEMap.emplace(result.cast<Node>(), ce);
}
diff --git a/src/mongo/db/query/optimizer/utils/utils.h b/src/mongo/db/query/optimizer/utils/utils.h
index 48589995a14..1d53c3ef3f1 100644
--- a/src/mongo/db/query/optimizer/utils/utils.h
+++ b/src/mongo/db/query/optimizer/utils/utils.h
@@ -44,11 +44,13 @@ inline void updateHashUnordered(size_t& result, const size_t hash) {
result ^= hash;
}
-template <class T, class T1 = std::conditional_t<std::is_arithmetic_v<T>, const T, const T&>>
+template <class T,
+ class Hasher = std::hash<T>,
+ class T1 = std::conditional_t<std::is_arithmetic_v<T>, const T, const T&>>
inline size_t computeVectorHash(const std::vector<T>& v) {
size_t result = 17;
for (T1 e : v) {
- updateHash(result, std::hash<T>()(e));
+ updateHash(result, Hasher()(e));
}
return result;
}
@@ -89,21 +91,21 @@ inline void maybeComposePaths(ABTVector& paths) {
while (paths.size() > 1) {
const size_t half = paths.size() / 2;
for (size_t i = 0; i < half; i++) {
- maybeComposePath<Element>(paths.at(i), std::move(paths.at(paths.size() - i - 1)));
+ maybeComposePath<Element>(paths.at(i), std::move(paths.back()));
+ paths.pop_back();
}
- paths.resize(paths.size() - half, make<Blackhole>());
}
}
/**
- * Used to vend out fresh ids for projection names.
+ * Used to vend out fresh projection names.
*/
class PrefixId {
public:
- std::string getNextId(const std::string& key);
+ ProjectionName getNextId(const StringData& prefix);
private:
- opt::unordered_map<std::string, int> _idCounterPerKey;
+ opt::unordered_map<std::string, uint64_t> _idCounterPerPrefix;
};
ProjectionNameOrderedSet convertToOrderedSet(ProjectionNameSet unordered);
@@ -136,7 +138,7 @@ struct CollationSplitResult {
* Split a collation requirement between an outer (left) and inner (right) side. The outer side must
* be a prefix in the collation spec, and the right side a suffix.
*/
-CollationSplitResult splitCollationSpec(const ProjectionName& ridProjName,
+CollationSplitResult splitCollationSpec(const boost::optional<ProjectionName>& ridProjName,
const ProjectionCollationSpec& collationSpec,
const ProjectionNameSet& leftProjections,
const ProjectionNameSet& rightProjections);
diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp
index ca8e52b6e1c..6250b02b87c 100644
--- a/src/mongo/db/query/sbe_stage_builder.cpp
+++ b/src/mongo/db/query/sbe_stage_builder.cpp
@@ -859,7 +859,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
}
}
- const std::string rootStr = "rowStoreRoot";
+ const optimizer::ProjectionName rootStr = "rowStoreRoot";
optimizer::FieldMapBuilder builder(rootStr, true);
// When building its output document (in 'recordSlot'), the 'ColumnStoreStage' should not try to