risingwave_expr/aggregate/
user_defined.rs1use std::sync::Arc;
16
17use anyhow::Context;
18use risingwave_common::array::Op;
19use risingwave_common::array::arrow::arrow_array_udf::ArrayRef;
20use risingwave_common::array::arrow::arrow_schema_udf::{Field, Fields, Schema, SchemaRef};
21use risingwave_common::array::arrow::{UdfArrowConvert, UdfFromArrow, UdfToArrow};
22use risingwave_common::bitmap::Bitmap;
23use risingwave_pb::expr::PbUserDefinedFunctionMetadata;
24
25use super::*;
26use crate::sig::{BuildOptions, UdfImpl, UdfKind};
27
28#[derive(Debug)]
29pub struct UserDefinedAggregateFunction {
30 arg_schema: SchemaRef,
31 return_type: DataType,
32 return_field: Field,
33 state_field: Field,
34 runtime: Box<dyn UdfImpl>,
35}
36
37#[async_trait::async_trait]
38impl AggregateFunction for UserDefinedAggregateFunction {
39 fn return_type(&self) -> DataType {
40 self.return_type.clone()
41 }
42
43 fn create_state(&self) -> Result<AggregateState> {
45 futures::executor::block_on(async {
47 let state = self.runtime.call_agg_create_state().await?;
48 Ok(AggregateState::Any(Box::new(State(state))))
49 })
50 }
51
52 async fn update(&self, state: &mut AggregateState, input: &StreamChunk) -> Result<()> {
54 let state = &mut state.downcast_mut::<State>().0;
55 let ops = input
56 .visibility()
57 .iter_ones()
58 .map(|i| Some(matches!(input.ops()[i], Op::Delete | Op::UpdateDelete)))
59 .collect();
60 let arrow_input = UdfArrowConvert::default()
62 .to_record_batch(self.arg_schema.clone(), input.data_chunk())?;
63 let new_state = self
64 .runtime
65 .call_agg_accumulate_or_retract(state, &ops, &arrow_input)
66 .await?;
67 *state = new_state;
68 Ok(())
69 }
70
71 async fn update_range(
73 &self,
74 state: &mut AggregateState,
75 input: &StreamChunk,
76 range: Range<usize>,
77 ) -> Result<()> {
78 let vis = input.visibility() & Bitmap::from_range(input.capacity(), range);
80 let input = input.clone_with_vis(vis);
81 self.update(state, &input).await
82 }
83
84 async fn get_result(&self, state: &AggregateState) -> Result<Datum> {
86 let state = &state.downcast_ref::<State>().0;
87 let arrow_output = self.runtime.call_agg_finish(state).await?;
88 let output = UdfArrowConvert::default().from_array(&self.return_field, &arrow_output)?;
89 if output.data_type() != self.return_type {
93 return Err(anyhow::anyhow!(
94 "UDF returned a value of type {} while the declared return type is {}",
95 output.data_type(),
96 self.return_type
97 )
98 .into());
99 }
100 Ok(output.datum_at(0))
101 }
102
103 fn encode_state(&self, state: &AggregateState) -> Result<Datum> {
105 let state = &state.downcast_ref::<State>().0;
106 let state = UdfArrowConvert::default().from_array(&self.state_field, state)?;
107 Ok(state.datum_at(0))
108 }
109
110 fn decode_state(&self, datum: Datum) -> Result<AggregateState> {
112 let array = {
113 let mut builder = DataType::Bytea.create_array_builder(1);
114 builder.append(datum);
115 builder.finish()
116 };
117 let state = UdfArrowConvert::default().to_array(self.state_field.data_type(), &array)?;
118 Ok(AggregateState::Any(Box::new(State(state))))
119 }
120}
121
122#[derive(Debug)]
126struct State(ArrayRef);
127
128impl EstimateSize for State {
129 fn estimated_heap_size(&self) -> usize {
130 self.0.get_array_memory_size()
131 }
132}
133
134impl AggStateDyn for State {}
135
136pub fn new_user_defined(
138 return_type: &DataType,
139 udf: &PbUserDefinedFunctionMetadata,
140) -> Result<BoxedAggregateFunction> {
141 let arg_types = udf.arg_types.iter().map(|t| t.into()).collect::<Vec<_>>();
142 let language = udf.language.as_str();
143 let runtime = udf.runtime.as_deref();
144 let link = udf.link.as_deref();
145
146 let name_in_runtime = udf
147 .name_in_runtime()
148 .expect("SQL UDF won't get here, other UDFs must have `name_in_runtime`");
149
150 let build_fn = crate::sig::find_udf_impl(language, runtime, link)?.build_fn;
151 let runtime = build_fn(BuildOptions {
152 kind: UdfKind::Aggregate,
153 body: udf.body.as_deref(),
154 compressed_binary: udf.compressed_binary.as_deref(),
155 link: udf.link.as_deref(),
156 name_in_runtime,
157 arg_names: &udf.arg_names,
158 arg_types: &arg_types,
159 return_type,
160 always_retry_on_network_error: false,
161 language,
162 is_async: udf.is_async,
163 is_batched: udf.is_batched,
164 })
165 .context("failed to build UDF runtime")?;
166
167 let arrow_convert = UdfArrowConvert::default();
170 let arg_schema = Arc::new(Schema::new(
171 arg_types
172 .iter()
173 .map(|t| arrow_convert.to_arrow_field("", t))
174 .try_collect::<_, Fields, _>()?,
175 ));
176
177 Ok(Box::new(UserDefinedAggregateFunction {
178 return_field: arrow_convert.to_arrow_field("", return_type)?,
179 state_field: Field::new(
180 "state",
181 risingwave_common::array::arrow::arrow_schema_udf::DataType::Binary,
182 true,
183 ),
184 return_type: return_type.clone(),
185 arg_schema,
186 runtime,
187 }))
188}
189
190#[cfg(test)]
191mod tests {
192 use anyhow::Result;
193 use futures::stream::BoxStream;
194 use risingwave_common::array::arrow::arrow_array_udf::{
195 Float64Array, Int32Array, Int64Array, MapArray, RecordBatch, StringArray, StructArray,
196 };
197 use risingwave_common::array::arrow::arrow_buffer_udf::OffsetBuffer;
198 use risingwave_common::array::arrow::arrow_schema_udf::DataType as ArrowDataType;
199 use risingwave_common::types::{MapType, StructType};
200
201 use super::*;
202 use crate::sig::UdfImpl;
203
204 #[derive(Debug)]
206 struct MistypedRuntime(ArrayRef);
207
208 #[async_trait::async_trait]
209 impl UdfImpl for MistypedRuntime {
210 async fn call(&self, _input: &RecordBatch) -> Result<RecordBatch> {
211 unimplemented!()
212 }
213
214 async fn call_table_function<'a>(
215 &'a self,
216 _input: &'a RecordBatch,
217 ) -> Result<BoxStream<'a, Result<RecordBatch>>> {
218 unimplemented!()
219 }
220
221 async fn call_agg_finish(&self, _state: &ArrayRef) -> Result<ArrayRef> {
222 Ok(self.0.clone())
223 }
224 }
225
226 async fn get_result_err(return_type: DataType, output: ArrayRef) -> String {
228 let convert = UdfArrowConvert::default();
229 let agg = UserDefinedAggregateFunction {
230 return_field: convert.to_arrow_field("", &return_type).unwrap(),
231 state_field: Field::new("state", ArrowDataType::Binary, true),
232 return_type,
233 arg_schema: Arc::new(Schema::new(Vec::<Field>::new())),
234 runtime: Box::new(MistypedRuntime(output)),
235 };
236 let state = AggregateState::Any(Box::new(State(
237 Arc::new(Int64Array::from(vec![0i64])) as ArrayRef
238 )));
239 let err = agg.get_result(&state).await.unwrap_err();
240 format!("{:?}", anyhow::anyhow!(err))
241 }
242
243 #[tokio::test]
244 async fn misbehaving_runtime_mistyped_finish() {
245 let fields: Fields = vec![Field::new("a", ArrowDataType::Utf8, true)].into();
247 let struct_output: ArrayRef = Arc::new(StructArray::new(
248 fields,
249 vec![Arc::new(StringArray::from(vec![Some("oops")])) as ArrayRef],
250 None,
251 ));
252 let msg = get_result_err(
253 DataType::Struct(StructType::new(vec![("a", DataType::Int64)])),
254 struct_output,
255 )
256 .await;
257 assert!(
258 msg.contains("declared return type"),
259 "unexpected error: {msg}"
260 );
261
262 let msg = get_result_err(DataType::Int64, Arc::new(Int32Array::from(vec![Some(1)]))).await;
264 assert!(
265 msg.contains("declared return type"),
266 "unexpected error: {msg}"
267 );
268
269 let entries_fields: Fields = vec![
272 Field::new("key", ArrowDataType::Float64, false),
273 Field::new("value", ArrowDataType::Int32, true),
274 ]
275 .into();
276 let entries = StructArray::new(
277 entries_fields.clone(),
278 vec![
279 Arc::new(Float64Array::from(vec![Some(1.5)])) as ArrayRef,
280 Arc::new(Int32Array::from(vec![Some(42)])),
281 ],
282 None,
283 );
284 let map_output: ArrayRef = Arc::new(MapArray::new(
285 Arc::new(Field::new(
286 "entries",
287 ArrowDataType::Struct(entries_fields),
288 false,
289 )),
290 OffsetBuffer::new(vec![0, 1].into()),
291 entries,
292 None,
293 false,
294 ));
295 let msg = get_result_err(
296 DataType::Map(MapType::from_kv(DataType::Varchar, DataType::Int32)),
297 map_output,
298 )
299 .await;
300 assert!(
301 msg.contains("invalid map key type"),
302 "unexpected error: {msg}"
303 );
304 }
305}