From acb5d97a8a8de5296989740f97db3773fe3aa45a Mon Sep 17 00:00:00 2001 From: "xikai.wxk" Date: Mon, 26 Jun 2023 17:38:19 +0800 Subject: [PATCH] Support hex string literal --- datafusion/sql/src/expr/value.rs | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs index f1c93d8bdc3a..0926ce34022a 100644 --- a/datafusion/sql/src/expr/value.rs +++ b/datafusion/sql/src/expr/value.rs @@ -39,6 +39,15 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { Value::Placeholder(param) => { Self::create_placeholder_expr(param, param_data_types) } + Value::HexStringLiteral(s) => { + if let Some(v) = try_decode_hex_literal(&s) { + Ok(lit(v)) + } else { + Err(DataFusionError::Plan(format!( + "Invalid HexStringLiteral '{s}'" + ))) + } + } _ => Err(DataFusionError::Plan(format!( "Unsupported Value '{value:?}'", ))), @@ -250,3 +259,63 @@ fn has_units(val: &str) -> bool { || val.ends_with("nanosecond") || val.ends_with("nanoseconds") } + +/// Try to decode bytes from hex literal string. +/// +/// None will be returned if the input literal is hex-invalid. +fn try_decode_hex_literal(s: &str) -> Option> { + let hex_bytes = s.as_bytes(); + + let mut decoded_bytes = Vec::with_capacity((hex_bytes.len() + 1) / 2); + + let start_idx = hex_bytes.len() % 2; + if start_idx > 0 { + // The first byte is formed of only one char. + decoded_bytes.push(try_decode_hex_char(hex_bytes[0])?); + } + + for i in (start_idx..hex_bytes.len()).step_by(2) { + let high = try_decode_hex_char(hex_bytes[i])?; + let low = try_decode_hex_char(hex_bytes[i + 1])?; + decoded_bytes.push(high << 4 | low); + } + + Some(decoded_bytes) +} + +/// Try to decode a byte from a hex char. +/// +/// None will be returned if the input char is hex-invalid. +const fn try_decode_hex_char(c: u8) -> Option { + match c { + b'A'..=b'F' => Some(c - b'A' + 10), + b'a'..=b'f' => Some(c - b'a' + 10), + b'0'..=b'9' => Some(c - b'0'), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_decode_hex_literal() { + let cases = [ + ("", Some(vec![])), + ("FF00", Some(vec![255, 0])), + ("a00a", Some(vec![160, 10])), + ("FF0", Some(vec![15, 240])), + ("f", Some(vec![15])), + ("FF0X", None), + ("X0", None), + ("XX", None), + ("x", None), + ]; + + for (input, expect) in cases { + let output = try_decode_hex_literal(input); + assert_eq!(output, expect); + } + } +}