datafusion_spark/function/string/
quote.rs1use arrow::array::{ArrayRef, OffsetSizeTrait, StringArray};
19use arrow::datatypes::DataType;
20use datafusion_expr::{Coercion, ColumnarValue, Signature, TypeSignatureClass};
21use datafusion_common::cast::{as_generic_string_array, as_string_view_array};
22use datafusion_common::types::{NativeType, logical_string};
23use datafusion_common::utils::take_function_args;
24use datafusion_common::{Result, exec_err};
25use datafusion_expr::{ScalarFunctionArgs, ScalarUDFImpl, Volatility};
26use datafusion_functions::utils::make_scalar_function;
27
28use std::sync::Arc;
29
30#[derive(Debug, PartialEq, Eq, Hash)]
33pub struct SparkQuote {
34 signature: Signature,
35}
36
37impl Default for SparkQuote {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43impl SparkQuote {
44 pub fn new() -> Self {
45 let str_coercion = Coercion::new_implicit(
46 TypeSignatureClass::Native(logical_string()),
47 vec![TypeSignatureClass::Any],
48 NativeType::String,
49 );
50 Self {
51 signature: Signature::coercible(vec![str_coercion], Volatility::Immutable),
52 }
53 }
54}
55
56impl ScalarUDFImpl for SparkQuote {
57 fn name(&self) -> &str {
58 "quote"
59 }
60
61 fn signature(&self) -> &Signature {
62 &self.signature
63 }
64
65 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
66 match &arg_types[0] {
67 DataType::LargeUtf8 => Ok(DataType::LargeUtf8),
68 _ => Ok(DataType::Utf8),
69 }
70 }
71
72 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
73 make_scalar_function(spark_quote_inner, vec![])(&args.args)
74 }
75}
76
77fn spark_quote_inner(arg: &[ArrayRef]) -> Result<ArrayRef> {
78 let [array] = take_function_args("quote", arg)?;
79 match &array.data_type() {
80 DataType::Utf8 => quote_array::<i32>(array),
81 DataType::LargeUtf8 => quote_array::<i64>(array),
82 DataType::Utf8View => quote_view(array),
83 other => {
84 exec_err!("unsupported data type {other:?} for function `quote`")
85 }
86 }
87}
88
89fn quote_array<T: OffsetSizeTrait>(array: &ArrayRef) -> Result<ArrayRef> {
90 let str_array = as_generic_string_array::<T>(array)?;
91 let result = str_array
92 .iter()
93 .map(|s| s.map(compute_quote))
94 .collect::<StringArray>();
95 Ok(Arc::new(result))
96}
97
98fn quote_view(str_view: &ArrayRef) -> Result<ArrayRef> {
99 let str_array = as_string_view_array(str_view)?;
100 let result = str_array
101 .iter()
102 .map(|opt_str| opt_str.map(compute_quote))
103 .collect::<StringArray>();
104 Ok(Arc::new(result) as ArrayRef)
105}
106
107const QUOTE_CHAR: char = '\'';
108const ESCAPE_CHAR: char = '\\';
109
110fn compute_quote(s: &str) -> String {
111 let mut quoted = String::with_capacity(s.len() + 2);
112 quoted.push(QUOTE_CHAR);
113 for c in s.chars() {
114 if c == QUOTE_CHAR {
115 quoted.push(ESCAPE_CHAR);
116 }
117 quoted.push(c);
118 }
119 quoted.push(QUOTE_CHAR);
120 quoted
121}