From e3c284153d17f317e8f517ca56c3e437982aaf6b Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:58:26 +0200 Subject: [PATCH] Make some member functions of `GroupBy` `const` or even `static` (#1510) This makes the static analysis happier and (in the case of the static methods) avoids the passing around of fully-fledged `GroupBy` objects. --- src/engine/GroupBy.cpp | 64 ++++++++++++++++----------------- src/engine/GroupBy.h | 57 ++++++++++++++++------------- src/engine/LazyGroupBy.cpp | 7 ++-- src/engine/LazyGroupBy.h | 4 +-- test/engine/LazyGroupByTest.cpp | 22 ++++++------ 5 files changed, 80 insertions(+), 74 deletions(-) diff --git a/src/engine/GroupBy.cpp b/src/engine/GroupBy.cpp index 21c61ab1c6..329b2b3890 100644 --- a/src/engine/GroupBy.cpp +++ b/src/engine/GroupBy.cpp @@ -332,7 +332,7 @@ ProtoResult GroupBy::computeResult(bool requestLaziness) { // Always request child operation to provide a lazy result if the aggregate // expressions allow to compute the full result in chunks metadataForUnsequentialData = - computeUnsequentialProcessingMetadata(aggregates); + computeUnsequentialProcessingMetadata(aggregates, _groupByVariables); subresult = _subtree->getResult(metadataForUnsequentialData.has_value()); } @@ -519,7 +519,7 @@ cppcoro::generator GroupBy::computeResultLazily( if (groupSplitAcrossTables) { lazyGroupBy.processBlock(evaluationContext, blockStart, blockEnd); lazyGroupBy.commitRow(resultTable, evaluationContext, - currentGroupBlock, *this); + currentGroupBlock); groupSplitAcrossTables = false; } else { // This processes the whole block in batches if possible @@ -566,13 +566,12 @@ cppcoro::generator GroupBy::computeResultLazily( sparqlExpression::EvaluationContext evaluationContext = createEvaluationContext(*localVocab, idTable); - lazyGroupBy.commitRow(resultTable, evaluationContext, currentGroupBlock, - *this); + lazyGroupBy.commitRow(resultTable, evaluationContext, currentGroupBlock); co_yield resultTable; } // _____________________________________________________________________________ -std::optional GroupBy::computeGroupByForSingleIndexScan() { +std::optional GroupBy::computeGroupByForSingleIndexScan() const { // The child must be an `IndexScan` for this optimization. auto indexScan = std::dynamic_pointer_cast(_subtree->getRootOperation()); @@ -621,7 +620,7 @@ std::optional GroupBy::computeGroupByForSingleIndexScan() { } // ____________________________________________________________________________ -std::optional GroupBy::computeGroupByObjectWithCount() { +std::optional GroupBy::computeGroupByObjectWithCount() const { // The child must be an `IndexScan` with exactly two variables. auto indexScan = std::dynamic_pointer_cast(_subtree->getRootOperation()); @@ -671,7 +670,7 @@ std::optional GroupBy::computeGroupByObjectWithCount() { } // _____________________________________________________________________________ -std::optional GroupBy::computeGroupByForFullIndexScan() { +std::optional GroupBy::computeGroupByForFullIndexScan() const { if (_groupByVariables.size() != 1) { return std::nullopt; } @@ -766,7 +765,7 @@ std::optional GroupBy::getPermutationForThreeVariableTriple( // ____________________________________________________________________________ std::optional GroupBy::checkIfJoinWithFullScan( - const Join& join) { + const Join& join) const { if (_groupByVariables.size() != 1) { return std::nullopt; } @@ -809,7 +808,7 @@ std::optional GroupBy::checkIfJoinWithFullScan( } // ____________________________________________________________________________ -std::optional GroupBy::computeGroupByForJoinWithFullScan() { +std::optional GroupBy::computeGroupByForJoinWithFullScan() const { auto join = std::dynamic_pointer_cast(_subtree->getRootOperation()); if (!join) { return std::nullopt; @@ -875,7 +874,7 @@ std::optional GroupBy::computeGroupByForJoinWithFullScan() { } // _____________________________________________________________________________ -std::optional GroupBy::computeOptimizedGroupByIfPossible() { +std::optional GroupBy::computeOptimizedGroupByIfPossible() const { // TODO Use `std::optional::or_else`. if (!RuntimeParameters().get<"group-by-disable-index-scan-optimizations">()) { if (auto result = computeGroupByForSingleIndexScan()) { @@ -897,7 +896,8 @@ std::optional GroupBy::computeOptimizedGroupByIfPossible() { // _____________________________________________________________________________ std::optional GroupBy::computeUnsequentialProcessingMetadata( - std::vector& aliases) { + std::vector& aliases, + const std::vector& groupByVariables) { // Get pointers to all aggregate expressions and their parents size_t numAggregates = 0; std::vector aliasesWithAggregateInfo; @@ -914,10 +914,10 @@ GroupBy::computeUnsequentialProcessingMetadata( // Find all grouped variables occurring in the alias expression std::vector groupedVariables; - groupedVariables.reserve(_groupByVariables.size()); + groupedVariables.reserve(groupByVariables.size()); // TODO use views::enumerate size_t i = 0; - for (const auto& groupedVariable : _groupByVariables) { + for (const auto& groupedVariable : groupByVariables) { groupedVariables.emplace_back(groupedVariable, i, findGroupedVariable(expr, groupedVariable)); ++i; @@ -933,7 +933,8 @@ GroupBy::computeUnsequentialProcessingMetadata( // _____________________________________________________________________________ std::optional -GroupBy::checkIfHashMapOptimizationPossible(std::vector& aliases) { +GroupBy::checkIfHashMapOptimizationPossible( + std::vector& aliases) const { if (!RuntimeParameters().get<"group-by-hash-map-enabled">()) { return std::nullopt; } @@ -941,7 +942,7 @@ GroupBy::checkIfHashMapOptimizationPossible(std::vector& aliases) { if (!std::dynamic_pointer_cast(_subtree->getRootOperation())) { return std::nullopt; } - return computeUnsequentialProcessingMetadata(aliases); + return computeUnsequentialProcessingMetadata(aliases, _groupByVariables); } // _____________________________________________________________________________ @@ -1096,9 +1097,8 @@ GroupBy::getHashMapAggregationResults( IdTable* resultTable, const HashMapAggregationData& aggregationData, size_t dataIndex, size_t beginIndex, size_t endIndex, - LocalVocab* localVocab) const { - sparqlExpression::VectorWithMemoryLimit aggregateResults( - getExecutionContext()->getAllocator()); + LocalVocab* localVocab, const Allocator& allocator) { + sparqlExpression::VectorWithMemoryLimit aggregateResults(allocator); aggregateResults.resize(endIndex - beginIndex); auto& aggregateDataVariant = @@ -1136,13 +1136,13 @@ GroupBy::getHashMapAggregationResults( // _____________________________________________________________________________ void GroupBy::substituteGroupVariable( const std::vector& occurrences, IdTable* resultTable, - size_t beginIndex, size_t count, size_t columnIndex) const { + size_t beginIndex, size_t count, size_t columnIndex, + const Allocator& allocator) { decltype(auto) groupValues = resultTable->getColumn(columnIndex).subspan(beginIndex, count); for (const auto& occurrence : occurrences) { - sparqlExpression::VectorWithMemoryLimit values( - getExecutionContext()->getAllocator()); + sparqlExpression::VectorWithMemoryLimit values(allocator); values.resize(groupValues.size()); std::ranges::copy(groupValues, values.begin()); @@ -1161,7 +1161,7 @@ GroupBy::substituteAllAggregates( std::vector& info, size_t beginIndex, size_t endIndex, const HashMapAggregationData& aggregationData, - IdTable* resultTable, LocalVocab* localVocab) const { + IdTable* resultTable, LocalVocab* localVocab, const Allocator& allocator) { std::vector> originalChildren; originalChildren.reserve(info.size()); @@ -1169,7 +1169,7 @@ GroupBy::substituteAllAggregates( for (auto& aggregate : info) { auto aggregateResults = getHashMapAggregationResults( resultTable, aggregationData, aggregate.aggregateDataIndex_, beginIndex, - endIndex, localVocab); + endIndex, localVocab, allocator); // Substitute the resulting vector as a literal auto newExpression = std::make_unique( @@ -1276,7 +1276,7 @@ void GroupBy::evaluateAlias( HashMapAliasInformation& alias, IdTable* result, sparqlExpression::EvaluationContext& evaluationContext, const HashMapAggregationData& aggregationData, - LocalVocab* localVocab) const { + LocalVocab* localVocab, const Allocator& allocator) { auto& info = alias.aggregateInfo_; // Either: @@ -1304,8 +1304,7 @@ void GroupBy::evaluateAlias( outValues.begin() + evaluationContext._beginIndex); // We also need to store it for possible future use - sparqlExpression::VectorWithMemoryLimit values( - getExecutionContext()->getAllocator()); + sparqlExpression::VectorWithMemoryLimit values(allocator); values.resize(groupValues.size()); std::ranges::copy(groupValues, values.begin()); @@ -1321,7 +1320,8 @@ void GroupBy::evaluateAlias( // Get aggregate results auto aggregateResults = getHashMapAggregationResults( result, aggregationData, aggregate.aggregateDataIndex_, - evaluationContext._beginIndex, evaluationContext._endIndex, localVocab); + evaluationContext._beginIndex, evaluationContext._endIndex, localVocab, + allocator); // Copy to result table decltype(auto) outValues = result->getColumn(alias.outCol_); @@ -1339,7 +1339,7 @@ void GroupBy::evaluateAlias( // Substitute in the values of the grouped variable substituteGroupVariable( occurrences, result, evaluationContext._beginIndex, - evaluationContext.size(), substitution.resultColumnIndex_); + evaluationContext.size(), substitution.resultColumnIndex_, allocator); } // Substitute in the results of all aggregates contained in the @@ -1347,7 +1347,7 @@ void GroupBy::evaluateAlias( std::vector> originalChildren = substituteAllAggregates( info, evaluationContext._beginIndex, evaluationContext._endIndex, - aggregationData, result, localVocab); + aggregationData, result, localVocab, allocator); // Evaluate top-level alias expression sparqlExpression::ExpressionResult expressionResult = @@ -1378,7 +1378,7 @@ template IdTable GroupBy::createResultFromHashMap( const HashMapAggregationData& aggregationData, std::vector& aggregateAliases, - LocalVocab* localVocab) { + LocalVocab* localVocab) const { // Create result table, filling in the group values, since they might be // required in evaluation ad_utility::Timer sortingTimer{ad_utility::Timer::Started}; @@ -1408,7 +1408,7 @@ IdTable GroupBy::createResultFromHashMap( for (auto& alias : aggregateAliases) { evaluateAlias(alias, &result, evaluationContext, aggregationData, - localVocab); + localVocab, allocator()); } } runtimeInfo().addDetail("timeEvaluationAndResults", @@ -1449,7 +1449,7 @@ template IdTable GroupBy::computeGroupByForHashMapOptimization( std::vector& aggregateAliases, const IdTable& subresult, const std::vector& columnIndices, - LocalVocab* localVocab) { + LocalVocab* localVocab) const { AD_CONTRACT_CHECK(columnIndices.size() == NUM_GROUP_COLUMNS || NUM_GROUP_COLUMNS == 0); diff --git a/src/engine/GroupBy.h b/src/engine/GroupBy.h index 3055a9a913..1066b34981 100644 --- a/src/engine/GroupBy.h +++ b/src/engine/GroupBy.h @@ -33,6 +33,7 @@ class GroupBy : public Operation { using string = std::string; template using vector = std::vector; + using Allocator = ad_utility::AllocatorWithLimit; std::shared_ptr _subtree; vector _groupByVariables; @@ -168,7 +169,7 @@ class GroupBy : public Operation { // This function checks whether such a case applies. In this case the result // is computed and returned wrapped in a optional. If no such case applies the // optional remains empty. - std::optional computeOptimizedGroupByIfPossible(); + std::optional computeOptimizedGroupByIfPossible() const; // Check if the query represented by this GROUP BY is of the following form: // @@ -181,7 +182,7 @@ class GroupBy : public Operation { // The COUNT may be computed on any of the variables in the triple. If the // query has that form, the result of the query (which consists of one line) // is computed and returned. If not, an empty optional is returned. - std::optional computeGroupByForSingleIndexScan(); + std::optional computeGroupByForSingleIndexScan() const; // Check if the query represented by this GROUP BY is of the following form: // @@ -192,7 +193,7 @@ class GroupBy : public Operation { // NOTE: This is exactly what we need for a context-sensitive object AC query // without connected triples. The GROUP BY variable can also be omitted in // the SELECT clause. - std::optional computeGroupByObjectWithCount(); + std::optional computeGroupByObjectWithCount() const; // Check if the query represented by this GROUP BY is of the following form: // @@ -205,7 +206,7 @@ class GroupBy : public Operation { // or `?z`. In the SELECT clause, both of the elements may be omitted, so in // the example it is possible to only select `?x` or to only select the // `COUNT`. - std::optional computeGroupByForFullIndexScan(); + std::optional computeGroupByForFullIndexScan() const; // Check if the query represented by this GROUP BY is of the following form: // @@ -217,7 +218,7 @@ class GroupBy : public Operation { // Note that `?x` can also be the predicate or object of the three variable // triple, and that the COUNT may be by any of the variables `?x`, `?y`, or // `?z`. - std::optional computeGroupByForJoinWithFullScan(); + std::optional computeGroupByForJoinWithFullScan() const; // Stores information required for substitution of an expression in an // expression tree. @@ -307,7 +308,7 @@ class GroupBy : public Operation { IdTable computeGroupByForHashMapOptimization( std::vector& aggregateAliases, const IdTable& subresult, const std::vector& columnIndices, - LocalVocab* localVocab); + LocalVocab* localVocab) const; using AggregationData = std::variant - sparqlExpression::VectorWithMemoryLimit getHashMapAggregationResults( + static sparqlExpression::VectorWithMemoryLimit + getHashMapAggregationResults( IdTable* resultTable, const HashMapAggregationData& aggregationData, size_t dataIndex, size_t beginIndex, size_t endIndex, - LocalVocab* localVocab) const; + LocalVocab* localVocab, const Allocator& allocator); // Substitute away any occurrences of the grouped variable and of aggregate // results, if necessary, and subsequently evaluate the expression of an // alias template - void evaluateAlias( + static void evaluateAlias( HashMapAliasInformation& alias, IdTable* result, sparqlExpression::EvaluationContext& evaluationContext, const HashMapAggregationData& aggregationData, - LocalVocab* localVocab) const; + LocalVocab* localVocab, const Allocator& allocator); // Sort the HashMap by key and create result table. template IdTable createResultFromHashMap( const HashMapAggregationData& aggregationData, std::vector& aggregateAliases, - LocalVocab* localVocab); + LocalVocab* localVocab) const; // Reusable implementation of `checkIfHashMapOptimizationPossible`. - std::optional computeUnsequentialProcessingMetadata( - std::vector& aggregates); + static std::optional + computeUnsequentialProcessingMetadata( + std::vector& aggregates, + const std::vector& groupByVariables); // Check if hash map optimization is applicable. This is the case when // the following conditions hold true: // - Runtime parameter is set // - Child operation is SORT std::optional checkIfHashMapOptimizationPossible( - std::vector& aggregates); + std::vector& aggregates) const; // Extract values from `expressionResult` and store them in the rows of // `resultTable` specified by the indices in `evaluationContext`, in column @@ -454,32 +458,34 @@ class GroupBy : public Operation { IdTable* resultTable, LocalVocab* localVocab, size_t outCol); // Substitute the group values for all occurrences of a group variable. - void substituteGroupVariable( + static void substituteGroupVariable( const std::vector& occurrences, IdTable* resultTable, - size_t beginIndex, size_t count, size_t columnIndex) const; + size_t beginIndex, size_t count, size_t columnIndex, + const Allocator& allocator); // Substitute the results for all aggregates in `info`. The values of the // grouped variable should be at column 0 in `groupValues`. Return a vector of // the replaced `SparqlExpression`s to potentially put them pack afterwards. template - std::vector> - substituteAllAggregates( - std::vector& info, size_t beginIndex, - size_t endIndex, - const HashMapAggregationData& aggregationData, - IdTable* resultTable, LocalVocab* localVocab) const; + std:: + vector> static substituteAllAggregates( + std::vector& info, size_t beginIndex, + size_t endIndex, + const HashMapAggregationData& aggregationData, + IdTable* resultTable, LocalVocab* localVocab, + const Allocator& allocator); // Check if an expression is a currently supported aggregate. static std::optional isSupportedAggregate( sparqlExpression::SparqlExpression* expr); // Find all occurrences of grouped by variable for expression `expr`. - std::variant, OccurAsRoot> + static std::variant, OccurAsRoot> findGroupedVariable(sparqlExpression::SparqlExpression* expr, const Variable& groupedVariable); // The recursive implementation of `findGroupedVariable` (see above). - void findGroupedVariableImpl( + static void findGroupedVariableImpl( sparqlExpression::SparqlExpression* expr, std::optional parentAndChildIndex, std::variant, OccurAsRoot>& @@ -521,7 +527,8 @@ class GroupBy : public Operation { // Check if the previously described optimization can be applied. The argument // Must be the single subtree of this GROUP BY, properly cast to a `const // Join*`. - std::optional checkIfJoinWithFullScan(const Join& join); + std::optional checkIfJoinWithFullScan( + const Join& join) const; // Check if the following is true: the `tree` represents a three variable // triple, that contains both `variableByWhichToSort` and diff --git a/src/engine/LazyGroupBy.cpp b/src/engine/LazyGroupBy.cpp index 3498de136f..0575d9fb89 100644 --- a/src/engine/LazyGroupBy.cpp +++ b/src/engine/LazyGroupBy.cpp @@ -13,6 +13,7 @@ LazyGroupBy::LazyGroupBy( const ad_utility::AllocatorWithLimit& allocator, size_t numGroupColumns) : localVocab_{localVocab}, aggregateAliases_{std::move(aggregateAliases)}, + allocator_{allocator}, aggregationData_{allocator, aggregateAliases_, numGroupColumns} { for (const auto& aggregateInfo : allAggregateInfoView()) { visitAggregate( @@ -40,7 +41,7 @@ void LazyGroupBy::resetAggregationData() { void LazyGroupBy::commitRow( IdTable& resultTable, sparqlExpression::EvaluationContext& evaluationContext, - const GroupBy::GroupBlock& currentGroupBlock, const GroupBy& groupBy) { + const GroupBy::GroupBlock& currentGroupBlock) { resultTable.emplace_back(); size_t colIdx = 0; for (const auto& [_, value] : currentGroupBlock) { @@ -52,8 +53,8 @@ void LazyGroupBy::commitRow( evaluationContext._endIndex = resultTable.size(); for (auto& alias : aggregateAliases_) { - groupBy.evaluateAlias(alias, &resultTable, evaluationContext, - aggregationData_, &localVocab_); + GroupBy::evaluateAlias(alias, &resultTable, evaluationContext, + aggregationData_, &localVocab_, allocator_); } resetAggregationData(); } diff --git a/src/engine/LazyGroupBy.h b/src/engine/LazyGroupBy.h index 6917ad1e62..f95915eb09 100644 --- a/src/engine/LazyGroupBy.h +++ b/src/engine/LazyGroupBy.h @@ -12,6 +12,7 @@ class LazyGroupBy { LocalVocab& localVocab_; std::vector aggregateAliases_; + const ad_utility::AllocatorWithLimit& allocator_; GroupBy::HashMapAggregationData<0> aggregationData_; public: @@ -30,8 +31,7 @@ class LazyGroupBy { // into the `resultTable` and reset the aggregation data for the next group. void commitRow(IdTable& resultTable, sparqlExpression::EvaluationContext& evaluationContext, - const GroupBy::GroupBlock& currentGroupBlock, - const GroupBy& groupBy); + const GroupBy::GroupBlock& currentGroupBlock); // Process the next potentially partial group as a block of rows. This // modifies the state of `aggregateData_`. Call `commitRow` to write the final diff --git a/test/engine/LazyGroupByTest.cpp b/test/engine/LazyGroupByTest.cpp index d94a572fe9..1b952f9f7b 100644 --- a/test/engine/LazyGroupByTest.cpp +++ b/test/engine/LazyGroupByTest.cpp @@ -43,16 +43,15 @@ class LazyGroupByTest : public ::testing::Test { std::make_shared( qec_, IdTable{2, ad_utility::makeAllocatorWithLimit(0_B)}, std::vector{std::optional{xVar_}, std::optional{yVar_}})); - GroupBy groupBy_{qec_, {yVar_}, {}, subtree_}; std::vector aggregates_{ {std::move(sparqlExpression_), 1}}; LocalVocab localVocab_{}; LazyGroupBy lazyGroupBy_{ localVocab_, - groupBy_.computeUnsequentialProcessingMetadata(aggregates_) + GroupBy::computeUnsequentialProcessingMetadata(aggregates_, {yVar_}) ->aggregateAliases_, - groupBy_.allocator(), 1}; + ua, 1}; sparqlExpression::EvaluationContext makeEvaluationContext( const IdTable& idTable) const { @@ -60,7 +59,7 @@ class LazyGroupByTest : public ::testing::Test { *qec_, subtree_->getVariableColumns(), idTable, - groupBy_.allocator(), + ua, localVocab_, std::make_shared>(), std::chrono::steady_clock::time_point::max()}; @@ -80,13 +79,13 @@ TEST_F(LazyGroupByTest, verifyEmptyGroupsAreAggregatedCorrectly) { auto evaluationContext = makeEvaluationContext(idTable); lazyGroupBy_.processBlock(evaluationContext, 0, 0); - lazyGroupBy_.commitRow(resultTable, evaluationContext, block, groupBy_); + lazyGroupBy_.commitRow(resultTable, evaluationContext, block); // The grouped variable has the value 7 here + 0 is still 7. EXPECT_EQ(resultTable, makeIdTableFromVector({{7, 7}}, I)); block.at(0).second = I(9); - lazyGroupBy_.commitRow(resultTable, evaluationContext, block, groupBy_); + lazyGroupBy_.commitRow(resultTable, evaluationContext, block); // The grouped variable has the value 9 here + 0 is still 9. EXPECT_EQ(resultTable, makeIdTableFromVector({{7, 7}, {9, 9}}, I)); @@ -100,7 +99,7 @@ TEST_F(LazyGroupByTest, verifyGroupsAreAggregatedCorrectly) { auto evaluationContext = makeEvaluationContext(idTable); lazyGroupBy_.processBlock(evaluationContext, 1, 3); - lazyGroupBy_.commitRow(resultTable, evaluationContext, block, groupBy_); + lazyGroupBy_.commitRow(resultTable, evaluationContext, block); // The `7` is the current group, and the aggregate computes the SUM between // the elements at index 1 and 2, which is 7 + 3 + 5 = 15. @@ -112,7 +111,7 @@ TEST_F(LazyGroupByTest, verifyGroupsAreAggregatedCorrectly) { lazyGroupBy_.processBlock(evaluationContext, 3, 4); // add 7 -> 17 lazyGroupBy_.processBlock(evaluationContext, 4, 4); // add 0 (empty) -> 17 block.at(0).second = I(9); // add 9 -> 26 - lazyGroupBy_.commitRow(resultTable, evaluationContext, block, groupBy_); + lazyGroupBy_.commitRow(resultTable, evaluationContext, block); EXPECT_EQ(resultTable, makeIdTableFromVector({{7, 15}, {9, 26}}, I)); } @@ -129,7 +128,7 @@ TEST_F(LazyGroupByTest, verifyCommitWorksWhenOriginalIdTableIsGone) { } IdTable idTable = makeIdTableFromVector({}, I); auto evaluationContext = makeEvaluationContext(idTable); - lazyGroupBy_.commitRow(resultTable, evaluationContext, block, groupBy_); + lazyGroupBy_.commitRow(resultTable, evaluationContext, block); // 3 + 3 + 5 = 11 EXPECT_EQ(resultTable, makeIdTableFromVector({{3, 11}}, I)); @@ -149,14 +148,13 @@ TEST(LazyGroupBy, verifyGroupConcatIsCorrectlyInitialized) { qec, std::make_shared( qec, IdTable{1, ad_utility::makeAllocatorWithLimit(0_B)}, std::vector{std::optional{variable}})); - GroupBy groupBy{qec, {variable}, {}, subtree}; std::vector aggregates{{std::move(sparqlExpression), 0}}; LocalVocab localVocab{}; LazyGroupBy lazyGroupBy{ localVocab, - groupBy.computeUnsequentialProcessingMetadata(aggregates) + GroupBy::computeUnsequentialProcessingMetadata(aggregates, {variable}) ->aggregateAliases_, - groupBy.allocator(), 1}; + qec->getAllocator(), 1}; using namespace ::testing; using Gc = GroupConcatAggregationData; EXPECT_THAT(lazyGroupBy.aggregationData_.getAggregationDataVariant(0),