datafusion_functions/crypto/
md5.rs1use arrow::{array::StringViewArray, datatypes::DataType};
19use datafusion_common::{
20 Result, ScalarValue,
21 cast::as_binary_array,
22 internal_err,
23 types::{logical_binary, logical_string},
24 utils::hex::{HexCase, encode_bytes},
25 utils::take_function_args,
26};
27use datafusion_expr::{
28 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
29 TypeSignature, Volatility,
30};
31use datafusion_expr_common::signature::{Coercion, TypeSignatureClass};
32use datafusion_macros::user_doc;
33use std::sync::Arc;
34
35use crate::crypto::basic::{DigestAlgorithm, digest_process};
36
37#[user_doc(
38 doc_section(label = "Hashing Functions"),
39 description = "Computes an MD5 128-bit checksum for a string expression.",
40 syntax_example = "md5(expression)",
41 sql_example = r#"```sql
42> select md5('foo');
43+----------------------------------+
44| md5(Utf8("foo")) |
45+----------------------------------+
46| acbd18db4cc2f85cedef654fccc4a4d8 |
47+----------------------------------+
48```"#,
49 standard_argument(name = "expression", prefix = "String")
50)]
51#[derive(Debug, PartialEq, Eq, Hash)]
52pub struct Md5Func {
53 signature: Signature,
54}
55
56impl Default for Md5Func {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl Md5Func {
63 pub fn new() -> Self {
64 Self {
65 signature: Signature::one_of(
66 vec![
67 TypeSignature::Coercible(vec![Coercion::new_exact(
68 TypeSignatureClass::Native(logical_string()),
69 )]),
70 TypeSignature::Coercible(vec![Coercion::new_exact(
71 TypeSignatureClass::Native(logical_binary()),
72 )]),
73 ],
74 Volatility::Immutable,
75 ),
76 }
77 }
78}
79
80impl ScalarUDFImpl for Md5Func {
81 fn name(&self) -> &str {
82 "md5"
83 }
84
85 fn signature(&self) -> &Signature {
86 &self.signature
87 }
88
89 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
90 Ok(DataType::Utf8View)
91 }
92
93 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
94 md5(&args.args)
95 }
96
97 fn documentation(&self) -> Option<&Documentation> {
98 self.doc()
99 }
100}
101
102fn md5(args: &[ColumnarValue]) -> Result<ColumnarValue> {
103 let [data] = take_function_args("md5", args)?;
104 let value = digest_process(data, DigestAlgorithm::Md5)?;
105
106 Ok(match value {
108 ColumnarValue::Array(array) => {
109 let binary_array = as_binary_array(&array)?;
110 let string_array: StringViewArray = binary_array
111 .iter()
112 .map(|opt| opt.map(|b| encode_bytes(b, HexCase::Lower)))
113 .collect();
114 ColumnarValue::Array(Arc::new(string_array))
115 }
116 ColumnarValue::Scalar(ScalarValue::Binary(opt)) => ColumnarValue::Scalar(
117 ScalarValue::Utf8View(opt.map(|b| encode_bytes(&b, HexCase::Lower))),
118 ),
119 _ => return internal_err!("Impossibly got invalid results from digest"),
120 })
121}