datafusion_functions_json/
json_get_bool.rs1use datafusion::arrow::array::BooleanArray;
2use datafusion::arrow::datatypes::DataType;
3use datafusion::common::Result as DataFusionResult;
4use datafusion::logical_expr::{ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl, Signature, Volatility};
5use jiter::Peek;
6
7use crate::common::{get_err, invoke, jiter_json_find, return_type_check, GetError, JsonPath};
8use crate::common_macros::make_udf_function;
9
10make_udf_function!(
11 JsonGetBool,
12 json_get_bool,
13 json_data path,
14 r#"Get an boolean value from a JSON string by its "path""#
15);
16
17#[derive(Debug, PartialEq, Eq, Hash)]
18pub(super) struct JsonGetBool {
19 signature: Signature,
20 aliases: [String; 1],
21}
22
23impl Default for JsonGetBool {
24 fn default() -> Self {
25 Self {
26 signature: Signature::variadic_any(Volatility::Immutable),
27 aliases: ["json_get_bool".to_string()],
28 }
29 }
30}
31
32impl ScalarUDFImpl for JsonGetBool {
33 fn name(&self) -> &str {
34 self.aliases[0].as_str()
35 }
36
37 fn signature(&self) -> &Signature {
38 &self.signature
39 }
40
41 fn return_type(&self, arg_types: &[DataType]) -> DataFusionResult<DataType> {
42 return_type_check(arg_types, self.name(), DataType::Boolean).map(|_| DataType::Boolean)
43 }
44
45 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> DataFusionResult<ColumnarValue> {
46 invoke::<BooleanArray>(&args.args, jiter_json_get_bool)
47 }
48
49 fn aliases(&self) -> &[String] {
50 &self.aliases
51 }
52
53 fn placement(
54 &self,
55 args: &[datafusion::logical_expr::ExpressionPlacement],
56 ) -> datafusion::logical_expr::ExpressionPlacement {
57 if args.len() >= 2
60 && matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
61 && args[1..]
62 .iter()
63 .all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
64 {
65 datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
66 } else {
67 datafusion::logical_expr::ExpressionPlacement::KeepInPlace
68 }
69 }
70}
71
72fn jiter_json_get_bool(json_data: Option<&str>, path: &[JsonPath]) -> Result<bool, GetError> {
73 if let Some((mut jiter, peek)) = jiter_json_find(json_data, path) {
74 match peek {
75 Peek::True | Peek::False => Ok(jiter.known_bool(peek)?),
76 Peek::String => {
77 let s = jiter.known_str()?;
78 s.parse::<bool>().map_err(|_| GetError)
79 }
80 _ => get_err!(),
81 }
82 } else {
83 get_err!()
84 }
85}