Skip to content

Commit

Permalink
Support hex string literal
Browse files Browse the repository at this point in the history
  • Loading branch information
ShiKaiWi committed Jun 26, 2023
1 parent 13314c3 commit acb5d97
Showing 1 changed file with 69 additions and 0 deletions.
69 changes: 69 additions & 0 deletions datafusion/sql/src/expr/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:?}'",
))),
Expand Down Expand Up @@ -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<Vec<u8>> {
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<u8> {
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);
}
}
}

0 comments on commit acb5d97

Please sign in to comment.