sql_cli/sql/functions/
hash.rs

1use anyhow::Result;
2use md5;
3use sha1::Sha1;
4use sha2::{Digest, Sha256, Sha512};
5
6use crate::data::datatable::DataValue;
7use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
8
9/// MD5 hash function
10pub struct Md5Function;
11
12impl SqlFunction for Md5Function {
13    fn signature(&self) -> FunctionSignature {
14        FunctionSignature {
15            name: "MD5",
16            category: FunctionCategory::String,
17            arg_count: ArgCount::Fixed(1),
18            description: "Calculate MD5 hash of a string",
19            returns: "String (32 character hex digest)",
20            examples: vec![
21                "MD5('hello') = '5d41402abc4b2a76b9719d911017c592'",
22                "MD5('test') = '098f6bcd4621d373cade4e832627b4f6'",
23            ],
24        }
25    }
26
27    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
28        let input_string = match &args[0] {
29            DataValue::String(s) => s.clone(),
30            DataValue::InternedString(idx) => idx.to_string(), // Convert interned string index to string
31            DataValue::Integer(i) => i.to_string(),
32            DataValue::Float(f) => f.to_string(),
33            DataValue::Boolean(b) => b.to_string(),
34            DataValue::DateTime(dt) => dt.to_string(),
35            DataValue::Vector(v) => {
36                let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
37                format!("[{}]", components.join(","))
38            }
39            DataValue::Null => return Ok(DataValue::Null),
40        };
41
42        let digest = md5::compute(input_string.as_bytes());
43        Ok(DataValue::String(format!("{:x}", digest)))
44    }
45}
46
47/// SHA1 hash function
48pub struct Sha1Function;
49
50impl SqlFunction for Sha1Function {
51    fn signature(&self) -> FunctionSignature {
52        FunctionSignature {
53            name: "SHA1",
54            category: FunctionCategory::String,
55            arg_count: ArgCount::Fixed(1),
56            description: "Calculate SHA1 hash of a string",
57            returns: "String (40 character hex digest)",
58            examples: vec![
59                "SHA1('hello') = '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'",
60                "SHA1('test') = 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'",
61            ],
62        }
63    }
64
65    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
66        let input_string = match &args[0] {
67            DataValue::String(s) => s.clone(),
68            DataValue::InternedString(idx) => idx.to_string(), // Convert interned string index to string
69            DataValue::Integer(i) => i.to_string(),
70            DataValue::Float(f) => f.to_string(),
71            DataValue::Boolean(b) => b.to_string(),
72            DataValue::DateTime(dt) => dt.to_string(),
73            DataValue::Vector(v) => {
74                let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
75                format!("[{}]", components.join(","))
76            }
77            DataValue::Null => return Ok(DataValue::Null),
78        };
79
80        let mut hasher = Sha1::new();
81        hasher.update(input_string.as_bytes());
82        let result = hasher.finalize();
83        Ok(DataValue::String(format!("{:x}", result)))
84    }
85}
86
87/// SHA256 hash function
88pub struct Sha256Function;
89
90impl SqlFunction for Sha256Function {
91    fn signature(&self) -> FunctionSignature {
92        FunctionSignature {
93            name: "SHA256",
94            category: FunctionCategory::String,
95            arg_count: ArgCount::Fixed(1),
96            description: "Calculate SHA256 hash of a string",
97            returns: "String (64 character hex digest)",
98            examples: vec![
99                "SHA256('hello') = '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'",
100                "SHA256('test') = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'",
101            ],
102        }
103    }
104
105    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
106        let input_string = match &args[0] {
107            DataValue::String(s) => s.clone(),
108            DataValue::InternedString(idx) => idx.to_string(), // Convert interned string index to string
109            DataValue::Integer(i) => i.to_string(),
110            DataValue::Float(f) => f.to_string(),
111            DataValue::Boolean(b) => b.to_string(),
112            DataValue::DateTime(dt) => dt.to_string(),
113            DataValue::Vector(v) => {
114                let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
115                format!("[{}]", components.join(","))
116            }
117            DataValue::Null => return Ok(DataValue::Null),
118        };
119
120        let mut hasher = Sha256::new();
121        hasher.update(input_string.as_bytes());
122        let result = hasher.finalize();
123        Ok(DataValue::String(format!("{:x}", result)))
124    }
125}
126
127/// SHA512 hash function
128pub struct Sha512Function;
129
130impl SqlFunction for Sha512Function {
131    fn signature(&self) -> FunctionSignature {
132        FunctionSignature {
133            name: "SHA512",
134            category: FunctionCategory::String,
135            arg_count: ArgCount::Fixed(1),
136            description: "Calculate SHA512 hash of a string",
137            returns: "String (128 character hex digest)",
138            examples: vec![
139                "SHA512('hello') = '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043'",
140                "SHA512('test') = 'ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff'",
141            ],
142        }
143    }
144
145    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
146        let input_string = match &args[0] {
147            DataValue::String(s) => s.clone(),
148            DataValue::InternedString(idx) => idx.to_string(), // Convert interned string index to string
149            DataValue::Integer(i) => i.to_string(),
150            DataValue::Float(f) => f.to_string(),
151            DataValue::Boolean(b) => b.to_string(),
152            DataValue::DateTime(dt) => dt.to_string(),
153            DataValue::Vector(v) => {
154                let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
155                format!("[{}]", components.join(","))
156            }
157            DataValue::Null => return Ok(DataValue::Null),
158        };
159
160        let mut hasher = Sha512::new();
161        hasher.update(input_string.as_bytes());
162        let result = hasher.finalize();
163        Ok(DataValue::String(format!("{:x}", result)))
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn test_md5() {
173        let func = Md5Function;
174
175        // Test basic string
176        let result = func
177            .evaluate(&[DataValue::String("hello".to_string())])
178            .unwrap();
179        assert_eq!(
180            result,
181            DataValue::String("5d41402abc4b2a76b9719d911017c592".to_string())
182        );
183
184        // Test NULL
185        let result = func.evaluate(&[DataValue::Null]).unwrap();
186        assert_eq!(result, DataValue::Null);
187    }
188
189    #[test]
190    fn test_sha1() {
191        let func = Sha1Function;
192
193        // Test basic string
194        let result = func
195            .evaluate(&[DataValue::String("test".to_string())])
196            .unwrap();
197        assert_eq!(
198            result,
199            DataValue::String("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3".to_string())
200        );
201
202        // Test NULL
203        let result = func.evaluate(&[DataValue::Null]).unwrap();
204        assert_eq!(result, DataValue::Null);
205    }
206
207    #[test]
208    fn test_sha256() {
209        let func = Sha256Function;
210
211        // Test basic string
212        let result = func
213            .evaluate(&[DataValue::String("test".to_string())])
214            .unwrap();
215        assert_eq!(
216            result,
217            DataValue::String(
218                "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08".to_string()
219            )
220        );
221
222        // Test NULL
223        let result = func.evaluate(&[DataValue::Null]).unwrap();
224        assert_eq!(result, DataValue::Null);
225    }
226
227    #[test]
228    fn test_sha512() {
229        let func = Sha512Function;
230
231        // Test basic string
232        let result = func
233            .evaluate(&[DataValue::String("test".to_string())])
234            .unwrap();
235        assert_eq!(result, DataValue::String("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff".to_string()));
236
237        // Test empty string
238        let result = func.evaluate(&[DataValue::String("".to_string())]).unwrap();
239        assert_eq!(result, DataValue::String("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e".to_string()));
240    }
241}