-
Notifications
You must be signed in to change notification settings - Fork 104
Implement more GSP features #2532
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
0594154
0e55ca0
931cfad
de393f0
31aac32
c099ac4
3b83158
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #include "parser/RdfParser.h" | ||
| #include "parser/SparqlParser.h" | ||
| #include "util/http/HttpUtils.h" | ||
| #include "util/http/ResponseMiddleware.h" | ||
| #include "util/http/UrlParser.h" | ||
|
|
||
| // Transform SPARQL Graph Store Protocol requests to their equivalent | ||
|
|
@@ -95,20 +96,50 @@ | |
| Quads::BlankNodeAdder& blankNodeAdder); | ||
| FRIEND_TEST(GraphStoreProtocolTest, convertTriples); | ||
|
|
||
| static ResponseMiddleware makePostNewGraphMiddleware( | ||
| const ad_utility::triple_component::Iri& newGraph); | ||
|
|
||
| // Generates a random graph IRI. The IRI is generated randomly with an | ||
| // internal prefix. NOTE: It is not guaranteed that the IRI does not exist. | ||
| static ad_utility::triple_component::Iri generateGraphIri(); | ||
|
|
||
| // Transform a SPARQL Graph Store Protocol POST to an equivalent ParsedQuery | ||
| // which is an SPARQL Update. | ||
| CPP_template_2(typename RequestT)( | ||
| requires ad_utility::httpUtils::HttpRequest<RequestT>) static ParsedQuery | ||
| transformPost(const RequestT& rawRequest, const GraphOrDefault& graph, | ||
| const Index& index) { | ||
| throwIfRequestBodyEmpty(rawRequest); | ||
| // For a `POST` when the graph identifies the QLever instance itself then | ||
| // the data must be stored in a newly generated graph which is returned in | ||
| // the response. | ||
| // TODO: test this with jena | ||
|
Check warning on line 116 in src/engine/GraphStoreProtocol.h
|
||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO |
||
| bool generateNewGraph = [&rawRequest, &graph]() { | ||
| if (std::holds_alternative<GraphRef>(graph) && | ||
| rawRequest.find(boost::beast::http::field::host) != | ||
| rawRequest.end()) { | ||
| return std::get<GraphRef>(graph) == | ||
| ad_utility::triple_component::Iri::fromIriref( | ||
| "<http://" + | ||
| std::string(rawRequest[boost::beast::http::field::host]) + | ||
| "/" + GSP_DIRECT_GRAPH_IDENTIFICATION_PREFIX + ">"); | ||
| } | ||
| return false; | ||
| }(); | ||
| const GraphOrDefault effectiveGraph = | ||
| generateNewGraph ? generateGraphIri() : graph; | ||
| auto triples = | ||
| parseTriples(rawRequest.body(), extractMediatype(rawRequest)); | ||
| Quads::BlankNodeAdder bn{{}, {}, index.getBlankNodeManager()}; | ||
| auto convertedTriples = convertTriples(graph, std::move(triples), bn); | ||
| updateClause::GraphUpdate up{std::move(convertedTriples), {}}; | ||
| ParsedQuery res; | ||
| res._clause = parsedQuery::UpdateClause{std::move(up)}; | ||
| parsedQuery::UpdateClause clause{std::move(up)}; | ||
| if (generateNewGraph) { | ||
| res.responseMiddleware_ = makePostNewGraphMiddleware( | ||
| std::get<ad_utility::triple_component::Iri>(effectiveGraph)); | ||
| } | ||
| res._clause = std::move(clause); | ||
| // Graph store protocol POST requests might have a very large body. Limit | ||
| // the length used for the string representation. | ||
| res._originalString = truncatedStringRepresentation("POST", rawRequest); | ||
|
|
@@ -136,11 +167,16 @@ | |
| } | ||
|
|
||
| // Transform a SPARQL Graph Store Protocol GET to an equivalent ParsedQuery | ||
| // which is an SPARQL Query. | ||
| // which is a SPARQL Query. | ||
| static ParsedQuery transformGet(const GraphOrDefault& graph, | ||
| const EncodedIriManager* encodedIriManager); | ||
| FRIEND_TEST(GraphStoreProtocolTest, transformGet); | ||
|
|
||
| // Transform a SPARQL Graph Store Protocol HEAD to an equivalent ParsedQuery. | ||
| // The response is the same as for GET but without the body. | ||
| static ParsedQuery transformHead(const GraphOrDefault& graph, | ||
| const EncodedIriManager* encodedIriManager); | ||
|
|
||
| // Transform a SPARQL Graph Store Protocol PUT to equivalent ParsedQueries | ||
| // which are SPARQL Updates. | ||
| CPP_template_2(typename RequestT)( | ||
|
|
@@ -178,6 +214,23 @@ | |
| auto convertedTriples = convertTriples(graph, std::move(triples), bn); | ||
| updateClause::GraphUpdate up{std::move(convertedTriples), {}}; | ||
| ParsedQuery insertData; | ||
| // Interpretation of the very vague GSP 5.3: | ||
| // - 201 Created if a new graph is created | ||
| // - 200 Ok or 204 No Content if an existing graph is modified | ||
| // When the drop (first operation) deletes triples then the graph has | ||
| // existed before in our model of implicit graph existence. | ||
| drop.responseMiddleware_ = | ||
| ResponseMiddleware([](ResponseMiddleware::ResponseT&& response, | ||
|
Check warning on line 223 in src/engine/GraphStoreProtocol.h
|
||
| std::vector<UpdateMetadata> updateMetadata) { | ||
| AD_CORRECTNESS_CHECK(updateMetadata.size() == 2 && | ||
| updateMetadata.at(0).inUpdate_.has_value()); | ||
| if (updateMetadata.at(0).inUpdate_.value().triplesDeleted_ > 0) { | ||
| response.result(boost::beast::http::status::ok); | ||
| } else { | ||
| response.result(boost::beast::http::status::created); | ||
| } | ||
| return response; | ||
| }); | ||
| insertData._clause = parsedQuery::UpdateClause{std::move(up)}; | ||
| insertData._originalString = stringRepresentation; | ||
| return {std::move(drop), std::move(insertData)}; | ||
|
|
@@ -217,7 +270,7 @@ | |
| // DATA` of the payload. | ||
| return {transformTsop(rawRequest, operation.graph_, index)}; | ||
| } else if (method == "HEAD") { | ||
| throwNotYetImplementedHTTPMethod("HEAD"); | ||
| return {transformHead(operation.graph_, &index.encodedIriManager())}; | ||
| } else if (method == "PATCH") { | ||
| throwNotYetImplementedHTTPMethod("PATCH"); | ||
| } else { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Copyright 2025 The QLever Authors, in particular: | ||
| // | ||
| // 2025 Julian Mundhahs <[email protected]>, UFR | ||
| // | ||
| // UFR = University of Freiburg, Chair of Algorithms and Data Structures | ||
|
|
||
|
|
||
| #ifndef QLEVER_SRC_ENGINE_UPDATETYPES_H | ||
| #define QLEVER_SRC_ENGINE_UPDATETYPES_H | ||
|
|
||
| #include "index/DeltaTriples.h" | ||
|
|
||
| // Metadata of a single update operation: number of inserted and deleted triples | ||
| // before the operation, of the operation, and after the operation. | ||
| struct UpdateMetadata { | ||
| std::optional<DeltaTriplesCount> countBefore_; | ||
| std::optional<DeltaTriplesCount> inUpdate_; | ||
| std::optional<DeltaTriplesCount> countAfter_; | ||
| }; | ||
|
|
||
| #endif //QLEVER_SRC_ENGINE_UPDATETYPES_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this might (though probably more in theory than in practice) generate the same graph twice making it incorrect. Keeping tracks of all graphs and/or explicit graph existence would go beyond the scope of this PR. As an intermediate solution the graphs will be generated with the internal prefix and a counter.