From 43d8260acf79c673d327d871472eb150fe819496 Mon Sep 17 00:00:00 2001 From: Tpt Date: Thu, 31 Dec 2020 22:17:04 +0100 Subject: [PATCH] Makes SPARQL UPDATE parser more strict Does not allow variables in INSERT/DELETE DATA and blank nodes in DELETE --- lib/src/sparql/algebra.rs | 27 +++++++---- lib/src/sparql/parser.rs | 75 +++++++++++++++++++++++-------- lib/src/sparql/update.rs | 94 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 162 insertions(+), 34 deletions(-) diff --git a/lib/src/sparql/algebra.rs b/lib/src/sparql/algebra.rs index b28239a0..39a7e2b3 100644 --- a/lib/src/sparql/algebra.rs +++ b/lib/src/sparql/algebra.rs @@ -1705,9 +1705,9 @@ impl fmt::Display for QueryDataset { #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphUpdateOperation { /// [insert data](https://www.w3.org/TR/sparql11-update/#def_insertdataoperation) - InsertData { data: Vec }, + InsertData { data: Vec }, /// [delete data](https://www.w3.org/TR/sparql11-update/#def_deletedataoperation) - DeleteData { data: Vec }, + DeleteData { data: Vec }, /// [delete insert](https://www.w3.org/TR/sparql11-update/#def_deleteinsertoperation) DeleteInsert { delete: Vec, @@ -1734,16 +1734,12 @@ impl fmt::Display for GraphUpdateOperation { match self { GraphUpdateOperation::InsertData { data } => { writeln!(f, "INSERT DATA {{")?; - for quad in data { - writeln!(f, "\t{}", SparqlQuadPattern(quad))?; - } + write_quads(data, f)?; write!(f, "}}") } GraphUpdateOperation::DeleteData { data } => { writeln!(f, "DELETE DATA {{")?; - for quad in data { - writeln!(f, "\t{}", SparqlQuadPattern(quad))?; - } + write_quads(data, f)?; write!(f, "}}") } GraphUpdateOperation::DeleteInsert { @@ -1823,6 +1819,21 @@ impl fmt::Display for GraphUpdateOperation { } } +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(()) +} + /// A target RDF graph for update operations /// /// Could be a specific graph, all named graphs or the complete dataset. diff --git a/lib/src/sparql/parser.rs b/lib/src/sparql/parser.rs index 456be338..6c54f989 100644 --- a/lib/src/sparql/parser.rs +++ b/lib/src/sparql/parser.rs @@ -1007,26 +1007,34 @@ parser! { } //[39] - rule DeleteData() -> Vec = i("DELETE") _ i("DATA") _ data:QuadData() { - vec![GraphUpdateOperation::DeleteData { data }] + rule DeleteData() -> Vec = i("DELETE") _ i("DATA") _ data:QuadData() {? + if data.iter().any(|quad| quad.subject.is_blank_node() || quad.object.is_blank_node() || quad.graph_name.is_blank_node()) { + Err("Blank nodes are not allowed in DELETE DATA") + } else { + Ok(vec![GraphUpdateOperation::DeleteData { data }]) + } } //[40] - rule DeleteWhere() -> Vec = i("DELETE") _ i("WHERE") _ d:QuadData() { - let pattern = d.iter().map(|q| { - let bgp = GraphPattern::BGP(vec![TriplePattern::new(q.subject.clone(), q.predicate.clone(), q.object.clone())]); - if let Some(graph_name) = &q.graph_name { - GraphPattern::Graph { graph_name: graph_name.clone(), inner: Box::new(bgp) } - } else { - bgp - } - }).fold(GraphPattern::BGP(Vec::new()), new_join); - vec![GraphUpdateOperation::DeleteInsert { - delete: d, - insert: Vec::new(), - using: QueryDataset::default(), - pattern: Box::new(pattern) - }] + rule DeleteWhere() -> Vec = i("DELETE") _ i("WHERE") _ d:QuadPattern() {? + if d.iter().any(|quad| matches!(quad.subject, TermOrVariable::Term(Term::BlankNode(_))) || matches!(quad.object, TermOrVariable::Term(Term::BlankNode(_)))) { + Err("Blank nodes are not allowed in DELETE WHERE") + } else { + let pattern = d.iter().map(|q| { + let bgp = GraphPattern::BGP(vec![TriplePattern::new(q.subject.clone(), q.predicate.clone(), q.object.clone())]); + if let Some(graph_name) = &q.graph_name { + GraphPattern::Graph { graph_name: graph_name.clone(), inner: Box::new(bgp) } + } else { + bgp + } + }).fold(GraphPattern::BGP(Vec::new()), new_join); + Ok(vec![GraphUpdateOperation::DeleteInsert { + delete: d, + insert: Vec::new(), + using: QueryDataset::default(), + pattern: Box::new(pattern) + }]) + } } //[41] @@ -1088,7 +1096,13 @@ parser! { } //[42] - rule DeleteClause() -> Vec = i("DELETE") _ q:QuadPattern() { q } + rule DeleteClause() -> Vec = i("DELETE") _ q:QuadPattern() {? + if q.iter().any(|quad| matches!(quad.subject, TermOrVariable::Term(Term::BlankNode(_))) || matches!(quad.object, TermOrVariable::Term(Term::BlankNode(_)))) { + Err("Blank nodes are not allowed in DELETE") + } else { + Ok(q) + } + } //[43] rule InsertClause() -> Vec = i("INSERT") _ q:QuadPattern() { q } @@ -1122,7 +1136,30 @@ parser! { rule QuadPattern() -> Vec = "{" _ q:Quads() _ "}" { q } //[49] - rule QuadData() -> Vec = "{" _ q:Quads() _ "}" { q } + rule QuadData() -> Vec = "{" _ q:Quads() _ "}" {? + q.into_iter().map(|q| Ok(Quad { + subject: match q.subject { + TermOrVariable::Term(Term::NamedNode(t)) => t.into(), + TermOrVariable::Term(Term::BlankNode(t)) => t.into(), + _ => return Err(()) + }, + predicate: if let NamedNodeOrVariable::NamedNode(t) = q.predicate { + t + } else { + return Err(()) + }, + object: if let TermOrVariable::Term(t) = q.object { + t + } else { + return Err(()) + }, + graph_name: match q.graph_name { + Some(NamedNodeOrVariable::NamedNode(t)) => t.into(), + None => GraphName::DefaultGraph, + _ => return Err(()) + } + })).collect::, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA") + } //[50] rule Quads() -> Vec = q:(Quads_TriplesTemplate() / Quads_QuadsNotTriples()) ** (_) { diff --git a/lib/src/sparql/update.rs b/lib/src/sparql/update.rs index dbd5337e..2b156664 100644 --- a/lib/src/sparql/update.rs +++ b/lib/src/sparql/update.rs @@ -1,6 +1,6 @@ use crate::error::{invalid_data_error, invalid_input_error}; use crate::io::GraphFormat; -use crate::model::{BlankNode, GraphNameRef, NamedNode, Term}; +use crate::model::{BlankNode, GraphNameRef, NamedNode, NamedOrBlankNode, Quad, Term}; use crate::sparql::algebra::{ GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern, QueryDataset, TermOrVariable, @@ -87,19 +87,19 @@ where } } - fn eval_insert_data(&mut self, data: &[QuadPattern]) -> Result<(), EvaluationError> { + fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> { let mut bnodes = HashMap::new(); for quad in data { - if let Some(quad) = self.encode_quad_for_insertion(quad, &[], &[], &mut bnodes)? { + if let Some(quad) = self.encode_quad_for_insertion(quad, &mut bnodes)? { self.write.insert_encoded(&quad).map_err(to_eval_error)?; } } Ok(()) } - fn eval_delete_data(&mut self, data: &[QuadPattern]) -> Result<(), EvaluationError> { + fn eval_delete_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> { for quad in data { - if let Some(quad) = self.encode_quad_for_deletion(quad, &[], &[])? { + if let Some(quad) = self.encode_quad_for_deletion(quad)? { self.write.remove_encoded(&quad).map_err(to_eval_error)?; } } @@ -156,13 +156,15 @@ where .collect::, EvaluationError>>()?; for quad in delete { - if let Some(quad) = self.encode_quad_for_deletion(quad, &variables, &tuple)? { + if let Some(quad) = + self.encode_quad_pattern_for_deletion(quad, &variables, &tuple)? + { self.write.remove_encoded(&quad).map_err(to_eval_error)?; } } for quad in insert { if let Some(quad) = - self.encode_quad_for_insertion(quad, &variables, &tuple, &mut bnodes)? + self.encode_quad_pattern_for_insertion(quad, &variables, &tuple, &mut bnodes)? { self.write.insert_encoded(&quad).map_err(to_eval_error)?; } @@ -277,6 +279,40 @@ where } fn encode_quad_for_insertion( + &mut self, + quad: &Quad, + bnodes: &mut HashMap, + ) -> Result>, EvaluationError> { + Ok(Some(EncodedQuad { + subject: match &quad.subject { + NamedOrBlankNode::NamedNode(subject) => { + self.write.encode_named_node(subject.as_ref()) + } + NamedOrBlankNode::BlankNode(subject) => self + .write + .encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref()), + } + .map_err(to_eval_error)?, + predicate: self + .write + .encode_named_node(quad.predicate.as_ref()) + .map_err(to_eval_error)?, + object: match &quad.object { + Term::NamedNode(object) => self.write.encode_named_node(object.as_ref()), + Term::BlankNode(object) => self + .write + .encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref()), + Term::Literal(object) => self.write.encode_literal(object.as_ref()), + } + .map_err(to_eval_error)?, + graph_name: self + .write + .encode_graph_name(quad.graph_name.as_ref()) + .map_err(to_eval_error)?, + })) + } + + fn encode_quad_pattern_for_insertion( &mut self, quad: &QuadPattern, variables: &[Variable], @@ -388,6 +424,50 @@ where } fn encode_quad_for_deletion( + &mut self, + quad: &Quad, + ) -> Result>, EvaluationError> { + Ok(Some(EncodedQuad { + subject: if let Some(subject) = self + .read + .get_encoded_named_or_blank_node(quad.subject.as_ref()) + .map_err(to_eval_error)? + { + subject + } else { + return Ok(None); + }, + predicate: if let Some(predicate) = self + .read + .get_encoded_named_node(quad.predicate.as_ref()) + .map_err(to_eval_error)? + { + predicate + } else { + return Ok(None); + }, + object: if let Some(object) = self + .read + .get_encoded_term(quad.object.as_ref()) + .map_err(to_eval_error)? + { + object + } else { + return Ok(None); + }, + graph_name: if let Some(graph_name) = self + .read + .get_encoded_graph_name(quad.graph_name.as_ref()) + .map_err(to_eval_error)? + { + graph_name + } else { + return Ok(None); + }, + })) + } + + fn encode_quad_pattern_for_deletion( &self, quad: &QuadPattern, variables: &[Variable],