Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CONSTRUCT query processing #528

Merged
merged 55 commits into from
Jan 1, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a3bf864
Initial changes to implement construct parser
RobinTF Dec 14, 2021
88b22ec
Fix types and temporary values
RobinTF Dec 14, 2021
c7d7fdc
Start adding tests
RobinTF Dec 14, 2021
75ce36c
Fix bad reference
RobinTF Dec 14, 2021
f25dea9
Enable usage of a keyword
RobinTF Dec 14, 2021
a9d435a
Use proper iri notation
RobinTF Dec 15, 2021
b8df565
Create blank nodes instead of variables
RobinTF Dec 15, 2021
6956b68
Add TODO
RobinTF Dec 15, 2021
577356d
Start implementing construct parsing
RobinTF Dec 16, 2021
554030e
Start implementing turtle format
RobinTF Dec 18, 2021
348075c
Fix casting issues and enhance visitor
RobinTF Dec 19, 2021
91bb21c
Use more complex types
RobinTF Dec 19, 2021
d5fbd0a
Unify variable substitution
RobinTF Dec 19, 2021
95626be
Clean up code a little
RobinTF Dec 19, 2021
9a2bb0e
Reformat code
RobinTF Dec 19, 2021
7a36a3e
Introduce co_return to avoid potential bugs
RobinTF Dec 21, 2021
726070c
Remove broken test
RobinTF Dec 21, 2021
b8a5078
Inline appendVector
RobinTF Dec 21, 2021
f1d4473
Inline and rename reversed nodes wrapper
RobinTF Dec 21, 2021
dbb076b
Rename variable
RobinTF Dec 21, 2021
841ba8e
Inline BlankNodeCreator into Visitor
RobinTF Dec 21, 2021
781eaf6
Refactor method order
RobinTF Dec 21, 2021
cf6100d
Use constructor over make_pair
RobinTF Dec 21, 2021
02f9658
Fix failing test case
RobinTF Dec 21, 2021
b94b9c1
Move Data Types into dedicated data directory
RobinTF Dec 21, 2021
e051c2d
Prefer std::variant over std::function
RobinTF Dec 21, 2021
6563fa2
Introduce Iri Type
RobinTF Dec 21, 2021
f02f537
Add comment
RobinTF Dec 21, 2021
de936a7
Fix syntax
RobinTF Dec 22, 2021
feda6fc
Properly handle turtle accept header
RobinTF Dec 22, 2021
c529c2d
Fix code style
RobinTF Dec 22, 2021
f09c003
Fix member name due to rebase
RobinTF Dec 23, 2021
793ab27
Remove redundant TODO
RobinTF Dec 23, 2021
10c48e8
Introduce basic rdf graph checking
RobinTF Dec 24, 2021
7eaf74d
Add newline at end of file
RobinTF Dec 24, 2021
11efd69
Unify _selectClause and _constructClause in variant
RobinTF Dec 26, 2021
505a766
Address PR comments
RobinTF Dec 26, 2021
2480a68
Format files
RobinTF Dec 26, 2021
635ea19
Introduce separator comments
RobinTF Dec 26, 2021
75c7d9e
Fix failing E2E tests
RobinTF Dec 27, 2021
f552aea
Use strict error strategy
RobinTF Dec 27, 2021
e644715
Fix formatting
RobinTF Dec 27, 2021
21d3807
Prefer constexpr
RobinTF Dec 30, 2021
5605f6c
Use helper functions for common type code
RobinTF Dec 30, 2021
ac1c969
Properly use references
RobinTF Dec 30, 2021
5b880c1
Adress simple one-line fixes
RobinTF Dec 30, 2021
b9585bb
Use ctre
RobinTF Dec 30, 2021
54fafd4
Revert accidental CMakeLists change
RobinTF Dec 31, 2021
9c0b92b
Address more PR comments
RobinTF Dec 31, 2021
eb96bd9
Rename functions for clarity
RobinTF Dec 31, 2021
6385f13
Clearly separate non-textual use of variable name
RobinTF Dec 31, 2021
35323c4
Add invariant for Variable class
RobinTF Dec 31, 2021
e5065cb
Remove redundant test code
RobinTF Dec 31, 2021
58c02e6
Add trailing newline
RobinTF Dec 31, 2021
87fa97d
Potentially final commit for this PR
RobinTF Jan 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/engine/QueryExecutionTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,9 @@ QueryExecutionTree::writeRdfGraphTurtle(
for (size_t i = offset; i < upperBound; i++) {
RobinTF marked this conversation as resolved.
Show resolved Hide resolved
Context context{i, *res, variableColumns, _qec->getIndex()};
for (const auto& triple : constructTriples) {
auto subject = triple[0].toString(context, SUBJECT);
auto verb = triple[1].toString(context, VERB);
auto object = triple[2].toString(context, OBJECT);
auto subject = triple[0].evaluate(context, SUBJECT);
auto verb = triple[1].evaluate(context, VERB);
auto object = triple[2].evaluate(context, OBJECT);
if (!subject.has_value() || !verb.has_value() || !object.has_value()) {
continue;
}
Expand Down
7 changes: 4 additions & 3 deletions src/engine/QueryPlanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ QueryExecutionTree QueryPlanner::createExecutionTree(ParsedQuery& pq) {
if (doGrouping && !usePatternTrick) {
plans.emplace_back(getGroupByRow(pq, plans));
} else if (usePatternTrick) {
plans.emplace_back(getPatternTrickRow(pq, plans, patternTrickTriple));
plans.emplace_back(
getPatternTrickRow(pq.selectClause(), plans, patternTrickTriple));
}

// HAVING
Expand Down Expand Up @@ -897,9 +898,9 @@ vector<QueryPlanner::SubtreePlan> QueryPlanner::getDistinctRow(

// _____________________________________________________________________________
vector<QueryPlanner::SubtreePlan> QueryPlanner::getPatternTrickRow(
const ParsedQuery& pq, const vector<vector<SubtreePlan>>& dpTab,
const ParsedQuery::SelectClause& selectClause,
const vector<vector<SubtreePlan>>& dpTab,
const SparqlTriple& patternTrickTriple) {
const auto& selectClause = pq.selectClause();
const vector<SubtreePlan>* previous = nullptr;
if (!dpTab.empty()) {
previous = &dpTab.back();
Expand Down
3 changes: 2 additions & 1 deletion src/engine/QueryPlanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ class QueryPlanner {
const vector<vector<SubtreePlan>>& dpTab) const;

vector<SubtreePlan> getPatternTrickRow(
const ParsedQuery& pq, const vector<vector<SubtreePlan>>& dpTab,
const ParsedQuery::SelectClause& selectClause,
const vector<vector<SubtreePlan>>& dpTab,
const SparqlTriple& patternTrickTriple);

vector<SubtreePlan> getHavingRow(
Expand Down
22 changes: 10 additions & 12 deletions src/engine/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,33 +380,31 @@ boost::asio::awaitable<void> Server::processQuery(
request));
}

const auto throwIfConstructClause = [&pq]() {
if (pq.hasConstructClause()) {
throw std::runtime_error{
"CONSTRUCT queries only support turtle syntax right now"};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two remarks remaining here:

  1. You should add the turtle media type in the supportedMediaTypes above (line 345) s.t. we can also specify it via an accept header.
  2. replace "turtle syntax" by "RDF Turtle as an export format". And Add "Please specify "action=turtle-export" as a query parameter or the accept header "...(add corresponding header here)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's currently called turtle_export to be consistent with csv_export and tsv_export and all the other options

}
};

AD_CHECK(mediaType.has_value());
switch (mediaType.value()) {
case ad_utility::MediaType::csv: {
if (pq.hasConstructClause()) {
throw std::runtime_error{
"CONSTRUCT queries only support turtle syntax right now"};
}
throwIfConstructClause();
auto responseGenerator = composeResponseSepValues(pq, qet, ',');
auto response = createOkResponse(std::move(responseGenerator), request,
ad_utility::MediaType::csv);
co_await send(std::move(response));
} break;
case ad_utility::MediaType::tsv: {
if (pq.hasConstructClause()) {
throw std::runtime_error{
"CONSTRUCT queries only support turtle syntax right now"};
}
throwIfConstructClause();
auto responseGenerator = composeResponseSepValues(pq, qet, '\t');
auto response = createOkResponse(std::move(responseGenerator), request,
ad_utility::MediaType::tsv);
co_await send(std::move(response));
} break;
case ad_utility::MediaType::qleverJson: {
if (pq.hasConstructClause()) {
throw std::runtime_error{
"CONSTRUCT queries only support turtle syntax right now"};
}
throwIfConstructClause();
// Normal case: JSON response
auto responseString =
composeResponseQleverJson(pq, qet, requestTimer, maxSend);
Expand Down
9 changes: 2 additions & 7 deletions src/index/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

add_library(index
Index.h Index.cpp Index.Text.cpp
Vocabulary.h Vocabulary.cpp
Expand All @@ -13,10 +14,4 @@ add_library(index
PrefixHeuristic.cpp PrefixHeuristic.h
CompressedRelation.h CompressedRelation.cpp)


find_package(OpenMP)
if(OpenMP_CXX_FOUND)
target_link_libraries(index parser ${STXXL_LIBRARIES} ${ICU_LIBRARIES} absl::flat_hash_map absl::flat_hash_set zstd OpenMP::OpenMP_CXX)
else()
target_link_libraries(index parser ${STXXL_LIBRARIES} ${ICU_LIBRARIES} absl::flat_hash_map absl::flat_hash_set zstd)
endif()
target_link_libraries(index parser ${STXXL_LIBRARIES} ${ICU_LIBRARIES} absl::flat_hash_map absl::flat_hash_set zstd)
6 changes: 3 additions & 3 deletions src/parser/ParsedQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ string ParsedQuery::asString() const {
const auto& constructClause = this->constructClause();
os << "\n CONSTRUCT {\n\t";
for (const auto& triple : constructClause) {
os << triple[0].toString();
os << triple[0].toSparql();
os << ' ';
os << triple[1].toString();
os << triple[1].toSparql();
os << ' ';
os << triple[2].toString();
os << triple[2].toSparql();
os << " .\n";
}
os << "}";
Expand Down
6 changes: 4 additions & 2 deletions src/parser/ParsedQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,11 @@ class ParsedQuery {
return std::get<ConstructClause>(_clause);
}

decltype(auto) selectClause() { return std::get<SelectClause>(_clause); }
[[nodiscard]] decltype(auto) selectClause() {
return std::get<SelectClause>(_clause);
}

decltype(auto) constructClause() {
[[nodiscard]] decltype(auto) constructClause() {
return std::get<ConstructClause>(_clause);
}

Expand Down
6 changes: 4 additions & 2 deletions src/parser/SparqlParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ void SparqlParser::parseQuery(ParsedQuery* query, QueryType queryType) {
auto& constructClause = query->constructClause();
for (const auto& triple : constructClause) {
for (const auto& varOrTerm : triple) {
if (std::holds_alternative<Variable>(varOrTerm)) {
const auto var = varOrTerm.toString();
if (auto variable = std::get_if<Variable>(&varOrTerm)) {
const auto var = variable->getName();
RobinTF marked this conversation as resolved.
Show resolved Hide resolved
if (std::find(query->_groupByVariables.begin(),
query->_groupByVariables.end(),
var) == query->_groupByVariables.end()) {
Expand Down Expand Up @@ -209,6 +209,8 @@ OrderKey SparqlParser::parseOrderKey(const std::string& order,
_lexer.expect(")");
s << ")";
RobinTF marked this conversation as resolved.
Show resolved Hide resolved
} else if (query->hasSelectClause() && _lexer.accept("(")) {
// TODO This assumes that aliases can stand in the ORDER BY
// This is not true, only expression may stand there
ParsedQuery::Alias a = parseAliasWithAntlr();
auto& selectClause = query->selectClause();

Expand Down
93 changes: 29 additions & 64 deletions src/parser/SparqlParserHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,90 +21,55 @@ struct ParserAndVisitor {
public:
SparqlAutomaticParser _parser{&_tokens};
SparqlQleverVisitor _visitor;
explicit ParserAndVisitor(string input) : _input{std::move(input)} {}
explicit ParserAndVisitor(string input,
SparqlQleverVisitor::PrefixMap prefixMap)
: _input{std::move(input)}, _visitor{std::move(prefixMap)} {}
explicit ParserAndVisitor(string input) : _input{std::move(input)} {
_parser.setErrorHandler(std::make_shared<ThrowingErrorStrategy>());
}

template <typename ResultType, typename ContextType>
auto parse(const std::string& input,
auto parse(const std::string& input, const std::string_view& name,
RobinTF marked this conversation as resolved.
Show resolved Hide resolved
ContextType* (SparqlAutomaticParser::*F)(void)) {
_parser.setErrorHandler(std::make_shared<ThrowingErrorStrategy>());
auto context = (_parser.*F)();
auto resultOfParse =
std::move(context->accept(&(_visitor)).template as<ResultType>());
try {
auto context = (_parser.*F)();
auto resultOfParse =
std::move(context->accept(&(_visitor)).template as<ResultType>());

auto remainingString =
input.substr(_parser.getCurrentToken()->getStartIndex());
return ResultOfParseAndRemainingText{std::move(resultOfParse),
std::move(remainingString)};
auto remainingString =
input.substr(_parser.getCurrentToken()->getStartIndex());
return ResultOfParseAndRemainingText{std::move(resultOfParse),
std::move(remainingString)};
} catch (const antlr4::ParseCancellationException& e) {
throw std::runtime_error{"Failed to parse " + name + ": " + e.what()};
}
}
};

// ____________________________________________________________________________
ResultOfParseAndRemainingText<sparqlExpression::SparqlExpressionPimpl>
parseExpression(const std::string& input) {
try {
ParserAndVisitor p{input};
auto resultOfParseAndRemainingText =
p.parse<sparqlExpression::SparqlExpression::Ptr>(
input, &SparqlAutomaticParser::expression);
ParserAndVisitor p{input};
auto resultOfParseAndRemainingText =
p.parse<sparqlExpression::SparqlExpression::Ptr>(
input, "expression", &SparqlAutomaticParser::expression);

return ResultOfParseAndRemainingText{
sparqlExpression::SparqlExpressionPimpl{
std::move(resultOfParseAndRemainingText._resultOfParse)},
std::move(resultOfParseAndRemainingText._remainingText)};
} catch (const antlr4::ParseCancellationException& e) {
throw std::runtime_error{"Failed to parse expression: "s + e.what()};
}
return ResultOfParseAndRemainingText{
sparqlExpression::SparqlExpressionPimpl{
std::move(resultOfParseAndRemainingText._resultOfParse)},
std::move(resultOfParseAndRemainingText._remainingText)};
}

// ____________________________________________________________________________
ResultOfParseAndRemainingText<ParsedQuery::Alias> parseAlias(
const std::string& input) {
try {
ParserAndVisitor p{input};
return p.parse<ParsedQuery::Alias>(
input, &SparqlAutomaticParser::aliasWithouBrackes);
} catch (const antlr4::ParseCancellationException& e) {
throw std::runtime_error{"Failed to parse alias: "s + e.what()};
}
ParserAndVisitor p{input};
return p.parse<ParsedQuery::Alias>(
input, "alias", &SparqlAutomaticParser::aliasWithouBrackes);
}
// _____________________________________________________________________________

ResultOfParseAndRemainingText<std::vector<std::array<VarOrTerm, 3>>>
parseConstructTemplate(const std::string& input) {
try {
ParserAndVisitor p{input};
return p.parse<std::vector<std::array<VarOrTerm, 3>>>(
input, &SparqlAutomaticParser::constructTemplate);
} catch (const antlr4::ParseCancellationException& e) {
throw std::runtime_error{"Failed to parse construct template: "s +
e.what()};
}
}

// ______________________________________________________________________________
std::pair<SparqlQleverVisitor::PrefixMap, size_t> parsePrologue(
const string& input) {
ParserAndVisitor p{input};
auto prologueContext = p._parser.prologue();
auto prologueSize = prologueContext->getText().size();
p._visitor.visitPrologue(prologueContext);
const auto& constVisitor = p._visitor;
return {constVisitor.prefixMap(), prologueSize};
return p.parse<std::vector<std::array<VarOrTerm, 3>>>(
input, "construct template", &SparqlAutomaticParser::constructTemplate);
}

// Parse a prefix of `input` as an iri, return the iri and the number of bytes
// that were consumed from the `input`.
std::pair<string, size_t> parseIri(const string& input,
SparqlQleverVisitor::PrefixMap prefixMap) {
ParserAndVisitor p{input, std::move(prefixMap)};
auto iriContext = p._parser.iri();
auto iriSize = iriContext->getText().size();
auto resultString = p._visitor.visitIri(iriContext).as<string>();
// const auto& constVisitor = p.visitor;
return {std::move(resultString), iriSize};
}

} // namespace sparqlParserHelpers
} // namespace sparqlParserHelpers
4 changes: 2 additions & 2 deletions src/parser/data/BlankNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class BlankNode {
: _generated{generated}, _label{std::move(label)} {}

// ___________________________________________________________________________
[[nodiscard]] std::optional<std::string> toString(
[[nodiscard]] std::optional<std::string> evaluate(
const Context& context, [[maybe_unused]] ContextRole role) const {
std::ostringstream stream;
stream << "_:";
Expand All @@ -33,7 +33,7 @@ class BlankNode {
}

// ___________________________________________________________________________
[[nodiscard]] std::string toString() const {
[[nodiscard]] std::string toSparql() const {
std::ostringstream stream;
stream << "_:";
// generated or user-defined
Expand Down
8 changes: 4 additions & 4 deletions src/parser/data/GraphTerm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ class GraphTerm : public GraphTermBase {
using GraphTermBase::GraphTermBase;

// ___________________________________________________________________________
[[nodiscard]] std::optional<std::string> toString(const Context& context,
[[nodiscard]] std::optional<std::string> evaluate(const Context& context,
ContextRole role) const {
// TODO<C++23>: remove static_cast as soon as we can visit types that
// inherit from std::variant
return std::visit(
[&](const auto& object) { return object.toString(context, role); },
[&](const auto& object) { return object.evaluate(context, role); },
static_cast<const GraphTermBase&>(*this));
}

// ___________________________________________________________________________
[[nodiscard]] std::string toString() const {
[[nodiscard]] std::string toSparql() const {
// TODO<C++23>: remove static_cast as soon as we can visit types that
// inherit from std::variant
return std::visit([&](const auto& object) { return object.toString(); },
return std::visit([&](const auto& object) { return object.toSparql(); },
static_cast<const GraphTermBase&>(*this));
}
};
4 changes: 2 additions & 2 deletions src/parser/data/Iri.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ class Iri {
}
RobinTF marked this conversation as resolved.
Show resolved Hide resolved

// ___________________________________________________________________________
[[nodiscard]] std::optional<std::string> toString(
[[nodiscard]] std::optional<std::string> evaluate(
[[maybe_unused]] const Context& context,
[[maybe_unused]] ContextRole role) const {
return _string;
}

// ___________________________________________________________________________
[[nodiscard]] std::string toString() const { return _string; }
[[nodiscard]] std::string toSparql() const { return _string; }
};
4 changes: 2 additions & 2 deletions src/parser/data/Literal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Literal {
: _stringRepresentation(toString(std::forward<decltype(t)>(t))) {}

// ___________________________________________________________________________
[[nodiscard]] std::optional<std::string> toString(
[[nodiscard]] std::optional<std::string> evaluate(
[[maybe_unused]] const Context& context, ContextRole role) const {
if (role == OBJECT) {
return _stringRepresentation;
Expand All @@ -31,5 +31,5 @@ class Literal {
}

// ___________________________________________________________________________
[[nodiscard]] std::string toString() const { return _stringRepresentation; }
[[nodiscard]] std::string toSparql() const { return _stringRepresentation; }
};
8 changes: 4 additions & 4 deletions src/parser/data/VarOrTerm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ class VarOrTerm : public VarOrTermBase {
using VarOrTermBase::VarOrTermBase;

// ___________________________________________________________________________
[[nodiscard]] std::optional<std::string> toString(const Context& context,
[[nodiscard]] std::optional<std::string> evaluate(const Context& context,
ContextRole role) const {
// TODO<C++23>: remove static_cast as soon as we can visit types that
// inherit from std::variant
return std::visit(
[&](const auto& object) { return object.toString(context, role); },
[&](const auto& object) { return object.evaluate(context, role); },
static_cast<const VarOrTermBase&>(*this));
RobinTF marked this conversation as resolved.
Show resolved Hide resolved
}

// ___________________________________________________________________________
[[nodiscard]] std::string toString() const {
[[nodiscard]] std::string toSparql() const {
// TODO<C++23>: remove static_cast as soon as we can visit types that
// inherit from std::variant
return std::visit([&](const auto& object) { return object.toString(); },
return std::visit([&](const auto& object) { return object.toSparql(); },
static_cast<const VarOrTermBase&>(*this));
}
};