1use crate::{
19 config::SessionConfig, memory_pool::MemoryPool, registry::FunctionRegistry,
20 runtime_env::RuntimeEnv,
21};
22use datafusion_common::{Result, internal_datafusion_err, plan_datafusion_err};
23use datafusion_expr::planner::ExprPlanner;
24use datafusion_expr::{AggregateUDF, HigherOrderUDF, ScalarUDF, WindowUDF};
25use std::collections::HashSet;
26use std::{collections::HashMap, sync::Arc};
27
28#[derive(Debug)]
52pub struct TaskContext {
53 session_id: String,
55 task_id: Option<String>,
57 session_config: SessionConfig,
59 scalar_functions: HashMap<String, Arc<ScalarUDF>>,
61 higher_order_functions: HashMap<String, Arc<HigherOrderUDF>>,
63 aggregate_functions: HashMap<String, Arc<AggregateUDF>>,
65 window_functions: HashMap<String, Arc<WindowUDF>>,
67 runtime: Arc<RuntimeEnv>,
69}
70
71impl Default for TaskContext {
72 fn default() -> Self {
73 let runtime = Arc::new(RuntimeEnv::default());
74
75 Self {
77 session_id: "DEFAULT".to_string(),
78 task_id: None,
79 session_config: SessionConfig::new(),
80 scalar_functions: HashMap::new(),
81 higher_order_functions: HashMap::new(),
82 aggregate_functions: HashMap::new(),
83 window_functions: HashMap::new(),
84 runtime,
85 }
86 }
87}
88
89impl TaskContext {
90 #[expect(clippy::too_many_arguments)]
96 pub fn new(
97 task_id: Option<String>,
98 session_id: String,
99 session_config: SessionConfig,
100 scalar_functions: HashMap<String, Arc<ScalarUDF>>,
101 higher_order_functions: HashMap<String, Arc<HigherOrderUDF>>,
102 aggregate_functions: HashMap<String, Arc<AggregateUDF>>,
103 window_functions: HashMap<String, Arc<WindowUDF>>,
104 runtime: Arc<RuntimeEnv>,
105 ) -> Self {
106 Self {
107 task_id,
108 session_id,
109 session_config,
110 scalar_functions,
111 higher_order_functions,
112 aggregate_functions,
113 window_functions,
114 runtime,
115 }
116 }
117
118 pub fn session_config(&self) -> &SessionConfig {
120 &self.session_config
121 }
122
123 pub fn session_id(&self) -> String {
125 self.session_id.clone()
126 }
127
128 pub fn task_id(&self) -> Option<String> {
130 self.task_id.clone()
131 }
132
133 pub fn memory_pool(&self) -> &Arc<dyn MemoryPool> {
135 &self.runtime.memory_pool
136 }
137
138 pub fn runtime_env(&self) -> Arc<RuntimeEnv> {
140 Arc::clone(&self.runtime)
141 }
142
143 pub fn scalar_functions(&self) -> &HashMap<String, Arc<ScalarUDF>> {
144 &self.scalar_functions
145 }
146
147 pub fn higher_order_functions(&self) -> &HashMap<String, Arc<HigherOrderUDF>> {
148 &self.higher_order_functions
149 }
150
151 pub fn aggregate_functions(&self) -> &HashMap<String, Arc<AggregateUDF>> {
152 &self.aggregate_functions
153 }
154
155 pub fn window_functions(&self) -> &HashMap<String, Arc<WindowUDF>> {
156 &self.window_functions
157 }
158
159 pub fn with_session_config(mut self, session_config: SessionConfig) -> Self {
161 self.session_config = session_config;
162 self
163 }
164
165 pub fn with_runtime(mut self, runtime: Arc<RuntimeEnv>) -> Self {
167 self.runtime = runtime;
168 self
169 }
170
171 pub fn with_task_id(mut self, task_id: String) -> Self {
173 self.task_id = Some(task_id);
174 self
175 }
176}
177
178impl FunctionRegistry for TaskContext {
179 fn udfs(&self) -> HashSet<String> {
180 self.scalar_functions.keys().cloned().collect()
181 }
182
183 fn udf(&self, name: &str) -> Result<Arc<ScalarUDF>> {
184 let result = self.scalar_functions.get(name);
185
186 result.cloned().ok_or_else(|| {
187 plan_datafusion_err!("There is no UDF named \"{name}\" in the TaskContext")
188 })
189 }
190
191 fn higher_order_function(&self, name: &str) -> Result<Arc<HigherOrderUDF>> {
192 let result = self.higher_order_functions.get(name);
193
194 result.cloned().ok_or_else(|| {
195 plan_datafusion_err!(
196 "There is no higher-order function named \"{name}\" in the TaskContext"
197 )
198 })
199 }
200
201 fn udaf(&self, name: &str) -> Result<Arc<AggregateUDF>> {
202 let result = self.aggregate_functions.get(name);
203
204 result.cloned().ok_or_else(|| {
205 plan_datafusion_err!("There is no UDAF named \"{name}\" in the TaskContext")
206 })
207 }
208
209 fn udwf(&self, name: &str) -> Result<Arc<WindowUDF>> {
210 let result = self.window_functions.get(name);
211
212 result.cloned().ok_or_else(|| {
213 internal_datafusion_err!(
214 "There is no UDWF named \"{name}\" in the TaskContext"
215 )
216 })
217 }
218 fn register_udaf(
219 &mut self,
220 udaf: Arc<AggregateUDF>,
221 ) -> Result<Option<Arc<AggregateUDF>>> {
222 udaf.aliases().iter().for_each(|alias| {
223 self.aggregate_functions
224 .insert(alias.clone(), Arc::clone(&udaf));
225 });
226 Ok(self.aggregate_functions.insert(udaf.name().into(), udaf))
227 }
228 fn register_udwf(&mut self, udwf: Arc<WindowUDF>) -> Result<Option<Arc<WindowUDF>>> {
229 udwf.aliases().iter().for_each(|alias| {
230 self.window_functions
231 .insert(alias.clone(), Arc::clone(&udwf));
232 });
233 Ok(self.window_functions.insert(udwf.name().into(), udwf))
234 }
235 fn register_udf(&mut self, udf: Arc<ScalarUDF>) -> Result<Option<Arc<ScalarUDF>>> {
236 udf.aliases().iter().for_each(|alias| {
237 self.scalar_functions
238 .insert(alias.clone(), Arc::clone(&udf));
239 });
240 Ok(self.scalar_functions.insert(udf.name().into(), udf))
241 }
242
243 fn register_higher_order_function(
244 &mut self,
245 function: Arc<HigherOrderUDF>,
246 ) -> Result<Option<Arc<HigherOrderUDF>>> {
247 function.aliases().iter().for_each(|alias| {
248 self.higher_order_functions
249 .insert(alias.clone(), Arc::clone(&function));
250 });
251 Ok(self
252 .higher_order_functions
253 .insert(function.name().into(), function))
254 }
255
256 fn expr_planners(&self) -> Vec<Arc<dyn ExprPlanner>> {
257 vec![]
258 }
259
260 fn higher_order_function_names(&self) -> HashSet<String> {
261 self.higher_order_functions.keys().cloned().collect()
262 }
263
264 fn udafs(&self) -> HashSet<String> {
265 self.aggregate_functions.keys().cloned().collect()
266 }
267
268 fn udwfs(&self) -> HashSet<String> {
269 self.window_functions.keys().cloned().collect()
270 }
271}
272
273pub trait TaskContextProvider {
275 fn task_ctx(&self) -> Arc<TaskContext>;
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use datafusion_common::{
282 config::{ConfigExtension, ConfigOptions, Extensions},
283 extensions_options,
284 };
285
286 extensions_options! {
287 struct TestExtension {
288 value: usize, default = 42
289 option_value: Option<usize>, default = None
290 }
291 }
292
293 impl ConfigExtension for TestExtension {
294 const PREFIX: &'static str = "test";
295 }
296
297 #[test]
298 fn task_context_extensions() -> Result<()> {
299 let runtime = Arc::new(RuntimeEnv::default());
300 let mut extensions = Extensions::new();
301 extensions.insert(TestExtension::default());
302
303 let mut config = ConfigOptions::new().with_extensions(extensions);
304 config.set("test.value", "24")?;
305 config.set("test.option_value", "42")?;
306 let session_config = SessionConfig::from(config);
307
308 let task_context = TaskContext::new(
309 Some("task_id".to_string()),
310 "session_id".to_string(),
311 session_config,
312 HashMap::default(),
313 HashMap::default(),
314 HashMap::default(),
315 HashMap::default(),
316 runtime,
317 );
318
319 let test = task_context
320 .session_config()
321 .options()
322 .extensions
323 .get::<TestExtension>();
324 assert!(test.is_some());
325
326 assert_eq!(test.unwrap().value, 24);
327 assert_eq!(test.unwrap().option_value, Some(42));
328
329 Ok(())
330 }
331
332 #[test]
333 fn task_context_extensions_default() -> Result<()> {
334 let runtime = Arc::new(RuntimeEnv::default());
335 let mut extensions = Extensions::new();
336 extensions.insert(TestExtension::default());
337
338 let config = ConfigOptions::new().with_extensions(extensions);
339 let session_config = SessionConfig::from(config);
340
341 let task_context = TaskContext::new(
342 Some("task_id".to_string()),
343 "session_id".to_string(),
344 session_config,
345 HashMap::default(),
346 HashMap::default(),
347 HashMap::default(),
348 HashMap::default(),
349 runtime,
350 );
351
352 let test = task_context
353 .session_config()
354 .options()
355 .extensions
356 .get::<TestExtension>();
357 assert!(test.is_some());
358
359 assert_eq!(test.unwrap().value, 42);
360 assert_eq!(test.unwrap().option_value, None);
361
362 Ok(())
363 }
364}