| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- """
- Code for carrying out Update Operations
- """
- from __future__ import annotations
- from typing import TYPE_CHECKING, Iterator, Mapping, Optional, Sequence
- from rdflib.graph import Graph
- from rdflib.plugins.sparql.evaluate import evalBGP, evalPart
- from rdflib.plugins.sparql.evalutils import _fillTemplate, _join
- from rdflib.plugins.sparql.parserutils import CompValue
- from rdflib.plugins.sparql.sparql import FrozenDict, QueryContext, Update
- from rdflib.term import Identifier, URIRef, Variable
- def _graphOrDefault(ctx: QueryContext, g: str) -> Optional[Graph]:
- if g == "DEFAULT":
- return ctx.graph
- else:
- return ctx.dataset.get_context(g)
- def _graphAll(ctx: QueryContext, g: str) -> Sequence[Graph]:
- """
- return a list of graphs
- """
- if g == "DEFAULT":
- # type error: List item 0 has incompatible type "Optional[Graph]"; expected "Graph"
- return [ctx.graph] # type: ignore[list-item]
- elif g == "NAMED":
- return [
- # type error: Item "None" of "Optional[Graph]" has no attribute "identifier"
- c
- for c in ctx.dataset.contexts()
- if c.identifier != ctx.graph.identifier # type: ignore[union-attr]
- ]
- elif g == "ALL":
- return list(ctx.dataset.contexts())
- else:
- return [ctx.dataset.get_context(g)]
- def evalLoad(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#load
- """
- if TYPE_CHECKING:
- assert isinstance(u.iri, URIRef)
- if u.graphiri:
- ctx.load(u.iri, default=False, into=u.graphiri)
- else:
- ctx.load(u.iri, default=True)
- def evalCreate(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#create
- """
- g = ctx.dataset.get_context(u.graphiri)
- if len(g) > 0:
- raise Exception("Graph %s already exists." % g.identifier)
- raise Exception("Create not implemented!")
- def evalClear(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#clear
- """
- for g in _graphAll(ctx, u.graphiri):
- g.remove((None, None, None))
- def evalDrop(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#drop
- """
- if ctx.dataset.store.graph_aware:
- for g in _graphAll(ctx, u.graphiri):
- ctx.dataset.store.remove_graph(g)
- else:
- evalClear(ctx, u)
- def evalInsertData(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#insertData
- """
- # add triples
- g = ctx.graph
- g += u.triples
- # add quads
- # u.quads is a dict of graphURI=>[triples]
- for g in u.quads:
- # type error: Argument 1 to "get_context" of "ConjunctiveGraph" has incompatible type "Optional[Graph]"; expected "Union[IdentifiedNode, str, None]"
- cg = ctx.dataset.get_context(g) # type: ignore[arg-type]
- cg += u.quads[g]
- def evalDeleteData(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#deleteData
- """
- # remove triples
- g = ctx.graph
- g -= u.triples
- # remove quads
- # u.quads is a dict of graphURI=>[triples]
- for g in u.quads:
- # type error: Argument 1 to "get_context" of "ConjunctiveGraph" has incompatible type "Optional[Graph]"; expected "Union[IdentifiedNode, str, None]"
- cg = ctx.dataset.get_context(g) # type: ignore[arg-type]
- cg -= u.quads[g]
- def evalDeleteWhere(ctx: QueryContext, u: CompValue) -> None:
- """
- http://www.w3.org/TR/sparql11-update/#deleteWhere
- """
- res: Iterator[FrozenDict] = evalBGP(ctx, u.triples)
- for g in u.quads:
- cg = ctx.dataset.get_context(g)
- c = ctx.pushGraph(cg)
- res = _join(res, list(evalBGP(c, u.quads[g])))
- # type error: Incompatible types in assignment (expression has type "FrozenBindings", variable has type "QueryContext")
- for c in res: # type: ignore[assignment]
- g = ctx.graph
- g -= _fillTemplate(u.triples, c)
- for g in u.quads:
- cg = ctx.dataset.get_context(c.get(g))
- cg -= _fillTemplate(u.quads[g], c)
- def evalModify(ctx: QueryContext, u: CompValue) -> None:
- originalctx = ctx
- # Using replaces the dataset for evaluating the where-clause
- dg: Optional[Graph]
- if u.using:
- otherDefault = False
- for d in u.using:
- if d.default:
- if not otherDefault:
- # replace current default graph
- dg = Graph()
- ctx = ctx.pushGraph(dg)
- otherDefault = True
- ctx.load(d.default, default=True)
- elif d.named:
- g = d.named
- ctx.load(g, default=False)
- # "The WITH clause provides a convenience for when an operation
- # primarily refers to a single graph. If a graph name is specified
- # in a WITH clause, then - for the purposes of evaluating the
- # WHERE clause - this will define an RDF Dataset containing a
- # default graph with the specified name, but only in the absence
- # of USING or USING NAMED clauses. In the presence of one or more
- # graphs referred to in USING clauses and/or USING NAMED clauses,
- # the WITH clause will be ignored while evaluating the WHERE
- # clause."
- if not u.using and u.withClause:
- g = ctx.dataset.get_context(u.withClause)
- ctx = ctx.pushGraph(g)
- res = evalPart(ctx, u.where)
- if u.using:
- if otherDefault:
- ctx = originalctx # restore original default graph
- if u.withClause:
- g = ctx.dataset.get_context(u.withClause)
- ctx = ctx.pushGraph(g)
- for c in list(res):
- # TODO: Make this more intentional and without the weird type checking logic
- # once ConjunctiveGraph is removed and Dataset no longer inherits from
- # Graph.
- dg = ctx.graph if type(ctx.graph) is Graph else ctx.dataset.default_context
- if u.delete:
- # type error: Unsupported left operand type for - ("None")
- # type error: Unsupported operand types for - ("Graph" and "Generator[Tuple[Identifier, Identifier, Identifier], None, None]")
- dg -= _fillTemplate(u.delete.triples, c) # type: ignore[operator]
- for g, q in u.delete.quads.items():
- cg = ctx.dataset.get_context(c.get(g))
- cg -= _fillTemplate(q, c)
- if u.insert:
- # type error: Unsupported left operand type for + ("None")
- # type error: Unsupported operand types for + ("Graph" and "Generator[Tuple[Identifier, Identifier, Identifier], None, None]")
- dg += _fillTemplate(u.insert.triples, c) # type: ignore[operator]
- for g, q in u.insert.quads.items():
- cg = ctx.dataset.get_context(c.get(g))
- cg += _fillTemplate(q, c)
- def evalAdd(ctx: QueryContext, u: CompValue) -> None:
- """
- add all triples from src to dst
- http://www.w3.org/TR/sparql11-update/#add
- """
- src, dst = u.graph
- srcg = _graphOrDefault(ctx, src)
- dstg = _graphOrDefault(ctx, dst)
- # type error: Item "None" of "Optional[Graph]" has no attribute "identifier"
- if srcg.identifier == dstg.identifier: # type: ignore[union-attr]
- return
- # type error: Unsupported left operand type for + ("None")
- dstg += srcg # type: ignore[operator]
- def evalMove(ctx: QueryContext, u: CompValue) -> None:
- """
- remove all triples from dst
- add all triples from src to dst
- remove all triples from src
- http://www.w3.org/TR/sparql11-update/#move
- """
- src, dst = u.graph
- srcg = _graphOrDefault(ctx, src)
- dstg = _graphOrDefault(ctx, dst)
- # type error: Item "None" of "Optional[Graph]" has no attribute "identifier"
- if srcg.identifier == dstg.identifier: # type: ignore[union-attr]
- return
- # type error: Item "None" of "Optional[Graph]" has no attribute "remove"
- dstg.remove((None, None, None)) # type: ignore[union-attr]
- # type error: Unsupported left operand type for + ("None")
- dstg += srcg # type: ignore[operator]
- if ctx.dataset.store.graph_aware:
- # type error: Argument 1 to "remove_graph" of "Store" has incompatible type "Optional[Graph]"; expected "Graph"
- ctx.dataset.store.remove_graph(srcg) # type: ignore[arg-type]
- else:
- # type error: Item "None" of "Optional[Graph]" has no attribute "remove"
- srcg.remove((None, None, None)) # type: ignore[union-attr]
- def evalCopy(ctx: QueryContext, u: CompValue) -> None:
- """
- remove all triples from dst
- add all triples from src to dst
- http://www.w3.org/TR/sparql11-update/#copy
- """
- src, dst = u.graph
- srcg = _graphOrDefault(ctx, src)
- dstg = _graphOrDefault(ctx, dst)
- # type error: Item "None" of "Optional[Graph]" has no attribute "remove"
- if srcg.identifier == dstg.identifier: # type: ignore[union-attr]
- return
- # type error: Item "None" of "Optional[Graph]" has no attribute "remove"
- dstg.remove((None, None, None)) # type: ignore[union-attr]
- # type error: Unsupported left operand type for + ("None")
- dstg += srcg # type: ignore[operator]
- def evalUpdate(
- graph: Graph,
- update: Update,
- initBindings: Optional[Mapping[str, Identifier]] = None,
- ) -> None:
- """http://www.w3.org/TR/sparql11-update/#updateLanguage
- 'A request is a sequence of operations [...] Implementations MUST
- ensure that operations of a single request are executed in a
- fashion that guarantees the same effects as executing them in
- lexical order.
- Operations all result either in success or failure.
- If multiple operations are present in a single request, then a
- result of failure from any operation MUST abort the sequence of
- operations, causing the subsequent operations to be ignored.'
- This will return None on success and raise Exceptions on error
- !!! warning "Security Considerations"
- This method can access indirectly requested network endpoints, for
- example, query processing will attempt to access network endpoints
- specified in `SERVICE` directives.
- When processing untrusted or potentially malicious queries, measures
- should be taken to restrict network and file access.
- For information on available security measures, see the RDFLib
- [Security Considerations](../security_considerations.md)
- documentation.
- """
- for u in update.algebra:
- initBindings = dict((Variable(k), v) for k, v in (initBindings or {}).items())
- ctx = QueryContext(graph, initBindings=initBindings)
- ctx.prologue = u.prologue
- try:
- if u.name == "Load":
- evalLoad(ctx, u)
- elif u.name == "Clear":
- evalClear(ctx, u)
- elif u.name == "Drop":
- evalDrop(ctx, u)
- elif u.name == "Create":
- evalCreate(ctx, u)
- elif u.name == "Add":
- evalAdd(ctx, u)
- elif u.name == "Move":
- evalMove(ctx, u)
- elif u.name == "Copy":
- evalCopy(ctx, u)
- elif u.name == "InsertData":
- evalInsertData(ctx, u)
- elif u.name == "DeleteData":
- evalDeleteData(ctx, u)
- elif u.name == "DeleteWhere":
- evalDeleteWhere(ctx, u)
- elif u.name == "Modify":
- evalModify(ctx, u)
- else:
- raise Exception("Unknown update operation: %s" % (u,))
- except: # noqa: E722
- if not u.silent:
- raise
|