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