Skip to content

SPARQL UPDATE ​

pgrdf.sparql() also runs SPARQL 1.1 UPDATE requests. An update runs inside your transaction and returns one summary row.

Examples use the sample data, which lives in the graph http://example.org/people. To try them without keeping the changes, run them between BEGIN and ROLLBACK.

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX ex:   <http://example.org/>
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  INSERT DATA { ex:dana foaf:name "Dana" . ex:dana ex:age 52 . }
$$);
--  {"_update": {"form": "INSERT_DATA", "elapsed_ms": 0.42, "graphs_touched": ["DEFAULT"],
--               "triples_deleted": 0, "triples_inserted": 2}}
KeyMeaning
formINSERT_DATA, DELETE_DATA, INSERT_WHERE, DELETE_WHERE, DELETE_INSERT_WHERE, CREATE, CLEAR or DROP; MIXED when one request holds several operations separated by ;
triples_inserted, triples_deletedHow many triples changed.
graphs_touchedThe graphs written to, by IRI; "DEFAULT" is the default graph.
elapsed_msTime taken, in milliseconds.

Which graph an update reads and writes ​

Learn this rule before writing updates:

  • The WHERE part matches across all graphs, like a query, unless you scope it with WITH or GRAPH.
  • A template — INSERT { … }, DELETE { … }, INSERT DATA { … } — writes to (or deletes from) the default graph, unless it names a graph with GRAPH <iri> { … } or the update starts with WITH <iri>.

So an unscoped update over data that lives in a named graph does not do what it seems to:

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  DELETE { ?s foaf:mbox ?m }
  INSERT { ?s foaf:email ?m }
  WHERE  { ?s foaf:mbox ?m }
$$);
--  {"_update": {"form": "DELETE_INSERT_WHERE", ..., "graphs_touched": ["DEFAULT"],
--               "triples_deleted": 0, "triples_inserted": 2}}

The WHERE found both mailboxes in the people graph, but the DELETE looked for them in the default graph (nothing deleted) and the INSERT put two new triples there. The WITH version below does the rename properly.

INSERT DATA and DELETE DATA ​

Add or remove specific triples. Wrap them in GRAPH <iri> { … } to target a named graph:

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX ex:   <http://example.org/>
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  INSERT DATA {
    GRAPH <http://example.org/people> {
      ex:erin a foaf:Person ; foaf:name "Erin" .
    }
  }
$$);
--  {"_update": {"form": "INSERT_DATA", ..., "graphs_touched": ["http://example.org/people"],
--               "triples_deleted": 0, "triples_inserted": 2}}

SELECT * FROM pgrdf.sparql($$
  PREFIX ex: <http://example.org/>
  DELETE DATA { GRAPH <http://example.org/people> { ex:bob ex:age 41 } }
$$);
  • If the named graph doesn't exist yet, INSERT DATA creates it.
  • Deleting a triple that isn't there is not an error; it deletes nothing.

INSERT … WHERE and DELETE … WHERE ​

For every solution of the WHERE pattern, fill in the template and insert or delete the result. WITH <iri> scopes both parts to one graph:

sql
-- Everyone who is a foaf:Person is also an ex:Agent.
SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  PREFIX ex:   <http://example.org/>
  WITH <http://example.org/people>
  INSERT { ?p a ex:Agent }
  WHERE  { ?p a foaf:Person }
$$);
--  {"_update": {"form": "INSERT_WHERE", ..., "graphs_touched": ["http://example.org/people"],
--               "triples_deleted": 0, "triples_inserted": 3}}

-- Remove ages over 40.
SELECT * FROM pgrdf.sparql($$
  PREFIX ex: <http://example.org/>
  WITH <http://example.org/people>
  DELETE { ?s ex:age ?a }
  WHERE  { ?s ex:age ?a FILTER(?a > 40) }
$$);
--  {"_update": {"form": "DELETE_WHERE", ..., "triples_deleted": 1, "triples_inserted": 0}}

DELETE WHERE { … } is the shorthand for when the template equals the pattern. It takes GRAPH rather than WITH:

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX ex: <http://example.org/>
  DELETE WHERE { GRAPH <http://example.org/people> { ?p a ex:Agent } }
$$);
--  {"_update": {"form": "DELETE_WHERE", ..., "triples_deleted": 3, "triples_inserted": 0}}

