datafusion_expr/execution_props.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::var_provider::{VarProvider, VarType};
19use chrono::{DateTime, Utc};
20use datafusion_common::HashMap;
21use datafusion_common::alias::AliasGenerator;
22use datafusion_common::config::ConfigOptions;
23use std::sync::Arc;
24
25/// Holds properties and scratch state used while optimizing a [`LogicalPlan`]
26/// and translating it into an executable physical plan, such as the statement
27/// start time used during simplification.
28///
29/// An [`ExecutionProps`] is created each time a `LogicalPlan` is
30/// prepared for execution (optimized). If the same plan is optimized
31/// multiple times, a new `ExecutionProps` is created each time.
32///
33/// It is important that this structure be cheap to create as it is
34/// done so during predicate pruning and expression simplification
35///
36/// # Relationship with [`TaskContext`]
37///
38/// [`ExecutionProps`] is intentionally distinct from [`TaskContext`].
39/// It is used while optimizing a logical plan and constructing physical
40/// expressions and physical plans, before physical operators are run.
41///
42/// [`TaskContext`] is the runtime context passed to physical operators during
43/// physical-plan execution.
44///
45/// Keeping these structures separate avoids threading execution/runtime state
46/// through planning APIs, and avoids making execution depend on planner-only
47/// scratch state.
48///
49/// [`TaskContext`]: https://docs.rs/datafusion/latest/datafusion/execution/struct.TaskContext.html
50/// [`LogicalPlan`]: crate::LogicalPlan
51#[derive(Clone, Debug)]
52pub struct ExecutionProps {
53 /// The time at which the query execution started. If `None`,
54 /// functions like `now()` will not be simplified during optimization.
55 pub query_execution_start_time: Option<DateTime<Utc>>,
56 /// Alias generator used by subquery optimizer rules
57 pub alias_generator: Arc<AliasGenerator>,
58 /// Snapshot of config options when the query started
59 pub config_options: Option<Arc<ConfigOptions>>,
60 /// Providers for scalar variables
61 pub var_providers: Option<HashMap<VarType, Arc<dyn VarProvider + Send + Sync>>>,
62}
63
64impl Default for ExecutionProps {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl ExecutionProps {
71 /// Creates a new execution props
72 pub fn new() -> Self {
73 ExecutionProps {
74 query_execution_start_time: None,
75 alias_generator: Arc::new(AliasGenerator::new()),
76 config_options: None,
77 var_providers: None,
78 }
79 }
80
81 /// Set the query execution start time to use
82 pub fn with_query_execution_start_time(
83 mut self,
84 query_execution_start_time: DateTime<Utc>,
85 ) -> Self {
86 self.query_execution_start_time = Some(query_execution_start_time);
87 self
88 }
89
90 #[deprecated(since = "50.0.0", note = "Use mark_start_execution instead")]
91 pub fn start_execution(&mut self) -> &Self {
92 let default_config = Arc::new(ConfigOptions::default());
93 self.mark_start_execution(default_config)
94 }
95
96 /// Marks the execution of query started timestamp.
97 /// This also instantiates a new alias generator.
98 pub fn mark_start_execution(&mut self, config_options: Arc<ConfigOptions>) -> &Self {
99 self.query_execution_start_time = Some(Utc::now());
100 self.alias_generator = Arc::new(AliasGenerator::new());
101 self.config_options = Some(config_options);
102 &*self
103 }
104
105 /// Registers a variable provider, returning the existing provider, if any
106 pub fn add_var_provider(
107 &mut self,
108 var_type: VarType,
109 provider: Arc<dyn VarProvider + Send + Sync>,
110 ) -> Option<Arc<dyn VarProvider + Send + Sync>> {
111 let mut var_providers = self.var_providers.take().unwrap_or_default();
112
113 let old_provider = var_providers.insert(var_type, provider);
114
115 self.var_providers = Some(var_providers);
116
117 old_provider
118 }
119
120 /// Returns the provider for the `var_type`, if any
121 #[expect(clippy::needless_pass_by_value)]
122 pub fn get_var_provider(
123 &self,
124 var_type: VarType,
125 ) -> Option<Arc<dyn VarProvider + Send + Sync>> {
126 self.var_providers
127 .as_ref()
128 .and_then(|var_providers| var_providers.get(&var_type).cloned())
129 }
130
131 /// Returns the configuration properties for this execution
132 /// if the execution has started
133 pub fn config_options(&self) -> Option<&Arc<ConfigOptions>> {
134 self.config_options.as_ref()
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use super::*;
141
142 #[test]
143 fn debug() {
144 let props = ExecutionProps::new();
145 assert_eq!(
146 "ExecutionProps { query_execution_start_time: None, alias_generator: AliasGenerator { next_id: 1 }, config_options: None, var_providers: None }",
147 format!("{props:?}")
148 );
149 }
150}