sql_cli/data/
datavalue_compare.rs

1use crate::data::datatable::DataValue;
2use std::cmp::Ordering;
3
4/// Utility function to compare two DataValues, handling all types including InternedString
5/// This centralizes comparison logic to avoid duplicating InternedString handling everywhere
6pub fn compare_datavalues(a: &DataValue, b: &DataValue) -> Ordering {
7    match (a, b) {
8        // Integer comparisons
9        (DataValue::Integer(a), DataValue::Integer(b)) => a.cmp(b),
10
11        // Float comparisons
12        (DataValue::Float(a), DataValue::Float(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
13
14        // String comparisons
15        (DataValue::String(a), DataValue::String(b)) => a.cmp(b),
16
17        // InternedString comparisons
18        (DataValue::InternedString(a), DataValue::InternedString(b)) => a.as_ref().cmp(b.as_ref()),
19
20        // Mixed String and InternedString comparisons
21        (DataValue::String(a), DataValue::InternedString(b)) => a.cmp(b.as_ref()),
22        (DataValue::InternedString(a), DataValue::String(b)) => a.as_ref().cmp(b),
23
24        // Boolean comparisons
25        (DataValue::Boolean(a), DataValue::Boolean(b)) => a.cmp(b),
26
27        // DateTime comparisons
28        (DataValue::DateTime(a), DataValue::DateTime(b)) => a.cmp(b),
29
30        // Null handling
31        (DataValue::Null, DataValue::Null) => Ordering::Equal,
32        (DataValue::Null, _) => Ordering::Less,
33        (_, DataValue::Null) => Ordering::Greater,
34
35        // Cross-type comparisons - treat as unequal with consistent ordering
36        // Order: Null < Boolean < Integer < Float < String/InternedString < DateTime
37        (DataValue::Boolean(_), DataValue::Integer(_)) => Ordering::Less,
38        (DataValue::Boolean(_), DataValue::Float(_)) => Ordering::Less,
39        (DataValue::Boolean(_), DataValue::String(_)) => Ordering::Less,
40        (DataValue::Boolean(_), DataValue::InternedString(_)) => Ordering::Less,
41        (DataValue::Boolean(_), DataValue::DateTime(_)) => Ordering::Less,
42
43        (DataValue::Integer(_), DataValue::Boolean(_)) => Ordering::Greater,
44        (DataValue::Integer(i), DataValue::Float(f)) => {
45            // Compare actual numeric values, not types
46            (*i as f64).partial_cmp(f).unwrap_or(Ordering::Equal)
47        }
48        (DataValue::Integer(_), DataValue::String(_)) => Ordering::Less,
49        (DataValue::Integer(_), DataValue::InternedString(_)) => Ordering::Less,
50        (DataValue::Integer(_), DataValue::DateTime(_)) => Ordering::Less,
51
52        (DataValue::Float(_), DataValue::Boolean(_)) => Ordering::Greater,
53        (DataValue::Float(f), DataValue::Integer(i)) => {
54            // Compare actual numeric values, not types
55            f.partial_cmp(&(*i as f64)).unwrap_or(Ordering::Equal)
56        }
57        (DataValue::Float(_), DataValue::String(_)) => Ordering::Less,
58        (DataValue::Float(_), DataValue::InternedString(_)) => Ordering::Less,
59        (DataValue::Float(_), DataValue::DateTime(_)) => Ordering::Less,
60
61        (DataValue::String(_), DataValue::Boolean(_)) => Ordering::Greater,
62        (DataValue::String(_), DataValue::Integer(_)) => Ordering::Greater,
63        (DataValue::String(_), DataValue::Float(_)) => Ordering::Greater,
64        (DataValue::String(_), DataValue::DateTime(_)) => Ordering::Less,
65
66        (DataValue::InternedString(_), DataValue::Boolean(_)) => Ordering::Greater,
67        (DataValue::InternedString(_), DataValue::Integer(_)) => Ordering::Greater,
68        (DataValue::InternedString(_), DataValue::Float(_)) => Ordering::Greater,
69        (DataValue::InternedString(_), DataValue::DateTime(_)) => Ordering::Less,
70
71        (DataValue::DateTime(_), DataValue::Boolean(_)) => Ordering::Greater,
72        (DataValue::DateTime(_), DataValue::Integer(_)) => Ordering::Greater,
73        (DataValue::DateTime(_), DataValue::Float(_)) => Ordering::Greater,
74        (DataValue::DateTime(_), DataValue::String(_)) => Ordering::Greater,
75        (DataValue::DateTime(_), DataValue::InternedString(_)) => Ordering::Greater,
76    }
77}
78
79/// Compare DataValues with optional values (handling None)
80pub fn compare_optional_datavalues(a: Option<&DataValue>, b: Option<&DataValue>) -> Ordering {
81    match (a, b) {
82        (None, None) => Ordering::Equal,
83        (None, Some(_)) => Ordering::Less,
84        (Some(_), None) => Ordering::Greater,
85        (Some(a), Some(b)) => compare_datavalues(a, b),
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use std::sync::Arc;
93
94    #[test]
95    fn test_integer_comparison() {
96        assert_eq!(
97            compare_datavalues(&DataValue::Integer(1), &DataValue::Integer(2)),
98            Ordering::Less
99        );
100        assert_eq!(
101            compare_datavalues(&DataValue::Integer(2), &DataValue::Integer(2)),
102            Ordering::Equal
103        );
104        assert_eq!(
105            compare_datavalues(&DataValue::Integer(3), &DataValue::Integer(2)),
106            Ordering::Greater
107        );
108    }
109
110    #[test]
111    fn test_string_comparison() {
112        assert_eq!(
113            compare_datavalues(
114                &DataValue::String("apple".to_string()),
115                &DataValue::String("banana".to_string())
116            ),
117            Ordering::Less
118        );
119    }
120
121    #[test]
122    fn test_interned_string_comparison() {
123        let a = Arc::new("apple".to_string());
124        let b = Arc::new("banana".to_string());
125        assert_eq!(
126            compare_datavalues(&DataValue::InternedString(a), &DataValue::InternedString(b)),
127            Ordering::Less
128        );
129    }
130
131    #[test]
132    fn test_mixed_string_comparison() {
133        let interned = Arc::new("banana".to_string());
134        assert_eq!(
135            compare_datavalues(
136                &DataValue::String("apple".to_string()),
137                &DataValue::InternedString(interned.clone())
138            ),
139            Ordering::Less
140        );
141        assert_eq!(
142            compare_datavalues(
143                &DataValue::InternedString(interned),
144                &DataValue::String("apple".to_string())
145            ),
146            Ordering::Greater
147        );
148    }
149
150    #[test]
151    fn test_null_comparison() {
152        assert_eq!(
153            compare_datavalues(&DataValue::Null, &DataValue::Integer(1)),
154            Ordering::Less
155        );
156        assert_eq!(
157            compare_datavalues(&DataValue::Integer(1), &DataValue::Null),
158            Ordering::Greater
159        );
160        assert_eq!(
161            compare_datavalues(&DataValue::Null, &DataValue::Null),
162            Ordering::Equal
163        );
164    }
165
166    #[test]
167    fn test_cross_type_comparison() {
168        // Test the type ordering (except Integer/Float which compare by value)
169        assert_eq!(
170            compare_datavalues(&DataValue::Boolean(true), &DataValue::Integer(1)),
171            Ordering::Less
172        );
173
174        // Integer and Float now compare by numeric value, not type
175        assert_eq!(
176            compare_datavalues(&DataValue::Integer(1), &DataValue::Float(1.0)),
177            Ordering::Equal // 1 == 1.0
178        );
179        assert_eq!(
180            compare_datavalues(&DataValue::Integer(1), &DataValue::Float(1.5)),
181            Ordering::Less // 1 < 1.5
182        );
183        assert_eq!(
184            compare_datavalues(&DataValue::Integer(2), &DataValue::Float(1.5)),
185            Ordering::Greater // 2 > 1.5
186        );
187
188        assert_eq!(
189            compare_datavalues(&DataValue::Float(1.0), &DataValue::String("a".to_string())),
190            Ordering::Less
191        );
192    }
193}