DELETE … INSERT … WHERE ​

Delete and insert in one operation, for example to rename a property:

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  WITH <http://example.org/people>
  DELETE { ?s foaf:mbox  ?m }
  INSERT { ?s foaf:email ?m }
  WHERE  { ?s foaf:mbox  ?m }
$$);
--  {"_update": {"form": "DELETE_INSERT_WHERE", ..., "graphs_touched": ["http://example.org/people"],
--               "triples_deleted": 2, "triples_inserted": 2}}

SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  SELECT ?name ?email
  WHERE { GRAPH <http://example.org/people> { ?s foaf:name ?name ; foaf:email ?email } }
  ORDER BY ?name
$$);
--  {"name": "Alice", "email": "mailto:alice@example.org"}
--  {"name": "Carol", "email": "mailto:carol@example.org"}

Copying between graphs ​

Read from one graph and write to another. Unlike copy_graph, you can filter what gets copied:

sql
SELECT pgrdf.add_graph('http://example.org/adults');

SELECT * FROM pgrdf.sparql($$
  PREFIX ex: <http://example.org/>
  INSERT { GRAPH <http://example.org/adults> { ?s ex:age ?a } }
  WHERE  { GRAPH <http://example.org/people> { ?s ex:age ?a FILTER(?a >= 30) } }
$$);
--  {"_update": {"form": "INSERT_WHERE", ..., "graphs_touched": ["http://example.org/adults"],
--               "triples_deleted": 0, "triples_inserted": 2}}

A target graph that doesn't exist is created. USING <iri> before WHERE restricts the WHERE part to one graph without changing where the template writes.

Creating, clearing and dropping graphs ​

OperationEffect
CREATE GRAPH <iri>Create an empty graph. Refused if it already exists.
CLEAR GRAPH <iri>Remove the graph's triples; keep the graph.
DROP GRAPH <iri>Remove the graph and its triples.
CLEAR DEFAULTRemove the default graph's triples.
CLEAR NAMEDRemove the triples of every named graph.
DROP ALLRemove every named graph and empty the default graph.
SILENTAdd after the keyword (DROP SILENT GRAPH …, CREATE SILENT GRAPH …) to skip the error when the graph is missing or already exists.
sql
SELECT * FROM pgrdf.sparql('CREATE GRAPH <http://example.org/v3>');
SELECT * FROM pgrdf.sparql('DROP SILENT GRAPH <http://example.org/stale>');

Without SILENT, creating an existing graph or clearing or dropping a missing one is refused with an error naming the graph (graph already exists, graph not bound). The same operations are available as SQL functions; see Managing graphs.

Transactions ​

Updates run in your transaction, so ROLLBACK undoes them:

sql
BEGIN;
SELECT * FROM pgrdf.sparql($$ PREFIX ex: <http://example.org/> INSERT DATA { ex:zed ex:age 99 } $$);
SELECT * FROM pgrdf.sparql($$ PREFIX ex: <http://example.org/> ASK { ex:zed ex:age 99 } $$);
--  {"_ask": "true"}
ROLLBACK;

SELECT * FROM pgrdf.sparql($$ PREFIX ex: <http://example.org/> ASK { ex:zed ex:age 99 } $$);
--  {"_ask": "false"}

If any statement between BEGIN and COMMIT fails, none of the changes are kept.

Locked graphs ​

An update that would write to a locked graph is refused with SQLSTATE 55P03, and the message says how to unlock it:

sql
SELECT pgrdf.lock_graph(pgrdf.graph_id('http://example.org/people'), 'release review');

SELECT * FROM pgrdf.sparql($$
  PREFIX ex: <http://example.org/>
  INSERT DATA { GRAPH <http://example.org/people> { ex:x ex:y ex:z } }
$$);
-- ERROR:  55P03: pgrdf: graph 1 is locked (release review): SPARQL UPDATE refused.
--         Unlock with pgrdf.unlock_graph(1, '<reason>').

This covers CLEAR GRAPH and DROP GRAPH too. Updates that only touch other graphs are not affected.

Not supported ​

See also ​

pgRDF is released under the MIT license. Documentation built with VitePress, served via GitHub Pages.