#[allow(unused_imports)]
use crate::algebra::{Algebra, EvaluationContext, Term, TriplePattern, Variable};
use crate::executor::ExecutionContext;
use oxirs_core::model::{BlankNode, GraphName, Literal as CoreLiteral, NamedNode, Quad};
use oxirs_core::OxirsError;
use oxirs_core::Store;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum UpdateOperation {
InsertData { data: Vec<QuadPattern> },
DeleteData { data: Vec<QuadPattern> },
DeleteWhere { pattern: Box<Algebra> },
InsertWhere {
pattern: Box<Algebra>,
template: Vec<QuadPattern>,
},
DeleteInsertWhere {
delete_template: Vec<QuadPattern>,
insert_template: Vec<QuadPattern>,
pattern: Box<Algebra>,
using: Option<Vec<GraphReference>>,
},
Clear { target: GraphTarget, silent: bool },
Drop { target: GraphTarget, silent: bool },
Create { graph: GraphReference, silent: bool },
Copy {
from: GraphTarget,
to: GraphTarget,
silent: bool,
},
Move {
from: GraphTarget,
to: GraphTarget,
silent: bool,
},
Add {
from: GraphTarget,
to: GraphTarget,
silent: bool,
},
Load {
source: String,
graph: Option<GraphReference>,
silent: bool,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct QuadPattern {
pub subject: Term,
pub predicate: Term,
pub object: Term,
pub graph: Option<GraphReference>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GraphReference {
Iri(String),
Default,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GraphTarget {
Graph(GraphReference),
All,
Named,
Default,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateResult {
pub inserted: usize,
pub deleted: usize,
pub graphs_created: Vec<String>,
pub graphs_dropped: Vec<String>,
}
pub struct UpdateExecutor<'a> {
store: &'a mut dyn Store,
#[allow(dead_code)]
context: ExecutionContext,
transaction_mode: bool,
batch_size: usize,
stats: UpdateStatistics,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateStatistics {
pub total_operations: usize,
pub total_execution_time: std::time::Duration,
pub operations_per_second: f64,
pub memory_usage: usize,
pub batch_count: usize,
}
impl<'a> UpdateExecutor<'a> {
pub fn new(store: &'a mut dyn Store) -> Self {
UpdateExecutor {
store,
context: ExecutionContext::default(),
transaction_mode: false,
batch_size: 10000, stats: UpdateStatistics::default(),
}
}
pub fn with_transaction(store: &'a mut dyn Store) -> Self {
UpdateExecutor {
store,
context: ExecutionContext::default(),
transaction_mode: true,
batch_size: 10000,
stats: UpdateStatistics::default(),
}
}
pub fn with_batch_size(mut self, batch_size: usize) -> Self {
self.batch_size = batch_size.max(1);
self
}
pub fn statistics(&self) -> &UpdateStatistics {
&self.stats
}
pub fn reset_statistics(&mut self) {
self.stats = UpdateStatistics::default();
}
pub fn execute(&mut self, operation: &UpdateOperation) -> Result<UpdateResult, OxirsError> {
let start_time = std::time::Instant::now();
self.stats.total_operations += 1;
if self.transaction_mode {
}
let result = match operation {
UpdateOperation::InsertData { data } => self.execute_insert_data_enhanced(data),
UpdateOperation::DeleteData { data } => self.execute_delete_data_enhanced(data),
UpdateOperation::DeleteWhere { pattern } => self.execute_delete_where(pattern),
UpdateOperation::InsertWhere { pattern, template } => {
self.execute_insert_where(pattern, template)
}
UpdateOperation::DeleteInsertWhere {
delete_template,
insert_template,
pattern,
using,
} => self.execute_delete_insert_where(delete_template, insert_template, pattern, using),
UpdateOperation::Clear { target, silent } => self.execute_clear(target, *silent),
UpdateOperation::Drop { target, silent } => self.execute_drop(target, *silent),
UpdateOperation::Create { graph, silent } => self.execute_create(graph, *silent),
UpdateOperation::Copy { from, to, silent } => self.execute_copy(from, to, *silent),
UpdateOperation::Move { from, to, silent } => self.execute_move(from, to, *silent),
UpdateOperation::Add { from, to, silent } => self.execute_add(from, to, *silent),
UpdateOperation::Load {
source,
graph,
silent,
} => self.execute_load(source, graph.as_ref(), *silent),
};
match &result {
Ok(_) => {
if self.transaction_mode {
}
}
Err(_) => {
if self.transaction_mode {
}
}
}
let execution_time = start_time.elapsed();
self.stats.total_execution_time += execution_time;
self.stats.operations_per_second =
self.stats.total_operations as f64 / self.stats.total_execution_time.as_secs_f64();
result
}
#[allow(dead_code)]
fn execute_insert_data(&mut self, data: &[QuadPattern]) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
for pattern in data {
let quad = self.pattern_to_quad(pattern)?;
self.store.insert(&quad)?;
result.inserted += 1;
}
Ok(result)
}
fn execute_insert_data_enhanced(
&mut self,
data: &[QuadPattern],
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
if data.is_empty() {
return Ok(result);
}
if data.len() > self.batch_size {
for chunk in data.chunks(self.batch_size) {
let batch_result = self.execute_insert_data_batch(chunk)?;
result.inserted += batch_result.inserted;
self.stats.batch_count += 1;
}
} else {
for pattern in data {
self.validate_quad_pattern(pattern)?;
let quad = self.pattern_to_quad(pattern)?;
self.store.insert(&quad)?;
result.inserted += 1;
}
}
Ok(result)
}
fn execute_insert_data_batch(
&mut self,
batch: &[QuadPattern],
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let mut quads_to_insert = Vec::with_capacity(batch.len());
for pattern in batch {
self.validate_quad_pattern(pattern)?;
let quad = self.pattern_to_quad(pattern)?;
quads_to_insert.push(quad);
}
for quad in quads_to_insert {
self.store.insert(&quad)?;
result.inserted += 1;
}
Ok(result)
}
fn validate_quad_pattern(&self, pattern: &QuadPattern) -> Result<(), OxirsError> {
if matches!(pattern.subject, Term::Variable(_)) {
return Err(OxirsError::Query(
"Variables not allowed in INSERT DATA subject".to_string(),
));
}
if matches!(pattern.predicate, Term::Variable(_)) {
return Err(OxirsError::Query(
"Variables not allowed in INSERT DATA predicate".to_string(),
));
}
if matches!(pattern.object, Term::Variable(_)) {
return Err(OxirsError::Query(
"Variables not allowed in INSERT DATA object".to_string(),
));
}
if let Term::Iri(iri) = &pattern.subject {
if iri.as_str().is_empty() {
return Err(OxirsError::Query("Empty IRI in subject".to_string()));
}
}
if let Term::Iri(iri) = &pattern.predicate {
if iri.as_str().is_empty() {
return Err(OxirsError::Query("Empty IRI in predicate".to_string()));
}
}
if let Term::Iri(iri) = &pattern.object {
if iri.as_str().is_empty() {
return Err(OxirsError::Query("Empty IRI in object".to_string()));
}
}
Ok(())
}
#[allow(dead_code)]
fn execute_delete_data(&mut self, data: &[QuadPattern]) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
for pattern in data {
let quad = self.pattern_to_quad(pattern)?;
if self.store.remove(&quad)? {
result.deleted += 1;
}
}
Ok(result)
}
fn execute_delete_data_enhanced(
&mut self,
data: &[QuadPattern],
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
if data.is_empty() {
return Ok(result);
}
if data.len() > self.batch_size {
for chunk in data.chunks(self.batch_size) {
let batch_result = self.execute_delete_data_batch(chunk)?;
result.deleted += batch_result.deleted;
self.stats.batch_count += 1;
}
} else {
for pattern in data {
self.validate_quad_pattern(pattern)?;
let quad = self.pattern_to_quad(pattern)?;
if self.store.remove(&quad)? {
result.deleted += 1;
}
}
}
Ok(result)
}
fn execute_delete_data_batch(
&mut self,
batch: &[QuadPattern],
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let mut quads_to_delete = Vec::with_capacity(batch.len());
for pattern in batch {
self.validate_quad_pattern(pattern)?;
let quad = self.pattern_to_quad(pattern)?;
quads_to_delete.push(quad);
}
for quad in quads_to_delete {
if self.store.remove(&quad)? {
result.deleted += 1;
}
}
Ok(result)
}
fn execute_delete_where(&mut self, pattern: &Algebra) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let bindings = self.evaluate_pattern(pattern)?;
if bindings.is_empty() {
return Ok(result); }
let mut quads_to_delete = Vec::new();
for binding in &bindings {
let quads = self.apply_binding_to_pattern(pattern, binding)?;
quads_to_delete.extend(quads);
}
quads_to_delete.sort();
quads_to_delete.dedup();
if quads_to_delete.len() > self.batch_size {
for chunk in quads_to_delete.chunks(self.batch_size) {
for quad in chunk {
if self.store.remove(quad)? {
result.deleted += 1;
}
}
self.stats.batch_count += 1;
}
} else {
for quad in quads_to_delete {
if self.store.remove(&quad)? {
result.deleted += 1;
}
}
}
Ok(result)
}
fn execute_insert_where(
&mut self,
pattern: &Algebra,
template: &[QuadPattern],
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
if template.is_empty() {
return Ok(result); }
let bindings = self.evaluate_pattern(pattern)?;
if bindings.is_empty() {
return Ok(result); }
let mut quads_to_insert = Vec::new();
for binding in &bindings {
for quad_pattern in template {
match self.instantiate_template(quad_pattern, binding) {
Ok(quad) => quads_to_insert.push(quad),
Err(e) => {
eprintln!("Warning: Failed to instantiate template: {e}");
}
}
}
}
quads_to_insert.sort();
quads_to_insert.dedup();
if quads_to_insert.len() > self.batch_size {
for chunk in quads_to_insert.chunks(self.batch_size) {
for quad in chunk {
self.store.insert(quad)?;
result.inserted += 1;
}
self.stats.batch_count += 1;
}
} else {
for quad in quads_to_insert {
self.store.insert(&quad)?;
result.inserted += 1;
}
}
Ok(result)
}
fn execute_delete_insert_where(
&mut self,
delete_template: &[QuadPattern],
insert_template: &[QuadPattern],
pattern: &Algebra,
_using: &Option<Vec<GraphReference>>,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
if delete_template.is_empty() && insert_template.is_empty() {
return Ok(result); }
let bindings = self.evaluate_pattern(pattern)?;
if bindings.is_empty() {
return Ok(result); }
let mut quads_to_delete = Vec::new();
if !delete_template.is_empty() {
for binding in &bindings {
for quad_pattern in delete_template {
match self.instantiate_template(quad_pattern, binding) {
Ok(quad) => quads_to_delete.push(quad),
Err(e) => {
eprintln!("Warning: Failed to instantiate delete template: {e}");
}
}
}
}
quads_to_delete.sort();
quads_to_delete.dedup();
}
let mut quads_to_insert = Vec::new();
if !insert_template.is_empty() {
for binding in &bindings {
for quad_pattern in insert_template {
match self.instantiate_template(quad_pattern, binding) {
Ok(quad) => quads_to_insert.push(quad),
Err(e) => {
eprintln!("Warning: Failed to instantiate insert template: {e}");
}
}
}
}
quads_to_insert.sort();
quads_to_insert.dedup();
}
if !quads_to_delete.is_empty() {
if quads_to_delete.len() > self.batch_size {
for chunk in quads_to_delete.chunks(self.batch_size) {
for quad in chunk {
if self.store.remove(quad)? {
result.deleted += 1;
}
}
self.stats.batch_count += 1;
}
} else {
for quad in quads_to_delete {
if self.store.remove(&quad)? {
result.deleted += 1;
}
}
}
}
if !quads_to_insert.is_empty() {
if quads_to_insert.len() > self.batch_size {
for chunk in quads_to_insert.chunks(self.batch_size) {
for quad in chunk {
self.store.insert(quad)?;
result.inserted += 1;
}
self.stats.batch_count += 1;
}
} else {
for quad in quads_to_insert {
self.store.insert(&quad)?;
result.inserted += 1;
}
}
}
Ok(result)
}
fn execute_clear(
&mut self,
target: &GraphTarget,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
match target {
GraphTarget::All => {
result.deleted = self.store.clear_all()?;
}
GraphTarget::Named => {
result.deleted = self.store.clear_named_graphs()?;
}
GraphTarget::Default => {
result.deleted = self.store.clear_default_graph()?;
}
GraphTarget::Graph(graph_ref) => {
let graph_name = self.graph_ref_to_named_node(graph_ref)?;
match self
.store
.clear_graph(Some(&GraphName::NamedNode(graph_name)))
{
Ok(count) => result.deleted = count,
Err(e) if !silent => return Err(e),
_ => {} }
}
}
Ok(result)
}
fn execute_drop(
&mut self,
target: &GraphTarget,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
match target {
GraphTarget::All => {
let graphs = self.store.graphs()?;
for graph in graphs {
self.store
.drop_graph(Some(&GraphName::NamedNode(graph.clone())))?;
result.graphs_dropped.push(graph.as_str().to_string());
}
}
GraphTarget::Named => {
let graphs = self.store.named_graphs()?;
for graph in graphs {
self.store
.drop_graph(Some(&GraphName::NamedNode(graph.clone())))?;
result.graphs_dropped.push(graph.as_str().to_string());
}
}
GraphTarget::Default => {
if !silent {
return Err(OxirsError::Query("Cannot DROP DEFAULT graph".to_string()));
}
}
GraphTarget::Graph(graph_ref) => {
let graph_name = self.graph_ref_to_named_node(graph_ref)?;
match self
.store
.drop_graph(Some(&GraphName::NamedNode(graph_name.clone())))
{
Ok(_) => result.graphs_dropped.push(graph_name.as_str().to_string()),
Err(e) if !silent => return Err(e),
_ => {} }
}
}
Ok(result)
}
fn execute_create(
&mut self,
graph: &GraphReference,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let graph_name = self.graph_ref_to_named_node(graph)?;
match self.store.create_graph(Some(&graph_name)) {
Ok(_) => {
result.graphs_created.push(graph_name.as_str().to_string());
}
Err(e) => {
if !silent {
return Err(e);
}
}
}
Ok(result)
}
fn execute_copy(
&mut self,
from: &GraphTarget,
to: &GraphTarget,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
self.execute_clear(to, silent)?;
self.execute_add(from, to, silent)
}
fn execute_move(
&mut self,
from: &GraphTarget,
to: &GraphTarget,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
let result = self.execute_copy(from, to, silent)?;
self.execute_clear(from, silent)?;
Ok(result)
}
fn execute_add(
&mut self,
from: &GraphTarget,
to: &GraphTarget,
_silent: bool,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let source_quads = self.get_quads_from_target(from)?;
let target_graph = match to {
GraphTarget::Graph(g) => Some(self.graph_ref_to_named_node(g)?),
GraphTarget::Default => None,
_ => {
return Err(OxirsError::Query(
"Invalid target for ADD operation".to_string(),
))
}
};
for quad in source_quads {
let new_quad = if let Some(ref target) = target_graph {
Quad::new(
quad.subject().clone(),
quad.predicate().clone(),
quad.object().clone(),
GraphName::NamedNode(target.clone()),
)
} else {
Quad::new(
quad.subject().clone(),
quad.predicate().clone(),
quad.object().clone(),
GraphName::DefaultGraph,
)
};
self.store.insert(&new_quad)?;
result.inserted += 1;
}
Ok(result)
}
fn execute_load(
&mut self,
source: &str,
graph: Option<&GraphReference>,
silent: bool,
) -> Result<UpdateResult, OxirsError> {
let mut result = UpdateResult::default();
let target_graph = graph.map(|g| self.graph_ref_to_named_node(g)).transpose()?;
match self.store.load_from_url(source, target_graph.as_ref()) {
Ok(count) => {
result.inserted = count;
}
Err(e) if !silent => return Err(e),
_ => {} }
Ok(result)
}
fn pattern_to_quad(&self, pattern: &QuadPattern) -> Result<Quad, OxirsError> {
let subject = self.term_to_subject(&pattern.subject)?;
let predicate = self.term_to_predicate(&pattern.predicate)?;
let object = self.term_to_object(&pattern.object)?;
let graph_name = pattern
.graph
.as_ref()
.map(|g| self.graph_ref_to_named_node(g))
.transpose()?
.map(GraphName::NamedNode)
.unwrap_or(GraphName::DefaultGraph);
Ok(Quad::new(subject, predicate, object, graph_name))
}
fn term_to_subject(&self, term: &Term) -> Result<oxirs_core::model::Subject, OxirsError> {
match term {
Term::Iri(iri) => Ok(NamedNode::new(iri.as_str())?.into()),
Term::BlankNode(id) => Ok(BlankNode::new(id)?.into()),
Term::Variable(_) => Err(OxirsError::Query(
"Variables not allowed in concrete data".to_string(),
)),
Term::Literal(_) => Err(OxirsError::Query(
"Literals cannot be used as subjects".to_string(),
)),
Term::QuotedTriple(_) => Err(OxirsError::Query(
"Quoted triples not yet supported as subjects in concrete data".to_string(),
)),
Term::PropertyPath(_) => Err(OxirsError::Query(
"Property paths not allowed as subjects in concrete data".to_string(),
)),
}
}
fn term_to_predicate(&self, term: &Term) -> Result<NamedNode, OxirsError> {
match term {
Term::Iri(iri) => NamedNode::new(iri.as_str()),
Term::Variable(_) => Err(OxirsError::Query(
"Variables not allowed in concrete data".to_string(),
)),
Term::BlankNode(_) => Err(OxirsError::Query(
"Blank nodes cannot be used as predicates in most RDF contexts".to_string(),
)),
Term::Literal(_) => Err(OxirsError::Query(
"Literals cannot be used as predicates".to_string(),
)),
Term::QuotedTriple(_) => Err(OxirsError::Query(
"Quoted triples not supported as predicates in concrete data".to_string(),
)),
Term::PropertyPath(_) => Err(OxirsError::Query(
"Property paths not allowed as predicates in concrete data".to_string(),
)),
}
}
fn term_to_object(&self, term: &Term) -> Result<oxirs_core::model::Object, OxirsError> {
match term {
Term::Iri(iri) => Ok(NamedNode::new(iri.as_str())?.into()),
Term::BlankNode(id) => Ok(BlankNode::new(id)?.into()),
Term::Literal(lit) => {
let literal = if let Some(lang) = &lit.language {
CoreLiteral::new_language_tagged_literal(&lit.value, lang)?
} else if let Some(dt) = &lit.datatype {
CoreLiteral::new_typed(&lit.value, dt.clone())
} else {
CoreLiteral::new(&lit.value)
};
Ok(literal.into())
}
Term::Variable(_) => Err(OxirsError::Query(
"Variables not allowed in concrete data".to_string(),
)),
Term::QuotedTriple(_) => Err(OxirsError::Query(
"Quoted triples not yet supported in concrete data".to_string(),
)),
Term::PropertyPath(_) => Err(OxirsError::Query(
"Property paths not allowed in concrete data".to_string(),
)),
}
}
fn graph_ref_to_named_node(&self, graph_ref: &GraphReference) -> Result<NamedNode, OxirsError> {
match graph_ref {
GraphReference::Iri(iri) => NamedNode::new(iri),
GraphReference::Default => Err(OxirsError::Query(
"DEFAULT is not a valid graph IRI".to_string(),
)),
}
}
fn evaluate_pattern(
&mut self,
pattern: &Algebra,
) -> Result<Vec<HashMap<String, oxirs_core::model::Term>>, OxirsError> {
use crate::executor::QueryExecutor;
let mut context = EvaluationContext::default();
let mut executor = QueryExecutor::new();
let results = executor
.execute_algebra(pattern, &mut context)
.map_err(|e| OxirsError::Query(e.to_string()))?;
let mut bindings = Vec::new();
for solution in results {
for binding in solution {
let mut converted_binding = HashMap::new();
for (var, term) in binding {
let arq_term = crate::term::Term::from_algebra_term(&term);
let core_term = self.convert_term_to_core(&arq_term)?;
converted_binding.insert(var.as_str().to_string(), core_term);
}
bindings.push(converted_binding);
}
}
Ok(bindings)
}
fn apply_binding_to_pattern(
&self,
pattern: &Algebra,
binding: &HashMap<String, oxirs_core::model::Term>,
) -> Result<Vec<Quad>, OxirsError> {
let mut quads = Vec::new();
let triple_patterns = self.extract_triple_patterns(pattern);
for triple_pattern in triple_patterns {
let subject_term = self.instantiate_algebra_term(&triple_pattern.subject, binding)?;
let predicate_term =
self.instantiate_algebra_term(&triple_pattern.predicate, binding)?;
let object_term = self.instantiate_algebra_term(&triple_pattern.object, binding)?;
let subject = self.core_term_to_subject(subject_term)?;
let predicate = self.core_term_to_predicate(predicate_term)?;
let object = self.core_term_to_object(object_term)?;
let graph_name = GraphName::DefaultGraph;
let quad = Quad::new(subject, predicate, object, graph_name);
quads.push(quad);
}
Ok(quads)
}
fn instantiate_template(
&self,
template: &QuadPattern,
binding: &HashMap<String, oxirs_core::model::Term>,
) -> Result<Quad, OxirsError> {
let subject = self.instantiate_term(&template.subject, binding)?;
let predicate = self.instantiate_term(&template.predicate, binding)?;
let object = self.instantiate_term(&template.object, binding)?;
let subject = self.term_to_subject(&subject)?;
let predicate = self.term_to_predicate(&predicate)?;
let object = self.term_to_object(&object)?;
let graph_name = template
.graph
.as_ref()
.map(|g| self.graph_ref_to_named_node(g))
.transpose()?
.map(GraphName::NamedNode)
.unwrap_or(GraphName::DefaultGraph);
Ok(Quad::new(subject, predicate, object, graph_name))
}
fn instantiate_term(
&self,
term: &Term,
binding: &HashMap<String, oxirs_core::model::Term>,
) -> Result<Term, OxirsError> {
match term {
Term::Variable(var) => binding
.get(var.as_str())
.map(|t| self.core_term_to_arq_term(t))
.ok_or_else(|| OxirsError::Query(format!("Unbound variable: {var}"))),
_ => Ok(term.clone()),
}
}
fn core_term_to_arq_term(&self, term: &oxirs_core::model::Term) -> Term {
match term {
oxirs_core::model::Term::NamedNode(n) => Term::Iri(n.clone()),
oxirs_core::model::Term::BlankNode(b) => Term::BlankNode(b.as_str().to_string()),
oxirs_core::model::Term::Literal(l) => {
let lit = crate::algebra::Literal {
value: l.value().to_string(),
language: l.language().map(|s| s.to_string()),
datatype: Some(l.datatype().into()),
};
Term::Literal(lit)
}
oxirs_core::model::Term::Variable(_) | oxirs_core::model::Term::QuotedTriple(_) => {
panic!("Variables and quoted triples not supported in update operations")
}
}
}
fn get_quads_from_target(&self, target: &GraphTarget) -> Result<Vec<Quad>, OxirsError> {
match target {
GraphTarget::All => self.store.quads(),
GraphTarget::Named => self.store.named_graph_quads(),
GraphTarget::Default => self.store.default_graph_quads(),
GraphTarget::Graph(graph_ref) => {
let graph = self.graph_ref_to_named_node(graph_ref)?;
self.store.graph_quads(Some(&graph))
}
}
}
fn convert_term_to_core(
&self,
term: &crate::term::Term,
) -> Result<oxirs_core::model::Term, OxirsError> {
use oxirs_core::model::Term as CoreTerm;
match term {
crate::term::Term::Iri(iri) => Ok(CoreTerm::NamedNode(NamedNode::new(iri)?)),
crate::term::Term::BlankNode(id) => Ok(CoreTerm::BlankNode(BlankNode::new(id)?)),
crate::term::Term::Literal(lit) => {
let core_literal = if let Some(lang) = &lit.language_tag {
CoreLiteral::new_language_tagged_literal(&lit.lexical_form, lang)?
} else if lit.datatype != "http://www.w3.org/2001/XMLSchema#string" {
CoreLiteral::new_typed(&lit.lexical_form, NamedNode::new(&lit.datatype)?)
} else {
CoreLiteral::new_simple_literal(&lit.lexical_form)
};
Ok(CoreTerm::Literal(core_literal))
}
crate::term::Term::Variable(_) => Err(OxirsError::Query(
"Cannot convert variable to concrete term".to_string(),
)),
crate::term::Term::QuotedTriple(_) => Err(OxirsError::Query(
"Cannot convert quoted triple to concrete term".to_string(),
)),
crate::term::Term::PropertyPath(_) => Err(OxirsError::Query(
"Cannot convert property path to concrete term".to_string(),
)),
}
}
#[allow(clippy::only_used_in_recursion)]
fn extract_triple_patterns(&self, algebra: &Algebra) -> Vec<TriplePattern> {
let mut patterns = Vec::new();
match algebra {
Algebra::Bgp(bgp_patterns) => {
patterns.extend(bgp_patterns.iter().cloned());
}
Algebra::Join { left, right } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
Algebra::LeftJoin { left, right, .. } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
Algebra::Union { left, right } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
Algebra::Filter { pattern, .. } => {
patterns.extend(self.extract_triple_patterns(pattern));
}
Algebra::Extend { pattern, .. } => {
patterns.extend(self.extract_triple_patterns(pattern));
}
Algebra::Minus { left, right } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
Algebra::Service { pattern, .. } => {
patterns.extend(self.extract_triple_patterns(pattern));
}
_ => {}
}
patterns
}
fn instantiate_algebra_term(
&self,
term: &Term,
binding: &HashMap<String, oxirs_core::model::Term>,
) -> Result<oxirs_core::model::Term, OxirsError> {
match term {
Term::Variable(var) => binding
.get(var.as_str())
.cloned()
.ok_or_else(|| OxirsError::Query(format!("Unbound variable: {var}"))),
_ => {
let arq_term = crate::term::Term::from_algebra_term(term);
self.convert_term_to_core(&arq_term)
}
}
}
fn core_term_to_subject(
&self,
term: oxirs_core::model::Term,
) -> Result<oxirs_core::Subject, OxirsError> {
match term {
oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Subject::NamedNode(node)),
oxirs_core::model::Term::BlankNode(node) => Ok(oxirs_core::Subject::BlankNode(node)),
oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Subject::Variable(var)),
_ => Err(OxirsError::Query("Invalid subject term type".to_string())),
}
}
fn core_term_to_predicate(
&self,
term: oxirs_core::model::Term,
) -> Result<oxirs_core::Predicate, OxirsError> {
match term {
oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Predicate::NamedNode(node)),
oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Predicate::Variable(var)),
_ => Err(OxirsError::Query("Invalid predicate term type".to_string())),
}
}
fn core_term_to_object(
&self,
term: oxirs_core::model::Term,
) -> Result<oxirs_core::Object, OxirsError> {
match term {
oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Object::NamedNode(node)),
oxirs_core::model::Term::BlankNode(node) => Ok(oxirs_core::Object::BlankNode(node)),
oxirs_core::model::Term::Literal(lit) => Ok(oxirs_core::Object::Literal(lit)),
oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Object::Variable(var)),
oxirs_core::model::Term::QuotedTriple(qt) => Ok(oxirs_core::Object::QuotedTriple(qt)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_update_result_default() {
let result = UpdateResult::default();
assert_eq!(result.inserted, 0);
assert_eq!(result.deleted, 0);
assert!(result.graphs_created.is_empty());
assert!(result.graphs_dropped.is_empty());
}
#[test]
fn test_graph_reference() {
let iri_ref = GraphReference::Iri("http://example.org/graph".to_string());
let default_ref = GraphReference::Default;
assert_ne!(iri_ref, default_ref);
}
#[test]
fn test_quad_pattern() {
let pattern = QuadPattern {
subject: Term::Variable(Variable::new("s").unwrap()),
predicate: Term::Iri(NamedNode::new("http://example.org/pred").unwrap()),
object: Term::Literal(crate::algebra::Literal {
value: "test".to_string(),
language: None,
datatype: None,
}),
graph: None,
};
assert_eq!(pattern.subject, Term::Variable(Variable::new("s").unwrap()));
}
#[test]
fn test_enhanced_update_operations() {
let insert_data = UpdateOperation::InsertData {
data: vec![QuadPattern {
subject: Term::Iri(NamedNode::new("http://example.org/subject").unwrap()),
predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
object: Term::Literal(crate::algebra::Literal {
value: "test_value".to_string(),
language: None,
datatype: None,
}),
graph: None,
}],
};
let delete_data = UpdateOperation::DeleteData {
data: vec![QuadPattern {
subject: Term::Iri(NamedNode::new("http://example.org/subject").unwrap()),
predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
object: Term::Literal(crate::algebra::Literal {
value: "test_value".to_string(),
language: None,
datatype: None,
}),
graph: None,
}],
};
match insert_data {
UpdateOperation::InsertData { data } => {
assert_eq!(data.len(), 1);
}
_ => panic!("Expected InsertData operation"),
}
match delete_data {
UpdateOperation::DeleteData { data } => {
assert_eq!(data.len(), 1);
}
_ => panic!("Expected DeleteData operation"),
}
}
#[test]
fn test_update_statistics() {
let stats = UpdateStatistics::default();
assert_eq!(stats.total_operations, 0);
assert_eq!(stats.batch_count, 0);
assert_eq!(stats.operations_per_second, 0.0);
}
#[test]
fn test_validation_errors() {
let _invalid_pattern = QuadPattern {
subject: Term::Variable(Variable::new("s").unwrap()),
predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
object: Term::Literal(crate::algebra::Literal {
value: "test".to_string(),
language: None,
datatype: None,
}),
graph: None,
};
let validation_result = std::panic::catch_unwind(|| {
});
assert!(validation_result.is_ok());
}
}