diff --git a/benchmark/GroupByHashMapBenchmark.cpp b/benchmark/GroupByHashMapBenchmark.cpp index 780785e9bc..1335ebc5bd 100644 --- a/benchmark/GroupByHashMapBenchmark.cpp +++ b/benchmark/GroupByHashMapBenchmark.cpp @@ -6,12 +6,12 @@ #include #include "../benchmark/infrastructure/Benchmark.h" -#include "../test/engine/ValuesForTesting.h" #include "../test/util/IdTableHelpers.h" #include "../test/util/IndexTestHelpers.h" #include "engine/GroupBy.h" #include "engine/Sort.h" #include "engine/Values.h" +#include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/GroupConcatExpression.h" #include "engine/sparqlExpressions/LiteralExpression.h" diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 7e8cbbc953..cc8acadd80 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -15,5 +15,5 @@ add_library(engine TextLimit.cpp LazyGroupBy.cpp GroupByHashMapOptimization.cpp SpatialJoin.cpp CountConnectedSubgraphs.cpp SpatialJoinAlgorithms.cpp PathSearch.cpp ExecuteUpdate.cpp Describe.cpp GraphStoreProtocol.cpp - QueryExecutionContext.cpp ExistsJoin.cpp) + QueryExecutionContext.cpp ExistsJoin.cpp NamedQueryCache.cpp) qlever_target_link_libraries(engine util index parser sparqlExpressions http SortPerformanceEstimator Boost::iostreams s2) diff --git a/src/engine/CheckUsePatternTrick.cpp b/src/engine/CheckUsePatternTrick.cpp index 431382da34..c025abaf73 100644 --- a/src/engine/CheckUsePatternTrick.cpp +++ b/src/engine/CheckUsePatternTrick.cpp @@ -72,9 +72,11 @@ bool isVariableContainedInGraphPatternOperation( } else if constexpr (std::is_same_v) { return ad_utility::contains(arg.visibleVariables_, variable); } else { - static_assert( - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v); + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); // The `TransPath` is set up later in the query planning, when this // function should not be called anymore. AD_FAIL(); diff --git a/src/engine/Describe.cpp b/src/engine/Describe.cpp index a0c43222d2..61960d90b2 100644 --- a/src/engine/Describe.cpp +++ b/src/engine/Describe.cpp @@ -4,9 +4,9 @@ #include "engine/Describe.h" -#include "../../test/engine/ValuesForTesting.h" #include "engine/IndexScan.h" #include "engine/Join.h" +#include "engine/ValuesForTesting.h" // _____________________________________________________________________________ Describe::Describe(QueryExecutionContext* qec, diff --git a/src/engine/NamedQueryCache.cpp b/src/engine/NamedQueryCache.cpp new file mode 100644 index 0000000000..276f9b0c17 --- /dev/null +++ b/src/engine/NamedQueryCache.cpp @@ -0,0 +1,40 @@ +// Copyright 2025, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Johannes Kalmbach + +#include "engine/NamedQueryCache.h" + +// _____________________________________________________________________________ +std::shared_ptr NamedQueryCache ::getOperation( + const Key& key, QueryExecutionContext* ctx) { + const auto& ptr = get(key); + const auto& [table, map, sortedOn] = *ptr; + // TODO Add a local vocab, and consider also passing a shared_ptr for + // the local vocab. + return std::make_shared(ctx, table, map, sortedOn); +} + +// _____________________________________________________________________________ +auto NamedQueryCache::get(const Key& key) -> std::shared_ptr { + auto l = cache_.wlock(); + if (!l->contains(key)) { + throw std::runtime_error{ + absl::StrCat("The named query with the name \"", key, + "\" was not pinned to the named query cache")}; + } + return (*l)[key]; +} + +// _____________________________________________________________________________ +void NamedQueryCache::store(const Key& key, Value value) { + // TODO Check the overwrite semantics of the cache class. + cache_.wlock()->insert(key, std::move(value)); +} + +// _____________________________________________________________________________ +void NamedQueryCache::clear() { cache_.wlock()->clearAll(); } + +// _____________________________________________________________________________ +size_t NamedQueryCache::numEntries() const { + return cache_.rlock()->numNonPinnedEntries(); +} diff --git a/src/engine/NamedQueryCache.h b/src/engine/NamedQueryCache.h new file mode 100644 index 0000000000..23a65e0a57 --- /dev/null +++ b/src/engine/NamedQueryCache.h @@ -0,0 +1,55 @@ +// Copyright 2025, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Johannes Kalmbach +#pragma once + +#include "engine/ValuesForTesting.h" +#include "util/Cache.h" +#include "util/Synchronized.h" + +// A simple thread-safe cache that associates query results with an explicit +// name. +class NamedQueryCache { + public: + // The cache value. It stores all the information required to construct a + // proper `QueryExecutionTree` later on. + // TODO We definitely need the local vocab here... + struct Value { + std::shared_ptr result_; + VariableToColumnMap varToColMap_; + std::vector resultSortedOn_; + }; + + // TODO Use a better size getter for better statistics. + struct ValueSizeGetter { + ad_utility::MemorySize operator()(const Value&) { + return ad_utility::MemorySize::bytes(1); + } + }; + using Key = std::string; + using Cache = ad_utility::LRUCache; + + private: + ad_utility::Synchronized cache_; + + public: + // Store an explicit query result with a given `key`. Previously stored + // `value`s with the same `key` are overwritten. + void store(const Key& key, Value value); + + // Clear the cache. + void clear(); + + // Get the number of cached queries. + size_t numEntries() const; + + // Retrieve the query result that is associated with the `key`. + // Throw an exception if the `key` doesn't exist. + std::shared_ptr get(const Key& key); + + // Retrieve the query result with the given `key` and convert it into an + // explicit `ValuesForTesting` operation that can be used as part of a + // `QueryExecutionTree`. + std::shared_ptr getOperation(const Key& key, + QueryExecutionContext* ctx); +}; diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 0f94ff7886..0b72a672ec 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -4,8 +4,7 @@ #include "engine/Operation.h" -#include - +#include "engine/NamedQueryCache.h" #include "engine/QueryExecutionTree.h" #include "global/RuntimeParameters.h" #include "util/OnDestructionDontThrowDuringStackUnwinding.h" @@ -293,6 +292,12 @@ std::shared_ptr Operation::getResult( _executionContext->_pinResult && isRoot; const bool pinResult = _executionContext->_pinSubtrees || pinFinalResultButNotSubtrees; + const bool pinWithName = + _executionContext->pinWithExplicitName().has_value() && isRoot; + + if (pinWithName) { + computationMode = ComputationMode::FULLY_MATERIALIZED; + } try { // In case of an exception, create the correct runtime info, no matter which @@ -339,6 +344,21 @@ std::shared_ptr Operation::getResult( updateRuntimeInformationOnSuccess(result, timer.msecs()); } + if (pinWithName) { + const auto& name = _executionContext->pinWithExplicitName().value(); + // The query is to be pinned in the named cache. In this case we don't + // return the result, but only pin it. + const auto& actualResult = result._resultPointer->resultTable(); + AD_CORRECTNESS_CHECK(actualResult.isFullyMaterialized()); + // TODO probably we don't need to `clone()` the IdTable here. + auto t = NamedQueryCache::Value( + std::make_shared(actualResult.idTable().clone()), + getExternallyVisibleVariableColumns(), actualResult.sortedBy()); + _executionContext->namedQueryCache().store(name, std::move(t)); + + runtimeInfo().addDetail("pinned-with-explicit-name", name); + } + return result._resultPointer->resultTablePtr(); } catch (ad_utility::CancellationException& e) { e.setOperation(getDescriptor()); diff --git a/src/engine/QueryExecutionContext.cpp b/src/engine/QueryExecutionContext.cpp index b458206613..91e37d3d7d 100644 --- a/src/engine/QueryExecutionContext.cpp +++ b/src/engine/QueryExecutionContext.cpp @@ -1,6 +1,7 @@ // Copyright 2025, University of Freiburg, // Chair of Algorithms and Data Structures. -// Author: Robin Textor-Falconi +// Authors: Robin Textor-Falconi +// Johannes Kalmbach #include "engine/QueryExecutionContext.h" @@ -9,3 +10,19 @@ bool QueryExecutionContext::areWebSocketUpdatesEnabled() { return RuntimeParameters().get<"websocket-updates-enabled">(); } +// _____________________________________________________________________________ +QueryExecutionContext::QueryExecutionContext( + const Index& index, QueryResultCache* const cache, + ad_utility::AllocatorWithLimit allocator, + SortPerformanceEstimator sortPerformanceEstimator, + NamedQueryCache* namedCache, + std::function updateCallback, const bool pinSubtrees, + const bool pinResult) + : _pinSubtrees(pinSubtrees), + _pinResult(pinResult), + _index(index), + _subtreeCache(cache), + _allocator(std::move(allocator)), + _sortPerformanceEstimator(sortPerformanceEstimator), + updateCallback_(std::move(updateCallback)), + namedQueryCache_{namedCache} {} diff --git a/src/engine/QueryExecutionContext.h b/src/engine/QueryExecutionContext.h index 501f2ba538..8c71dd77ac 100644 --- a/src/engine/QueryExecutionContext.h +++ b/src/engine/QueryExecutionContext.h @@ -62,6 +62,9 @@ class CacheValue { }; }; +// Forward declaration because of cyclic dependencies +class NamedQueryCache; + // The key for the `QueryResultCache` below. It consists of a `string` (the // actual cache key of a `QueryExecutionTree` and the index of the // `LocatedTriplesSnapshot` that was used to create the corresponding value. @@ -86,6 +89,9 @@ struct QueryCacheKey { using QueryResultCache = ad_utility::ConcurrentCache< ad_utility::LRUCache>; +// Forward declaration because of cyclic dependency +class NamedQueryCache; + // Execution context for queries. // Holds references to index and engine, implements caching. class QueryExecutionContext { @@ -94,17 +100,10 @@ class QueryExecutionContext { const Index& index, QueryResultCache* const cache, ad_utility::AllocatorWithLimit allocator, SortPerformanceEstimator sortPerformanceEstimator, + NamedQueryCache* namedCache, std::function updateCallback = [](std::string) { /* No-op by default for testing */ }, - const bool pinSubtrees = false, const bool pinResult = false) - : _pinSubtrees(pinSubtrees), - _pinResult(pinResult), - _index(index), - _subtreeCache(cache), - _allocator(std::move(allocator)), - _costFactors(), - _sortPerformanceEstimator(sortPerformanceEstimator), - updateCallback_(std::move(updateCallback)) {} + bool pinSubtrees = false, bool pinResult = false); QueryResultCache& getQueryTreeCache() { return *_subtreeCache; } @@ -148,10 +147,16 @@ class QueryExecutionContext { return areWebsocketUpdatesEnabled_; } - private: - static bool areWebSocketUpdatesEnabled(); + NamedQueryCache& namedQueryCache() { + AD_CORRECTNESS_CHECK(namedQueryCache_ != nullptr); + return *namedQueryCache_; + } + + auto& pinWithExplicitName() { return pinWithExplicitName_; } + const auto& pinWithExplicitName() const { return pinWithExplicitName_; } private: + static bool areWebSocketUpdatesEnabled(); const Index& _index; // When the `QueryExecutionContext` is constructed, get a stable read-only @@ -169,4 +174,8 @@ class QueryExecutionContext { // Cache the state of that runtime parameter to reduce the contention of the // mutex. bool areWebsocketUpdatesEnabled_ = areWebSocketUpdatesEnabled(); + + NamedQueryCache* namedQueryCache_ = nullptr; + + std::optional pinWithExplicitName_ = std::nullopt; }; diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index be7cfe27d5..3d3c317ccd 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -13,6 +13,7 @@ #include #include +#include "NamedQueryCache.h" #include "backports/algorithm.h" #include "engine/Bind.h" #include "engine/CartesianProductJoin.h" @@ -2436,6 +2437,8 @@ void QueryPlanner::GraphPatternPlanner::graphPatternOperationVisitor(Arg& arg) { visitDescribe(arg); } else if constexpr (std::is_same_v) { visitSpatialSearch(arg); + } else if constexpr (std::is_same_v) { + visitNamedCachedQuery(arg); } else { static_assert(std::is_same_v); visitBasicGraphPattern(arg); @@ -2609,6 +2612,15 @@ void QueryPlanner::GraphPatternPlanner::visitSpatialSearch( visitGroupOptionalOrMinus(std::move(candidatesOut)); } +// _____________________________________________________________________________ +void QueryPlanner::GraphPatternPlanner::visitNamedCachedQuery( + parsedQuery::NamedCachedQuery& arg) { + auto candidate = SubtreePlan{ + planner_._qec, planner_._qec->namedQueryCache().getOperation( + arg.validateAndGetIdentifier(), planner_._qec)}; + visitGroupOptionalOrMinus(std::vector{std::move(candidate)}); +} + // _______________________________________________________________ void QueryPlanner::GraphPatternPlanner::visitUnion(parsedQuery::Union& arg) { // TODO here we could keep all the candidates, and create a diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index d3801cba88..e726a34909 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -544,6 +544,7 @@ class QueryPlanner { void visitTransitivePath(parsedQuery::TransPath& transitivePath); void visitPathSearch(parsedQuery::PathQuery& config); void visitSpatialSearch(parsedQuery::SpatialQuery& config); + void visitNamedCachedQuery(parsedQuery::NamedCachedQuery& config); void visitUnion(parsedQuery::Union& un); void visitSubquery(parsedQuery::Subquery& subquery); void visitDescribe(parsedQuery::Describe& describe); diff --git a/src/engine/Result.cpp b/src/engine/Result.cpp index 11ce5324fc..4e4c87aca7 100644 --- a/src/engine/Result.cpp +++ b/src/engine/Result.cpp @@ -59,6 +59,18 @@ Result::Result(IdTable idTable, std::vector sortedBy, assertSortOrderIsRespected(this->idTable(), sortedBy_); } +// _____________________________________________________________________________ +Result::Result(std::shared_ptr idTablePtr, + std::vector sortedBy, LocalVocab&& localVocab) + : data_{IdTableSharedLocalVocabPair{ + std::move(idTablePtr), + std::make_shared(std::move(localVocab))}}, + sortedBy_{std::move(sortedBy)} { + AD_CONTRACT_CHECK(std::get(data_).localVocab_ != + nullptr); + assertSortOrderIsRespected(this->idTable(), sortedBy_); +} + // _____________________________________________________________________________ Result::Result(IdTable idTable, std::vector sortedBy, LocalVocab&& localVocab) @@ -124,8 +136,13 @@ void Result::applyLimitOffset( } if (isFullyMaterialized()) { ad_utility::timer::Timer limitTimer{ad_utility::timer::Timer::Started}; - resizeIdTable(std::get(data_).idTable_, - limitOffset); + + auto& tableOrPtr = std::get(data_).idTable_; + if (auto sharedTable = + std::get_if>(&tableOrPtr)) { + tableOrPtr = (**sharedTable).clone(); + } + resizeIdTable(std::get(tableOrPtr), limitOffset); limitTimeCallback(limitTimer.msecs(), idTable()); } else { auto generator = [](LazyResult original, LimitOffsetClause limitOffset, @@ -181,7 +198,7 @@ void Result::assertThatLimitWasRespected(const LimitOffsetClause& limitOffset) { // _____________________________________________________________________________ void Result::checkDefinedness(const VariableToColumnMap& varColMap) { - auto performCheck = [](const auto& map, IdTable& idTable) { + auto performCheck = [](const auto& map, const IdTable& idTable) { return ql::ranges::all_of(map, [&](const auto& varAndCol) { const auto& [columnIndex, mightContainUndef] = varAndCol.second; if (mightContainUndef == ColumnIndexAndTypeInfo::AlwaysDefined) { @@ -193,8 +210,7 @@ void Result::checkDefinedness(const VariableToColumnMap& varColMap) { }); }; if (isFullyMaterialized()) { - AD_EXPENSIVE_CHECK(performCheck( - varColMap, std::get(data_).idTable_)); + AD_EXPENSIVE_CHECK(performCheck(varColMap, idTable())); } else { auto generator = [](LazyResult original, [[maybe_unused]] VariableToColumnMap varColMap, @@ -254,7 +270,17 @@ void Result::assertSortOrderIsRespected( // _____________________________________________________________________________ const IdTable& Result::idTable() const { AD_CONTRACT_CHECK(isFullyMaterialized()); - return std::get(data_).idTable_; + auto visitor = [](const T& arg) -> const IdTable& { + if constexpr (std::is_same_v) { + return arg; + } else { + static_assert(std::is_same_v>); + AD_CORRECTNESS_CHECK(arg != nullptr); + return *arg; + } + }; + return std::visit(visitor, + std::get(data_).idTable_); } // _____________________________________________________________________________ diff --git a/src/engine/Result.h b/src/engine/Result.h index 18d92e5806..b2ce690498 100644 --- a/src/engine/Result.h +++ b/src/engine/Result.h @@ -55,7 +55,7 @@ class Result { using LocalVocabPtr = std::shared_ptr; struct IdTableSharedLocalVocabPair { - IdTable idTable_; + std::variant, IdTable> idTable_; // The local vocabulary of the result. LocalVocabPtr localVocab_; }; @@ -115,6 +115,8 @@ class Result { SharedLocalVocabWrapper localVocab); Result(IdTable idTable, std::vector sortedBy, LocalVocab&& localVocab); + Result(std::shared_ptr idTablePtr, + std::vector sortedBy, LocalVocab&& localVocab); Result(IdTableVocabPair pair, std::vector sortedBy); Result(Generator idTables, std::vector sortedBy); Result(LazyResult idTables, std::vector sortedBy); diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index f1c80ac15d..e96f65bf04 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -20,7 +20,6 @@ #include "index/IndexImpl.h" #include "util/AsioHelpers.h" #include "util/MemorySize/MemorySize.h" -#include "util/OnDestructionDontThrowDuringStackUnwinding.h" #include "util/ParseableDuration.h" #include "util/TypeIdentity.h" #include "util/TypeTraits.h" @@ -288,6 +287,11 @@ CPP_template_2(typename RequestT, typename ResponseT)( logCommand(cmd, "clear cache completely (including unpinned elements)"); cache_.clearAll(); response = createJsonResponse(composeCacheStatsJson(), request); + } else if (auto cmd = checkParameter("cmd", "clear-named-cache")) { + requireValidAccessToken("clear-named-cache"); + logCommand(cmd, "clear the cache for named queries"); + namedQueryCache_.clear(); + response = createJsonResponse(composeCacheStatsJson(), request); } else if (auto cmd = checkParameter("cmd", "clear-delta-triples")) { requireValidAccessToken("clear-delta-triples"); logCommand(cmd, "clear delta triples"); @@ -381,7 +385,8 @@ CPP_template_2(typename RequestT, typename ResponseT)( queryHub_, request, std::invoke(opFieldString, op)); auto [parsedOperation, qec, cancellationHandle, cancelTimeoutOnDestruction] = - parseOperation(messageSender, parameters, op, timeLimit.value()); + parseOperation(messageSender, parameters, op, timeLimit.value(), + accessTokenOk); if (pred(parsedOperation)) { throw std::runtime_error( absl::StrCat(msg, parsedOperation._originalString)); @@ -492,7 +497,8 @@ CPP_template_2(typename Operation)( requires QueryOrUpdate) auto Server:: parseOperation(ad_utility::websocket::MessageSender& messageSender, const ad_utility::url_parser::ParamValueMap& params, - const Operation& operation, TimeLimit timeLimit) { + const Operation& operation, TimeLimit timeLimit, + bool accessTokenOk) { // The operation string was to be copied, do it here at the beginning. const auto [operationName, operationSPARQL] = [&operation]() -> std::pair { @@ -510,13 +516,24 @@ CPP_template_2(typename Operation)( // Do the query planning. This creates a `QueryExecutionTree`, which will // then be used to process the query. auto [pinSubtrees, pinResult] = determineResultPinning(params); + std::optional pinNamed = + ad_utility::url_parser::checkParameter(params, "pin-named-query", {}); LOG(INFO) << "Processing the following " << operationName << ":" << (pinResult ? " [pin result]" : "") << (pinSubtrees ? " [pin subresults]" : "") << "\n" + << (pinNamed ? absl::StrCat(" [pin named as ]", pinNamed.value()) + : "") << operationSPARQL << std::endl; QueryExecutionContext qec(index_, &cache_, allocator_, - sortPerformanceEstimator_, std::ref(messageSender), - pinSubtrees, pinResult); + sortPerformanceEstimator_, &namedQueryCache_, + std::ref(messageSender), pinSubtrees, pinResult); + if (pinNamed.has_value()) { + if (!accessTokenOk) { + throw std::runtime_error( + "The pinning of named queries requires a valid access token"); + } + qec.pinWithExplicitName() = std::move(pinNamed); + } ParsedQuery parsedQuery = SparqlParser::parseQuery(std::move(operationSPARQL)); // SPARQL Protocol 2.1.4 specifies that the dataset from the query @@ -625,6 +642,7 @@ nlohmann::json Server::composeCacheStatsJson() const { nlohmann::json result; result["num-non-pinned-entries"] = cache_.numNonPinnedEntries(); result["num-pinned-entries"] = cache_.numPinnedEntries(); + result["num-named-queries"] = namedQueryCache_.numEntries(); // TODO Get rid of the `getByte()`, once `MemorySize` has it's own json // converter. @@ -790,7 +808,6 @@ CPP_template_2(typename RequestT, typename ResponseT)( // format. co_await sendStreamableResponse(request, send, mediaType, plannedQuery, qet, requestTimer, cancellationHandle); - // Print the runtime info. This needs to be done after the query // was computed. LOG(INFO) << "Done processing query and sending result" @@ -893,6 +910,7 @@ json Server::processUpdateImpl( // update anyway (The index of the located triples snapshot is part of the // cache key). cache_.clearAll(); + namedQueryCache_.clear(); return createResponseMetadataForUpdate(requestTimer, index_, deltaTriples, plannedUpdate, qet, countBefore, diff --git a/src/engine/Server.h b/src/engine/Server.h index 8c483c8fbe..a8452218d9 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -12,6 +12,7 @@ #include "ExecuteUpdate.h" #include "engine/Engine.h" +#include "engine/NamedQueryCache.h" #include "engine/QueryExecutionContext.h" #include "engine/QueryExecutionTree.h" #include "engine/SortPerformanceEstimator.h" @@ -73,6 +74,7 @@ class Server { unsigned short port_; std::string accessToken_; QueryResultCache cache_; + NamedQueryCache namedQueryCache_; ad_utility::AllocatorWithLimit allocator_; SortPerformanceEstimator sortPerformanceEstimator_; Index index_; @@ -183,7 +185,8 @@ class Server { const ad_utility::url_parser:: ParamValueMap& params, const Operation& operation, - TimeLimit timeLimit); + TimeLimit timeLimit, + bool accessTokenOk); // Plan a parsed query. Awaitable planQuery(net::static_thread_pool& thread_pool, diff --git a/test/engine/ValuesForTesting.h b/src/engine/ValuesForTesting.h similarity index 67% rename from test/engine/ValuesForTesting.h rename to src/engine/ValuesForTesting.h index ec9b55cd30..23e9fbda57 100644 --- a/test/engine/ValuesForTesting.h +++ b/src/engine/ValuesForTesting.h @@ -9,14 +9,23 @@ #include "engine/Result.h" #include "util/Algorithm.h" #include "util/Random.h" +#include "util/TransparentFunctors.h" // An operation that yields a given `IdTable` as its result. It is used for // unit testing purposes when we need to specify the subtrees of another // operation. +namespace detail { +auto getTables(const auto& tables) { + return ql::views::transform(tables, ad_utility::dereference); +} +} // namespace detail class ValuesForTesting : public Operation { + public: + using VarVector = std::vector>; + private: - std::vector tables_; - std::vector> variables_; + std::vector> tables_; + VariableToColumnMap variables_; bool supportsLimit_; // Those can be manually overwritten for testing using the respective getters. size_t sizeEstimate_; @@ -29,15 +38,13 @@ class ValuesForTesting : public Operation { // `variables`. The number of variables must be equal to the number // of columns in the table. explicit ValuesForTesting(QueryExecutionContext* ctx, IdTable table, - std::vector> variables, + const VarVector& variables, bool supportsLimit = false, std::vector sortedColumns = {}, LocalVocab localVocab = LocalVocab{}, std::optional multiplicity = std::nullopt, bool forceFullyMaterialized = false) : Operation{ctx}, - tables_{}, - variables_{std::move(variables)}, supportsLimit_{supportsLimit}, sizeEstimate_{table.numRows()}, costEstimate_{table.numRows()}, @@ -45,18 +52,43 @@ class ValuesForTesting : public Operation { localVocab_{std::move(localVocab)}, multiplicity_{multiplicity}, forceFullyMaterialized_{forceFullyMaterialized} { - AD_CONTRACT_CHECK(variables_.size() == table.numColumns()); + AD_CONTRACT_CHECK(variables.size() == table.numColumns()); + tables_.push_back(std::make_shared(std::move(table))); + variables_ = computeVarMapFromVector(variables); + } + + ValuesForTesting(QueryExecutionContext* ctx, + std::shared_ptr table, + VariableToColumnMap variables, + std::vector sortedColumns = {}, + LocalVocab localVocab = LocalVocab{}) + : Operation{ctx}, + variables_{std::move(variables)}, + supportsLimit_{false}, + sizeEstimate_{table->numRows()}, + costEstimate_{0}, + resultSortedColumns_{std::move(sortedColumns)}, + localVocab_{std::move(localVocab)}, + multiplicity_{}, + forceFullyMaterialized_{false} { tables_.push_back(std::move(table)); } + + ValuesForTesting(QueryExecutionContext* ctx, IdTable table, + VariableToColumnMap variables, + std::vector sortedColumns = {}, + LocalVocab localVocab = LocalVocab{}) + : ValuesForTesting{ctx, std::make_shared(std::move(table)), + std::move(variables), std::move(sortedColumns), + std::move(localVocab)} {} + explicit ValuesForTesting(QueryExecutionContext* ctx, - std::vector tables, - std::vector> variables, + std::vector tables, VarVector variables, bool unlikelyToFitInCache = false, std::vector sortedColumns = {}, LocalVocab localVocab = LocalVocab{}) : Operation{ctx}, - tables_{std::move(tables)}, - variables_{std::move(variables)}, + tables_{}, supportsLimit_{false}, sizeEstimate_{0}, costEstimate_{0}, @@ -64,15 +96,21 @@ class ValuesForTesting : public Operation { resultSortedColumns_{std::move(sortedColumns)}, localVocab_{std::move(localVocab)}, multiplicity_{std::nullopt} { - AD_CONTRACT_CHECK(ql::ranges::all_of(tables_, [this](const IdTable& table) { - return variables_.size() == table.numColumns(); - })); + tables_.reserve(tables.size()); + for (auto& table : tables) { + tables_.push_back(std::make_shared(std::move(table))); + } + AD_CONTRACT_CHECK(ql::ranges::all_of( + detail::getTables(tables_), [&variables](const IdTable& table) { + return variables.size() == table.numColumns(); + })); size_t totalRows = 0; - for (const IdTable& idTable : tables_) { + for (const IdTable& idTable : detail::getTables(tables_)) { totalRows += idTable.numRows(); } sizeEstimate_ = totalRows; costEstimate_ = totalRows; + variables_ = computeVarMapFromVector(variables); } // Accessors for the estimates for manual testing. @@ -81,12 +119,12 @@ class ValuesForTesting : public Operation { // ___________________________________________________________________________ ProtoResult computeResult(bool requestLaziness) override { - if (requestLaziness && !forceFullyMaterialized_) { + if (requestLaziness && !forceFullyMaterialized_ && tables_.size() != 1) { // Not implemented yet AD_CORRECTNESS_CHECK(!supportsLimit_); std::vector clones; clones.reserve(tables_.size()); - for (const IdTable& idTable : tables_) { + for (const IdTable& idTable : detail::getTables(tables_)) { clones.push_back(idTable.clone()); } auto generator = [](auto idTables, @@ -97,17 +135,21 @@ class ValuesForTesting : public Operation { }(std::move(clones), localVocab_.clone()); return {std::move(generator), resultSortedOn()}; } + + if (tables_.size() == 1 && getLimit().isUnconstrained()) { + return {tables_.at(0), resultSortedOn(), localVocab_.clone()}; + } std::optional optionalTable; - if (tables_.size() > 1) { - IdTable aggregateTable{tables_.at(0).numColumns(), - tables_.at(0).getAllocator()}; - for (const IdTable& idTable : tables_) { + if (detail::getTables(tables_).size() > 1) { + IdTable aggregateTable{tables_.at(0)->numColumns(), + tables_.at(0)->getAllocator()}; + for (const IdTable& idTable : detail::getTables(tables_)) { aggregateTable.insertAtEnd(idTable); } optionalTable = std::move(aggregateTable); } auto table = optionalTable.has_value() ? std::move(optionalTable).value() - : tables_.at(0).clone(); + : tables_.at(0)->clone(); if (supportsLimit_) { table.erase(table.begin() + getLimit().upperBound(table.size()), table.end()); @@ -135,15 +177,16 @@ class ValuesForTesting : public Operation { // ___________________________________________________________________________ string getCacheKeyImpl() const override { std::stringstream str; - auto numRowsView = tables_ | ql::views::transform(&IdTable::numRows); + auto numRowsView = + detail::getTables(tables_) | ql::views::transform(&IdTable::numRows); auto totalNumRows = std::reduce(numRowsView.begin(), numRowsView.end(), 0); - auto numCols = tables_.empty() ? 0 : tables_.at(0).numColumns(); + auto numCols = tables_.empty() ? 0 : tables_.at(0)->numColumns(); str << "Values for testing with " << numCols << " columns and " << totalNumRows << " rows. "; if (totalNumRows > 1000) { str << ad_utility::FastRandomIntGenerator{}(); } else { - for (const IdTable& idTable : tables_) { + for (const IdTable& idTable : detail::getTables(tables_)) { for (size_t i = 0; i < idTable.numColumns(); ++i) { for (Id entry : idTable.getColumn(i)) { str << entry << ' '; @@ -163,7 +206,7 @@ class ValuesForTesting : public Operation { size_t getResultWidth() const override { // Assume a width of 1 if we have no tables and no other information to base // it on because 0 would otherwise cause stuff to break. - return tables_.empty() ? 1 : tables_.at(0).numColumns(); + return tables_.empty() ? 1 : tables_.at(0)->numColumns(); } vector resultSortedOn() const override { @@ -188,27 +231,31 @@ class ValuesForTesting : public Operation { bool knownEmptyResult() override { return ql::ranges::all_of( - tables_, [](const IdTable& table) { return table.empty(); }); + detail::getTables(tables_), + [](const IdTable& table) { return table.empty(); }); } private: - VariableToColumnMap computeVariableToColumnMap() const override { + VariableToColumnMap computeVarMapFromVector(const VarVector& vars) const { VariableToColumnMap m; - for (auto i = ColumnIndex{0}; i < variables_.size(); ++i) { - if (!variables_.at(i).has_value()) { + for (auto i = ColumnIndex{0}; i < vars.size(); ++i) { + if (!vars.at(i).has_value()) { continue; } - bool containsUndef = - ql::ranges::any_of(tables_, [&i](const IdTable& table) { + bool containsUndef = ql::ranges::any_of( + detail::getTables(tables_), [&i](const IdTable& table) { return ql::ranges::any_of(table.getColumn(i), [](Id id) { return id.isUndefined(); }); }); using enum ColumnIndexAndTypeInfo::UndefStatus; - m[variables_.at(i).value()] = ColumnIndexAndTypeInfo{ + m[vars.at(i).value()] = ColumnIndexAndTypeInfo{ i, containsUndef ? PossiblyUndefined : AlwaysDefined}; } return m; } + VariableToColumnMap computeVariableToColumnMap() const override { + return variables_; + } std::vector resultSortedColumns_; LocalVocab localVocab_; diff --git a/src/global/Constants.h b/src/global/Constants.h index dfc56cbca4..c2f460db1b 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -73,6 +73,13 @@ constexpr inline std::string_view DEFAULT_GRAPH_IRI = constexpr inline std::string_view QLEVER_INTERNAL_GRAPH_IRI = makeQleverInternalIriConst<"internal-graph">(); +// The prefix of a SERVICE IRI that refers to a query that has been pinned with +// an explicit name. The format currently is `ql:named-cached-query-$query-id$`. +// NOTE: This constant does not include the leading '<'. +constexpr inline std::string_view NAMED_CACHED_QUERY_PREFIX = + ad_utility::constexprStrCat(); + constexpr inline std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; constexpr inline std::pair MATH_PREFIX = { diff --git a/src/parser/GraphPatternOperation.h b/src/parser/GraphPatternOperation.h index 8f7d4a8505..9c454302ec 100644 --- a/src/parser/GraphPatternOperation.h +++ b/src/parser/GraphPatternOperation.h @@ -13,6 +13,7 @@ #include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/DatasetClauses.h" #include "parser/GraphPattern.h" +#include "parser/NamedCachedQuery.h" #include "parser/PathQuery.h" #include "parser/SpatialQuery.h" #include "parser/TripleComponent.h" @@ -178,8 +179,8 @@ struct Bind { // class actually becomes `using GraphPatternOperation = std::variant<...>` using GraphPatternOperationVariant = std::variant; + Values, Service, PathQuery, SpatialQuery, NamedCachedQuery, + Minus, GroupGraphPattern, Describe>; struct GraphPatternOperation : public GraphPatternOperationVariant, public VisitMixin { diff --git a/src/parser/NamedCachedQuery.h b/src/parser/NamedCachedQuery.h new file mode 100644 index 0000000000..72e9adc64b --- /dev/null +++ b/src/parser/NamedCachedQuery.h @@ -0,0 +1,34 @@ +// Copyright 2025, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Johannes Kalmbach + +#pragma once + +#include "parser/MagicServiceQuery.h" + +namespace parsedQuery { +// A magic service for queries that are pinned with an explicit query name. +class NamedCachedQuery : public MagicServiceQuery { + std::string identifier_; + + public: + // Construct with the name of the named query. + NamedCachedQuery(std::string identifier) + : identifier_{std::move(identifier)} {} + + // Currently the body of the SERVICE clause must be empty. + void addParameter([[maybe_unused]] const SparqlTriple& triple) override { + throw std::runtime_error{ + "The body of a named cache query request must be empty"}; + } + + // Return the name of the named query, and check, that the configuration is + // valid (which currently means, that the body of the SERVICE clause was + // empty. + const std::string& validateAndGetIdentifier() const { + // TODO Better error messages. + AD_CORRECTNESS_CHECK(!childGraphPattern_.has_value()); + return identifier_; + } +}; +} // namespace parsedQuery diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 91fdf2df67..5982a13eca 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -29,6 +29,7 @@ #include "parser/GraphPatternOperation.h" #include "parser/MagicServiceIriConstants.h" #include "parser/MagicServiceQuery.h" +#include "parser/NamedCachedQuery.h" #include "parser/RdfParser.h" #include "parser/SparqlParser.h" #include "parser/SpatialQuery.h" @@ -960,6 +961,38 @@ GraphPatternOperation Visitor::visitPathQuery( return pathQuery; } +// _____________________________________________________________________________ +GraphPatternOperation Visitor::visitNamedCachedQuery( + const TripleComponent::Iri& target, + Parser::ServiceGraphPatternContext* ctx) { + auto parseContent = [ctx](parsedQuery::NamedCachedQuery& namedQuery, + const parsedQuery::GraphPatternOperation& op) { + if (std::holds_alternative(op)) { + namedQuery.addBasicPattern(std::get(op)); + } else if (std::holds_alternative(op)) { + namedQuery.addGraph(op); + } else { + reportError(ctx, + "Unsupported element in named cached query." + "A named cached query currently must have an empty body"); + } + }; + + auto view = asStringViewUnsafe(target.getContent()); + AD_CORRECTNESS_CHECK(view.starts_with(NAMED_CACHED_QUERY_PREFIX)); + // Remove the prefix + view.remove_prefix(NAMED_CACHED_QUERY_PREFIX.size()); + + parsedQuery::GraphPattern graphPattern = visit(ctx->groupGraphPattern()); + parsedQuery::NamedCachedQuery namedQuery{std::string{view}}; + for (const auto& op : graphPattern._graphPatterns) { + parseContent(namedQuery, op); + } + [[maybe_unused]] const auto& validated = + namedQuery.validateAndGetIdentifier(); + return namedQuery; +} + GraphPatternOperation Visitor::visitSpatialQuery( Parser::ServiceGraphPatternContext* ctx) { auto parseSpatialQuery = [ctx](parsedQuery::SpatialQuery& spatialQuery, @@ -1020,6 +1053,9 @@ GraphPatternOperation Visitor::visit(Parser::ServiceGraphPatternContext* ctx) { return visitPathQuery(ctx); } else if (serviceIri.toStringRepresentation() == SPATIAL_SEARCH_IRI) { return visitSpatialQuery(ctx); + } else if (asStringViewUnsafe(serviceIri.getContent()) + .starts_with(NAMED_CACHED_QUERY_PREFIX)) { + return visitNamedCachedQuery(serviceIri, ctx); } // Parse the body of the SERVICE query. Add the visible variables from the // SERVICE clause to the visible variables so far, but also remember them diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.h b/src/parser/sparqlParser/SparqlQleverVisitor.h index 412f2677f6..e0dfdda8fa 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.h +++ b/src/parser/sparqlParser/SparqlQleverVisitor.h @@ -281,6 +281,10 @@ class SparqlQleverVisitor { GraphPatternOperation visitSpatialQuery( Parser::ServiceGraphPatternContext* ctx); + GraphPatternOperation visitNamedCachedQuery( + const TripleComponent::Iri& target, + Parser::ServiceGraphPatternContext* ctx); + parsedQuery::GraphPatternOperation visit(Parser::BindContext* ctx); parsedQuery::GraphPatternOperation visit(Parser::InlineDataContext* ctx); diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index cd2ef57259..42f6ef4ef1 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -7,6 +7,7 @@ #include #include "engine/IndexScan.h" +#include "engine/NamedQueryCache.h" #include "engine/NeutralElementOperation.h" #include "engine/ValuesForTesting.h" #include "global/RuntimeParameters.h" @@ -125,11 +126,16 @@ class OperationTestFixture : public testing::Test { Index index = makeTestIndex("OperationTest", std::nullopt, true, true, true, 32_B); QueryResultCache cache; + NamedQueryCache namedCache; QueryExecutionContext qec{ - index, &cache, makeAllocator(), SortPerformanceEstimator{}, + index, + &cache, + makeAllocator(), + SortPerformanceEstimator{}, + &namedCache, [&](std::string json) { jsonHistory.emplace_back(std::move(json)); }}; IdTable table = makeIdTableFromVector({{}, {}, {}}); - ValuesForTesting operation{&qec, std::move(table), {}}; + ValuesForTesting operation{&qec, std::move(table), VariableToColumnMap{}}; }; // _____________________________________________________________________________ @@ -297,7 +303,8 @@ TEST(Operation, updateRuntimeStatsWorksCorrectly) { auto qec = getQec(); auto idTable = makeIdTableFromVector({{3, 4}, {7, 8}, {9, 123}}); ValuesForTesting valuesForTesting{ - qec, std::move(idTable), {Variable{"?x"}, Variable{"?y"}}}; + qec, std::move(idTable), + ValuesForTesting::VarVector{Variable{"?x"}, Variable{"?y"}}}; auto& rti = valuesForTesting.runtimeInfo(); @@ -419,9 +426,14 @@ TEST(Operation, ensureFailedStatusIsSetWhenGeneratorThrowsException) { "ensureFailedStatusIsSetWhenGeneratorThrowsException", std::nullopt, true, true, true, ad_utility::MemorySize::bytes(16), false); QueryResultCache cache{}; + NamedQueryCache namedCache{}; QueryExecutionContext context{ - index, &cache, makeAllocator(ad_utility::MemorySize::megabytes(100)), - SortPerformanceEstimator{}, [&](std::string) { signaledUpdate = true; }}; + index, + &cache, + makeAllocator(ad_utility::MemorySize::megabytes(100)), + SortPerformanceEstimator{}, + &namedCache, + [&](std::string) { signaledUpdate = true; }}; AlwaysFailOperation operation{&context}; ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; auto result = @@ -446,9 +458,14 @@ TEST(Operation, ensureSignalUpdateIsOnlyCalledEvery50msAndAtTheEnd) { "ensureSignalUpdateIsOnlyCalledEvery50msAndAtTheEnd", std::nullopt, true, true, true, ad_utility::MemorySize::bytes(16), false); QueryResultCache cache{}; + NamedQueryCache namedCache{}; QueryExecutionContext context{ - index, &cache, makeAllocator(ad_utility::MemorySize::megabytes(100)), - SortPerformanceEstimator{}, [&](std::string) { ++updateCallCounter; }}; + index, + &cache, + makeAllocator(ad_utility::MemorySize::megabytes(100)), + SortPerformanceEstimator{}, + &namedCache, + [&](std::string) { ++updateCallCounter; }}; CustomGeneratorOperation operation{ &context, [](const IdTable& idTable) -> Result::Generator { std::this_thread::sleep_for(50ms); @@ -489,9 +506,14 @@ TEST(Operation, ensureSignalUpdateIsCalledAtTheEndOfPartialConsumption) { "ensureSignalUpdateIsCalledAtTheEndOfPartialConsumption", std::nullopt, true, true, true, ad_utility::MemorySize::bytes(16), false); QueryResultCache cache{}; + NamedQueryCache namedCache{}; QueryExecutionContext context{ - index, &cache, makeAllocator(ad_utility::MemorySize::megabytes(100)), - SortPerformanceEstimator{}, [&](std::string) { ++updateCallCounter; }}; + index, + &cache, + makeAllocator(ad_utility::MemorySize::megabytes(100)), + SortPerformanceEstimator{}, + &namedCache, + [&](std::string) { ++updateCallCounter; }}; CustomGeneratorOperation operation{ &context, [](const IdTable& idTable) -> Result::Generator { co_yield {idTable.clone(), LocalVocab{}}; diff --git a/test/ValuesForTestingTest.cpp b/test/ValuesForTestingTest.cpp index 8c4b86d019..c8108f4d98 100644 --- a/test/ValuesForTestingTest.cpp +++ b/test/ValuesForTestingTest.cpp @@ -16,7 +16,8 @@ TEST(ValuesForTesting, valuesForTesting) { (ValuesForTesting{getQec(), table.clone(), {Variable{"?x"}}})); ValuesForTesting v{ - getQec(), table.clone(), {Variable{"?x"}, {Variable{"?y"}}}}; + getQec(), table.clone(), + ValuesForTesting::VarVector{Variable{"?x"}, {Variable{"?y"}}}}; // The following line has no effect. TODO provide default // implementations for such boilerplate methods in the `Operation` base class. ASSERT_EQ(v.getResultWidth(), 2u); @@ -42,7 +43,7 @@ TEST(ValuesForTesting, cornerCasesCacheKey) { auto empty = makeIdTableFromVector({}); auto neutral = makeIdTableFromVector({{}}); - ValuesForTesting vEmpty{getQec(), empty.clone(), {}}; - ValuesForTesting vNeutral{getQec(), neutral.clone(), {}}; + ValuesForTesting vEmpty{getQec(), empty.clone(), VariableToColumnMap{}}; + ValuesForTesting vNeutral{getQec(), neutral.clone(), VariableToColumnMap{}}; EXPECT_NE(vEmpty.getCacheKey(), vNeutral.getCacheKey()); } diff --git a/test/engine/BindTest.cpp b/test/engine/BindTest.cpp index 34ef0eb370..43039c47f7 100644 --- a/test/engine/BindTest.cpp +++ b/test/engine/BindTest.cpp @@ -6,8 +6,8 @@ #include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" -#include "./ValuesForTesting.h" #include "engine/Bind.h" +#include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/LiteralExpression.h" using namespace sparqlExpression; diff --git a/test/engine/CMakeLists.txt b/test/engine/CMakeLists.txt index 41b2b463ad..2fdd14ad20 100644 --- a/test/engine/CMakeLists.txt +++ b/test/engine/CMakeLists.txt @@ -13,3 +13,4 @@ addLinkAndRunAsSingleTest(SpatialJoinAlgorithmsTest engine) addLinkAndDiscoverTestSerial(QueryExecutionTreeTest engine) addLinkAndDiscoverTestSerial(DescribeTest engine) addLinkAndDiscoverTestSerial(ExistsJoinTest engine) +addLinkAndDiscoverTestSerial(NamedQueryCacheTest) diff --git a/test/engine/CartesianProductJoinTest.cpp b/test/engine/CartesianProductJoinTest.cpp index 8727aa223a..3bc01b077a 100644 --- a/test/engine/CartesianProductJoinTest.cpp +++ b/test/engine/CartesianProductJoinTest.cpp @@ -4,12 +4,12 @@ #include -#include "../engine/ValuesForTesting.h" #include "../util/GTestHelpers.h" #include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" #include "engine/CartesianProductJoin.h" #include "engine/QueryExecutionTree.h" +#include "engine/ValuesForTesting.h" using namespace ad_utility::testing; using ad_utility::source_location; diff --git a/test/engine/LazyGroupByTest.cpp b/test/engine/LazyGroupByTest.cpp index 1b952f9f7b..23bc903618 100644 --- a/test/engine/LazyGroupByTest.cpp +++ b/test/engine/LazyGroupByTest.cpp @@ -6,9 +6,9 @@ #include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" -#include "./ValuesForTesting.h" #include "engine/GroupBy.h" #include "engine/LazyGroupBy.h" +#include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/GroupConcatExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" diff --git a/test/engine/NamedQueryCacheTest.cpp b/test/engine/NamedQueryCacheTest.cpp new file mode 100644 index 0000000000..1522a64237 --- /dev/null +++ b/test/engine/NamedQueryCacheTest.cpp @@ -0,0 +1,21 @@ +// +// Created by kalmbacj on 2/6/25. +// + +#include + +#include "../util/IdTableHelpers.h" +#include "engine/NamedQueryCache.h" + +TEST(NamedQueryCache, basicWorkflow) { + NamedQueryCache cache; + EXPECT_EQ(cache.numEntries(), 0); + auto table = makeIdTableFromVector({{3, 7}, {9, 11}}); + using V = Variable; + VariableToColumnMap varColMap{{V{"?x"}, makeAlwaysDefinedColumn(0)}, + {V{"?y"}, makeAlwaysDefinedColumn(1)}}; + NamedQueryCache::Value value{ + std::make_shared(table.clone()), varColMap, {1, 0}}; + cache.store("query-1", std::move(value)); + EXPECT_EQ(cache.numEntries(), 1); +} diff --git a/test/engine/QueryExecutionTreeTest.cpp b/test/engine/QueryExecutionTreeTest.cpp index c67e17202f..d464e9b837 100644 --- a/test/engine/QueryExecutionTreeTest.cpp +++ b/test/engine/QueryExecutionTreeTest.cpp @@ -6,8 +6,8 @@ #include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" -#include "./ValuesForTesting.h" #include "engine/QueryExecutionTree.h" +#include "engine/ValuesForTesting.h" using namespace ad_utility::testing; diff --git a/test/util/IdTableHelpers.cpp b/test/util/IdTableHelpers.cpp index 34ad9414e7..b4708634aa 100644 --- a/test/util/IdTableHelpers.cpp +++ b/test/util/IdTableHelpers.cpp @@ -7,7 +7,7 @@ #include #include -#include "../engine/ValuesForTesting.h" +#include "engine/ValuesForTesting.h" #include "engine/idTable/IdTable.h" #include "global/ValueId.h" #include "util/Algorithm.h" diff --git a/test/util/IdTableHelpers.h b/test/util/IdTableHelpers.h index c831c12564..d363ba8ab1 100644 --- a/test/util/IdTableHelpers.h +++ b/test/util/IdTableHelpers.h @@ -13,13 +13,13 @@ #include #include -#include "../engine/ValuesForTesting.h" #include "./AllocatorTestHelpers.h" #include "./GTestHelpers.h" #include "./IdTestHelpers.h" #include "engine/CallFixedSize.h" #include "engine/Engine.h" #include "engine/QueryExecutionTree.h" +#include "engine/ValuesForTesting.h" #include "engine/idTable/IdTable.h" #include "global/ValueId.h" #include "util/Algorithm.h" diff --git a/test/util/IndexTestHelpers.cpp b/test/util/IndexTestHelpers.cpp index 8e1a693209..26c2698be0 100644 --- a/test/util/IndexTestHelpers.cpp +++ b/test/util/IndexTestHelpers.cpp @@ -6,6 +6,7 @@ #include "./GTestHelpers.h" #include "./TripleComponentTestHelpers.h" +#include "engine/NamedQueryCache.h" #include "global/SpecialIds.h" #include "index/IndexImpl.h" #include "util/ProgressBar.h" @@ -277,10 +278,11 @@ QueryExecutionContext* getQec(std::optional turtleInput, TypeErasedCleanup cleanup_; std::unique_ptr index_; std::unique_ptr cache_; + std::unique_ptr namedCache_; std::unique_ptr qec_ = std::make_unique( *index_, cache_.get(), makeAllocator(MemorySize::megabytes(100)), - SortPerformanceEstimator{}); + SortPerformanceEstimator{}, namedCache_.get()); }; using Key = std::tuple, bool, bool, bool, @@ -308,7 +310,8 @@ QueryExecutionContext* getQec(std::optional turtleInput, usePatterns, usePrefixCompression, blocksizePermutations, createTextIndex, addWordsFromLiterals, contentsOfWordsFileAndDocsFile)), - std::make_unique()}); + std::make_unique(), + std::make_unique()}); } auto* qec = contextMap.at(key).qec_.get(); qec->getIndex().getImpl().setGlobalIndexAndComparatorOnlyForTesting();