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