vibesql_types/sql_value/hash.rs
1//! Hash implementation for SqlValue
2
3use std::hash::{Hash, Hasher};
4
5use crate::sql_value::SqlValue;
6
7/// Hash implementation for SqlValue
8///
9/// Custom implementation to handle floating-point values correctly:
10/// - NaN values are treated as equal (hash to same value)
11/// - Uses to_bits() for floats to ensure consistent hashing
12/// - NULL hashes to a specific value
13impl Hash for SqlValue {
14 fn hash<H: Hasher>(&self, state: &mut H) {
15 use SqlValue::*;
16
17 // Hash discriminant first to distinguish variants
18 std::mem::discriminant(self).hash(state);
19
20 match self {
21 Integer(i) => i.hash(state),
22 Smallint(i) => i.hash(state),
23 Bigint(i) => i.hash(state),
24 Unsigned(u) => u.hash(state),
25 Numeric(f) => {
26 if f.is_nan() {
27 f64::NAN.to_bits().hash(state);
28 } else {
29 f.to_bits().hash(state);
30 }
31 }
32 // For floats, use to_bits() to get consistent hash for NaN
33 Float(f) => {
34 if f.is_nan() {
35 // All NaN values hash the same
36 f32::NAN.to_bits().hash(state);
37 } else {
38 f.to_bits().hash(state);
39 }
40 }
41 Real(f) => {
42 if f.is_nan() {
43 f32::NAN.to_bits().hash(state);
44 } else {
45 f.to_bits().hash(state);
46 }
47 }
48 Double(f) => {
49 if f.is_nan() {
50 f64::NAN.to_bits().hash(state);
51 } else {
52 f.to_bits().hash(state);
53 }
54 }
55
56 Character(s) => s.hash(state),
57 Varchar(s) => s.hash(state),
58 Boolean(b) => b.hash(state),
59 Date(s) => s.hash(state),
60 Time(s) => s.hash(state),
61 Timestamp(s) => s.hash(state),
62 Interval(s) => s.hash(state),
63 Vector(v) => {
64 // Hash each f32 value using to_bits() for consistent NaN handling
65 for f in v.iter() {
66 if f.is_nan() {
67 f32::NAN.to_bits().hash(state);
68 } else {
69 f.to_bits().hash(state);
70 }
71 }
72 }
73 Blob(b) => b.hash(state),
74
75 // NULL hashes to nothing (discriminant is enough)
76 Null => {}
77 }
78 }
79}