Skip to main content

reddb_server/storage/query/engine/
binding.rs

1//! Variable Bindings
2//!
3//! Maps variables to values during query execution.
4//!
5//! # Design
6//!
7//! - Immutable bindings for thread safety
8//! - Parent binding support for scoped lookups
9//! - Size-optimized implementations for common cases
10//! - Builder pattern for construction
11
12use std::collections::HashMap;
13use std::fmt;
14use std::hash::{Hash, Hasher};
15use std::sync::Arc;
16
17use crate::json;
18use crate::serde_json::{Map as JsonMap, Value as JsonValue};
19
20/// A variable in a query
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub struct Var {
23    name: String,
24}
25
26impl Var {
27    /// Create a new variable
28    pub fn new(name: &str) -> Self {
29        Self {
30            name: name.to_string(),
31        }
32    }
33
34    /// Get variable name
35    pub fn name(&self) -> &str {
36        &self.name
37    }
38
39    /// Check if this is an anonymous variable
40    pub fn is_anonymous(&self) -> bool {
41        self.name.starts_with('_')
42    }
43}
44
45impl fmt::Display for Var {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        write!(f, "?{}", self.name)
48    }
49}
50
51impl From<&str> for Var {
52    fn from(s: &str) -> Self {
53        // Strip leading ? if present
54        let name = s.strip_prefix('?').unwrap_or(s);
55        Self::new(name)
56    }
57}
58
59/// A value that can be bound to a variable
60#[derive(Debug, Clone)]
61pub enum Value {
62    /// Node/vertex ID
63    Node(String),
64    /// Edge ID
65    Edge(String),
66    /// Literal string
67    String(String),
68    /// Integer
69    Integer(i64),
70    /// Float
71    Float(f64),
72    /// Boolean
73    Boolean(bool),
74    /// URI/IRI
75    Uri(String),
76    /// Null/unbound
77    Null,
78}
79
80impl Value {
81    /// Get as string
82    pub fn as_string(&self) -> Option<&str> {
83        match self {
84            Value::String(s) | Value::Node(s) | Value::Edge(s) | Value::Uri(s) => Some(s),
85            _ => None,
86        }
87    }
88
89    /// Get as i64
90    pub fn as_integer(&self) -> Option<i64> {
91        match self {
92            Value::Integer(i) => Some(*i),
93            _ => None,
94        }
95    }
96
97    /// Get as f64
98    pub fn as_float(&self) -> Option<f64> {
99        match self {
100            Value::Float(f) => Some(*f),
101            Value::Integer(i) => Some(*i as f64),
102            _ => None,
103        }
104    }
105
106    /// Check if null
107    pub fn is_null(&self) -> bool {
108        matches!(self, Value::Null)
109    }
110
111    /// Convert to JSON
112    pub fn to_json(&self) -> JsonValue {
113        match self {
114            Value::Node(id) => json!({ "type": "node", "id": id }),
115            Value::Edge(id) => json!({ "type": "edge", "id": id }),
116            Value::String(s) => JsonValue::String(s.clone()),
117            Value::Integer(i) => json!(i),
118            Value::Float(f) => json!(f),
119            Value::Boolean(b) => JsonValue::Bool(*b),
120            Value::Uri(uri) => json!({ "type": "uri", "value": uri }),
121            Value::Null => JsonValue::Null,
122        }
123    }
124}
125
126impl PartialEq for Value {
127    fn eq(&self, other: &Self) -> bool {
128        match (self, other) {
129            (Value::Node(a), Value::Node(b)) => a == b,
130            (Value::Edge(a), Value::Edge(b)) => a == b,
131            (Value::String(a), Value::String(b)) => a == b,
132            (Value::Integer(a), Value::Integer(b)) => a == b,
133            (Value::Float(a), Value::Float(b)) => {
134                (a - b).abs() < f64::EPSILON || (a.is_nan() && b.is_nan())
135            }
136            (Value::Boolean(a), Value::Boolean(b)) => a == b,
137            (Value::Uri(a), Value::Uri(b)) => a == b,
138            (Value::Null, Value::Null) => true,
139            _ => false,
140        }
141    }
142}
143
144impl Eq for Value {}
145
146impl Hash for Value {
147    fn hash<H: Hasher>(&self, state: &mut H) {
148        match self {
149            Value::Node(s) => {
150                0u8.hash(state);
151                s.hash(state);
152            }
153            Value::Edge(s) => {
154                1u8.hash(state);
155                s.hash(state);
156            }
157            Value::String(s) => {
158                2u8.hash(state);
159                s.hash(state);
160            }
161            Value::Integer(i) => {
162                3u8.hash(state);
163                i.hash(state);
164            }
165            Value::Boolean(b) => {
166                4u8.hash(state);
167                b.hash(state);
168            }
169            Value::Uri(s) => {
170                5u8.hash(state);
171                s.hash(state);
172            }
173            Value::Float(f) => {
174                6u8.hash(state);
175                f.to_bits().hash(state);
176            }
177            Value::Null => {
178                7u8.hash(state);
179            }
180        }
181    }
182}
183
184impl fmt::Display for Value {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            Value::Node(id) => write!(f, "<node:{}>", id),
188            Value::Edge(id) => write!(f, "<edge:{}>", id),
189            Value::String(s) => write!(f, "\"{}\"", s),
190            Value::Integer(i) => write!(f, "{}", i),
191            Value::Float(fl) => write!(f, "{}", fl),
192            Value::Boolean(b) => write!(f, "{}", b),
193            Value::Uri(uri) => write!(f, "<{}>", uri),
194            Value::Null => write!(f, "NULL"),
195        }
196    }
197}
198
199/// Immutable binding from variables to values
200#[derive(Debug, Clone)]
201pub struct Binding {
202    /// Variable mappings
203    bindings: HashMap<Var, Value>,
204    /// Parent binding for scoped lookups
205    parent: Option<Arc<Binding>>,
206}
207
208impl Binding {
209    /// Create an empty binding
210    pub fn empty() -> Self {
211        Self {
212            bindings: HashMap::new(),
213            parent: None,
214        }
215    }
216
217    /// Create binding with single variable
218    pub fn one(var: Var, value: Value) -> Self {
219        let mut bindings = HashMap::new();
220        bindings.insert(var, value);
221        Self {
222            bindings,
223            parent: None,
224        }
225    }
226
227    /// Create binding with two variables
228    pub fn two(var1: Var, val1: Value, var2: Var, val2: Value) -> Self {
229        let mut bindings = HashMap::new();
230        bindings.insert(var1, val1);
231        bindings.insert(var2, val2);
232        Self {
233            bindings,
234            parent: None,
235        }
236    }
237
238    /// Create binding with parent scope
239    pub fn with_parent(self, parent: Arc<Binding>) -> Self {
240        Self {
241            bindings: self.bindings,
242            parent: Some(parent),
243        }
244    }
245
246    /// Get value for variable
247    pub fn get(&self, var: &Var) -> Option<&Value> {
248        self.bindings
249            .get(var)
250            .or_else(|| self.parent.as_ref().and_then(|p| p.get(var)))
251    }
252
253    /// Check if variable is bound
254    pub fn contains(&self, var: &Var) -> bool {
255        self.bindings.contains_key(var)
256            || self
257                .parent
258                .as_ref()
259                .map(|p| p.contains(var))
260                .unwrap_or(false)
261    }
262
263    /// Get all variables in this binding (not parent)
264    pub fn vars(&self) -> impl Iterator<Item = &Var> {
265        self.bindings.keys()
266    }
267
268    /// Get all variables including parent
269    pub fn all_vars(&self) -> Vec<&Var> {
270        let mut vars: Vec<&Var> = self.bindings.keys().collect();
271        if let Some(ref parent) = self.parent {
272            for v in parent.all_vars() {
273                if !vars.contains(&v) {
274                    vars.push(v);
275                }
276            }
277        }
278        vars
279    }
280
281    /// Number of bindings (not including parent)
282    pub fn size(&self) -> usize {
283        self.bindings.len()
284    }
285
286    /// Check if empty
287    pub fn is_empty(&self) -> bool {
288        self.bindings.is_empty() && self.parent.as_ref().map(|p| p.is_empty()).unwrap_or(true)
289    }
290
291    /// Merge two bindings (returns None if conflict)
292    pub fn merge(&self, other: &Binding) -> Option<Binding> {
293        let mut merged = self.bindings.clone();
294
295        for (var, value) in &other.bindings {
296            if let Some(existing) = self.get(var) {
297                if existing != value {
298                    return None; // Conflict
299                }
300            } else {
301                merged.insert(var.clone(), value.clone());
302            }
303        }
304
305        Some(Binding {
306            bindings: merged,
307            parent: self.parent.clone(),
308        })
309    }
310
311    /// Project to subset of variables
312    pub fn project(&self, vars: &[Var]) -> Binding {
313        let mut projected = HashMap::new();
314        for var in vars {
315            if let Some(value) = self.get(var) {
316                projected.insert(var.clone(), value.clone());
317            }
318        }
319        Binding {
320            bindings: projected,
321            parent: None,
322        }
323    }
324
325    /// Extend with additional binding
326    pub fn extend(&self, var: Var, value: Value) -> Binding {
327        let mut bindings = self.bindings.clone();
328        bindings.insert(var, value);
329        Binding {
330            bindings,
331            parent: self.parent.clone(),
332        }
333    }
334
335    /// Convert to map
336    pub fn to_map(&self) -> HashMap<String, Value> {
337        let mut map = HashMap::new();
338        if let Some(ref parent) = self.parent {
339            for (k, v) in parent.to_map() {
340                map.insert(k, v);
341            }
342        }
343        for (var, value) in &self.bindings {
344            map.insert(var.name().to_string(), value.clone());
345        }
346        map
347    }
348
349    /// Convert to JSON
350    pub fn to_json(&self) -> JsonValue {
351        let map: JsonMap<String, JsonValue> = self
352            .to_map()
353            .into_iter()
354            .map(|(k, v)| (k, v.to_json()))
355            .collect();
356        JsonValue::Object(map)
357    }
358}
359
360impl Default for Binding {
361    fn default() -> Self {
362        Self::empty()
363    }
364}
365
366impl PartialEq for Binding {
367    fn eq(&self, other: &Self) -> bool {
368        // Compare flattened bindings
369        let self_map = self.to_map();
370        let other_map = other.to_map();
371        self_map == other_map
372    }
373}
374
375impl Eq for Binding {}
376
377impl Hash for Binding {
378    fn hash<H: Hasher>(&self, state: &mut H) {
379        // Hash sorted entries for consistency
380        let mut entries: Vec<_> = self.to_map().into_iter().collect();
381        entries.sort_by(|a, b| a.0.cmp(&b.0));
382        for (k, v) in entries {
383            k.hash(state);
384            v.hash(state);
385        }
386    }
387}
388
389/// Builder for creating bindings
390pub struct BindingBuilder {
391    bindings: HashMap<Var, Value>,
392    parent: Option<Arc<Binding>>,
393}
394
395impl BindingBuilder {
396    /// Create new builder
397    pub fn new() -> Self {
398        Self {
399            bindings: HashMap::new(),
400            parent: None,
401        }
402    }
403
404    /// Create builder from existing binding
405    pub fn from(binding: &Binding) -> Self {
406        Self {
407            bindings: binding.bindings.clone(),
408            parent: binding.parent.clone(),
409        }
410    }
411
412    /// Set parent binding
413    pub fn parent(mut self, parent: Arc<Binding>) -> Self {
414        self.parent = Some(parent);
415        self
416    }
417
418    /// Add variable binding
419    pub fn add(mut self, var: Var, value: Value) -> Self {
420        self.bindings.insert(var, value);
421        self
422    }
423
424    /// Add variable binding from string name
425    pub fn add_named(self, name: &str, value: Value) -> Self {
426        self.add(Var::from(name), value)
427    }
428
429    /// Remove variable
430    pub fn remove(mut self, var: &Var) -> Self {
431        self.bindings.remove(var);
432        self
433    }
434
435    /// Build the binding
436    pub fn build(self) -> Binding {
437        Binding {
438            bindings: self.bindings,
439            parent: self.parent,
440        }
441    }
442}
443
444impl Default for BindingBuilder {
445    fn default() -> Self {
446        Self::new()
447    }
448}
449
450#[cfg(test)]
451mod tests {
452    use super::*;
453
454    #[test]
455    fn test_var() {
456        let v = Var::new("x");
457        assert_eq!(v.name(), "x");
458        assert!(!v.is_anonymous());
459
460        let anon = Var::new("_g1");
461        assert!(anon.is_anonymous());
462    }
463
464    #[test]
465    fn test_var_from_string() {
466        let v1 = Var::from("x");
467        let v2 = Var::from("?x");
468        assert_eq!(v1, v2);
469    }
470
471    #[test]
472    fn test_binding_empty() {
473        let b = Binding::empty();
474        assert!(b.is_empty());
475        assert_eq!(b.size(), 0);
476    }
477
478    #[test]
479    fn test_binding_one() {
480        let b = Binding::one(Var::new("x"), Value::Integer(42));
481        assert!(!b.is_empty());
482        assert_eq!(b.size(), 1);
483        assert!(b.contains(&Var::new("x")));
484        assert_eq!(b.get(&Var::new("x")), Some(&Value::Integer(42)));
485    }
486
487    #[test]
488    fn test_binding_parent() {
489        let parent = Arc::new(Binding::one(Var::new("x"), Value::Integer(1)));
490        let child = Binding::one(Var::new("y"), Value::Integer(2)).with_parent(parent);
491
492        // Child can see parent's bindings
493        assert!(child.contains(&Var::new("x")));
494        assert!(child.contains(&Var::new("y")));
495
496        // Direct size doesn't include parent
497        assert_eq!(child.size(), 1);
498    }
499
500    #[test]
501    fn test_binding_merge() {
502        let b1 = Binding::one(Var::new("x"), Value::Integer(1));
503        let b2 = Binding::one(Var::new("y"), Value::Integer(2));
504
505        let merged = b1.merge(&b2).unwrap();
506        assert!(merged.contains(&Var::new("x")));
507        assert!(merged.contains(&Var::new("y")));
508    }
509
510    #[test]
511    fn test_binding_merge_conflict() {
512        let b1 = Binding::one(Var::new("x"), Value::Integer(1));
513        let b2 = Binding::one(Var::new("x"), Value::Integer(2));
514
515        let merged = b1.merge(&b2);
516        assert!(merged.is_none()); // Conflict
517    }
518
519    #[test]
520    fn test_binding_merge_same_value() {
521        let b1 = Binding::one(Var::new("x"), Value::Integer(1));
522        let b2 = Binding::one(Var::new("x"), Value::Integer(1));
523
524        let merged = b1.merge(&b2).unwrap();
525        assert_eq!(merged.get(&Var::new("x")), Some(&Value::Integer(1)));
526    }
527
528    #[test]
529    fn test_binding_project() {
530        let b = Binding::two(
531            Var::new("x"),
532            Value::Integer(1),
533            Var::new("y"),
534            Value::Integer(2),
535        );
536
537        let projected = b.project(&[Var::new("x")]);
538        assert!(projected.contains(&Var::new("x")));
539        assert!(!projected.contains(&Var::new("y")));
540    }
541
542    #[test]
543    fn test_binding_extend() {
544        let b = Binding::one(Var::new("x"), Value::Integer(1));
545        let extended = b.extend(Var::new("y"), Value::Integer(2));
546
547        assert!(extended.contains(&Var::new("x")));
548        assert!(extended.contains(&Var::new("y")));
549    }
550
551    #[test]
552    fn test_binding_builder() {
553        let b = BindingBuilder::new()
554            .add_named("x", Value::Integer(1))
555            .add_named("y", Value::String("hello".to_string()))
556            .build();
557
558        assert_eq!(b.size(), 2);
559        assert_eq!(b.get(&Var::new("x")), Some(&Value::Integer(1)));
560    }
561
562    #[test]
563    fn test_value_display() {
564        assert_eq!(format!("{}", Value::Integer(42)), "42");
565        assert_eq!(
566            format!("{}", Value::String("hello".to_string())),
567            "\"hello\""
568        );
569        assert_eq!(format!("{}", Value::Null), "NULL");
570    }
571}