datafusion_functions/string/
to_hex.rs1use std::sync::Arc;
19
20use arrow::array::{Array, ArrayRef, StringArray};
21use arrow::buffer::{Buffer, OffsetBuffer};
22use arrow::datatypes::{
23 ArrowNativeType, ArrowPrimitiveType, DataType, Int8Type, Int16Type, Int32Type,
24 Int64Type, UInt8Type, UInt16Type, UInt32Type, UInt64Type,
25};
26use datafusion_common::cast::as_primitive_array;
27use datafusion_common::utils::hex::{HexCase, ToHex};
28use datafusion_common::{Result, ScalarValue, exec_err, internal_err};
29use datafusion_expr::{
30 Coercion, ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
31 TypeSignatureClass, Volatility,
32};
33use datafusion_macros::user_doc;
34
35fn to_hex_array<T: ArrowPrimitiveType>(array: &ArrayRef) -> Result<ArrayRef>
38where
39 T::Native: ToHex,
40{
41 let integer_array = as_primitive_array::<T>(array)?;
42 let len = integer_array.len();
43
44 let max_hex_len = T::Native::get_byte_width() * 2;
46
47 let mut offsets: Vec<i32> = Vec::with_capacity(len + 1);
49 let mut values: Vec<u8> = Vec::with_capacity(len * max_hex_len);
50
51 let mut hex_buffer = [0u8; 16];
53
54 offsets.push(0);
56
57 for value in integer_array.values() {
60 values.extend_from_slice(value.write_hex(HexCase::Lower, &mut hex_buffer));
61 offsets.push(values.len() as i32);
62 }
63
64 let nulls = integer_array.nulls().cloned();
66
67 let offsets =
70 unsafe { OffsetBuffer::new_unchecked(Buffer::from_vec(offsets).into()) };
71 let result = StringArray::new(offsets, Buffer::from_vec(values), nulls);
72
73 Ok(Arc::new(result) as ArrayRef)
74}
75
76#[inline]
77fn to_hex_scalar<T: ToHex>(value: T) -> String {
78 let mut hex_buffer = [0u8; 16];
79 let hex = value.write_hex(HexCase::Lower, &mut hex_buffer);
80 unsafe { std::str::from_utf8_unchecked(hex).to_string() }
82}
83
84#[user_doc(
85 doc_section(label = "String Functions"),
86 description = "Converts an integer to a hexadecimal string.",
87 syntax_example = "to_hex(int)",
88 sql_example = r#"```sql
89> select to_hex(12345689);
90+-------------------------+
91| to_hex(Int64(12345689)) |
92+-------------------------+
93| bc6159 |
94+-------------------------+
95```"#,
96 standard_argument(name = "int", prefix = "Integer")
97)]
98#[derive(Debug, PartialEq, Eq, Hash)]
99pub struct ToHexFunc {
100 signature: Signature,
101}
102
103impl Default for ToHexFunc {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109impl ToHexFunc {
110 pub fn new() -> Self {
111 Self {
112 signature: Signature::coercible(
113 vec![Coercion::new_exact(TypeSignatureClass::Integer)],
114 Volatility::Immutable,
115 ),
116 }
117 }
118}
119
120impl ScalarUDFImpl for ToHexFunc {
121 fn name(&self) -> &str {
122 "to_hex"
123 }
124
125 fn signature(&self) -> &Signature {
126 &self.signature
127 }
128
129 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
130 Ok(DataType::Utf8)
131 }
132
133 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
134 let arg = &args.args[0];
135
136 match arg {
137 ColumnarValue::Scalar(ScalarValue::Int64(Some(v))) => Ok(
138 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
139 ),
140 ColumnarValue::Scalar(ScalarValue::UInt64(Some(v))) => Ok(
141 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
142 ),
143 ColumnarValue::Scalar(ScalarValue::Int32(Some(v))) => Ok(
144 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
145 ),
146 ColumnarValue::Scalar(ScalarValue::UInt32(Some(v))) => Ok(
147 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
148 ),
149 ColumnarValue::Scalar(ScalarValue::Int16(Some(v))) => Ok(
150 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
151 ),
152 ColumnarValue::Scalar(ScalarValue::UInt16(Some(v))) => Ok(
153 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
154 ),
155 ColumnarValue::Scalar(ScalarValue::Int8(Some(v))) => Ok(
156 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
157 ),
158 ColumnarValue::Scalar(ScalarValue::UInt8(Some(v))) => Ok(
159 ColumnarValue::Scalar(ScalarValue::Utf8(Some(to_hex_scalar(*v)))),
160 ),
161
162 ColumnarValue::Scalar(s) if s.is_null() => {
164 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
165 }
166
167 ColumnarValue::Array(array) => match array.data_type() {
168 DataType::Int64 => {
169 Ok(ColumnarValue::Array(to_hex_array::<Int64Type>(array)?))
170 }
171 DataType::UInt64 => {
172 Ok(ColumnarValue::Array(to_hex_array::<UInt64Type>(array)?))
173 }
174 DataType::Int32 => {
175 Ok(ColumnarValue::Array(to_hex_array::<Int32Type>(array)?))
176 }
177 DataType::UInt32 => {
178 Ok(ColumnarValue::Array(to_hex_array::<UInt32Type>(array)?))
179 }
180 DataType::Int16 => {
181 Ok(ColumnarValue::Array(to_hex_array::<Int16Type>(array)?))
182 }
183 DataType::UInt16 => {
184 Ok(ColumnarValue::Array(to_hex_array::<UInt16Type>(array)?))
185 }
186 DataType::Int8 => {
187 Ok(ColumnarValue::Array(to_hex_array::<Int8Type>(array)?))
188 }
189 DataType::UInt8 => {
190 Ok(ColumnarValue::Array(to_hex_array::<UInt8Type>(array)?))
191 }
192 other => exec_err!("Unsupported data type {other:?} for function to_hex"),
193 },
194
195 other => internal_err!(
196 "Unexpected argument type {:?} for function to_hex",
197 other.data_type()
198 ),
199 }
200 }
201
202 fn documentation(&self) -> Option<&Documentation> {
203 self.doc()
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use arrow::array::{
210 Int8Array, Int16Array, Int32Array, Int64Array, StringArray, UInt8Array,
211 UInt16Array, UInt32Array, UInt64Array,
212 };
213 use datafusion_common::cast::as_string_array;
214
215 use super::*;
216
217 macro_rules! test_to_hex_type {
218 ($name:ident, $arrow_type:ty, $array_type:ty) => {
220 test_to_hex_type!(
221 $name,
222 $arrow_type,
223 $array_type,
224 vec![Some(100), Some(0), None],
225 vec![Some("64"), Some("0"), None]
226 );
227 };
228
229 ($name:ident, $arrow_type:ty, $array_type:ty, $input:expr, $expected:expr) => {
231 #[test]
232 fn $name() -> Result<()> {
233 let input = $input;
234 let expected = $expected;
235
236 let array = <$array_type>::from(input);
237 let array_ref: ArrayRef = Arc::new(array);
238 let hex_result = to_hex_array::<$arrow_type>(&array_ref)?;
239 let hex_array = as_string_array(&hex_result)?;
240 let expected_array = StringArray::from(expected);
241
242 assert_eq!(&expected_array, hex_array);
243 Ok(())
244 }
245 };
246 }
247
248 test_to_hex_type!(
249 to_hex_int8,
250 Int8Type,
251 Int8Array,
252 vec![Some(100), Some(0), None, Some(-1)],
253 vec![Some("64"), Some("0"), None, Some("ffffffffffffffff")]
254 );
255 test_to_hex_type!(
256 to_hex_int16,
257 Int16Type,
258 Int16Array,
259 vec![Some(100), Some(0), None, Some(-1)],
260 vec![Some("64"), Some("0"), None, Some("ffffffffffffffff")]
261 );
262 test_to_hex_type!(
263 to_hex_int32,
264 Int32Type,
265 Int32Array,
266 vec![Some(100), Some(0), None, Some(-1)],
267 vec![Some("64"), Some("0"), None, Some("ffffffffffffffff")]
268 );
269 test_to_hex_type!(
270 to_hex_int64,
271 Int64Type,
272 Int64Array,
273 vec![Some(100), Some(0), None, Some(-1)],
274 vec![Some("64"), Some("0"), None, Some("ffffffffffffffff")]
275 );
276
277 test_to_hex_type!(to_hex_uint8, UInt8Type, UInt8Array);
278 test_to_hex_type!(to_hex_uint16, UInt16Type, UInt16Array);
279 test_to_hex_type!(to_hex_uint32, UInt32Type, UInt32Array);
280 test_to_hex_type!(to_hex_uint64, UInt64Type, UInt64Array);
281
282 test_to_hex_type!(
283 to_hex_large_signed,
284 Int64Type,
285 Int64Array,
286 vec![Some(i64::MAX), Some(i64::MIN)],
287 vec![Some("7fffffffffffffff"), Some("8000000000000000")]
288 );
289
290 test_to_hex_type!(
291 to_hex_large_unsigned,
292 UInt64Type,
293 UInt64Array,
294 vec![Some(u64::MAX), Some(u64::MIN)],
295 vec![Some("ffffffffffffffff"), Some("0")]
296 );
297}