From 90b0077f0721a0ca81edb878d67670cc5d9b3d0d Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Sat, 14 Sep 2024 06:57:31 -0400 Subject: [PATCH 1/3] Implement Extension registration API --- src/app/mod.rs | 45 +++++++------ src/cli/mod.rs | 5 +- src/execution/mod.rs | 77 ++++----------------- src/extensions/builder.rs | 130 ++++++++++++++++++++++++++++++++++++ src/extensions/deltalake.rs | 42 ++++++++++++ src/extensions/mod.rs | 49 ++++++++++++++ src/extensions/s3.rs | 74 ++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 10 ++- tests/tui.rs | 105 +++++++++++++++++++---------- 10 files changed, 409 insertions(+), 129 deletions(-) create mode 100644 src/extensions/builder.rs create mode 100644 src/extensions/deltalake.rs create mode 100644 src/extensions/mod.rs create mode 100644 src/extensions/s3.rs diff --git a/src/app/mod.rs b/src/app/mod.rs index e35515f..cbbe1fd 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -77,11 +77,10 @@ pub struct App<'app> { } impl<'app> App<'app> { - pub fn new(state: state::AppState<'app>) -> Self { + pub fn new(state: state::AppState<'app>, execution: ExecutionContext) -> Self { let (event_tx, event_rx) = mpsc::unbounded_channel(); let cancellation_token = CancellationToken::new(); let task = tokio::spawn(async {}); - let execution = Arc::new(ExecutionContext::new(state.config.execution.clone())); Self { state, @@ -89,7 +88,7 @@ impl<'app> App<'app> { event_rx, event_tx, cancellation_token, - execution, + execution: Arc::new(execution), } } @@ -310,30 +309,34 @@ impl Widget for &App<'_> { } } -pub async fn run_app(state: state::AppState<'_>) -> Result<()> { - info!("Running app with state: {:?}", state); - let mut app = App::new(state); +impl App<'_> { + /// Run the main event loop for the application + pub async fn run_app(self) -> Result<()> { + info!("Running app with state: {:?}", self.state); + let mut app = self; - app.execute_ddl(); + app.execute_ddl(); - #[cfg(feature = "flightsql")] - app.establish_flightsql_connection(); + #[cfg(feature = "flightsql")] + app.establish_flightsql_connection(); - let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(std::io::stdout())).unwrap(); - app.enter(true)?; - // Main loop for handling events - loop { - let event = app.next().await?; + let mut terminal = + ratatui::Terminal::new(CrosstermBackend::new(std::io::stdout())).unwrap(); + app.enter(true)?; + // Main loop for handling events + loop { + let event = app.next().await?; - if let AppEvent::Render = event.clone() { - terminal.draw(|f| f.render_widget(&app, f.area()))?; - }; + if let AppEvent::Render = event.clone() { + terminal.draw(|f| f.render_widget(&app, f.area()))?; + }; - app.handle_app_event(event)?; + app.handle_app_event(event)?; - if app.state.should_quit { - break; + if app.state.should_quit { + break; + } } + app.exit() } - app.exit() } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c0b98f9..8b0e44a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -16,7 +16,6 @@ // under the License. //! [`CliApp`]: Command Line User Interface -use crate::app::state; use crate::execution::ExecutionContext; use color_eyre::eyre::eyre; use datafusion::arrow::util::pretty::pretty_format_batches; @@ -35,9 +34,7 @@ pub struct CliApp { } impl CliApp { - pub fn new(state: state::AppState<'static>) -> Self { - let execution = ExecutionContext::new(state.config.execution.clone()); - + pub fn new(execution: ExecutionContext) -> Self { Self { execution } } diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 8de42bf..3368a37 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -20,18 +20,12 @@ use std::sync::Arc; use color_eyre::eyre::Result; -use datafusion::execution::runtime_env::RuntimeEnv; -use datafusion::execution::session_state::SessionStateBuilder; use datafusion::execution::SendableRecordBatchStream; use datafusion::physical_plan::{visit_execution_plan, ExecutionPlan, ExecutionPlanVisitor}; use datafusion::prelude::*; use datafusion::sql::parser::Statement; -#[cfg(feature = "deltalake")] -use deltalake::delta_datafusion::DeltaTableFactory; use log::info; use tokio_stream::StreamExt; -#[cfg(feature = "s3")] -use url::Url; #[cfg(feature = "flightsql")] use { arrow_flight::sql::client::FlightSqlServiceClient, tokio::sync::Mutex, @@ -39,6 +33,7 @@ use { }; use crate::config::ExecutionConfig; +use crate::extensions::{all_extensions, DftSessionStateBuilder}; /// Structure for executing queries either locally or remotely (via FlightSQL) /// @@ -61,68 +56,20 @@ pub struct ExecutionContext { impl ExecutionContext { /// Construct a new `ExecutionContext` with the specified configuration - pub fn new(config: ExecutionConfig) -> Self { - let _ = &config; // avoid unused variable warning (it is used when some features are enabled) - - let cfg = SessionConfig::default() - .with_batch_size(1) - .with_information_schema(true); - - let runtime_env = RuntimeEnv::default(); - - #[cfg(feature = "s3")] - { - if let Some(object_store_config) = &config.object_store { - if let Some(s3_configs) = &object_store_config.s3 { - info!("S3 configs exists"); - for s3_config in s3_configs { - match s3_config.to_object_store() { - Ok(object_store) => { - info!("Created object store"); - if let Some(object_store_url) = s3_config.object_store_url() { - info!("Endpoint exists"); - if let Ok(parsed_endpoint) = Url::parse(object_store_url) { - info!("Parsed endpoint"); - runtime_env.register_object_store( - &parsed_endpoint, - Arc::new(object_store), - ); - info!("Registered s3 object store"); - } - } - } - Err(e) => { - log::error!("Error creating object store: {:?}", e); - } - } - } - } - } - } - - #[allow(unused_mut)] // used when deltalake is enabled - let mut state = SessionStateBuilder::new() - .with_default_features() - .with_runtime_env(runtime_env.into()) - .with_config(cfg) - .build(); - - #[cfg(feature = "deltalake")] - { - state - .table_factories_mut() - .insert("DELTATABLE".to_string(), Arc::new(DeltaTableFactory {})); + pub fn try_new(config: &ExecutionConfig) -> Result { + let mut builder = DftSessionStateBuilder::new(); + for extension in all_extensions() { + builder = extension.register(config, builder)?; } - { - let session_ctx = SessionContext::new_with_state(state); + let state = builder.build()?; + let session_ctx = SessionContext::new_with_state(state); - Self { - session_ctx, - #[cfg(feature = "flightsql")] - flightsql_client: Mutex::new(None), - } - } + Ok(Self { + session_ctx, + #[cfg(feature = "flightsql")] + flightsql_client: Mutex::new(None), + }) } pub fn create_tables(&mut self) -> Result<()> { diff --git a/src/extensions/builder.rs b/src/extensions/builder.rs new file mode 100644 index 0000000..ccad3c6 --- /dev/null +++ b/src/extensions/builder.rs @@ -0,0 +1,130 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! [`DftSessionStateBuilder`] for configuring DataFusion [`SessionState`] + +use datafusion::catalog::TableProviderFactory; +use datafusion::execution::context::SessionState; +use datafusion::execution::runtime_env::RuntimeEnv; +use datafusion::execution::session_state::SessionStateBuilder; +use datafusion::prelude::SessionConfig; +use std::collections::HashMap; +use std::fmt::Debug; +use std::sync::Arc; + +/// Builds a DataFusion [`SessionState`] with any necessary configuration +/// +/// Ideally we would use the DataFusion [`SessionStateBuilder`], but it doesn't +/// currently have all the needed APIs. Once we have a good handle on the needed +/// APIs we can upstream them to DataFusion. +/// +/// List of things that would be nice to add upstream: +/// TODO: Implement Debug for SessionStateBuilder upstream +/// TODO: Implement with_table_factory to add a single TableProviderFactory to the list of factories +/// TODO: Implement some way to get access to the current RuntimeEnv (to register object stores) +/// TODO: Implement a way to add just a single TableProviderFactory +/// TODO: Make TableFactoryProvider implement Debug +/// TODO: rename RuntimeEnv::new() to RuntimeEnv::try_new() as it returns a Result +//#[derive(Debug)] +pub struct DftSessionStateBuilder { + session_config: SessionConfig, + table_factories: Option>>, + runtime_env: Option>, +} + +impl Debug for DftSessionStateBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DftSessionStateBuilder") + .field("session_config", &self.session_config) + //.field("table_factories", &self.table_factories) + .field( + "table_factories", + &"TODO TableFactoryDoes not implement Debug", + ) + .field("runtime_env", &self.runtime_env) + .finish() + } +} + +impl Default for DftSessionStateBuilder { + fn default() -> Self { + Self::new() + } +} + +impl DftSessionStateBuilder { + /// Create a new builder + pub fn new() -> Self { + let session_config = SessionConfig::default() + // TODO why is batch size 1? + .with_batch_size(1) + .with_information_schema(true); + + Self { + session_config, + table_factories: None, + runtime_env: None, + } + } + + /// Add a table factory to the list of factories on this builder + pub fn with_table_factory( + mut self, + name: &str, + factory: Arc, + ) -> Self { + if self.table_factories.is_none() { + self.table_factories = Some(HashMap::from([(name.to_string(), factory)])); + } else { + self.table_factories + .as_mut() + .unwrap() + .insert(name.to_string(), factory); + } + self + } + + /// Return the current [`RuntimeEnv`], creating a default if it doesn't exist + pub fn runtime_env(&mut self) -> &RuntimeEnv { + if self.runtime_env.is_none() { + self.runtime_env = Some(Arc::new(RuntimeEnv::default())); + } + self.runtime_env.as_ref().unwrap() + } + + /// Build the [`SessionState`] from the specified configuration + pub fn build(self) -> datafusion_common::Result { + let Self { + session_config, + table_factories, + runtime_env, + } = self; + + let mut builder = SessionStateBuilder::new() + .with_default_features() + .with_config(session_config); + + if let Some(runtime_env) = runtime_env { + builder = builder.with_runtime_env(runtime_env); + } + if let Some(table_factories) = table_factories { + builder = builder.with_table_factories(table_factories); + } + + Ok(builder.build()) + } +} diff --git a/src/extensions/deltalake.rs b/src/extensions/deltalake.rs new file mode 100644 index 0000000..5004e0d --- /dev/null +++ b/src/extensions/deltalake.rs @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! DeltaLake integration: [DeltaLakeExtension] + +use crate::config::ExecutionConfig; +use crate::extensions::{DftSessionStateBuilder, Extension}; +use deltalake::delta_datafusion::DeltaTableFactory; +use std::sync::Arc; + +#[derive(Debug, Default)] +pub struct DeltaLakeExtension {} + +impl DeltaLakeExtension { + pub fn new() -> Self { + Self {} + } +} + +impl Extension for DeltaLakeExtension { + fn register( + &self, + _config: &ExecutionConfig, + builder: DftSessionStateBuilder, + ) -> datafusion_common::Result { + Ok(builder.with_table_factory("DELTATABLE", Arc::new(DeltaTableFactory {}))) + } +} diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs new file mode 100644 index 0000000..6abfcf6 --- /dev/null +++ b/src/extensions/mod.rs @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! This module has code to register DataFusion extensions + +use crate::config::ExecutionConfig; +use datafusion::common::Result; +use std::fmt::Debug; + +mod builder; +#[cfg(feature = "deltalake")] +mod deltalake; +#[cfg(feature = "s3")] +mod s3; + +pub use builder::DftSessionStateBuilder; + +pub trait Extension: Debug { + /// Registers this extension with the DataFusion [`SessionStateBuilder`] + fn register( + &self, + _config: &ExecutionConfig, + _builder: DftSessionStateBuilder, + ) -> Result; +} + +/// Return all extensions currently enabled +pub fn all_extensions() -> Vec> { + vec![ + #[cfg(feature = "s3")] + Box::new(s3::AwsS3Extension::new()), + #[cfg(feature = "deltalake")] + Box::new(deltalake::DeltaLakeExtension::new()), + ] +} diff --git a/src/extensions/s3.rs b/src/extensions/s3.rs new file mode 100644 index 0000000..c3fe4dd --- /dev/null +++ b/src/extensions/s3.rs @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! AWS S3 Integration: [AwsS3Extension] + +use crate::config::ExecutionConfig; +use crate::extensions::{DftSessionStateBuilder, Extension}; +use log::info; +use std::sync::Arc; + +use url::Url; + +#[derive(Debug, Default)] +pub struct AwsS3Extension {} + +impl AwsS3Extension { + pub fn new() -> Self { + Self {} + } +} + +impl Extension for AwsS3Extension { + fn register( + &self, + config: &ExecutionConfig, + mut builder: DftSessionStateBuilder, + ) -> datafusion_common::Result { + let Some(object_store_config) = &config.object_store else { + return Ok(builder); + }; + + let Some(s3_configs) = &object_store_config.s3 else { + return Ok(builder); + }; + + info!("S3 configs exists"); + for s3_config in s3_configs { + match s3_config.to_object_store() { + Ok(object_store) => { + info!("Created object store"); + if let Some(object_store_url) = s3_config.object_store_url() { + info!("Endpoint exists"); + if let Ok(parsed_endpoint) = Url::parse(object_store_url) { + info!("Parsed endpoint"); + builder + .runtime_env() + .register_object_store(&parsed_endpoint, Arc::new(object_store)); + info!("Registered s3 object store"); + } + } + } + Err(e) => { + log::error!("Error creating object store: {:?}", e); + } + } + } + + Ok(builder) + } +} diff --git a/src/lib.rs b/src/lib.rs index a6e31bc..0a09c74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,4 +3,5 @@ pub mod args; pub mod cli; pub mod config; pub mod execution; +pub mod extensions; pub mod telemetry; diff --git a/src/main.rs b/src/main.rs index 6c7178e..316a5f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,9 +17,10 @@ use clap::Parser; use color_eyre::Result; -use dft::app::{run_app, state}; +use dft::app::{state, App}; use dft::args::DftArgs; use dft::cli::CliApp; +use dft::execution::ExecutionContext; use dft::telemetry; #[tokio::main] @@ -31,7 +32,8 @@ async fn main() -> Result<()> { // use env_logger to setup logging for CLI env_logger::init(); let state = state::initialize(cli.config_path()); - let app = CliApp::new(state); + let execution = ExecutionContext::try_new(&state.config.execution)?; + let app = CliApp::new(execution); app.execute_files_or_commands(cli.files.clone(), cli.commands.clone()) .await?; } @@ -40,7 +42,9 @@ async fn main() -> Result<()> { // use alternate logging for TUI telemetry::initialize_logs()?; let state = state::initialize(cli.config_path()); - run_app(state).await?; + let execution = ExecutionContext::try_new(&state.config.execution)?; + let app = App::new(state, execution); + app.run_app().await?; } Ok(()) diff --git a/tests/tui.rs b/tests/tui.rs index 27b84ed..5b9b7ce 100644 --- a/tests/tui.rs +++ b/tests/tui.rs @@ -19,70 +19,103 @@ use dft::app::state::initialize; use dft::app::{App, AppEvent}; +use dft::execution::ExecutionContext; use ratatui::crossterm::event; -use tempfile::tempdir; +use tempfile::{tempdir, TempDir}; #[tokio::test] -async fn run_app_with_no_args() { - let config_path = tempdir().unwrap(); - let state = initialize(config_path.path().to_path_buf()); - let _app = App::new(state); +async fn construct_with_no_args() { + let _test_app = TestApp::new(); } #[tokio::test] -async fn quit_app_from_all_tabs() { - let config_path = tempdir().unwrap(); - let state = initialize(config_path.path().to_path_buf()); - +async fn quit_app_from_sql_tab() { + let mut test_app = TestApp::new(); // SQL Tab - let mut app = App::new(state); let key = event::KeyEvent::new(event::KeyCode::Char('q'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(key); - app.handle_app_event(app_event).unwrap(); + test_app.handle_app_event(app_event).unwrap(); // Ideally, we figure out a way to check that the app actually quits - assert!(app.state().should_quit); + assert!(test_app.state().should_quit); +} - // FlightSQL Tab - let state = initialize(config_path.path().to_path_buf()); - let mut app = App::new(state); +#[tokio::test] +async fn quit_app_from_flightsql_tab() { + let mut test_app = TestApp::new(); let flightsql_key = event::KeyEvent::new(event::KeyCode::Char('2'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(flightsql_key); - app.handle_app_event(app_event).unwrap(); + test_app.handle_app_event(app_event).unwrap(); let key = event::KeyEvent::new(event::KeyCode::Char('q'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(key); - app.handle_app_event(app_event).unwrap(); - assert!(app.state().should_quit); + test_app.handle_app_event(app_event).unwrap(); + assert!(test_app.state().should_quit); +} - // History Tab - let state = initialize(config_path.path().to_path_buf()); - let mut app = App::new(state); +#[tokio::test] +async fn quit_app_from_history_tab() { + let mut test_app = TestApp::new(); let history_key = event::KeyEvent::new(event::KeyCode::Char('3'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(history_key); - app.handle_app_event(app_event).unwrap(); + test_app.handle_app_event(app_event).unwrap(); let key = event::KeyEvent::new(event::KeyCode::Char('q'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(key); - app.handle_app_event(app_event).unwrap(); - assert!(app.state().should_quit); + test_app.handle_app_event(app_event).unwrap(); + assert!(test_app.state().should_quit); +} - // Logs Tab - let state = initialize(config_path.path().to_path_buf()); - let mut app = App::new(state); +#[tokio::test] +async fn quit_app_from_logs_tab() { + let mut test_app = TestApp::new(); let logs_key = event::KeyEvent::new(event::KeyCode::Char('4'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(logs_key); - app.handle_app_event(app_event).unwrap(); + test_app.handle_app_event(app_event).unwrap(); let key = event::KeyEvent::new(event::KeyCode::Char('q'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(key); - app.handle_app_event(app_event).unwrap(); - assert!(app.state().should_quit); + test_app.handle_app_event(app_event).unwrap(); + assert!(test_app.state().should_quit); +} - // Context Tab - let state = initialize(config_path.path().to_path_buf()); - let mut app = App::new(state); +#[tokio::test] +async fn quit_app_from_context_tab() { + let mut test_app = TestApp::new(); let context_key = event::KeyEvent::new(event::KeyCode::Char('5'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(context_key); - app.handle_app_event(app_event).unwrap(); + test_app.handle_app_event(app_event).unwrap(); let key = event::KeyEvent::new(event::KeyCode::Char('q'), event::KeyModifiers::NONE); let app_event = AppEvent::Key(key); - app.handle_app_event(app_event).unwrap(); - assert!(app.state().should_quit); + test_app.handle_app_event(app_event).unwrap(); + assert!(test_app.state().should_quit); +} + +/// Fixture with an [`App`] instance and other temporary state +struct TestApp<'app> { + /// Temporary directory for configuration files + /// + /// The directory is removed when the object is dropped so this + /// field must remain alive while the app is running + #[allow(dead_code)] + config_path: TempDir, + /// The [`App`] instance + app: App<'app>, +} + +impl<'app> TestApp<'app> { + /// Create a new [`TestApp`] instance configured with a temporary directory + fn new() -> Self { + let config_path = tempdir().unwrap(); + let state = initialize(config_path.path().to_path_buf()); + let execution = ExecutionContext::try_new(&state.config.execution).unwrap(); + let app = App::new(state, execution); + Self { config_path, app } + } + + /// Call app.event_handler with the given event + pub fn handle_app_event(&mut self, event: AppEvent) -> color_eyre::Result<()> { + self.app.handle_app_event(event) + } + + /// Return the app state + pub fn state(&self) -> &dft::app::state::AppState { + self.app.state() + } } From eade252e3792bf92955795c2f8735792103b6dd0 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Sat, 14 Sep 2024 08:27:53 -0400 Subject: [PATCH 2/3] Update src/extensions/mod.rs Co-authored-by: Matthew Turner --- src/extensions/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 6abfcf6..41b11a1 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -39,7 +39,7 @@ pub trait Extension: Debug { } /// Return all extensions currently enabled -pub fn all_extensions() -> Vec> { +pub fn enabled_extensions() -> Vec> { vec![ #[cfg(feature = "s3")] Box::new(s3::AwsS3Extension::new()), From ab94a483d2a1624a3cce11c58e1e0d79a9a78765 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Sat, 14 Sep 2024 08:33:22 -0400 Subject: [PATCH 3/3] fix build --- src/execution/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 3368a37..6eb212a 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -33,7 +33,7 @@ use { }; use crate::config::ExecutionConfig; -use crate::extensions::{all_extensions, DftSessionStateBuilder}; +use crate::extensions::{enabled_extensions, DftSessionStateBuilder}; /// Structure for executing queries either locally or remotely (via FlightSQL) /// @@ -58,7 +58,7 @@ impl ExecutionContext { /// Construct a new `ExecutionContext` with the specified configuration pub fn try_new(config: &ExecutionConfig) -> Result { let mut builder = DftSessionStateBuilder::new(); - for extension in all_extensions() { + for extension in enabled_extensions() { builder = extension.register(config, builder)?; }