Skip to main content

datafusion_session/
session.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 async_trait::async_trait;
19use datafusion_common::config::{ConfigOptions, TableOptions};
20use datafusion_common::{DFSchema, Result};
21use datafusion_execution::TaskContext;
22use datafusion_execution::config::SessionConfig;
23use datafusion_execution::runtime_env::RuntimeEnv;
24use datafusion_expr::execution_props::ExecutionProps;
25use datafusion_expr::registry::ExtensionTypeRegistryRef;
26use datafusion_expr::{
27    AggregateUDF, Expr, HigherOrderUDF, LogicalPlan, ScalarUDF, WindowUDF,
28};
29use datafusion_physical_plan::operator_statistics::StatisticsRegistry;
30use datafusion_physical_plan::{ExecutionPlan, PhysicalExpr};
31
32use crate::CatalogProviderList;
33use parking_lot::{Mutex, RwLock};
34use std::any::Any;
35use std::collections::HashMap;
36use std::sync::{Arc, Weak};
37
38use crate::{PhysicalOptimizerRule, QueryPlanner, UnsupportedQueryPlanner};
39
40/// Interface for accessing [`SessionState`] from the catalog and data source.
41///
42/// This trait provides access to the information needed to plan and execute
43/// queries, such as configuration, functions, and runtime environment. See the
44/// documentation on [`SessionState`] for more information.
45///
46/// Historically, the `SessionState` struct was passed directly to catalog
47/// traits such as [`TableProvider`], which required a direct dependency on the
48/// DataFusion core. The interface required is now defined by this trait. See
49/// [#10782] for more details.
50///
51/// [#10782]: https://github.com/apache/datafusion/issues/10782
52///
53/// # Migration from `SessionState`
54///
55/// Using trait methods is preferred, as the implementation may change in future
56/// versions. However, you can downcast a `Session` to a `SessionState` as shown
57/// in the example below. If you find yourself needing to do this, please open
58/// an issue on the DataFusion repository so we can extend the trait to provide
59/// the required information.
60///
61/// ```
62/// # use datafusion_session::Session;
63/// # use datafusion_common::{Result, exec_datafusion_err};
64/// # struct SessionState {}
65/// // Given a `Session` reference, get the concrete `SessionState` reference
66/// // Note: this may stop working in future versions,
67/// fn session_state_from_session(session: &dyn Session) -> Result<&SessionState> {
68///     session
69///         .as_any()
70///         .downcast_ref::<SessionState>()
71///         .ok_or_else(|| {
72///             exec_datafusion_err!("Failed to downcast Session to SessionState")
73///         })
74/// }
75/// ```
76///
77/// [`SessionState`]: https://docs.rs/datafusion/latest/datafusion/execution/session_state/struct.SessionState.html
78/// [`TableProvider`]: https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html
79#[async_trait]
80pub trait Session: Send + Sync {
81    /// Return the session ID
82    fn session_id(&self) -> &str;
83
84    /// Return the [`SessionConfig`]
85    fn config(&self) -> &SessionConfig;
86
87    /// Return the catalogs registered with this session.
88    fn catalog_list(&self) -> Arc<dyn CatalogProviderList>;
89
90    /// return the [`ConfigOptions`]
91    fn config_options(&self) -> &ConfigOptions {
92        self.config().options()
93    }
94
95    /// Return the query planner for this session.
96    ///
97    /// # Warning
98    ///
99    /// The default implementation returns an [`UnsupportedQueryPlanner`], so
100    /// [`Session::create_physical_plan`] will fail. Sessions that support
101    /// physical planning should override this method (for example by returning
102    /// `SessionState::query_planner`).
103    fn query_planner(&self) -> Arc<dyn QueryPlanner + Send + Sync> {
104        Arc::new(UnsupportedQueryPlanner)
105    }
106
107    /// Optimize a logical plan.
108    ///
109    /// # Warning
110    ///
111    /// The default implementation returns the plan **unchanged**, applying no
112    /// logical optimizations whatsoever. This is almost never what you want:
113    /// without optimization, queries execute in their naive, unoptimized form
114    /// and may be dramatically slower or fail to run at all. The default exists
115    /// only so this crate need not depend on the optimizer; any real session
116    /// should override this method (for example by delegating to
117    /// `SessionState::optimize`).
118    fn optimize(&self, plan: &LogicalPlan) -> Result<LogicalPlan> {
119        Ok(plan.clone())
120    }
121
122    /// Return the physical optimizer rules for this session.
123    ///
124    /// # Warning
125    ///
126    /// The default implementation returns **no rules**. This is almost never
127    /// what you want: DataFusion relies on physical optimizer rules for
128    /// correctness-critical rewrites (such as inserting the repartitioning and
129    /// coalescing needed for parallel and multi-partition execution), so a
130    /// session with no rules will produce plans that are inefficient or that
131    /// fail to execute. The default exists only so this crate need not depend
132    /// on the optimizer; any real session should override this method (for
133    /// example by returning `SessionState::physical_optimizers`).
134    fn physical_optimizers(&self) -> &[Arc<dyn PhysicalOptimizerRule + Send + Sync>] {
135        &[]
136    }
137
138    /// Return the optional statistics registry used during physical optimization.
139    fn statistics_registry(&self) -> Option<&StatisticsRegistry> {
140        None
141    }
142
143    /// Creates a physical [`ExecutionPlan`] plan from a [`LogicalPlan`].
144    ///
145    /// Note: this will optimize the provided plan first.
146    ///
147    /// This function will error for [`LogicalPlan`]s such as catalog DDL like
148    /// `CREATE TABLE`, which do not have corresponding physical plans and must
149    /// be handled by another layer, typically the `SessionContext`.
150    async fn create_physical_plan(
151        &self,
152        logical_plan: &LogicalPlan,
153    ) -> Result<Arc<dyn ExecutionPlan>>;
154
155    /// Create a [`PhysicalExpr`] from an [`Expr`] after applying type
156    /// coercion, and function rewrites.
157    ///
158    /// Note: The expression is not simplified or otherwise optimized:  `a = 1
159    /// + 2` will not be simplified to `a = 3` as this is a more involved process.
160    /// See the [expr_api] example for how to simplify expressions.
161    ///
162    /// [expr_api]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/expr_api.rs
163    fn create_physical_expr(
164        &self,
165        expr: Expr,
166        df_schema: &DFSchema,
167    ) -> Result<Arc<dyn PhysicalExpr>>;
168
169    /// Return reference to scalar_functions
170    fn scalar_functions(&self) -> &HashMap<String, Arc<ScalarUDF>>;
171
172    /// Return reference to higher_order_functions
173    fn higher_order_functions(&self) -> &HashMap<String, Arc<HigherOrderUDF>>;
174
175    /// Return reference to aggregate_functions
176    fn aggregate_functions(&self) -> &HashMap<String, Arc<AggregateUDF>>;
177
178    /// Return reference to window functions
179    fn window_functions(&self) -> &HashMap<String, Arc<WindowUDF>>;
180
181    /// Return a reference to the extension type registry
182    fn extension_type_registry(&self) -> &ExtensionTypeRegistryRef;
183
184    /// Return the runtime env
185    fn runtime_env(&self) -> &Arc<RuntimeEnv>;
186
187    /// Return the execution properties
188    fn execution_props(&self) -> &ExecutionProps;
189
190    fn as_any(&self) -> &dyn Any;
191
192    /// Return the table options
193    fn table_options(&self) -> &TableOptions;
194
195    /// return the TableOptions options with its extensions
196    fn default_table_options(&self) -> TableOptions {
197        self.table_options()
198            .combine_with_session_config(self.config_options())
199    }
200
201    /// Returns a mutable reference to [`TableOptions`]
202    fn table_options_mut(&mut self) -> &mut TableOptions;
203
204    /// Get a new TaskContext to run in this session
205    fn task_ctx(&self) -> Arc<TaskContext>;
206}
207
208/// Create a new task context instance from Session
209impl From<&dyn Session> for TaskContext {
210    fn from(state: &dyn Session) -> Self {
211        let task_id = None;
212        TaskContext::new(
213            task_id,
214            state.session_id().to_string(),
215            state.config().clone(),
216            state.scalar_functions().clone(),
217            state.higher_order_functions().clone(),
218            state.aggregate_functions().clone(),
219            state.window_functions().clone(),
220            Arc::clone(state.runtime_env()),
221        )
222    }
223}
224type SessionRefLock = Arc<Mutex<Option<Weak<RwLock<dyn Session>>>>>;
225/// The state store that stores the reference of the runtime session state.
226#[derive(Debug)]
227pub struct SessionStore {
228    session: SessionRefLock,
229}
230
231impl SessionStore {
232    /// Create a new [SessionStore]
233    pub fn new() -> Self {
234        Self {
235            session: Arc::new(Mutex::new(None)),
236        }
237    }
238
239    /// Set the session state of the store
240    pub fn with_state(&self, state: Weak<RwLock<dyn Session>>) {
241        let mut lock = self.session.lock();
242        *lock = Some(state);
243    }
244
245    /// Get the current session of the store
246    pub fn get_session(&self) -> Weak<RwLock<dyn Session>> {
247        self.session.lock().clone().unwrap()
248    }
249}
250
251impl Default for SessionStore {
252    fn default() -> Self {
253        Self::new()
254    }
255}