Makes SPARQL UPDATE parser more strict

Does not allow variables in INSERT/DELETE DATA and blank nodes in DELETE
pull/71/head
Tpt 4 years ago
parent 700e47af1e
commit 43d8260acf
  1. 27
      lib/src/sparql/algebra.rs
  2. 51
      lib/src/sparql/parser.rs
  3. 94
      lib/src/sparql/update.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<QuadPattern> },
InsertData { data: Vec<Quad> },
/// [delete data](https://www.w3.org/TR/sparql11-update/#def_deletedataoperation)
DeleteData { data: Vec<QuadPattern> },
DeleteData { data: Vec<Quad> },
/// [delete insert](https://www.w3.org/TR/sparql11-update/#def_deleteinsertoperation)
DeleteInsert {
delete: Vec<QuadPattern>,
@ -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.

@ -1007,12 +1007,19 @@ parser! {
}
//[39]
rule DeleteData() -> Vec<GraphUpdateOperation> = i("DELETE") _ i("DATA") _ data:QuadData() {
vec![GraphUpdateOperation::DeleteData { data }]
rule DeleteData() -> Vec<GraphUpdateOperation> = 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<GraphUpdateOperation> = i("DELETE") _ i("WHERE") _ d:QuadData() {
rule DeleteWhere() -> Vec<GraphUpdateOperation> = 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 {
@ -1021,12 +1028,13 @@ parser! {
bgp
}
}).fold(GraphPattern::BGP(Vec::new()), new_join);
vec![GraphUpdateOperation::DeleteInsert {
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<QuadPattern> = i("DELETE") _ q:QuadPattern() { q }
rule DeleteClause() -> Vec<QuadPattern> = 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<QuadPattern> = i("INSERT") _ q:QuadPattern() { q }
@ -1122,7 +1136,30 @@ parser! {
rule QuadPattern() -> Vec<QuadPattern> = "{" _ q:Quads() _ "}" { q }
//[49]
rule QuadData() -> Vec<QuadPattern> = "{" _ q:Quads() _ "}" { q }
rule QuadData() -> Vec<Quad> = "{" _ 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::<Result<Vec<_>, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA")
}
//[50]
rule Quads() -> Vec<QuadPattern> = q:(Quads_TriplesTemplate() / Quads_QuadsNotTriples()) ** (_) {

@ -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::<Result<Vec<_>, 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<BlankNode, BlankNode>,
) -> Result<Option<EncodedQuad<R::StrId>>, 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<Option<EncodedQuad<R::StrId>>, 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],

Loading…
Cancel
Save