From c30c1018f61a45f9ff0c381d6b1df02a734e8ebb Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 17 Sep 2022 01:59:06 +0200 Subject: [PATCH] Parse complete SPARQL queries using ANTLR (#790) The ANTLR based parser now parses the complete query string into a `ParsedQuery`. The only thing that is till done via the old parser are FILTERs. Also add many checks for invariants of a valid SPARQL query into the `ParsedQuery` class and improve the unit tests for the parsing. --- CMakeLists.txt | 4 +- e2e/scientists_queries.yaml | 6 +- src/engine/Server.cpp | 20 +- src/parser/CMakeLists.txt | 6 +- src/parser/ConstructClause.h | 30 + src/parser/ParsedQuery.cpp | 125 ++-- src/parser/ParsedQuery.h | 65 +- src/parser/SelectClause.cpp | 25 +- src/parser/SelectClause.h | 25 +- src/parser/SparqlParser.cpp | 128 +--- src/parser/SparqlParser.h | 36 +- .../sparqlParser/SparqlQleverVisitor.cpp | 98 ++- src/parser/sparqlParser/SparqlQleverVisitor.h | 17 +- src/util/Algorithm.h | 15 + test/ParseExceptionTest.cpp | 18 +- test/QueryPlannerTest.cpp | 382 +++++----- test/QueryPlannerTestHelpers.h | 2 +- test/SparqlAntlrParserTest.cpp | 169 +++-- test/SparqlAntlrParserTestHelpers.h | 32 +- test/SparqlParserTest.cpp | 656 +++++++----------- 20 files changed, 903 insertions(+), 956 deletions(-) create mode 100644 src/parser/ConstructClause.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 846021caa6..f62cfb06fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,8 @@ if ("${CMAKE_GENERATOR}" STREQUAL "Ninja") endif () if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") AND - (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12") AND - (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.1")) +(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12") AND +(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.2")) message(STATUS "Adding -Wno-restrict for g++12.0 because of false positives") add_compile_options(-Wno-restrict) else() diff --git a/e2e/scientists_queries.yaml b/e2e/scientists_queries.yaml index ad3b2454e7..98520b545b 100644 --- a/e2e/scientists_queries.yaml +++ b/e2e/scientists_queries.yaml @@ -1118,10 +1118,10 @@ queries: ?t ql:contains-word "algo*" } checks: - - num_cols: 2 + - num_cols: 3 - num_rows: 11 - - selected: ["?x", "?t"] - - contains_row: ["","Hermann's algorithm for primary decomposition is still in use now."] + - selected: [ "?x", "?ql_textscore_t", "?t" ] + - contains_row: [ "",null,"Hermann's algorithm for primary decomposition is still in use now." ] - query : select_asterisk_regex-lastname-stein diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index d6678f9e88..93a5120974 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -437,8 +437,8 @@ Awaitable Server::composeResponseQleverJson( query.hasSelectClause() ? qet.writeResultAsQLeverJson(query.selectClause(), limit, offset, std::move(resultTable)) - : qet.writeRdfGraphJson(query.constructClause(), limit, offset, - std::move(resultTable)); + : qet.writeRdfGraphJson(query.constructClause().triples_, limit, + offset, std::move(resultTable)); requestTimer.stop(); } j["resultsize"] = query.hasSelectClause() ? resultSize : j["res"].size(); @@ -510,11 +510,11 @@ Server::composeResponseSepValues(const ParsedQuery& query, auto compute = [&] { size_t limit = query._limitOffset._limit; size_t offset = query._limitOffset._offset; - return query.hasSelectClause() - ? qet.generateResults(query.selectClause(), limit, - offset) - : qet.writeRdfGraphSeparatedValues( - query.constructClause(), limit, offset, qet.getResult()); + return query.hasSelectClause() ? qet.generateResults( + query.selectClause(), limit, offset) + : qet.writeRdfGraphSeparatedValues( + query.constructClause().triples_, + limit, offset, qet.getResult()); }; return computeInNewThread(compute); } @@ -530,8 +530,8 @@ ad_utility::streams::stream_generator Server::composeTurtleResponse( } size_t limit = query._limitOffset._limit; size_t offset = query._limitOffset._offset; - return qet.writeRdfGraphTurtle(query.constructClause(), limit, offset, - qet.getResult()); + return qet.writeRdfGraphTurtle(query.constructClause().triples_, limit, + offset, qet.getResult()); } // _____________________________________________________________________________ @@ -646,7 +646,7 @@ boost::asio::awaitable Server::processQuery( << (pinResult ? " [pin result]" : "") << (pinSubtrees ? " [pin subresults]" : "") << "\n" << query << std::endl; - ParsedQuery pq = SparqlParser(query).parse(); + ParsedQuery pq = SparqlParser::parseQuery(query); // The following code block determines the media type to be used for the // result. The media type is either determined by the "Accept:" header of diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index 9daf7650de..8be9e61d8f 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -19,6 +19,10 @@ add_library(parser SparqlParserHelpers.h SparqlParserHelpers.cpp TripleComponent.h GraphPatternOperation.cpp - PropertyPath.h PropertyPath.cpp Alias.h data/SolutionModifiers.h data/LimitOffsetClause.h data/SparqlFilter.h data/SparqlFilter.cpp data/OrderKey.h data/GroupKey.h ParseException.cpp SelectClause.cpp SelectClause.h GraphPatternOperation.cpp GraphPatternOperation.h GraphPattern.cpp GraphPattern.h) + PropertyPath.h PropertyPath.cpp Alias.h data/SolutionModifiers.h + data/LimitOffsetClause.h data/SparqlFilter.h data/SparqlFilter.cpp + data/OrderKey.h data/GroupKey.h ParseException.cpp SelectClause.cpp + SelectClause.h GraphPatternOperation.cpp GraphPatternOperation.h + GraphPattern.cpp GraphPattern.h ConstructClause.h) target_link_libraries(parser sparqlParser parserData sparqlExpressions rdfEscaping re2 absl::flat_hash_map util) diff --git a/src/parser/ConstructClause.h b/src/parser/ConstructClause.h new file mode 100644 index 0000000000..368909d63d --- /dev/null +++ b/src/parser/ConstructClause.h @@ -0,0 +1,30 @@ +// Copyright 2022, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Julian Mundhahs (mundhahj@informatik.uni-freiburg.de) + +#pragma once + +#include "parser/SelectClause.h" +#include "parser/data/Types.h" + +namespace parsedQuery { +struct ConstructClause : ClauseBase { + ad_utility::sparql_types::Triples triples_; + + ConstructClause() = default; + explicit ConstructClause(ad_utility::sparql_types::Triples triples) + : triples_(std::move(triples)) {} + + // Yields all variables that appear in this `ConstructClause`. Variables that + // appear multiple times are also yielded multiple times. + cppcoro::generator containedVariables() const { + for (const auto& triple : triples_) { + for (const auto& varOrTerm : triple) { + if (auto variable = std::get_if(&varOrTerm)) { + co_yield *variable; + } + } + } + } +}; +} // namespace parsedQuery diff --git a/src/parser/ParsedQuery.cpp b/src/parser/ParsedQuery.cpp index f23d3e8536..96c93f79dc 100644 --- a/src/parser/ParsedQuery.cpp +++ b/src/parser/ParsedQuery.cpp @@ -22,16 +22,6 @@ using std::vector; string ParsedQuery::asString() const { std::ostringstream os; - // PREFIX - os << "PREFIX: {"; - for (size_t i = 0; i < _prefixes.size(); ++i) { - os << "\n\t" << _prefixes[i].asString(); - if (i + 1 < _prefixes.size()) { - os << ','; - } - } - os << "\n}"; - bool usesSelect = hasSelectClause(); bool usesAsterisk = usesSelect && selectClause().isAsterisk(); @@ -56,7 +46,7 @@ string ParsedQuery::asString() const { os << "{"; } } else if (hasConstructClause()) { - const auto& constructClause = this->constructClause(); + const auto& constructClause = this->constructClause().triples_; os << "\n CONSTRUCT {\n\t"; for (const auto& triple : constructClause) { os << triple[0].toSparql(); @@ -130,24 +120,35 @@ Variable ParsedQuery::addInternalBind( // ________________________________________________________________________ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { - // Process groupClause - // TODO Check that all variables that are part of an - // expression that is grouped on are visible in the Query Body. - auto processVariable = [this](const Variable& groupKey) { - // TODO: implement for `ConstructClause` - if (hasSelectClause()) { - if (!ad_utility::contains(selectClause().getVisibleVariables(), - groupKey)) { - throw ParseException( - "Variable " + groupKey.name() + - " was used in an GROUP BY but is not visible in the query body."); - } + auto checkVariableIsVisible = [this](const Variable& var, + const std::string& locationDescription) { + if (!ad_utility::contains(getVisibleVariables(), var)) { + throw ParseException("Variable " + var.name() + " was used in " + + locationDescription + + ", but is not visible in the Query Body."); } + }; + auto checkUsedVariablesAreVisible = + [&checkVariableIsVisible]( + const sparqlExpression::SparqlExpressionPimpl& expression, + const std::string& locationDescription) { + for (const auto* var : expression.containedVariables()) { + checkVariableIsVisible(*var, locationDescription + " in Expression " + + expression.getDescriptor()); + } + }; + + // Process groupClause + auto processVariable = [this, + &checkVariableIsVisible](const Variable& groupKey) { + checkVariableIsVisible(groupKey, "GROUP BY"); _groupByVariables.emplace_back(groupKey.name()); }; auto processExpression = - [this](sparqlExpression::SparqlExpressionPimpl groupKey) { + [this, &checkUsedVariablesAreVisible]( + sparqlExpression::SparqlExpressionPimpl groupKey) { + checkUsedVariablesAreVisible(groupKey, "Group Key"); auto helperTarget = addInternalBind(std::move(groupKey)); _groupByVariables.emplace_back(helperTarget.name()); }; @@ -167,21 +168,27 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { } // Process havingClause + // TODO as soon as FILTER and HAVING support proper + // expressions, also add similar sanity checks for the HAVING clause here. _havingClauses = std::move(modifiers.havingClauses_); // Process orderClause - // TODO Check that all variables that are part of an - // expression that is ordered on are visible in the Query Body. - auto processVariableOrderKey = [this](VariableOrderKey orderKey) { + auto processVariableOrderKey = [this, &checkVariableIsVisible]( + VariableOrderKey orderKey) { // Check whether grouping is done. The variable being ordered by // must then be either grouped or the result of an alias in the select. const vector& groupByVariables = _groupByVariables; - if (!groupByVariables.empty() && - !ad_utility::contains(groupByVariables, orderKey.variable_) && - !ad_utility::contains_if(selectClause().getAliases(), - [&orderKey](const Alias& alias) { - return alias._target == orderKey.variable_; - })) { + if (groupByVariables.empty()) { + checkVariableIsVisible(orderKey.variable_, "ORDERY BY"); + } else if (!ad_utility::contains(groupByVariables, orderKey.variable_) && + // `ConstructClause` has no Aliases. So the variable can never be + // the result of an Alias. + (hasConstructClause() || + !ad_utility::contains_if(selectClause().getAliases(), + [&orderKey](const Alias& alias) { + return alias._target == + orderKey.variable_; + }))) { throw ParseException( "Variable " + orderKey.variable_.name() + " was used in an ORDER BY " @@ -195,8 +202,10 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { // QLever currently only supports ordering by variables. To allow // all `orderConditions`, the corresponding expression is bound to a new // internal variable. Ordering is then done by this variable. - auto processExpressionOrderKey = [this](ExpressionOrderKey orderKey) { - if (!_groupByVariables.empty()) + auto processExpressionOrderKey = [this, &checkUsedVariablesAreVisible]( + ExpressionOrderKey orderKey) { + checkUsedVariablesAreVisible(orderKey.expression_, "Order Key"); + if (!_groupByVariables.empty()) { // TODO Implement this by adding a hidden alias in the // SELECT clause. throw ParseException( @@ -206,6 +215,7 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { "\"). Please assign this expression to a " "new variable in the SELECT clause and then order by this " "variable."); + } auto additionalVariable = addInternalBind(std::move(orderKey.expression_)); _orderBy.emplace_back(additionalVariable, orderKey.isDescending_); }; @@ -219,6 +229,8 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { // Process limitOffsetClause _limitOffset = modifiers.limitOffset_; + // Check that the query is valid + auto checkAliasOutNamesHaveNoOverlapWith = [this](const auto& container, const std::string& message) { for (const auto& alias : selectClause().getAliases()) { @@ -228,8 +240,6 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { } }; - // Check that the query is valid - if (hasSelectClause()) { if (!_groupByVariables.empty()) { ad_utility::HashSet groupVariables{}; @@ -287,15 +297,27 @@ void ParsedQuery::addSolutionModifiers(SolutionModifiers modifiers) { throw ParseException("The variable name " + a._target.name() + " used in an alias was already selected on."); } - // TODO Check that all variables used in the expression of - // Aliases are visible in the QueryBody. + + checkUsedVariablesAreVisible(a._expression, "Alias"); + } + } else if (hasConstructClause()) { + if (_groupByVariables.empty()) { + return; + } + + for (const auto& variable : constructClause().containedVariables()) { + if (!ad_utility::contains(_groupByVariables, variable)) { + throw ParseException("Variable " + variable.name() + + " is used but not " + "aggregated despite the query not being " + "grouped by " + + variable.name() + "."); + } } } } void ParsedQuery::merge(const ParsedQuery& p) { - _prefixes.insert(_prefixes.begin(), p._prefixes.begin(), p._prefixes.end()); - auto& children = _rootGraphPattern._graphPatterns; auto& otherChildren = p._rootGraphPattern._graphPatterns; children.insert(children.end(), otherChildren.begin(), otherChildren.end()); @@ -305,6 +327,27 @@ void ParsedQuery::merge(const ParsedQuery& p) { _rootGraphPattern.recomputeIds(&_numGraphPatterns); } +// _____________________________________________________________________________ +const std::vector& ParsedQuery::getVisibleVariables() const { + return std::visit(&parsedQuery::ClauseBase::getVisibleVariables, _clause); +} + +// _____________________________________________________________________________ +void ParsedQuery::registerVariablesVisibleInQueryBody( + const vector& variables) { + for (const auto& var : variables) { + registerVariableVisibleInQueryBody(var); + } +} + +// _____________________________________________________________________________ +void ParsedQuery::registerVariableVisibleInQueryBody(const Variable& variable) { + auto addVariable = [&variable](auto& clause) { + clause.addVisibleVariable(variable); + }; + std::visit(addVariable, _clause); +} + // _____________________________________________________________________________ void ParsedQuery::GraphPattern::toString(std::ostringstream& os, int indentation) const { diff --git a/src/parser/ParsedQuery.h b/src/parser/ParsedQuery.h index 47ae18e3a7..e658fbae03 100644 --- a/src/parser/ParsedQuery.h +++ b/src/parser/ParsedQuery.h @@ -9,27 +9,28 @@ #include #include -#include "../engine/ResultType.h" -#include "../engine/sparqlExpressions/SparqlExpressionPimpl.h" -#include "../util/Algorithm.h" -#include "../util/Exception.h" -#include "../util/HashMap.h" -#include "../util/OverloadCallOperator.h" -#include "../util/StringUtils.h" -#include "./TripleComponent.h" -#include "Alias.h" -#include "ParseException.h" -#include "PropertyPath.h" -#include "data/GroupKey.h" -#include "data/LimitOffsetClause.h" -#include "data/OrderKey.h" -#include "data/SolutionModifiers.h" -#include "data/SparqlFilter.h" -#include "data/Types.h" -#include "data/VarOrTerm.h" +#include "engine/ResultType.h" +#include "engine/sparqlExpressions/SparqlExpressionPimpl.h" +#include "parser/Alias.h" +#include "parser/ConstructClause.h" #include "parser/GraphPattern.h" #include "parser/GraphPatternOperation.h" +#include "parser/ParseException.h" +#include "parser/PropertyPath.h" #include "parser/SelectClause.h" +#include "parser/TripleComponent.h" +#include "parser/data/GroupKey.h" +#include "parser/data/LimitOffsetClause.h" +#include "parser/data/OrderKey.h" +#include "parser/data/SolutionModifiers.h" +#include "parser/data/SparqlFilter.h" +#include "parser/data/Types.h" +#include "parser/data/VarOrTerm.h" +#include "util/Algorithm.h" +#include "util/Exception.h" +#include "util/HashMap.h" +#include "util/OverloadCallOperator.h" +#include "util/StringUtils.h" using std::string; using std::vector; @@ -88,13 +89,10 @@ class ParsedQuery { using SelectClause = parsedQuery::SelectClause; - using ConstructClause = ad_utility::sparql_types::Triples; + using ConstructClause = parsedQuery::ConstructClause; ParsedQuery() = default; - // The ql prefix for QLever specific additions is always defined. - vector _prefixes = {SparqlPrefix( - INTERNAL_PREDICATE_PREFIX_NAME, INTERNAL_PREDICATE_PREFIX_IRI)}; GraphPattern _rootGraphPattern; vector _havingClauses; size_t _numGraphPatterns = 1; @@ -134,23 +132,14 @@ class ParsedQuery { return std::get(_clause); } - // Add a variable, that was found in the SubQuery body, when query has a - // Select Clause. - bool registerVariableVisibleInQueryBody(const Variable& variable) { - if (!hasSelectClause()) return false; - selectClause().addVisibleVariable(variable); - return true; - } + // Add a variable, that was found in the query body. + void registerVariableVisibleInQueryBody(const Variable& variable); - // Add variables, that were found in the SubQuery body, when query has a - // Select Clause. - bool registerVariablesVisibleInQueryBody(const vector& variables) { - if (!hasSelectClause()) return false; - for (const auto& var : variables) { - selectClause().addVisibleVariable(var); - } - return true; - } + // Add variables, that were found in the query body. + void registerVariablesVisibleInQueryBody(const vector& variables); + + // Returns all variables that are visible in the Query Body. + const std::vector& getVisibleVariables() const; auto& children() { return _rootGraphPattern._graphPatterns; } [[nodiscard]] const auto& children() const { diff --git a/src/parser/SelectClause.cpp b/src/parser/SelectClause.cpp index 2f6eb29334..14e6fe49ae 100644 --- a/src/parser/SelectClause.cpp +++ b/src/parser/SelectClause.cpp @@ -9,6 +9,20 @@ #include "util/OverloadCallOperator.h" using namespace parsedQuery; + +// ____________________________________________________________________ +// TODO use a better mechanism to remove duplicates in the +// visible variables while still keeping the order. +void ClauseBase::addVisibleVariable(const Variable& variable) { + if (!ad_utility::contains(visibleVariables_, variable)) { + visibleVariables_.emplace_back(variable); + } +} + +const vector& ClauseBase::getVisibleVariables() const { + return visibleVariables_; +} + // ____________________________________________________________________ [[nodiscard]] bool SelectClause::isAsterisk() const { return std::holds_alternative(varsAndAliasesOrAsterisk_); @@ -42,17 +56,6 @@ void SelectClause::setSelected(std::vector variables) { setSelected(v); } -// ____________________________________________________________________ -void SelectClause::addVisibleVariable(const Variable& variable) { - if (!ad_utility::contains(visibleVariables_, variable)) { - visibleVariables_.emplace_back(variable); - } -} - -const vector& SelectClause::getVisibleVariables() { - return visibleVariables_; -} - // ____________________________________________________________________ [[nodiscard]] const std::vector& SelectClause::getSelectedVariables() const { diff --git a/src/parser/SelectClause.h b/src/parser/SelectClause.h index 7a4d300823..280f773ca0 100644 --- a/src/parser/SelectClause.h +++ b/src/parser/SelectClause.h @@ -13,10 +13,23 @@ namespace parsedQuery { +/// Base class for common functionality of `SelectClause` and `ConstructClause`. +struct ClauseBase { + // The variables that are visible in the query body. Will be used in the case + // of `SELECT *` and to check invariants of the `ParsedQuery`. + std::vector visibleVariables_; + + // Add a variable that is visible in the query body. + void addVisibleVariable(const Variable& variable); + + // Get all the variables that are visible in the query body. + const std::vector& getVisibleVariables() const; +}; + /// The `SELECT` clause of a SPARQL query. It contains the selected variables /// and aliases. If all variables are selected via `SELECT *` then this class /// also stores the variables to which the `*` will expand. -struct SelectClause { +struct SelectClause : ClauseBase { bool reduced_ = false; bool distinct_ = false; @@ -32,10 +45,6 @@ struct SelectClause { // The `char` means `SELECT *`. std::variant varsAndAliasesOrAsterisk_; - // The variables that are visible in the query body. Will be used in the case - // of `SELECT *` and to check invariants of the `ParsedQuery`. - std::vector visibleVariables_; - public: /// If true, then this `SelectClause` represents `SELECT *`. If false, /// variables and aliases were manually selected. @@ -55,12 +64,6 @@ struct SelectClause { // variables and no aliases are selected. void setSelected(std::vector variables); - // Add a variable that is visible in the query body. - void addVisibleVariable(const Variable& variable); - - // Get all the variables that are visible in the query body. - const std::vector& getVisibleVariables(); - /// Get all the selected variables. This includes the variables to which /// aliases are bound. For example for `SELECT ?x (?a + ?b AS ?c)`, /// `getSelectedVariables` will return `{?x, ?c}`. If `isAsterisk()` is true diff --git a/src/parser/SparqlParser.cpp b/src/parser/SparqlParser.cpp index 9af4dec119..acd370ba6f 100644 --- a/src/parser/SparqlParser.cpp +++ b/src/parser/SparqlParser.cpp @@ -19,99 +19,17 @@ SparqlParser::SparqlParser(const string& query) : lexer_(query), query_(query) { LOG(DEBUG) << "Parsing " << query << std::endl; } -namespace { -// Converts the PrefixMap to the legacy data format used by ParsedQuery -vector convertPrefixMap( - const SparqlQleverVisitor::PrefixMap& map) { - vector prefixes; - for (auto const& [label, iri] : map) { - prefixes.emplace_back(label, iri); - } - return prefixes; -} -} // namespace - // _____________________________________________________________________________ -ParsedQuery SparqlParser::parse() { - ParsedQuery result; - std::string originalString = query_; - // parsePrologue parses all the prefixes which are stored in a member - // PrefixMap. This member is returned on parse. - SparqlQleverVisitor::PrefixMap prefixes = parseWithAntlr( - &AntlrParser::prologue, - {{INTERNAL_PREDICATE_PREFIX_NAME, INTERNAL_PREDICATE_PREFIX_IRI}}); - - if (lexer_.accept("construct")) { - result._originalString = std::move(originalString); - result._prefixes = convertPrefixMap(prefixes); - parseQuery(&result, CONSTRUCT_QUERY); - } else if (lexer_.peek("select")) { - result = parseWithAntlr(&AntlrParser::selectQuery, prefixes); - result._originalString = std::move(originalString); - result._prefixes = convertPrefixMap(prefixes); - } else { - throw ParseException("Query must either be a SELECT or CONSTRUCT."); - } - lexer_.expectEmpty(); - - return result; -} - -// _____________________________________________________________________________ -void SparqlParser::parseQuery(ParsedQuery* query, QueryType queryType) { - AD_CHECK(queryType == CONSTRUCT_QUERY); - query->_clause = parseWithAntlr(&AntlrParser::constructTemplate, *query) - .value_or(ad_utility::sparql_types::Triples{}); - - parseWhere(query); - - parseSolutionModifiers(query); - - if (query->_groupByVariables.empty()) { - return; - } - - AD_CHECK(query->hasConstructClause()); - auto& constructClause = query->constructClause(); - for (const auto& triple : constructClause) { - for (const auto& varOrTerm : triple) { - if (auto variable = std::get_if(&varOrTerm)) { - if (!ad_utility::contains(query->_groupByVariables, *variable)) { - throw ParseException("Variable " + variable->name() + - " is used but not " - "aggregated despite the query not being " - "grouped by " + - variable->name() + ".\n" + lexer_.input()); - } - } - } - } -} - -// _____________________________________________________________________________ -SparqlQleverVisitor::PrefixMap SparqlParser::getPrefixMap( - const ParsedQuery& parsedQuery) { - SparqlQleverVisitor::PrefixMap prefixMap; - for (const auto& prefixDef : parsedQuery._prefixes) { - prefixMap[prefixDef._prefix] = prefixDef._uri; - } - return prefixMap; -} - -// _____________________________________________________________________________ -void SparqlParser::parseWhere(ParsedQuery* query) { - auto [pattern, visibleVariables] = - parseWithAntlr(&AntlrParser::whereClause, *query); - query->_rootGraphPattern = std::move(pattern); - query->registerVariablesVisibleInQueryBody(visibleVariables); -} - -// _____________________________________________________________________________ -void SparqlParser::parseSolutionModifiers(ParsedQuery* query) { - query->addSolutionModifiers( - parseWithAntlr(&AntlrParser::solutionModifier, *query)); - - lexer_.accept("}"); +ParsedQuery SparqlParser::parseQuery(std::string query) { + sparqlParserHelpers::ParserAndVisitor p{ + std::move(query), + {{INTERNAL_PREDICATE_PREFIX_NAME, INTERNAL_PREDICATE_PREFIX_IRI}}}; + auto resultOfParseAndRemainingText = p.parseTypesafe(&AntlrParser::query); + // The query rule ends with so the parse always has to consume the whole + // input. If this is not the case a ParseException should have been thrown at + // an earlier point. + AD_CHECK(resultOfParseAndRemainingText.remainingText_.empty()); + return std::move(resultOfParseAndRemainingText.resultOfParse_); } // _____________________________________________________________________________ @@ -324,32 +242,6 @@ SparqlFilter SparqlParser::parseRegexFilter(bool expectKeyword) { return f; } -// ________________________________________________________________________ -template -auto SparqlParser::parseWithAntlr( - ContextType* (SparqlAutomaticParser::*F)(void), - const ParsedQuery& parsedQuery) - -> decltype((std::declval()) - .parseTypesafe(F) - .resultOfParse_) { - return parseWithAntlr(F, getPrefixMap(parsedQuery)); -} - -// ________________________________________________________________________ -template -auto SparqlParser::parseWithAntlr( - ContextType* (SparqlAutomaticParser::*F)(void), - SparqlQleverVisitor::PrefixMap prefixMap) - -> decltype((std::declval()) - .parseTypesafe(F) - .resultOfParse_) { - sparqlParserHelpers::ParserAndVisitor p{lexer_.getUnconsumedInput(), - std::move(prefixMap)}; - auto resultOfParseAndRemainingText = p.parseTypesafe(F); - lexer_.reset(std::move(resultOfParseAndRemainingText.remainingText_)); - return std::move(resultOfParseAndRemainingText.resultOfParse_); -} - namespace { // The legacy way of expanding prefixes in an IRI. Currently used only by // `parserFilterExpression` below. diff --git a/src/parser/SparqlParser.h b/src/parser/SparqlParser.h index c20a59cb8b..6ba3f05b14 100644 --- a/src/parser/SparqlParser.h +++ b/src/parser/SparqlParser.h @@ -15,14 +15,16 @@ using std::string; -enum QueryType { CONSTRUCT_QUERY, SELECT_QUERY }; - -// A simple parser of SPARQL. -// No supposed to feature the complete query language. +// The SPARQL parser used by QLever. The actual parsing is delegated to a parser +// that is based on ANTLR4, which recognises the complete SPARQL 1.1 QL grammar. +// For valid SPARQL queries that are not supported by QLever a reasonable error +// message is given. The only thing that is still parsed manually by this class +// are FILTER clauses, because QLever doesn't support arbitrary expressions in +// filters yet. class SparqlParser { public: explicit SparqlParser(const string& query); - ParsedQuery parse(); + static ParsedQuery parseQuery(std::string query); /// Parse the expression of a filter statement (without the `FILTER` keyword). /// This helper method is needed as long as the set of expressions supported @@ -32,34 +34,10 @@ class SparqlParser { const SparqlQleverVisitor::PrefixMap& prefixMap); private: - void parseQuery(ParsedQuery* query, QueryType queryType); - void parseWhere(ParsedQuery* query); - void parseSolutionModifiers(ParsedQuery* query); // Returns true if it found a filter std::optional parseFilter(bool failOnNoFilter = true); SparqlLexer lexer_; string query_; SparqlFilter parseRegexFilter(bool expectKeyword); - - // Helper function that converts the prefix map from `parsedQuery` (a vector - // of pairs of prefix and IRI) to the prefix map we need for the - // `SparqlQleverVisitor` (a hash map from prefixes to IRIs). - static SparqlQleverVisitor::PrefixMap getPrefixMap( - const ParsedQuery& parsedQuery); - // Parse the clause with the prefixes of the given ParsedQuery. - template - auto parseWithAntlr(ContextType* (SparqlAutomaticParser::*F)(void), - const ParsedQuery& parsedQuery) - -> decltype((std::declval()) - .parseTypesafe(F) - .resultOfParse_); - - // Parse the clause with the given explicitly specified prefixes. - template - auto parseWithAntlr(ContextType* (SparqlAutomaticParser::*F)(void), - SparqlQleverVisitor::PrefixMap prefixMap) - -> decltype((std::declval()) - .parseTypesafe(F) - .resultOfParse_); }; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 525e613944..2764efc464 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -113,9 +113,7 @@ PathTuples joinPredicateAndObject(VarOrPath predicate, ObjectList objectList) { // TODO The fulltext index should perform the splitting of its keywords, // and not the SparqlParser. if (PropertyPath* path = std::get_if(&predicate)) { - if (path->asString() == CONTAINS_WORD_PREDICATE || - // TODO _NS no longer needed? - path->asString() == CONTAINS_WORD_PREDICATE_NS) { + if (path->asString() == CONTAINS_WORD_PREDICATE) { if (const Literal* literal = unwrapVariant(object)) { object = Literal{stripAndLowercaseKeywordLiteral(literal->literal())}; @@ -128,18 +126,25 @@ PathTuples joinPredicateAndObject(VarOrPath predicate, ObjectList objectList) { } // ____________________________________________________________________________________ -std::variant Visitor::visitTypesafe( - Parser::QueryContext* ctx) { +ParsedQuery Visitor::visitTypesafe(Parser::QueryContext* ctx) { // The prologue (BASE and PREFIX declarations) only affects the internal // state of the visitor. visitTypesafe(ctx->prologue()); + ParsedQuery query; + // TODO Check if there are more instances of this pattern + // (like `visitAlternative`, but with a custom error message), that would + // justify extracting this pattern. if (ctx->selectQuery()) { - return visitTypesafe(ctx->selectQuery()); - } - if (ctx->constructQuery()) { - return visitTypesafe(ctx->constructQuery()); + query = visitTypesafe(ctx->selectQuery()); + } else if (ctx->constructQuery()) { + query = visitTypesafe(ctx->constructQuery()); + } else { + reportError(ctx, "QLever only supports SELECT and CONSTRUCT queries."); } - reportError(ctx, "QLever only supports select and construct queries"); + + query._originalString = ctx->getStart()->getInputStream()->toString(); + + return query; } // ____________________________________________________________________________________ @@ -175,17 +180,25 @@ Alias Visitor::visitTypesafe(Parser::AliasWithoutBracketsContext* ctx) { } // ____________________________________________________________________________________ -Triples Visitor::visitTypesafe(Parser::ConstructQueryContext* ctx) { +ParsedQuery Visitor::visitTypesafe(Parser::ConstructQueryContext* ctx) { if (!ctx->datasetClause().empty()) { - reportError(ctx, "Datasets are not supported"); + reportError(ctx->datasetClause(0), + "QLever currently doesn't support FROM clauses"); } - // TODO: once where clause is supported also process whereClause and - // solutionModifiers + ParsedQuery query; if (ctx->constructTemplate()) { - return visitTypesafe(ctx->constructTemplate()).value_or(Triples{}); + query._clause = visitTypesafe(ctx->constructTemplate()) + .value_or(parsedQuery::ConstructClause{}); + auto [pattern, visibleVariables] = visitTypesafe(ctx->whereClause()); + query._rootGraphPattern = std::move(pattern); + query.registerVariablesVisibleInQueryBody(visibleVariables); } else { - return {}; + query._clause = parsedQuery::ConstructClause{ + visitOptional(ctx->triplesTemplate()).value_or(Triples{})}; } + query.addSolutionModifiers(visitTypesafe(ctx->solutionModifier())); + + return query; } // ____________________________________________________________________________________ @@ -476,9 +489,13 @@ vector Visitor::visitTypesafe(Parser::GroupClauseContext* ctx) { } // ____________________________________________________________________________________ -std::optional Visitor::visitTypesafe( +std::optional Visitor::visitTypesafe( Parser::ConstructTemplateContext* ctx) { - return visitOptional(ctx->constructTriples()); + if (ctx->constructTriples()) { + return parsedQuery::ConstructClause{visitTypesafe(ctx->constructTriples())}; + } else { + return std::nullopt; + } } // ____________________________________________________________________________________ @@ -586,8 +603,6 @@ ParsedQuery Visitor::visitTypesafe(Parser::SelectQueryContext* ctx) { query._rootGraphPattern = std::move(pattern); query.registerVariablesVisibleInQueryBody(visibleVariables); query.addSolutionModifiers(visitTypesafe(ctx->solutionModifier())); - // TODO: move up to visitTypesafe(QueryContext*) - query._originalString = ctx->getStart()->getInputStream()->toString(); return query; } @@ -829,16 +844,34 @@ vector Visitor::visitTypesafe( return visitVector(ctx->expression()); } -// ____________________________________________________________________________________ -Triples Visitor::visitTypesafe(Parser::ConstructTriplesContext* ctx) { +template +Triples Visitor::parseTriplesConstruction(Context* ctx) { auto result = visitTypesafe(ctx->triplesSameSubject()); - if (ctx->constructTriples()) { - auto newTriples = visitTypesafe(ctx->constructTriples()); + Context* subContext = [](Context* ctx) -> Context* { + if constexpr (std::is_same_v) { + return ctx->constructTriples(); + } else if constexpr (std::is_same_v) { + return ctx->triplesTemplate(); + } + }(ctx); + if (subContext) { + auto newTriples = visitTypesafe(subContext); ad_utility::appendVector(result, std::move(newTriples)); } return result; } +// ____________________________________________________________________________________ +Triples Visitor::visitTypesafe(Parser::ConstructTriplesContext* ctx) { + return parseTriplesConstruction(ctx); +} + +// ____________________________________________________________________________________ +Triples Visitor::visitTypesafe(Parser::TriplesTemplateContext* ctx) { + return parseTriplesConstruction(ctx); +} + // ____________________________________________________________________________________ Triples Visitor::visitTypesafe(Parser::TriplesSameSubjectContext* ctx) { Triples triples; @@ -927,6 +960,22 @@ Node Visitor::visitTypesafe(Parser::ObjectRContext* ctx) { // ___________________________________________________________________________ vector SparqlQleverVisitor::visitTypesafe( SparqlAutomaticParser::TriplesSameSubjectPathContext* ctx) { + // If a triple `?var ql:contains-word "words"` or `?var ql:contains-entity + // ` is contained in the query, then the variable `?ql_textscore_var` + // is implicitly created and visible in the query body. + auto setTextscoreVisibleIfPresent = [this](VarOrTerm& subject, + VarOrPath& predicate) { + if (auto* var = std::get_if(&subject)) { + if (auto* propertyPath = std::get_if(&predicate)) { + if (propertyPath->asString() == CONTAINS_ENTITY_PREDICATE || + propertyPath->asString() == CONTAINS_WORD_PREDICATE) { + addVisibleVariable( + absl::StrCat(TEXTSCORE_VARIABLE_PREFIX, var->name().substr(1))); + } + } + } + }; + if (ctx->varOrTerm()) { vector triples; auto subject = visitTypesafe(ctx->varOrTerm()); @@ -934,6 +983,7 @@ vector SparqlQleverVisitor::visitTypesafe( for (auto& [predicate, object] : tuples) { // TODO clang does not yet support emplace_back for // aggregates. + setTextscoreVisibleIfPresent(subject, predicate); triples.push_back(TripleWithPropertyPath{subject, std::move(predicate), std::move(object)}); } diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.h b/src/parser/sparqlParser/SparqlQleverVisitor.h index 94891e17d2..2921d058dc 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.h +++ b/src/parser/sparqlParser/SparqlQleverVisitor.h @@ -12,6 +12,7 @@ #include "engine/sparqlExpressions/SampleExpression.h" #include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/Alias.h" +#include "parser/ConstructClause.h" #include "parser/ParsedQuery.h" #include "parser/RdfEscaping.h" #include "parser/data/BlankNode.h" @@ -123,7 +124,7 @@ class SparqlQleverVisitor : public SparqlAutomaticVisitor { return visitTypesafe(ctx); } - std::variant visitTypesafe(Parser::QueryContext* ctx); + ParsedQuery visitTypesafe(Parser::QueryContext* ctx); // ___________________________________________________________________________ Any visitPrologue(Parser::PrologueContext* ctx) override { @@ -188,7 +189,7 @@ class SparqlQleverVisitor : public SparqlAutomaticVisitor { return visitTypesafe(ctx); } - Triples visitTypesafe(Parser::ConstructQueryContext* ctx); + ParsedQuery visitTypesafe(Parser::ConstructQueryContext* ctx); Any visitDescribeQuery(Parser::DescribeQueryContext* ctx) override { // TODO: unsupported @@ -297,9 +298,11 @@ class SparqlQleverVisitor : public SparqlAutomaticVisitor { Parser::ValuesClauseContext* ctx); Any visitTriplesTemplate(Parser::TriplesTemplateContext* ctx) override { - return visitChildren(ctx); + return visitTypesafe(ctx); } + Triples visitTypesafe(Parser::TriplesTemplateContext* ctx); + Any visitGroupGraphPattern(Parser::GroupGraphPatternContext* ctx) override { return visitTypesafe(ctx); } @@ -448,7 +451,8 @@ class SparqlQleverVisitor : public SparqlAutomaticVisitor { return visitTypesafe(ctx); } - std::optional visitTypesafe(Parser::ConstructTemplateContext* ctx); + std::optional visitTypesafe( + Parser::ConstructTemplateContext* ctx); Any visitConstructTriples(Parser::ConstructTriplesContext* ctx) override { return visitTypesafe(ctx); @@ -947,4 +951,9 @@ class SparqlQleverVisitor : public SparqlAutomaticVisitor { [[noreturn]] void reportError(antlr4::ParserRuleContext* ctx, const std::string& msg); + + // Parse both `ConstructTriplesContext` and `TriplesTemplateContext` because + // they have the same structure. + template + Triples parseTriplesConstruction(Context* ctx); }; diff --git a/src/util/Algorithm.h b/src/util/Algorithm.h index 8dc43c8c26..25b77ebc76 100644 --- a/src/util/Algorithm.h +++ b/src/util/Algorithm.h @@ -39,6 +39,21 @@ bool contains_if(const Container& container, const Predicate& predicate) { container.end(); } +/** + * Checks whether a container is contained in another container. + * + * @param container Container& Container to check + * @param containedContainer Container& Container where other container has to + * be contained in + * @return bool + */ +template +inline bool includes(const Container& container, + const Container& containedContainer) { + return std::includes(container.begin(), container.end(), + containedContainer.begin(), containedContainer.end()); +} + /** * Appends the second vector to the first one. * diff --git a/test/ParseExceptionTest.cpp b/test/ParseExceptionTest.cpp index cd32e3796a..8fe2863f4c 100644 --- a/test/ParseExceptionTest.cpp +++ b/test/ParseExceptionTest.cpp @@ -7,6 +7,7 @@ #include "SparqlAntlrParserTestHelpers.h" #include "parser/ParseException.h" #include "parser/SparqlParser.h" +#include "util/SourceLocation.h" TEST(ParseException, coloredError) { auto exampleQuery = "SELECT A ?var WHERE"; @@ -17,9 +18,11 @@ TEST(ParseException, coloredError) { } void expectParseExceptionWithMetadata( - const string& input, const std::optional& metadata) { + const string& input, const std::optional& metadata, + ad_utility::source_location l = ad_utility::source_location::current()) { + auto trace = generateLocationTrace(l); try { - SparqlParser(input).parse(); + SparqlParser::parseQuery(input); FAIL(); // Should be unreachable. } catch (const ParseException& e) { // The constructor has to be bracketed because EXPECT_EQ is a macro. @@ -28,23 +31,20 @@ void expectParseExceptionWithMetadata( } TEST(ParseException, MetadataGeneration) { - // The SparqlLexer changes the input that has already been read into a token - // when it is outputted with getUnconsumedInput (which is used for ANtLR). // A is not a valid argument for select. expectParseExceptionWithMetadata( "SELECT A ?a WHERE { ?a ?b ?c }", - {{"select A ?a WHERE { ?a ?b ?c }", 9, 9, 1, 9}}); - // The ANTLR Parser currently doesn't always have the whole query. + {{"SELECT A ?a WHERE { ?a ?b ?c }", 7, 7, 1, 7}}); // Error is the undefined Prefix "a". expectParseExceptionWithMetadata( "SELECT * WHERE { ?a a:b ?b }", - {{"select * WHERE { ?a a:b ?b }", 22, 24, 1, 22}}); + {{"SELECT * WHERE { ?a a:b ?b }", 20, 22, 1, 20}}); // "%" doesn't match any valid token. So in this case we will get an Error // from the Lexer. expectParseExceptionWithMetadata("SELECT * WHERE { % }", - {{"select * WHERE { % }", 19, 19, 1, 19}}); + {{"SELECT * WHERE { % }", 17, 17, 1, 17}}); // Error is the undefined Prefix "f". expectParseExceptionWithMetadata( "SELECT * WHERE {\n ?a ?b ?c . \n f:d ?d ?e\n}", - {{"select * WHERE {\n ?a ?b ?c . \n f:d ?d ?e\n}", 33, 35, 3, 1}}); + {{"SELECT * WHERE {\n ?a ?b ?c . \n f:d ?d ?e\n}", 31, 33, 3, 1}}); } diff --git a/test/QueryPlannerTest.cpp b/test/QueryPlannerTest.cpp index deb21e1222..6ebd9e73e4 100644 --- a/test/QueryPlannerTest.cpp +++ b/test/QueryPlannerTest.cpp @@ -17,14 +17,13 @@ TEST(QueryPlannerTest, createTripleGraph) { try { { - ParsedQuery pq = SparqlParser( - "PREFIX : \n" - "PREFIX ns: \n" - "PREFIX xxx: \n" - "SELECT ?x ?z \n " - "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y xxx:rel2 " - "}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : \n" + "PREFIX ns: \n" + "PREFIX xxx: \n" + "SELECT ?x ?z \n " + "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y xxx:rel2 " + "}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph( &pq._rootGraphPattern._graphPatterns[0].getBasic()); @@ -52,9 +51,8 @@ TEST(QueryPlannerTest, createTripleGraph) { } { - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); TripleGraph expected = @@ -75,10 +73,9 @@ TEST(QueryPlannerTest, createTripleGraph) { } { - ParsedQuery pq = SparqlParser( - "SELECT ?x WHERE { ?x . \n" - "?x }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x . \n" + "?x }"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); @@ -108,9 +105,8 @@ TEST(QueryPlannerTest, createTripleGraph) { TEST(QueryPlannerTest, testCpyCtorWithKeepNodes) { try { { - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ(2u, tg._nodeMap.find(0)->second->_variables.size()); @@ -170,9 +166,8 @@ TEST(QueryPlannerTest, testCpyCtorWithKeepNodes) { TEST(QueryPlannerTest, testBFSLeaveOut) { try { { - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x ?p . ?x ?p2 . ?p }"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ(3u, tg._adjLists.size()); @@ -191,9 +186,8 @@ TEST(QueryPlannerTest, testBFSLeaveOut) { ASSERT_EQ(1u, out.size()); } { - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE { ?x. ?x ?y. ?y }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x. ?x ?y. ?y }"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ad_utility::HashSet lo; @@ -226,11 +220,9 @@ TEST(QueryPlannerTest, testcollapseTextCliques) { try { { { - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " - "ql:contains-word \"abc\"}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " + "ql:contains-word \"abc\"}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ( @@ -264,13 +256,11 @@ TEST(QueryPlannerTest, testcollapseTextCliques) { ASSERT_TRUE(tg.isSimilar(expected)); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE {?x

. ?c " - " ?x. ?c " - " \"abc\" . ?c " - "ql:contains-entity ?y}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x

. ?c " + " ?x. ?c " + " \"abc\" . ?c " + "ql:contains-entity ?y}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ( @@ -307,12 +297,10 @@ TEST(QueryPlannerTest, testcollapseTextCliques) { ASSERT_TRUE(tg.isSimilar(expected)); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " - "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?y " - "}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " + "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?y " + "}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ( @@ -354,12 +342,10 @@ TEST(QueryPlannerTest, testcollapseTextCliques) { ASSERT_TRUE(tg.isSimilar(expected)); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " - "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?c2 " - "ql:contains-entity ?y. ?c2 ql:contains-word \"xx\"}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " + "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?c2 " + "ql:contains-entity ?y. ?c2 ql:contains-word \"xx\"}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); TripleGraph expected = TripleGraph(std::vector . ?c ql:contains-entity ?x. ?c " - "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?c2 " - "ql:contains-entity ?y. ?c2 ql:contains-word \"xx\". ?y " - "}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE {?x

. ?c ql:contains-entity ?x. ?c " + "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?c2 " + "ql:contains-entity ?y. ?c2 ql:contains-word \"xx\". ?y " + "}"); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq.children()[0].getBasic()); ASSERT_EQ( @@ -506,11 +490,10 @@ TEST(QueryPlannerTest, testcollapseTextCliques) { } TEST(QueryPlannerTest, testSPX) { - ParsedQuery pq = SparqlParser( - "PREFIX : \n" - "SELECT ?x \n " - "WHERE \t {?x :myrel :obj}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : \n" + "SELECT ?x \n " + "WHERE \t {?x :myrel :obj}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -520,11 +503,10 @@ TEST(QueryPlannerTest, testSPX) { } TEST(QueryPlannerTest, testXPO) { - ParsedQuery pq = SparqlParser( - "PREFIX : \n" - "SELECT ?x \n " - "WHERE \t {:subj :myrel ?x}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : \n" + "SELECT ?x \n " + "WHERE \t {:subj :myrel ?x}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -534,11 +516,10 @@ TEST(QueryPlannerTest, testXPO) { } TEST(QueryPlannerTest, testSP_free_) { - ParsedQuery pq = SparqlParser( - "PREFIX : \n" - "SELECT ?x \n " - "WHERE \t {?x :myrel ?y}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : \n" + "SELECT ?x \n " + "WHERE \t {?x :myrel ?y}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -549,11 +530,10 @@ TEST(QueryPlannerTest, testSP_free_) { TEST(QueryPlannerTest, testSPX_SPX) { try { - ParsedQuery pq = SparqlParser( - "PREFIX :

\n"
-                         "SELECT ?x \n "
-                         "WHERE \t {:s1 :r ?x. :s2 :r ?x}")
-                         .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "PREFIX : 
\n"
+        "SELECT ?x \n "
+        "WHERE \t {:s1 :r ?x. :s2 :r ?x}");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(
@@ -573,11 +553,10 @@ TEST(QueryPlannerTest, testSPX_SPX) {
 
 TEST(QueryPlannerTest, test_free_PX_SPX) {
   try {
-    ParsedQuery pq = SparqlParser(
-                         "PREFIX : 
\n"
-                         "SELECT ?x ?y \n "
-                         "WHERE  {?y :r ?x . :s2 :r ?x}")
-                         .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "PREFIX : 
\n"
+        "SELECT ?x ?y \n "
+        "WHERE  {?y :r ?x . :s2 :r ?x}");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(
@@ -597,11 +576,10 @@ TEST(QueryPlannerTest, test_free_PX_SPX) {
 
 TEST(QueryPlannerTest, test_free_PX__free_PX) {
   try {
-    ParsedQuery pq = SparqlParser(
-                         "PREFIX : 
\n"
-                         "SELECT ?x ?y ?z \n "
-                         "WHERE {?y :r ?x. ?z :r ?x}")
-                         .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "PREFIX : 
\n"
+        "SELECT ?x ?y ?z \n "
+        "WHERE {?y :r ?x. ?z :r ?x}");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(
@@ -621,13 +599,11 @@ TEST(QueryPlannerTest, test_free_PX__free_PX) {
 
 TEST(QueryPlannerTest, testActorsBornInEurope) {
   try {
-    ParsedQuery pq =
-        SparqlParser(
-            "PREFIX : 
\n"
-            "SELECT ?a \n "
-            "WHERE {?a :profession :Actor . ?a :born-in ?c. ?c :in :Europe}\n"
-            "ORDER BY ?a")
-            .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "PREFIX : 
\n"
+        "SELECT ?a \n "
+        "WHERE {?a :profession :Actor . ?a :born-in ?c. ?c :in :Europe}\n"
+        "ORDER BY ?a");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(18340u, qet.getCostEstimate());
@@ -655,15 +631,13 @@ TEST(QueryPlannerTest, testActorsBornInEurope) {
 TEST(QueryPlannerTest, testStarTwoFree) {
   try {
     {
-      ParsedQuery pq =
-          SparqlParser(
-              "PREFIX : \n"
-              "PREFIX ns: \n"
-              "PREFIX xxx: \n"
-              "SELECT ?x ?z \n "
-              "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z. ?y xxx:rel2 "
-              "}")
-              .parse();
+      ParsedQuery pq = SparqlParser::parseQuery(
+          "PREFIX : \n"
+          "PREFIX ns: \n"
+          "PREFIX xxx: \n"
+          "SELECT ?x ?z \n "
+          "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z. ?y xxx:rel2 "
+          "}");
       QueryPlanner qp(nullptr);
       QueryExecutionTree qet = qp.createExecutionTree(pq);
       ASSERT_EQ(
@@ -689,11 +663,10 @@ TEST(QueryPlannerTest, testStarTwoFree) {
 
 TEST(QueryPlannerTest, testFilterAfterSeed) {
   try {
-    ParsedQuery pq = SparqlParser(
-                         "SELECT ?x ?y ?z WHERE {"
-                         "?x  ?y . ?y  ?z . "
-                         "FILTER(?x != ?y) }")
-                         .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "SELECT ?x ?y ?z WHERE {"
+        "?x  ?y . ?y  ?z . "
+        "FILTER(?x != ?y) }");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(
@@ -715,11 +688,10 @@ TEST(QueryPlannerTest, testFilterAfterSeed) {
 
 TEST(QueryPlannerTest, testFilterAfterJoin) {
   try {
-    ParsedQuery pq = SparqlParser(
-                         "SELECT ?x ?y ?z WHERE {"
-                         "?x  ?y . ?y  ?z . "
-                         "FILTER(?x != ?z) }")
-                         .parse();
+    ParsedQuery pq = SparqlParser::parseQuery(
+        "SELECT ?x ?y ?z WHERE {"
+        "?x  ?y . ?y  ?z . "
+        "FILTER(?x != ?z) }");
     QueryPlanner qp(nullptr);
     QueryExecutionTree qet = qp.createExecutionTree(pq);
     ASSERT_EQ(
@@ -741,10 +713,9 @@ TEST(QueryPlannerTest, testFilterAfterJoin) {
 
 TEST(QueryPlannerTest, threeVarTriples) {
   try {
-    ParsedQuery pq = SparqlParser(
-                         "SELECT ?x ?p ?o WHERE {"
-                         " 

?x . ?x ?p ?o }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?p ?o WHERE {" + "

?x . ?x ?p ?o }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -763,10 +734,9 @@ TEST(QueryPlannerTest, threeVarTriples) { } try { - ParsedQuery pq = SparqlParser( - "SELECT ?x ?p ?o WHERE {" - " ?x . ?x ?p ?o }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?p ?o WHERE {" + " ?x . ?x ?p ?o }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -785,10 +755,9 @@ TEST(QueryPlannerTest, threeVarTriples) { } try { - ParsedQuery pq = SparqlParser( - "SELECT ?s ?p ?o WHERE {" - "

?p . ?s ?p ?o }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?s ?p ?o WHERE {" + "

?p . ?s ?p ?o }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -809,10 +778,9 @@ TEST(QueryPlannerTest, threeVarTriples) { TEST(QueryPlannerTest, threeVarTriplesTCJ) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?x ?p ?o WHERE {" - " ?p ?x . ?x ?p ?o }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?p ?o WHERE {" + " ?p ?x . ?x ?p ?o }"); QueryPlanner qp(nullptr); ASSERT_THROW(qp.createExecutionTree(pq), ad_semsearch::Exception); } catch (const ad_semsearch::Exception& e) { @@ -824,10 +792,9 @@ TEST(QueryPlannerTest, threeVarTriplesTCJ) { } try { - ParsedQuery pq = SparqlParser( - "SELECT ?s ?p ?o WHERE {" - "?s ?p ?o . ?s ?p }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?s ?p ?o WHERE {" + "?s ?p ?o . ?s ?p }"); QueryPlanner qp(nullptr); ASSERT_THROW(QueryExecutionTree qet = qp.createExecutionTree(pq), ad_semsearch::Exception); @@ -842,10 +809,9 @@ TEST(QueryPlannerTest, threeVarTriplesTCJ) { TEST(QueryPlannerTest, threeVarXthreeVarException) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?s ?s2 WHERE {" - "?s ?p ?o . ?s2 ?p ?o }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?s ?s2 WHERE {" + "?s ?p ?o . ?s2 ?p ?o }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); FAIL() << "Was expecting exception, but got" << qet.asString() << std::endl; @@ -863,10 +829,9 @@ TEST(QueryPlannerTest, threeVarXthreeVarException) { TEST(QueryExecutionTreeTest, testBooksbyNewman) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?x WHERE { ?x . " - "?x }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x . " + "?x }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -887,13 +852,12 @@ TEST(QueryExecutionTreeTest, testBooksbyNewman) { TEST(QueryExecutionTreeTest, testBooksGermanAwardNomAuth) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?x ?y WHERE { " - "?x . " - "?x . " - "?x ?y . " - "?y }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { " + "?x . " + "?x . " + "?x ?y . " + "?y }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_GT(qet.asString().size(), 0u); @@ -909,12 +873,10 @@ TEST(QueryExecutionTreeTest, testBooksGermanAwardNomAuth) { TEST(QueryExecutionTreeTest, testPlantsEdibleLeaves) { try { - ParsedQuery pq = - SparqlParser( - "SELECT ?a \n " - "WHERE {?a . ?c ql:contains-entity ?a. " - "?c ql:contains-word \"edible leaves\"} TEXTLIMIT 5") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?a \n " + "WHERE {?a . ?c ql:contains-entity ?a. " + "?c ql:contains-word \"edible leaves\"} TEXTLIMIT 5"); QueryPlanner qp(nullptr); QueryPlanner::TripleGraph tg = qp.createTripleGraph(&pq.children()[0].getBasic()); @@ -938,10 +900,9 @@ TEST(QueryExecutionTreeTest, testPlantsEdibleLeaves) { TEST(QueryExecutionTreeTest, testTextQuerySE) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?c \n " - "WHERE {?c ql:contains-word \"search engine\"}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?c \n " + "WHERE {?c ql:contains-word \"search engine\"}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -960,16 +921,15 @@ TEST(QueryExecutionTreeTest, testTextQuerySE) { TEST(QueryExecutionTreeTest, testBornInEuropeOwCocaine) { try { - ParsedQuery pq = SparqlParser( - "PREFIX : <>\n" - "SELECT ?x ?y ?c\n " - "WHERE \t {" - "?x :Place_of_birth ?y ." - "?y :Contained_by :Europe ." - "?c ql:contains-entity ?x ." - "?c ql:contains-word \"cocaine\" ." - "}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : <>\n" + "SELECT ?x ?y ?c\n " + "WHERE \t {" + "?x :Place_of_birth ?y ." + "?y :Contained_by :Europe ." + "?c ql:contains-entity ?x ." + "?c ql:contains-word \"cocaine\" ." + "}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -996,15 +956,14 @@ TEST(QueryExecutionTreeTest, testBornInEuropeOwCocaine) { TEST(QueryExecutionTreeTest, testCoOccFreeVar) { try { - ParsedQuery pq = SparqlParser( - "PREFIX : <>" - "SELECT ?x ?y WHERE {" - "?x :is-a :Politician ." - "?c ql:contains-entity ?x ." - "?c ql:contains-word \"friend*\" ." - "?c ql:contains-entity ?y ." - "}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : <>" + "SELECT ?x ?y WHERE {" + "?x :is-a :Politician ." + "?c ql:contains-entity ?x ." + "?c ql:contains-word \"friend*\" ." + "?c ql:contains-entity ?y ." + "}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -1025,17 +984,16 @@ TEST(QueryExecutionTreeTest, testCoOccFreeVar) { TEST(QueryExecutionTreeTest, testPoliticiansFriendWithScieManHatProj) { try { - ParsedQuery pq = SparqlParser( - "SELECT ?p ?s \n " - "WHERE {" - "?a . " - "?c ql:contains-entity ?a ." - "?c ql:contains-word \"friend*\" ." - "?c ql:contains-entity ?s ." - "?s ." - "?c2 ql:contains-entity ?s ." - "?c2 ql:contains-word \"manhattan project\"}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?p ?s \n " + "WHERE {" + "?a . " + "?c ql:contains-entity ?a ." + "?c ql:contains-word \"friend*\" ." + "?c ql:contains-entity ?s ." + "?s ." + "?c2 ql:contains-entity ?s ." + "?c2 ql:contains-word \"manhattan project\"}"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( @@ -1064,11 +1022,9 @@ TEST(QueryExecutionTreeTest, testPoliticiansFriendWithScieManHatProj) { TEST(QueryExecutionTreeTest, testCyclicQuery) { try { - ParsedQuery pq = - SparqlParser( - "SELECT ?x ?y ?m WHERE { ?x ?y . " - "?x ?m . ?y ?m }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y ?m WHERE { ?x ?y . " + "?x ?m . ?y ?m }"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); @@ -1186,21 +1142,19 @@ qet-width: 3 TEST(QueryExecutionTreeTest, testFormerSegfaultTriFilter) { try { - ParsedQuery pq = - SparqlParser( - "PREFIX fb: \n" - "SELECT DISTINCT ?1 ?0 WHERE {\n" - "fb:m.0fkvn fb:government.government_office_category.officeholders " - "?0 " - ".\n" - "?0 fb:government.government_position_held.jurisdiction_of_office " - "fb:m.0vmt .\n" - "?0 fb:government.government_position_held.office_holder ?1 .\n" - "FILTER (?1 != fb:m.0fkvn) .\n" - "FILTER (?1 != fb:m.0vmt) .\n" - "FILTER (?1 != fb:m.018mts)" - "} LIMIT 300") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX fb: \n" + "SELECT DISTINCT ?1 ?0 WHERE {\n" + "fb:m.0fkvn fb:government.government_office_category.officeholders " + "?0 " + ".\n" + "?0 fb:government.government_position_held.jurisdiction_of_office " + "fb:m.0vmt .\n" + "?0 fb:government.government_position_held.office_holder ?1 .\n" + "FILTER (?1 != fb:m.0fkvn) .\n" + "FILTER (?1 != fb:m.0vmt) .\n" + "FILTER (?1 != fb:m.018mts)" + "} LIMIT 300"); QueryPlanner qp(nullptr); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_TRUE(qet.varCovered("?1")); @@ -1218,10 +1172,9 @@ TEST(QueryPlannerTest, testSimpleOptional) { try { QueryPlanner qp(nullptr); - ParsedQuery pq = SparqlParser( - "SELECT ?a ?b \n " - "WHERE {?a ?b . OPTIONAL { ?a ?c }}") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?a ?b \n " + "WHERE {?a ?b . OPTIONAL { ?a ?c }}"); QueryExecutionTree qet = qp.createExecutionTree(pq); ASSERT_EQ( "{\n OPTIONAL_JOIN\n {\n SCAN PSO with P = \"\"\n " @@ -1231,11 +1184,10 @@ TEST(QueryPlannerTest, testSimpleOptional) { qet.asString()); - ParsedQuery pq2 = SparqlParser( - "SELECT ?a ?b \n " - "WHERE {?a ?b . " - "OPTIONAL { ?a ?c }} ORDER BY ?b") - .parse(); + ParsedQuery pq2 = SparqlParser::parseQuery( + "SELECT ?a ?b \n " + "WHERE {?a ?b . " + "OPTIONAL { ?a ?c }} ORDER BY ?b"); QueryExecutionTree qet2 = qp.createExecutionTree(pq2); ASSERT_EQ( "{\n SORT / ORDER BY on columns:asc(1) \n {\n OPTIONAL_JOIN\n " diff --git a/test/QueryPlannerTestHelpers.h b/test/QueryPlannerTestHelpers.h index 068914820c..5e7a481f21 100644 --- a/test/QueryPlannerTestHelpers.h +++ b/test/QueryPlannerTestHelpers.h @@ -46,7 +46,7 @@ auto IndexScan(TripleComponent subject, std::string predicate, /// Parse the given SPARQL `query`, pass it to a `QueryPlanner` with empty /// execution context, and return the resulting `QueryExecutionTree` QueryExecutionTree parseAndPlan(std::string query) { - ParsedQuery pq = SparqlParser{std::move(query)}.parse(); + ParsedQuery pq = SparqlParser::parseQuery(std::move(query)); // TODO make it impossible to pass `nullptr` here, properly mock a // queryExecutionContext. return QueryPlanner{nullptr}.createExecutionTree(pq); diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 51935287cb..a18edd8381 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -11,6 +11,7 @@ #include #include "SparqlAntlrParserTestHelpers.h" +#include "parser/ConstructClause.h" #include "parser/SparqlParserHelpers.h" #include "parser/sparqlParser/SparqlQleverVisitor.h" #include "util/SourceLocation.h" @@ -185,35 +186,32 @@ TEST(SparqlExpressionParser, First) { ASSERT_FLOAT_EQ(25.0, std::get(result)); } -TEST(SparqlParser, ComplexConstructQuery) { +TEST(SparqlParser, ComplexConstructTemplate) { string input = - "CONSTRUCT { [?a ( ?b (?c) )] ?d [?e [?f ?g]] . " + "{ [?a ( ?b (?c) )] ?d [?e [?f ?g]] . " " a " - " } " - "WHERE {}"; + " }"; - auto Var = m::VariableVariant; - auto Blank = [](const std::string& label) { - return m::BlankNode(true, label); - }; + using Var = Variable; + auto Blank = [](const std::string& label) { return BlankNode(true, label); }; expectCompleteParse( - parse<&Parser::constructQuery>(input), - ElementsAre( - ElementsAre(Blank("0"), Var("?a"), Blank("3")), - ElementsAre(Blank("1"), m::Iri(first), Blank("2")), - ElementsAre(Blank("1"), m::Iri(rest), m::Iri(nil)), - ElementsAre(Blank("2"), m::Iri(first), Var("?c")), - ElementsAre(Blank("2"), m::Iri(rest), m::Iri(nil)), - ElementsAre(Blank("3"), m::Iri(first), Var("?b")), - ElementsAre(Blank("3"), m::Iri(rest), Blank("1")), - ElementsAre(Blank("0"), Var("?d"), Blank("4")), - ElementsAre(Blank("4"), Var("?e"), Blank("5")), - ElementsAre(Blank("5"), Var("?f"), Var("?g")), - ElementsAre(m::Iri(""), - m::Iri(type), - m::Iri("")))); + parse<&Parser::constructTemplate>(input), + m::ConstructClause( + {{Blank("0"), Var("?a"), Blank("3")}, + {Blank("1"), Iri(first), Blank("2")}, + {Blank("1"), Iri(rest), Iri(nil)}, + {Blank("2"), Iri(first), Var("?c")}, + {Blank("2"), Iri(rest), Iri(nil)}, + {Blank("3"), Iri(first), Var("?b")}, + {Blank("3"), Iri(rest), Blank("1")}, + {Blank("0"), Var("?d"), Blank("4")}, + {Blank("4"), Var("?e"), Blank("5")}, + {Blank("5"), Var("?f"), Var("?g")}, + {Iri(""), + Iri(type), + Iri("")}})); } TEST(SparqlParser, GraphTerm) { @@ -876,28 +874,21 @@ TEST(SparqlParser, SelectQuery) { m::GraphPattern(m::Triples({{"?x", "?y", "?z"}})); expectSelectQuery( "SELECT * WHERE { ?a ?foo }", - testing::AllOf( - m::SelectQuery(m::AsteriskSelect(), m::GraphPattern(m::Triples( - {{"?a", "", "?foo"}}))), - m::pq::OriginalString("SELECT * WHERE { ?a ?foo }"))); - expectSelectQuery( - "SELECT * WHERE { ?x ?y ?z }", - testing::AllOf( - m::SelectQuery(m::AsteriskSelect(), DummyGraphPatternMatcher), - m::pq::OriginalString("SELECT * WHERE { ?x ?y ?z }"))); - expectSelectQuery( - "SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x", - testing::AllOf( - m::SelectQuery(m::VariablesSelect({"?x"}), DummyGraphPatternMatcher), - m::pq::OriginalString("SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x"), - m::pq::GroupKeys({Variable{"?x"}}))); + testing::AllOf(m::SelectQuery( + m::AsteriskSelect(), + m::GraphPattern(m::Triples({{"?a", "", "?foo"}}))))); + expectSelectQuery("SELECT * WHERE { ?x ?y ?z }", + testing::AllOf(m::SelectQuery(m::AsteriskSelect(), + DummyGraphPatternMatcher))); + expectSelectQuery("SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x", + testing::AllOf(m::SelectQuery(m::VariablesSelect({"?x"}), + DummyGraphPatternMatcher), + m::pq::GroupKeys({Variable{"?x"}}))); expectSelectQuery( "SELECT (COUNT(?y) as ?a) WHERE { ?x ?y ?z } GROUP BY ?x", testing::AllOf( m::SelectQuery(m::Select({std::pair{"COUNT(?y)", Variable{"?a"}}}), DummyGraphPatternMatcher), - m::pq::OriginalString( - "SELECT (COUNT(?y) as ?a) WHERE { ?x ?y ?z } GROUP BY ?x"), m::pq::GroupKeys({Variable{"?x"}}))); expectSelectQuery( "SELECT ?x WHERE { ?x ?y ?z . FILTER(?x != ) } LIMIT 10 TEXTLIMIT 5", @@ -914,21 +905,19 @@ TEST(SparqlParser, SelectQuery) { m::SelectQuery(m::Select({Variable{"?x"}}), DummyGraphPatternMatcher), m::pq::Having({{SparqlFilter::FilterType::GT, "?x", "5"}}), m::pq::OrderKeys({{Variable{"?y"}, false}}))); - // TODO enable Tests once the corresponding checks are - // implemented. // Grouping by a variable or expression which contains a variable // that is not visible in the query body is not allowed. expectSelectQueryFails("SELECT ?x WHERE { ?a ?b ?c } GROUP BY ?x"); - // expectSelectQueryFails("SELECT (COUNT(?a) as ?d) WHERE { ?a ?b ?c } GROUP - // BY (?x - 10)"); + expectSelectQueryFails( + "SELECT (COUNT(?a) as ?d) WHERE { ?a ?b ?c } GROUP BY (?x - 10)"); // Ordering by a variable or expression which contains a variable that is not // visible in the query body is not allowed. - // expectSelectQueryFails("SELECT ?a WHERE { ?a ?b ?c } ORDER BY ?x"); - // expectSelectQueryFails("SELECT ?a WHERE { ?a ?b ?c } ORDER BY (?x - 10)"); + expectSelectQueryFails("SELECT ?a WHERE { ?a ?b ?c } ORDER BY ?x"); + expectSelectQueryFails("SELECT ?a WHERE { ?a ?b ?c } ORDER BY (?x - 10)"); // All variables used in an aggregate must be visible in the query body. - // expectSelectQueryFails( - // "SELECT (COUNT(?x) as ?y) WHERE { ?a ?b ?c } GROUP BY ?a"); + expectSelectQueryFails( + "SELECT (COUNT(?x) as ?y) WHERE { ?a ?b ?c } GROUP BY ?a"); // `SELECT *` is not allowed while grouping. expectSelectQueryFails("SELECT * WHERE { ?x ?y ?z } GROUP BY ?x"); // `?x` is selected twice. Once as variable and once as the result of an @@ -940,3 +929,83 @@ TEST(SparqlParser, SelectQuery) { // Datasets are not supported. expectSelectQueryFails("SELECT * FROM WHERE { ?x ?y ?z }"); } + +TEST(SparqlParser, ConstructQuery) { + auto expectConstructQuery = ExpectCompleteParse<&Parser::constructQuery>{ + {{INTERNAL_PREDICATE_PREFIX_NAME, INTERNAL_PREDICATE_PREFIX_IRI}}}; + auto expectConstructQueryFails = ExpectParseFails<&Parser::constructQuery>{}; + expectConstructQuery( + "CONSTRUCT { } WHERE { ?a ?b ?c }", + m::ConstructQuery({}, m::GraphPattern(m::Triples({{"?a", "?b", "?c"}})))); + expectConstructQuery("CONSTRUCT { ?a ?c . } WHERE { ?a ?b ?c }", + testing::AllOf(m::ConstructQuery( + {{Variable{"?a"}, Iri{""}, Variable{"?c"}}}, + m::GraphPattern(m::Triples({{"?a", "?b", "?c"}}))))); + expectConstructQuery( + "CONSTRUCT { ?a ?c . ?b } WHERE { ?a ?b ?c . FILTER(?a " + "> 0) .}", + m::ConstructQuery( + {{Variable{"?a"}, Iri{""}, Variable{"?c"}}, + {Iri{""}, Variable{"?b"}, Iri{""}}}, + m::GraphPattern(false, {{SparqlFilter::FilterType::GT, "?a", "0"}}, + m::Triples({{"?a", "?b", "?c"}})))); + expectConstructQuery( + "CONSTRUCT { ?a ?c . } WHERE { ?a ?b ?c } ORDER BY ?a LIMIT 10", + testing::AllOf( + m::ConstructQuery({{Variable{"?a"}, Iri{""}, Variable{"?c"}}}, + m::GraphPattern(m::Triples({{"?a", "?b", "?c"}}))), + m::pq::LimitOffset({10}), + m::pq::OrderKeys({{Variable{"?a"}, false}}))); + // This case of the grammar is not useful without Datasets, but we still + // support it. + expectConstructQuery( + "CONSTRUCT WHERE { ?a ?b }", + m::ConstructQuery({{Variable{"?a"}, Iri{""}, Variable{"?b"}}}, + m::GraphPattern())); + // Datasets are not supported. + expectConstructQueryFails("CONSTRUCT { } FROM WHERE { ?a ?b ?c }"); + expectConstructQueryFails("CONSTRUCT FROM WHERE { }"); +} + +TEST(SparqlParser, Query) { + auto expectQuery = ExpectCompleteParse<&Parser::query>{ + {{INTERNAL_PREDICATE_PREFIX_NAME, INTERNAL_PREDICATE_PREFIX_IRI}}}; + auto expectQueryFails = ExpectParseFails<&Parser::query>{}; + // Test that `_originalString` is correctly set. + expectQuery("SELECT * WHERE { ?a ?foo }", + testing::AllOf( + m::SelectQuery( + m::AsteriskSelect(), + m::GraphPattern(m::Triples({{"?a", "", "?foo"}}))), + m::pq::OriginalString("SELECT * WHERE { ?a ?foo }"), + m::VisibleVariables({Variable{"?a"}, Variable{"?foo"}}))); + expectQuery("SELECT * WHERE { ?x ?y ?z }", + m::pq::OriginalString("SELECT * WHERE { ?x ?y ?z }")); + expectQuery( + "SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x", + m::pq::OriginalString("SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x")); + expectQuery( + "PREFIX a: SELECT (COUNT(?y) as ?a) WHERE { ?x ?y ?z } GROUP BY ?x", + m::pq::OriginalString("PREFIX a: SELECT (COUNT(?y) as ?a) WHERE { " + "?x ?y ?z } GROUP BY ?x")); + expectQuery( + "CONSTRUCT { ?a ?c . } WHERE { ?a ?b ?c }", + testing::AllOf( + m::ConstructQuery({{Variable{"?a"}, Iri{""}, Variable{"?c"}}}, + m::GraphPattern(m::Triples({{"?a", "?b", "?c"}}))), + m::VisibleVariables( + {Variable{"?a"}, Variable{"?b"}, Variable{"?c"}}))); + expectQuery( + "CONSTRUCT { ?x } WHERE { ?x ?y ?z } LIMIT 10", + testing::AllOf( + m::ConstructQuery({{Variable{"?x"}, Iri{""}, Iri{""}}}, + m::GraphPattern(m::Triples({{"?x", "?y", "?z"}}))), + m::pq::OriginalString( + "CONSTRUCT { ?x } WHERE { ?x ?y ?z } LIMIT 10"), + m::pq::LimitOffset({10}), + m::VisibleVariables( + {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}))); + // Describe and Ask Queries are not supported. + expectQueryFails("DESCRIBE *"); + expectQueryFails("ASK WHERE { ?x }"); +} diff --git a/test/SparqlAntlrParserTestHelpers.h b/test/SparqlAntlrParserTestHelpers.h index b91c5e3a56..1a017068ad 100644 --- a/test/SparqlAntlrParserTestHelpers.h +++ b/test/SparqlAntlrParserTestHelpers.h @@ -290,6 +290,13 @@ auto Literal = [](const std::string& value) { // _____________________________________________________________________________ +auto ConstructClause = [](const std::vector>& elems) + -> Matcher&> { + return testing::Optional( + AD_FIELD(parsedQuery::ConstructClause, triples_, testing::Eq(elems))); +}; + +// _____________________________________________________________________________ namespace detail { auto Expression = [](const std::string& descriptor) -> Matcher { @@ -578,6 +585,11 @@ auto Minus = [](auto&& subMatcher) -> Matcher { AD_FIELD(p::Minus, _child, subMatcher)); }; +auto RootGraphPattern = [](const Matcher& m) + -> Matcher { + return AD_FIELD(ParsedQuery, _rootGraphPattern, m); +}; + template struct MatcherWithDefaultFilters { Matcher operator()( @@ -671,7 +683,7 @@ auto SelectQuery = return testing::AllOf( AD_PROPERTY(ParsedQuery, hasSelectClause, testing::IsTrue()), AD_PROPERTY(ParsedQuery, selectClause, selectMatcher), - AD_FIELD(ParsedQuery, _rootGraphPattern, graphPatternMatcher)); + RootGraphPattern(graphPatternMatcher)); }; namespace pq { @@ -699,6 +711,24 @@ auto OrderKeys = auto GroupKeys = GroupByVariables; } // namespace pq +// _____________________________________________________________________________ +auto ConstructQuery(const std::vector>& elems, + const Matcher& m) + -> Matcher { + return testing::AllOf( + AD_PROPERTY(ParsedQuery, hasConstructClause, testing::IsTrue()), + AD_PROPERTY( + ParsedQuery, constructClause, + AD_FIELD(parsedQuery::ConstructClause, triples_, testing::Eq(elems))), + RootGraphPattern(m)); +} + +// _____________________________________________________________________________ +auto VisibleVariables = + [](const std::vector<::Variable>& elems) -> Matcher { + return AD_PROPERTY(ParsedQuery, getVisibleVariables, testing::Eq(elems)); +}; + } // namespace matchers #undef AD_PROPERTY diff --git a/test/SparqlParserTest.cpp b/test/SparqlParserTest.cpp index 9978d552e5..f099e41ce6 100644 --- a/test/SparqlParserTest.cpp +++ b/test/SparqlParserTest.cpp @@ -16,10 +16,9 @@ namespace p = parsedQuery; TEST(ParserTest, testParse) { try { { - auto pq = SparqlParser("SELECT ?x WHERE {?x ?y ?z}").parse(); + auto pq = SparqlParser::parseQuery("SELECT ?x WHERE {?x ?y ?z}"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, pq._rootGraphPattern._graphPatterns.size()); ASSERT_EQ( @@ -28,29 +27,19 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "PREFIX : \n" - "PREFIX ns: \n" - "PREFIX xxx: \n" - "SELECT ?x ?z \n " - "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y " - "}") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX : \n" + "PREFIX ns: \n" + "PREFIX xxx: \n" + "SELECT ?x ?z \n " + "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y " + "}"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause2 = pq.selectClause(); - ASSERT_EQ(4u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause2.getSelectedVariables().size()); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; ASSERT_EQ(3u, triples.size()); - - ASSERT_THAT(pq._prefixes, - testing::UnorderedElementsAre( - SparqlPrefix{INTERNAL_PREDICATE_PREFIX_NAME, - INTERNAL_PREDICATE_PREFIX_IRI}, - SparqlPrefix{"", ""}, - SparqlPrefix{"ns", ""}, - SparqlPrefix{"xxx", ""})); ASSERT_EQ(Variable{"?x"}, selectClause2.getSelectedVariables()[0]); ASSERT_EQ(Variable{"?z"}, selectClause2.getSelectedVariables()[1]); ASSERT_EQ("?x", triples[0]._s); @@ -67,29 +56,19 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "PREFIX : \n" - "PREFIX ns: \n" - "PREFIX xxx: \n" - "SELECT ?x ?z \n " - "WHERE \t {\n?x :myrel ?y. ?y ns:myrel ?z.\n?y " - "\n}") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX : \n" + "PREFIX ns: \n" + "PREFIX xxx: \n" + "SELECT ?x ?z \n " + "WHERE \t {\n?x :myrel ?y. ?y ns:myrel ?z.\n?y " + "\n}"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); - ASSERT_EQ(4u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; ASSERT_EQ(3u, triples.size()); - - ASSERT_THAT(pq._prefixes, - testing::UnorderedElementsAre( - SparqlPrefix{INTERNAL_PREDICATE_PREFIX_NAME, - INTERNAL_PREDICATE_PREFIX_IRI}, - SparqlPrefix{"", ""}, - SparqlPrefix{"ns", ""}, - SparqlPrefix{"xxx", ""})); ASSERT_EQ(Variable{"?x"}, selectClause.getSelectedVariables()[0]); ASSERT_EQ(Variable{"?z"}, selectClause.getSelectedVariables()[1]); ASSERT_EQ("?x", triples[0]._s); @@ -106,15 +85,13 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "PREFIX ns: " - "SELECT ?x ?z \n " - "WHERE \t {\n?x ?y. ?y ns:myrel.extend ?z.\n" - "?y \"Hello... World\"}") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX ns: " + "SELECT ?x ?z \n " + "WHERE \t {\n?x ?y. ?y ns:myrel.extend ?z.\n" + "?y \"Hello... World\"}"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); - ASSERT_EQ(2u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; @@ -136,10 +113,9 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." - "?y . FILTER(?y < ?x)} LIMIT 10") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." + "?y . FILTER(?y < ?x)} LIMIT 10"); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; auto filters = pq._rootGraphPattern._filters; @@ -154,10 +130,9 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." - "?y } LIMIT 10") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." + "?y } LIMIT 10"); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; auto filters = pq._rootGraphPattern._filters; @@ -169,11 +144,10 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." - "?y . ?c ql:contains-entity ?x." - "?c ql:contains-word \"coca* abuse\"} LIMIT 10") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE {?x . FILTER(?x != ?y)." + "?y . ?c ql:contains-entity ?x." + "?c ql:contains-word \"coca* abuse\"} LIMIT 10"); ASSERT_EQ(1u, pq.children().size()); const auto& triples = pq.children()[0].getBasic()._triples; auto filters = pq._rootGraphPattern._filters; @@ -191,31 +165,29 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "PREFIX : <>\n" - "SELECT ?x ?y ?z ?c ?ql_textscore_c ?c WHERE {\n" - "?x :is-a :Politician .\n" - "?c ql:contains-entity ?x .\n" - "?c ql:contains-word \"friend\" .\n" - "?c ql:contains-entity ?y .\n" - "?y :is-a :Scientist .\n" - "FILTER(?x != ?y) .\n" - "} ORDER BY ?c") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX : <>\n" + "SELECT ?x ?y ?z ?c ?ql_textscore_c ?c WHERE {\n" + "?x :is-a :Politician .\n" + "?c ql:contains-entity ?x .\n" + "?c ql:contains-word \"friend\" .\n" + "?c ql:contains-entity ?y .\n" + "?y :is-a :Scientist .\n" + "FILTER(?x != ?y) .\n" + "} ORDER BY ?c"); ASSERT_EQ(1u, pq._rootGraphPattern._filters.size()); // shouldn't have more checks?? } { - auto pq = SparqlParser( - "SELECT ?x ?z WHERE {\n" - " ?x ?y .\n" - " OPTIONAL {\n" - " ?y ?z .\n" - " }\n" - "}") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?z WHERE {\n" + " ?x ?y .\n" + " OPTIONAL {\n" + " ?y ?z .\n" + " }\n" + "}"); ASSERT_EQ(2u, pq.children().size()); const auto& opt = @@ -232,21 +204,20 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT ?x ?z WHERE {\n" - " ?x ?y .\n" - " OPTIONAL {\n" - " ?y ?z .\n" - " optional {\n" - " ?a ?b ?c .\n" - " FILTER(?c > 3)\n" - " }\n" - " optional {\n" - " ?d ?e ?f\n" - " }\n" - " }\n" - "}") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?z WHERE {\n" + " ?x ?y .\n" + " OPTIONAL {\n" + " ?y ?z .\n" + " optional {\n" + " ?a ?b ?c .\n" + " FILTER(?c > 3)\n" + " }\n" + " optional {\n" + " ?d ?e ?f\n" + " }\n" + " }\n" + "}"); ASSERT_EQ(2u, pq._rootGraphPattern._graphPatterns.size()); const auto& optA = std::get( pq._rootGraphPattern._graphPatterns[1]); // throws on error @@ -268,13 +239,12 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT ?a WHERE {\n" - " VALUES ?a { <1> \"2\"}\n" - " VALUES (?b ?c) {(<1> <2>) (\"1\" \"2\")}\n" - " ?a ?b ." - "}") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?a WHERE {\n" + " VALUES ?a { <1> \"2\"}\n" + " VALUES (?b ?c) {(<1> <2>) (\"1\" \"2\")}\n" + " ?a ?b ." + "}"); ASSERT_EQ(3u, pq._rootGraphPattern._graphPatterns.size()); const auto& c = pq.children()[2].getBasic(); ASSERT_EQ(1u, c._triples.size()); @@ -294,14 +264,13 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - R"(SELECT ?a ?b ?c WHERE { + auto pq = SparqlParser::parseQuery( + R"(SELECT ?a ?b ?c WHERE { VALUES ?a { } VALUES (?b ?c) { ( ) ( ) } } - )") - .parse(); + )"); ASSERT_EQ(2u, pq.children().size()); ASSERT_EQ(0u, pq._rootGraphPattern._filters.size()); @@ -321,15 +290,14 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "" - "PREFIX wd: \n" - "PREFIX wdt: \n" - "SELECT ?city WHERE {\n" - " VALUES ?citytype { wd:Q515 wd:Q262166}\n" - " ?city wdt:P31 ?citytype .\n" - "}\n") - .parse(); + auto pq = SparqlParser::parseQuery( + "" + "PREFIX wd: \n" + "PREFIX wdt: \n" + "SELECT ?city WHERE {\n" + " VALUES ?citytype { wd:Q515 wd:Q262166}\n" + " ?city wdt:P31 ?citytype .\n" + "}\n"); ASSERT_EQ(2u, pq.children().size()); const auto& c = pq.children()[1].getBasic(); @@ -357,7 +325,7 @@ TEST(ParserTest, testParse) { // C++ exception with description "ParseException, cause: // Expected a token of type AGGREGATE but got a token of // type RDFLITERAL (() in the input at pos 373 : (YEAR(?year)) - auto pq = SparqlParser( + auto pq = SparqlParser::parseQuery( "SELECT DISTINCT * WHERE { \n" " ?movie .\n" "\t{ \n" @@ -371,11 +339,11 @@ TEST(ParserTest, testParse) { "} \n" "LIMIT 10 \n" "ORDER BY DESC(?movie) ASC(?year) \n") - .parse(); + ; } { - auto pq = SparqlParser( + auto pq = SparqlParser::parseQuery( "SELECT * WHERE { \n" " VALUES ?x { 1 2 3 4 } .\n" "\t{ \n" @@ -390,11 +358,11 @@ TEST(ParserTest, testParse) { "LIMIT 5 \n" "OFFSET 2 \n" "ORDER BY DESC(?x) ASC(?concat) \n") - .parse(); + ; } { - auto pq = SparqlParser( + auto pq = SparqlParser::parseQuery( "SELECT REDUCED * WHERE { \n" " ?movie .\n" "\t{ \n" @@ -410,11 +378,11 @@ TEST(ParserTest, testParse) { "} \n" "LIMIT 10 \n" "ORDER BY DESC(?movie) ASC(?year) \n") - .parse(); + ; } { - auto pq = SparqlParser( + auto pq = SparqlParser::parseQuery( "SELECT DISTINCT * WHERE { \n" " ?movie .\n" "\t{ \n" @@ -429,19 +397,17 @@ TEST(ParserTest, testParse) { "LIMIT 20 \n" "OFFSET 3 \n" "ORDER BY DESC(?movie)\n") - .parse(); + ; } */ { - auto pq = SparqlParser( - "SELECT REDUCED * WHERE { \n" - " ?movie ?director .\n" - "} \n" - "ORDER BY ASC(?movie)\n" - "LIMIT 10 \n") - .parse(); - ASSERT_EQ(1u, pq._prefixes.size()); + auto pq = SparqlParser::parseQuery( + "SELECT REDUCED * WHERE { \n" + " ?movie ?director .\n" + "} \n" + "ORDER BY ASC(?movie)\n" + "LIMIT 10 \n"); ASSERT_EQ(1u, pq._rootGraphPattern._graphPatterns.size()); const auto& c = pq.children()[0].getBasic(); @@ -465,15 +431,13 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT DISTINCT * WHERE { \n" - " ?movie ?director .\n" - "} \n" - "ORDER BY DESC(?movie)\n" - "LIMIT 10 \n") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT DISTINCT * WHERE { \n" + " ?movie ?director .\n" + "} \n" + "ORDER BY DESC(?movie)\n" + "LIMIT 10 \n"); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(1u, pq._rootGraphPattern._graphPatterns.size()); const auto& c = pq.children()[0].getBasic(); @@ -497,24 +461,22 @@ TEST(ParserTest, testParse) { } { - auto pq = SparqlParser( - "SELECT DISTINCT * WHERE { \n" - " ?movie .\n" - "\t{ \n" - "\t SELECT * WHERE { \n" - "\t\t\t ?movie ?director .\n" - "\t\t\t ?movie ?year .\n" - "\t\t\t FILTER(?year > \"00-00-2000\") ." - "\t\t } \n" - "\t\t ORDER BY DESC(?director) \n" - "\t} \n" - "} \n" - "ORDER BY DESC(?movie)\n" - "LIMIT 20 \n" - "OFFSET 3 \n") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT DISTINCT * WHERE { \n" + " ?movie .\n" + "\t{ \n" + "\t SELECT * WHERE { \n" + "\t\t\t ?movie ?director .\n" + "\t\t\t ?movie ?year .\n" + "\t\t\t FILTER(?year > \"00-00-2000\") ." + "\t\t } \n" + "\t\t ORDER BY DESC(?director) \n" + "\t} \n" + "} \n" + "ORDER BY DESC(?movie)\n" + "LIMIT 20 \n" + "OFFSET 3 \n"); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(2u, pq._rootGraphPattern._graphPatterns.size()); const auto& c = pq.children()[0].getBasic(); @@ -579,28 +541,26 @@ TEST(ParserTest, testParse) { { // Query proving Select * working for n-subQuery - auto pq = SparqlParser( - "SELECT DISTINCT * WHERE { \n" - " ?movie .\n" - "\t{ \n" - "\t SELECT * WHERE { \n" - "\t\t\t ?movie ?director .\n" - "\t\t\t { \n" - "\t\t\t\t SELECT ?year WHERE { \n" - "\t\t\t\t\t ?movie ?year . \n" - "\t\t\t\t\t } \n" - "\t\t\t } \n" - "\t\t\t FILTER(?year > \"00-00-2000\") ." - "\t\t } \n" - "\t\t ORDER BY DESC(?director) \n" - "\t} \n" - "} \n" - "ORDER BY DESC(?movie)\n" - "LIMIT 20 \n" - "OFFSET 3 \n") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT DISTINCT * WHERE { \n" + " ?movie .\n" + "\t{ \n" + "\t SELECT * WHERE { \n" + "\t\t\t ?movie ?director .\n" + "\t\t\t { \n" + "\t\t\t\t SELECT ?year WHERE { \n" + "\t\t\t\t\t ?movie ?year . \n" + "\t\t\t\t\t } \n" + "\t\t\t } \n" + "\t\t\t FILTER(?year > \"00-00-2000\") ." + "\t\t } \n" + "\t\t ORDER BY DESC(?director) \n" + "\t} \n" + "} \n" + "ORDER BY DESC(?movie)\n" + "LIMIT 20 \n" + "OFFSET 3 \n"); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(2u, pq._rootGraphPattern._graphPatterns.size()); const auto& c = pq.children()[0].getBasic(); @@ -692,60 +652,50 @@ TEST(ParserTest, testParse) { // TODO Also add checks for the correct semantics. { // Check Parse Construct (1) - auto pq_1 = SparqlParser( + auto pq_1 = SparqlParser::parseQuery( "PREFIX foaf: \n" "PREFIX org: \n" "CONSTRUCT { ?x foaf:name ?name } \n" "WHERE { ?x org:employeeName ?name }"); - pq_1.parse(); // Check Parse Construct (2) - auto pq_2 = SparqlParser( + auto pq_2 = SparqlParser::parseQuery( "PREFIX foaf: \n" "PREFIX vcard: \n" "CONSTRUCT { vcard:FN ?name }\n" "WHERE { ?x foaf:name ?name } "); - pq_2.parse(); } { // Check if the correct ParseException is thrown after // GroupBy with Select '*' - auto pq = SparqlParser( - "SELECT DISTINCT * WHERE { \n" - " ?a ?c .\n" - "} \n" - "GROUP BY ?a ?c \n"); - ASSERT_THROW(pq.parse(), ParseException); + ASSERT_THROW( + SparqlParser::parseQuery( + "SELECT DISTINCT * WHERE { \n?a ?c .\n} \nGROUP BY ?a ?c \n"), + ParseException); } { // Check if the correct ParseException is thrown after: // Select [var_name]+ '*' - auto pq = SparqlParser( - "SELECT DISTINCT ?a * WHERE { \n" - " ?a ?c .\n" - "} \n"); - ASSERT_THROW(pq.parse(), ParseException); + ASSERT_THROW(SparqlParser::parseQuery( + "SELECT DISTINCT ?a * WHERE { \n?a ?c .\n} \n"), + ParseException); } { // Check if the correct ParseException is thrown after: // Select '*' [var_name]+ - auto pq = SparqlParser( - "SELECT DISTINCT * ?a WHERE { \n" - " ?a ?c .\n" - "} \n"); - ASSERT_THROW(pq.parse(), ParseException); + ASSERT_THROW(SparqlParser::parseQuery( + "SELECT DISTINCT * ?a WHERE { \n?a ?c .\n} \n"), + ParseException); } { // Check if the correct ParseException is thrown after: Select ['*']{2,} - auto pq = SparqlParser( - "SELECT DISTINCT * * WHERE { \n" - " ?a ?c .\n" - "} \n"); - ASSERT_THROW(pq.parse(), ParseException); + ASSERT_THROW(SparqlParser::parseQuery( + "SELECT DISTINCT * * WHERE { \n?a ?c .\n} \n"), + ParseException); } } catch (const ad_semsearch::Exception& e) { @@ -754,25 +704,22 @@ TEST(ParserTest, testParse) { } TEST(ParserTest, testFilterWithoutDot) { - ParsedQuery pq = - SparqlParser( - "PREFIX fb: \n" - "\n" - "SELECT DISTINCT ?1 WHERE {\n" - " fb:m.0fkvn fb:government.government_office_category.officeholders " - "?0 " - ".\n" - " ?0 fb:government.government_position_held.jurisdiction_of_office " - "fb:m.0vmt .\n" - " ?0 fb:government.government_position_held.office_holder ?1 .\n" - " FILTER (?1 != fb:m.0fkvn)\n" - " FILTER (?1 != fb:m.0vmt)\n" - "FILTER (?1 != fb:m.018mts) \n" - "} LIMIT 300") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX fb: \n" + "\n" + "SELECT DISTINCT ?1 WHERE {\n" + " fb:m.0fkvn fb:government.government_office_category.officeholders " + "?0 " + ".\n" + " ?0 fb:government.government_position_held.jurisdiction_of_office " + "fb:m.0vmt .\n" + " ?0 fb:government.government_position_held.office_holder ?1 .\n" + " FILTER (?1 != fb:m.0fkvn)\n" + " FILTER (?1 != fb:m.0vmt)\n" + "FILTER (?1 != fb:m.018mts) \n" + "} LIMIT 300"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); - ASSERT_EQ(2u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); @@ -791,27 +738,18 @@ TEST(ParserTest, testFilterWithoutDot) { } TEST(ParserTest, testExpandPrefixes) { - ParsedQuery pq = SparqlParser( - "PREFIX : \n" - "PREFIX ns: \n" - "PREFIX xxx: \n" - "SELECT ?x ?z \n WHERE \t {?x :myrel ?y. ?y ns:myrel " - "?z.?y }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX : \n" + "PREFIX ns: \n" + "PREFIX xxx: \n" + "SELECT ?x ?z \n WHERE \t {?x :myrel ?y. ?y ns:myrel " + "?z.?y }"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(4u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause.getSelectedVariables().size()); ASSERT_EQ(3u, c._triples.size()); - ASSERT_THAT(pq._prefixes, - testing::UnorderedElementsAre( - SparqlPrefix{INTERNAL_PREDICATE_PREFIX_NAME, - INTERNAL_PREDICATE_PREFIX_IRI}, - SparqlPrefix{"", ""}, - SparqlPrefix{"ns", ""}, - SparqlPrefix{"xxx", ""})); ASSERT_EQ(Variable{"?x"}, selectClause.getSelectedVariables()[0]); ASSERT_EQ(Variable{"?z"}, selectClause.getSelectedVariables()[1]); ASSERT_EQ("?x", c._triples[0]._s); @@ -828,16 +766,13 @@ TEST(ParserTest, testExpandPrefixes) { } TEST(ParserTest, testLiterals) { - ParsedQuery pq = - SparqlParser( - "PREFIX xsd: SELECT * WHERE { " - "true 10 . 10.2 \"2000-00-00\"^^xsd:date }") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "PREFIX xsd: SELECT * WHERE { " + "true 10 . 10.2 \"2000-00-00\"^^xsd:date }"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(2u, pq._prefixes.size()); ASSERT_TRUE(selectClause.isAsterisk()); ASSERT_EQ(2u, c._triples.size()); ASSERT_EQ("\"true\"^^", @@ -852,12 +787,11 @@ TEST(ParserTest, testLiterals) { TEST(ParserTest, testSolutionModifiers) { { ParsedQuery pq = - SparqlParser("SELECT ?x WHERE \t {?x ?y}").parse(); + SparqlParser::parseQuery("SELECT ?x WHERE \t {?x ?y}"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, c._triples.size()); ASSERT_EQ(std::numeric_limits::max(), pq._limitOffset._limit); @@ -868,11 +802,10 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser("SELECT ?x WHERE \t {?x ?y} LIMIT 10") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x WHERE \t {?x ?y} LIMIT 10"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); @@ -885,15 +818,13 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT ?x WHERE \t {?x ?y}\n" - "LIMIT 10 OFFSET 15") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x WHERE \t {?x ?y}\n" + "LIMIT 10 OFFSET 15"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, c._triples.size()); ASSERT_EQ(10u, pq._limitOffset._limit); @@ -904,15 +835,13 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT DISTINCT ?x ?y WHERE \t {?x ?y}\n" - "ORDER BY ?y LIMIT 10 OFFSET 15") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT DISTINCT ?x ?y WHERE \t {?x ?y}\n" + "ORDER BY ?y LIMIT 10 OFFSET 15"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, c._triples.size()); ASSERT_EQ(10u, pq._limitOffset._limit); @@ -925,16 +854,14 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT DISTINCT ?x ?ql_textscore_x ?y WHERE \t {?x " - " ?y}\n" - "ORDER BY ASC(?y) DESC(?ql_textscore_x) LIMIT 10 OFFSET 15") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT DISTINCT ?x ?ql_textscore_x ?y WHERE \t {?x " + "ql:contains-entity ?y}\n" + "ORDER BY ASC(?y) DESC(?ql_textscore_x) LIMIT 10 OFFSET 15"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(3u, selectClause.getSelectedVariables().size()); ASSERT_EQ(Variable{"?ql_textscore_x"}, selectClause.getSelectedVariables()[1]); @@ -951,15 +878,13 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT REDUCED ?x ?y WHERE \t {?x ?y}\n" - "ORDER BY DESC(?x) ASC(?y) LIMIT 10 OFFSET 15") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT REDUCED ?x ?y WHERE \t {?x ?y}\n" + "ORDER BY DESC(?x) ASC(?y) LIMIT 10 OFFSET 15"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(1u, pq._prefixes.size()); ASSERT_EQ(2u, selectClause.getSelectedVariables().size()); ASSERT_EQ(1u, c._triples.size()); ASSERT_EQ(10u, pq._limitOffset._limit); @@ -974,25 +899,23 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = - SparqlParser("SELECT ?x ?y WHERE {?x } LIMIT 10").parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE {?x } LIMIT 10"); ASSERT_EQ(10u, pq._limitOffset._limit); } { - auto pq = SparqlParser( - "PREFIX xsd: " - "SELECT DISTINCT ?movie WHERE { \n" - "\n" - "?movie \"2000-00-00\"^^xsd:date .\n" - "\n" - "?movie . } LIMIT 50") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX xsd: " + "SELECT DISTINCT ?movie WHERE { \n" + "\n" + "?movie \"2000-00-00\"^^xsd:date .\n" + "\n" + "?movie . } LIMIT 50"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(2u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(Variable{"?movie"}, selectClause.getSelectedVariables()[0]); ASSERT_EQ(2u, c._triples.size()); @@ -1005,19 +928,17 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "PREFIX xsd: " - "SELECT DISTINCT ?movie WHERE { \n" - "\n" - "?movie \"00-00-2000\"^^xsd:date .\n" - "\n" - "?movie . } LIMIT 50") - .parse(); + auto pq = SparqlParser::parseQuery( + "PREFIX xsd: " + "SELECT DISTINCT ?movie WHERE { \n" + "\n" + "?movie \"00-00-2000\"^^xsd:date .\n" + "\n" + "?movie . } LIMIT 50"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, pq.children().size()); const auto& c = pq.children()[0].getBasic(); - ASSERT_EQ(2u, pq._prefixes.size()); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); ASSERT_EQ(Variable{"?movie"}, selectClause.getSelectedVariables()[0]); ASSERT_EQ(2u, c._triples.size()); @@ -1031,13 +952,12 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT ?r (AVG(?r) as ?avg) WHERE {" - "?a ?b ." - "?a ql:has-relation ?r }" - "GROUP BY ?r " - "ORDER BY ?avg") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?r (AVG(?r) as ?avg) WHERE {" + "?a ?b ." + "?a ql:has-relation ?r }" + "GROUP BY ?r " + "ORDER BY ?avg"); ASSERT_EQ(1u, pq.children().size()); ASSERT_EQ(1u, pq._orderBy.size()); EXPECT_THAT(pq, m::GroupByVariables({Variable{"?r"}})); @@ -1046,13 +966,12 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = SparqlParser( - "SELECT ?r (COUNT(DISTINCT ?r) as ?count) WHERE {" - "?a ?b ." - "?a ql:has-relation ?r }" - "GROUP BY ?r " - "ORDER BY ?count") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?r (COUNT(DISTINCT ?r) as ?count) WHERE {" + "?a ?b ." + "?a ql:has-relation ?r }" + "GROUP BY ?r " + "ORDER BY ?count"); ASSERT_EQ(1u, pq._orderBy.size()); EXPECT_THAT(pq, m::GroupByVariables({Variable{"?r"}})); ASSERT_EQ(Variable{"?count"}, pq._orderBy[0].variable_); @@ -1060,14 +979,12 @@ TEST(ParserTest, testSolutionModifiers) { } { - auto pq = - SparqlParser( - "SELECT ?r (GROUP_CONCAT(?r;SEPARATOR=\"Cake\") as ?concat) WHERE {" - "?a ?b ." - "?a ql:has-relation ?r }" - "GROUP BY ?r " - "ORDER BY ?concat") - .parse(); + auto pq = SparqlParser::parseQuery( + "SELECT ?r (GROUP_CONCAT(?r;SEPARATOR=\"Cake\") as ?concat) WHERE {" + "?a ?b ." + "?a ql:has-relation ?r }" + "GROUP BY ?r " + "ORDER BY ?concat"); ASSERT_TRUE(pq.hasSelectClause()); const auto& aliases = pq.selectClause().getAliases(); ASSERT_EQ(1u, aliases.size()); @@ -1077,10 +994,8 @@ TEST(ParserTest, testSolutionModifiers) { } TEST(ParserTest, testGroupByAndAlias) { - ParsedQuery pq = - SparqlParser( - "SELECT (COUNT(?a) as ?count) WHERE { ?b ?a } GROUP BY ?b") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT (COUNT(?a) as ?count) WHERE { ?b ?a } GROUP BY ?b"); ASSERT_TRUE(pq.hasSelectClause()); const auto& selectClause = pq.selectClause(); ASSERT_EQ(1u, selectClause.getSelectedVariables().size()); @@ -1095,7 +1010,7 @@ TEST(ParserTest, testGroupByAndAlias) { TEST(ParserTest, Bind) { ParsedQuery pq = - SparqlParser("SELECT ?a WHERE { BIND (10 - 5 as ?a) . }").parse(); + SparqlParser::parseQuery("SELECT ?a WHERE { BIND (10 - 5 as ?a) . }"); ASSERT_TRUE(pq.hasSelectClause()); ASSERT_EQ(pq.children().size(), 1); p::GraphPatternOperation child = pq.children()[0]; @@ -1108,16 +1023,15 @@ TEST(ParserTest, Bind) { TEST(ParserTest, Order) { { ParsedQuery pq = - SparqlParser("SELECT ?x ?y WHERE { ?x ?y }").parse(); + SparqlParser::parseQuery("SELECT ?x ?y WHERE { ?x ?y }"); ASSERT_TRUE(pq._orderBy.empty()); ASSERT_EQ(pq._rootGraphPattern._graphPatterns.size(), 1); ASSERT_TRUE(holds_alternative( pq._rootGraphPattern._graphPatterns[0])); } { - ParsedQuery pq = - SparqlParser("SELECT ?x ?y WHERE { ?x ?y } ORDER BY ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { ?x ?y } ORDER BY ?x"); ASSERT_EQ(pq._orderBy.size(), 1); EXPECT_THAT(pq._orderBy[0], m::VariableOrderKey(Variable{"?x"}, false)); ASSERT_EQ(pq._rootGraphPattern._graphPatterns.size(), 1); @@ -1125,10 +1039,8 @@ TEST(ParserTest, Order) { pq._rootGraphPattern._graphPatterns[0])); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x ?y WHERE { ?x ?y } ORDER BY ASC(?y)") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { ?x ?y } ORDER BY ASC(?y)"); ASSERT_EQ(pq._orderBy.size(), 1); EXPECT_THAT(pq._orderBy[0], m::VariableOrderKey(Variable{"?y"}, false)); ASSERT_EQ(pq._rootGraphPattern._graphPatterns.size(), 1); @@ -1136,10 +1048,8 @@ TEST(ParserTest, Order) { pq._rootGraphPattern._graphPatterns[0])); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x ?y WHERE { ?x ?y } ORDER BY DESC(?x)") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { ?x ?y } ORDER BY DESC(?x)"); ASSERT_EQ(pq._orderBy.size(), 1); EXPECT_THAT(pq._orderBy[0], m::VariableOrderKey(Variable{"?x"}, true)); ASSERT_EQ(pq._rootGraphPattern._graphPatterns.size(), 1); @@ -1147,10 +1057,8 @@ TEST(ParserTest, Order) { pq._rootGraphPattern._graphPatterns[0])); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY ?x ORDER BY ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY ?x ORDER BY ?x"); ASSERT_EQ(pq._orderBy.size(), 1); EXPECT_THAT(pq._orderBy[0], m::VariableOrderKey(Variable{"?x"}, false)); ASSERT_EQ(pq._rootGraphPattern._graphPatterns.size(), 1); @@ -1158,18 +1066,15 @@ TEST(ParserTest, Order) { pq._rootGraphPattern._graphPatterns[0])); } { - ParsedQuery pq = SparqlParser( - "SELECT ?x (COUNT(?y) as ?c) WHERE { ?x " - "?y } GROUP BY ?x ORDER BY ?c") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x (COUNT(?y) as ?c) WHERE { ?x " + "?y } GROUP BY ?x ORDER BY ?c"); ASSERT_EQ(pq._orderBy.size(), 1); EXPECT_THAT(pq._orderBy[0], m::VariableOrderKey(Variable{"?c"}, false)); } { - ParsedQuery pq = - SparqlParser( - "SELECT ?x ?y WHERE { ?x ?y } ORDER BY (?x - ?y)") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { ?x ?y } ORDER BY (?x - ?y)"); ASSERT_EQ(pq._orderBy.size(), 1); auto variant = pq._rootGraphPattern._graphPatterns[1]; ASSERT_TRUE(holds_alternative(variant)); @@ -1180,47 +1085,42 @@ TEST(ParserTest, Order) { { // Ordering by variables that are not grouped is not allowed. EXPECT_THROW( - SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY ?x ORDER BY ?y") - .parse(), + SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY ?x ORDER BY ?y"), ParseException); } { // Ordering by an expression while grouping is currently not supported. - EXPECT_THROW(SparqlParser("SELECT ?y WHERE { ?x ?y } GROUP BY " - "?y ORDER BY (?x - ?y)") - .parse(), + EXPECT_THROW(SparqlParser::parseQuery( + "SELECT ?y WHERE { ?x ?y } GROUP BY " + "?y ORDER BY (?x - ?y)"), ParseException); } { // Ordering by an expression while grouping is currently not supported. - EXPECT_THROW(SparqlParser("SELECT ?y WHERE { ?x ?y } GROUP BY " - "?y ORDER BY (2 * ?y)") - .parse(), + EXPECT_THROW(SparqlParser::parseQuery( + "SELECT ?y WHERE { ?x ?y } GROUP BY " + "?y ORDER BY (2 * ?y)"), ParseException); } } TEST(ParserTest, Group) { { - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE { ?x ?y } GROUP BY ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY ?x"); EXPECT_THAT(pq, m::GroupByVariables({Variable{"?x"}})); } { // grouping by a variable - ParsedQuery pq = - SparqlParser("SELECT ?x WHERE { ?x ?y } GROUP BY ?y ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY ?y ?x"); EXPECT_THAT(pq, m::GroupByVariables({Variable{"?y"}, Variable{"?x"}})); } { // grouping by an expression - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY (?x - ?y) ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY (?x - ?y) ?x"); auto variant = pq._rootGraphPattern._graphPatterns[1]; ASSERT_TRUE(holds_alternative(variant)); auto helperBind = get(variant); @@ -1229,20 +1129,17 @@ TEST(ParserTest, Group) { } { // grouping by an expression with an alias - ParsedQuery pq = SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY (?x " - "- ?y AS ?foo) ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY (?x " + "- ?y AS ?foo) ?x"); EXPECT_THAT(pq._rootGraphPattern._graphPatterns[1], m::Bind(Variable{"?foo"}, "?x-?y")); EXPECT_THAT(pq, m::GroupByVariables({Variable{"?foo"}, Variable{"?x"}})); } { // grouping by a builtin call - ParsedQuery pq = - SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY COUNT(?x) ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY COUNT(?x) ?x"); auto variant = pq._rootGraphPattern._graphPatterns[1]; ASSERT_TRUE(holds_alternative(variant)); auto helperBind = get(variant); @@ -1251,40 +1148,27 @@ TEST(ParserTest, Group) { } { // grouping by a function call - ParsedQuery pq = SparqlParser( - "SELECT ?x WHERE { ?x ?y } GROUP BY " - " (?test) ?x") - .parse(); + ParsedQuery pq = SparqlParser::parseQuery( + "SELECT ?x WHERE { ?x ?y } GROUP BY " + " (?y) ?x"); auto variant = pq._rootGraphPattern._graphPatterns[1]; ASSERT_TRUE(holds_alternative(variant)); auto helperBind = get(variant); ASSERT_THAT( helperBind, m::BindExpression( - "(?test)")); + "(?y)")); EXPECT_THAT(pq, m::GroupByVariables({helperBind._target, Variable{"?x"}})); } { // selection of a variable that is not grouped/aggregated - EXPECT_THROW( - SparqlParser("SELECT ?x ?y WHERE { ?x ?y } GROUP BY ?x") - .parse(), - ParseException); + EXPECT_THROW(SparqlParser::parseQuery( + "SELECT ?x ?y WHERE { ?x ?y } GROUP BY ?x"), + ParseException); } } -TEST(ParserTest, Prefix) { - ParsedQuery pq = - SparqlParser( - "PREFIX descriptor: SELECT ?var WHERE { ?var }") - .parse(); - ASSERT_THAT(pq._prefixes, - testing::UnorderedElementsAre( - SparqlPrefix{"ql", ""}, - SparqlPrefix{"descriptor", ""})); -} - TEST(ParserTest, ParseFilterExpression) { auto f = SparqlParser::parseFilterExpression("(LANG(?x) = \"en\")", {}); ASSERT_EQ(f, (SparqlFilter{SparqlFilter::LANG_MATCHES, "?x", "\"en\""})); @@ -1299,10 +1183,8 @@ TEST(ParserTest, ParseFilterExpression) { TEST(ParserTest, LanguageFilterPostProcessing) { { - ParsedQuery q = - SparqlParser( - "SELECT * WHERE {?x