Skip to main content

qualia_core_db/sparql_library/
sparql_update.rs

1//! SPARQL Update Operations
2//!
3//! Implements SPARQL 1.1 Update Language (INSERT DATA, DELETE DATA, DELETE/INSERT WHERE).
4
5use crate::sparql_ast::*;
6use crate::NQuin;
7
8/// Update operation types
9#[repr(C)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum UpdateOperation {
12    /// INSERT DATA { triples }
13    InsertData { quins: [NQuin; 64], quin_count: u8 },
14    /// DELETE DATA { triples }
15    DeleteData { quins: [NQuin; 64], quin_count: u8 },
16    /// DELETE { pattern } INSERT { pattern } WHERE { pattern }
17    DeleteInsert {
18        delete_pattern: PatternId,
19        insert_pattern: PatternId,
20        where_pattern: PatternId,
21    },
22    /// LOAD <uri> INTO GRAPH <graph>
23    Load { uri: u64, graph: u64 },
24    /// CLEAR GRAPH <graph>
25    Clear { graph: u64 },
26    /// CREATE GRAPH <graph>
27    Create { graph: u64 },
28    /// DROP GRAPH <graph>
29    Drop { graph: u64 },
30}
31
32/// Update executor
33pub struct UpdateExecutor<'a> {
34    pub quins: &'a mut Vec<NQuin>,
35}
36
37impl<'a> UpdateExecutor<'a> {
38    pub fn new(quins: &'a mut Vec<NQuin>) -> Self {
39        Self { quins }
40    }
41
42    /// Execute an update operation
43    pub fn execute(
44        &mut self,
45        operation: &UpdateOperation,
46        ctx: &SparqlQueryContext,
47    ) -> Result<u64, String> {
48        match operation {
49            UpdateOperation::InsertData { quins, quin_count } => {
50                self.execute_insert_data(quins, *quin_count)
51            }
52            UpdateOperation::DeleteData { quins, quin_count } => {
53                self.execute_delete_data(quins, *quin_count)
54            }
55            UpdateOperation::DeleteInsert {
56                delete_pattern,
57                insert_pattern,
58                where_pattern,
59            } => self.execute_delete_insert(*delete_pattern, *insert_pattern, *where_pattern, ctx),
60            UpdateOperation::Load { uri, graph } => self.execute_load(*uri, *graph),
61            UpdateOperation::Clear { graph } => self.execute_clear(*graph),
62            UpdateOperation::Create { graph } => self.execute_create(*graph),
63            UpdateOperation::Drop { graph } => self.execute_drop(*graph),
64        }
65    }
66
67    fn execute_insert_data(&mut self, quins: &[NQuin], quin_count: u8) -> Result<u64, String> {
68        let count = quin_count as usize;
69        if count > quins.len() {
70            return Err("Quin count exceeds array length".to_string());
71        }
72
73        for i in 0..count {
74            self.quins.push(quins[i]);
75        }
76
77        Ok(count as u64)
78    }
79
80    fn execute_delete_data(&mut self, quins: &[NQuin], quin_count: u8) -> Result<u64, String> {
81        let count = quin_count as usize;
82        if count > quins.len() {
83            return Err("Quin count exceeds array length".to_string());
84        }
85
86        let mut deleted = 0;
87        for i in 0..count {
88            let target = quins[i];
89            // Remove all matching quins
90            self.quins.retain(|quin| {
91                quin.subject != target.subject
92                    || quin.predicate != target.predicate
93                    || quin.object != target.object
94            });
95            deleted += 1;
96        }
97
98        Ok(deleted)
99    }
100
101    fn execute_delete_insert(
102        &mut self,
103        delete_pattern: PatternId,
104        insert_pattern: PatternId,
105        where_pattern: PatternId,
106        ctx: &SparqlQueryContext,
107    ) -> Result<u64, String> {
108        // Plan WHERE clause
109        let mut plan = crate::sparql_library::sparql_planner::ExecutionPlan::new();
110        let root_op = crate::sparql_library::sparql_planner::QueryPlanner::plan_pattern(
111            where_pattern,
112            ctx,
113            &mut plan,
114        )?;
115        plan.root_operator = root_op;
116
117        // Execute WHERE clause
118        let bindings = {
119            let executor = crate::sparql_library::sparql_executor::QueryExecutor::new(&self.quins);
120            executor.execute(&plan, ctx)?
121        };
122
123        let mut deleted = 0;
124        let mut insert_quins = Vec::new();
125
126        // Helper to evaluate a pattern against a binding row into NQuin
127        let evaluate_pattern =
128            |pattern_id: PatternId, ctx: &SparqlQueryContext, row: &BindingRow| -> Option<NQuin> {
129                if let Some(pattern) = ctx.patterns.get(pattern_id as usize) {
130                    match pattern {
131                        Pattern::Triple {
132                            subject,
133                            predicate,
134                            object,
135                        } => {
136                            let s = if *subject < 0x8000_0000_0000_0000 {
137                                row.get(*subject as u8)?
138                            } else {
139                                *subject
140                            };
141                            let p = if *predicate < 0x8000_0000_0000_0000 {
142                                row.get(*predicate as u8)?
143                            } else {
144                                *predicate
145                            };
146                            let o = if *object < 0x8000_0000_0000_0000 {
147                                row.get(*object as u8)?
148                            } else {
149                                *object
150                            };
151                            Some(NQuin {
152                                subject: s,
153                                predicate: p,
154                                object: o,
155                                context: 0,
156                                metadata: 0,
157                                parity: 0,
158                            })
159                        }
160                        _ => None,
161                    }
162                } else {
163                    None
164                }
165            };
166
167        for binding in bindings {
168            if let Some(target) = evaluate_pattern(delete_pattern, ctx, &binding) {
169                let initial_len = self.quins.len();
170                self.quins.retain(|q| {
171                    q.subject != target.subject
172                        || q.predicate != target.predicate
173                        || q.object != target.object
174                });
175                deleted += initial_len - self.quins.len();
176            }
177            if let Some(new_quin) = evaluate_pattern(insert_pattern, ctx, &binding) {
178                insert_quins.push(new_quin);
179            }
180        }
181
182        self.quins.extend(insert_quins);
183        Ok(deleted as u64)
184    }
185
186    fn execute_load(&mut self, _uri: u64, _graph: u64) -> Result<u64, String> {
187        // In production, this would:
188        // 1. Resolve URI hash to actual URL
189        // 2. Fetch RDF from URL using HTTP client
190        // 3. Parse RDF into quins (Turtle, N-Triples, etc.)
191        // 4. Set context to graph
192        // 5. Insert quins into database
193
194        // Simplified: return 0 (requires HTTP client)
195        Ok(0)
196    }
197
198    fn execute_clear(&mut self, graph: u64) -> Result<u64, String> {
199        // Remove all quins with matching graph context
200        let original_len = self.quins.len();
201        self.quins.retain(|quin| quin.context != graph);
202        Ok((original_len - self.quins.len()) as u64)
203    }
204
205    fn execute_create(&mut self, _graph: u64) -> Result<u64, String> {
206        // Create a new named graph (metadata only)
207        // In production, this would:
208        // 1. Check if graph already exists
209        // 2. Create graph metadata entry
210        // 3. Set permissions/ACLs
211
212        // Simplified: return 1 (success)
213        Ok(1)
214    }
215
216    fn execute_drop(&mut self, graph: u64) -> Result<u64, String> {
217        // Remove all quins with matching graph context and delete graph metadata
218        let original_len = self.quins.len();
219        self.quins.retain(|quin| quin.context != graph);
220
221        // In production, this would also delete graph metadata
222        Ok((original_len - self.quins.len()) as u64)
223    }
224}
225
226/// Update query
227#[repr(C)]
228#[derive(Debug, Clone, Copy)]
229pub struct UpdateQuery {
230    pub operation: UpdateOperation,
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_insert_data() {
239        let mut quins = vec![];
240        let mut executor = UpdateExecutor::new(&mut quins);
241
242        let test_quin = NQuin {
243            subject: 1,
244            predicate: 2,
245            object: 3,
246            context: 0,
247            metadata: 0,
248            parity: 0,
249        };
250
251        let mut quins_array = [NQuin::default(); 64];
252        quins_array[0] = test_quin;
253
254        let result = executor.execute_insert_data(&quins_array, 1).unwrap();
255        assert_eq!(result, 1);
256        assert_eq!(executor.quins.len(), 1);
257    }
258
259    #[test]
260    fn test_delete_data() {
261        let mut quins = vec![NQuin {
262            subject: 1,
263            predicate: 2,
264            object: 3,
265            context: 0,
266            metadata: 0,
267            parity: 0,
268        }];
269
270        let mut executor = UpdateExecutor::new(&mut quins);
271
272        let test_quin = NQuin {
273            subject: 1,
274            predicate: 2,
275            object: 3,
276            context: 0,
277            metadata: 0,
278            parity: 0,
279        };
280
281        let mut quins_array = [NQuin::default(); 64];
282        quins_array[0] = test_quin;
283
284        let result = executor.execute_delete_data(&quins_array, 1).unwrap();
285        assert_eq!(result, 1);
286        assert_eq!(executor.quins.len(), 0);
287    }
288}