Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
oxigraph/lib/spargebra/src/update.rs

345 lines
11 KiB

use crate::algebra::*;
use crate::parser::{parse_update, ParseError};
use crate::term::*;
use oxiri::Iri;
use std::fmt;
use std::str::FromStr;
/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
///
/// ```
/// use spargebra::Update;
///
/// let update_str = "CLEAR ALL ;";
/// let update = Update::parse(update_str, None)?;
/// assert_eq!(update.to_string().trim(), update_str);
/// assert_eq!(update.to_sse(), "(update (clear all))");
/// # Ok::<_, spargebra::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Update {
/// The update base IRI.
pub base_iri: Option<Iri<String>>,
/// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate).
pub operations: Vec<GraphUpdateOperation>,
}
impl Update {
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_update(update, base_iri)
}
/// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
pub fn to_sse(&self) -> String {
let mut buffer = String::new();
self.fmt_sse(&mut buffer).unwrap();
buffer
}
/// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
if let Some(base_iri) = &self.base_iri {
write!(f, "(base <{base_iri}> ")?;
}
write!(f, "(update")?;
for op in &self.operations {
write!(f, " ")?;
op.fmt_sse(f)?;
}
write!(f, ")")?;
if self.base_iri.is_some() {
write!(f, ")")?;
}
Ok(())
}
}
impl fmt::Display for Update {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(base_iri) = &self.base_iri {
writeln!(f, "BASE <{base_iri}>")?;
}
for update in &self.operations {
writeln!(f, "{update} ;")?;
}
Ok(())
}
}
impl FromStr for Update {
type Err = ParseError;
fn from_str(update: &str) -> Result<Self, ParseError> {
Self::parse(update, None)
}
}
impl<'a> TryFrom<&'a str> for Update {
type Error = ParseError;
fn try_from(update: &str) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
impl<'a> TryFrom<&'a String> for Update {
type Error = ParseError;
fn try_from(update: &String) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
/// The [graph update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GraphUpdateOperation {
/// [insert data](https://www.w3.org/TR/sparql11-update/#defn_insertDataOperation).
InsertData { data: Vec<Quad> },
/// [delete data](https://www.w3.org/TR/sparql11-update/#defn_deleteDataOperation).
DeleteData { data: Vec<GroundQuad> },
/// [delete insert](https://www.w3.org/TR/sparql11-update/#defn_deleteInsertOperation).
DeleteInsert {
delete: Vec<GroundQuadPattern>,
insert: Vec<QuadPattern>,
using: Option<QueryDataset>,
pattern: Box<GraphPattern>,
},
/// [load](https://www.w3.org/TR/sparql11-update/#defn_loadOperation).
Load {
silent: bool,
source: NamedNode,
destination: GraphName,
},
/// [clear](https://www.w3.org/TR/sparql11-update/#defn_clearOperation).
Clear { silent: bool, graph: GraphTarget },
/// [create](https://www.w3.org/TR/sparql11-update/#defn_createOperation).
Create { silent: bool, graph: NamedNode },
/// [drop](https://www.w3.org/TR/sparql11-update/#defn_dropOperation).
Drop { silent: bool, graph: GraphTarget },
}
impl GraphUpdateOperation {
/// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
match self {
Self::InsertData { data } => {
write!(f, "(insertData (")?;
for (i, t) in data.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
t.fmt_sse(f)?;
}
write!(f, "))")
}
Self::DeleteData { data } => {
write!(f, "(deleteData (")?;
for (i, t) in data.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
t.fmt_sse(f)?;
}
write!(f, "))")
}
Self::DeleteInsert {
delete,
insert,
using,
pattern,
} => {
write!(f, "(modify ")?;
if let Some(using) = using {
write!(f, " (using ")?;
using.fmt_sse(f)?;
write!(f, " ")?;
pattern.fmt_sse(f)?;
write!(f, ")")?;
} else {
pattern.fmt_sse(f)?;
}
if !delete.is_empty() {
write!(f, " (delete (")?;
for (i, t) in delete.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
t.fmt_sse(f)?;
}
write!(f, "))")?;
}
if !insert.is_empty() {
write!(f, " (insert (")?;
for (i, t) in insert.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
t.fmt_sse(f)?;
}
write!(f, "))")?;
}
write!(f, ")")
}
Self::Load {
silent,
source,
destination,
} => {
write!(f, "(load ")?;
if *silent {
write!(f, "silent ")?;
}
write!(f, "{source} ")?;
destination.fmt_sse(f)?;
write!(f, ")")
}
Self::Clear { silent, graph } => {
write!(f, "(clear ")?;
if *silent {
write!(f, "silent ")?;
}
graph.fmt_sse(f)?;
write!(f, ")")
}
Self::Create { silent, graph } => {
write!(f, "(create ")?;
if *silent {
write!(f, "silent ")?;
}
write!(f, "{graph})")
}
Self::Drop { silent, graph } => {
write!(f, "(drop ")?;
if *silent {
write!(f, "silent ")?;
}
graph.fmt_sse(f)?;
write!(f, ")")
}
}
}
}
impl fmt::Display for GraphUpdateOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsertData { data } => {
writeln!(f, "INSERT DATA {{")?;
write_quads(data, f)?;
write!(f, "}}")
}
Self::DeleteData { data } => {
writeln!(f, "DELETE DATA {{")?;
write_ground_quads(data, f)?;
write!(f, "}}")
}
Self::DeleteInsert {
delete,
insert,
using,
pattern,
} => {
if !delete.is_empty() {
writeln!(f, "DELETE {{")?;
for quad in delete {
writeln!(f, "\t{quad} .")?;
}
writeln!(f, "}}")?;
}
if !insert.is_empty() {
writeln!(f, "INSERT {{")?;
for quad in insert {
writeln!(f, "\t{quad} .")?;
}
writeln!(f, "}}")?;
}
if let Some(using) = using {
for g in &using.default {
writeln!(f, "USING {g}")?;
}
if let Some(named) = &using.named {
for g in named {
writeln!(f, "USING NAMED {g}")?;
}
}
}
write!(
f,
"WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
Self::Load {
silent,
source,
destination,
} => {
write!(f, "LOAD ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{source}")?;
if destination != &GraphName::DefaultGraph {
write!(f, " INTO GRAPH {destination}")?;
}
Ok(())
}
Self::Clear { silent, graph } => {
write!(f, "CLEAR ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{graph}")
}
Self::Create { silent, graph } => {
write!(f, "CREATE ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "GRAPH {graph}")
}
Self::Drop { silent, graph } => {
write!(f, "DROP ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{graph}")
}
}
}
}
fn write_quads(quads: &[Quad], f: &mut fmt::Formatter<'_>) -> fmt::Result {
for quad in quads {
if quad.graph_name == GraphName::DefaultGraph {
writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?;
} else {
writeln!(
f,
"\tGRAPH {} {{ {} {} {} }}",
quad.graph_name, quad.subject, quad.predicate, quad.object
)?;
}
}
Ok(())
}
fn write_ground_quads(quads: &[GroundQuad], f: &mut fmt::Formatter<'_>) -> fmt::Result {
for quad in quads {
if quad.graph_name == GraphName::DefaultGraph {
writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?;
} else {
writeln!(
f,
"\tGRAPH {} {{ {} {} {} }}",
quad.graph_name, quad.subject, quad.predicate, quad.object
)?;
}
}
Ok(())
}