@@ -39,12 +39,17 @@ use std::{collections::HashMap, sync::Arc};
3939use tokio:: sync:: Mutex ;
4040use write:: DuckDBTableWriterBuilder ;
4141
42+ pub use self :: settings:: {
43+ DuckDBSetting , DuckDBSettingScope , DuckDBSettingsRegistry , MemoryLimitSetting ,
44+ PreserveInsertionOrderSetting , TempDirectorySetting ,
45+ } ;
4246use self :: sql_table:: DuckDBTable ;
4347
4448#[ cfg( feature = "duckdb-federation" ) ]
4549mod federation;
4650
4751mod creator;
52+ mod settings;
4853mod sql_table;
4954pub mod write;
5055pub use creator:: { RelationName , TableDefinition } ;
@@ -167,11 +172,16 @@ pub enum Error {
167172
168173type Result < T , E = Error > = std:: result:: Result < T , E > ;
169174
175+ const DUCKDB_DB_PATH_PARAM : & str = "open" ;
176+ const DUCKDB_DB_BASE_FOLDER_PARAM : & str = "data_directory" ;
177+ const DUCKDB_ATTACH_DATABASES_PARAM : & str = "attach_databases" ;
178+
170179pub struct DuckDBTableProviderFactory {
171180 access_mode : AccessMode ,
172181 instances : Arc < Mutex < HashMap < DbInstanceKey , DuckDbConnectionPool > > > ,
173182 unsupported_type_action : UnsupportedTypeAction ,
174183 dialect : Arc < dyn Dialect > ,
184+ settings_registry : DuckDBSettingsRegistry ,
175185}
176186
177187// Dialect trait does not implement Debug so we implement Debug manually
@@ -185,13 +195,6 @@ impl std::fmt::Debug for DuckDBTableProviderFactory {
185195 }
186196}
187197
188- const DUCKDB_DB_PATH_PARAM : & str = "open" ;
189- const DUCKDB_DB_BASE_FOLDER_PARAM : & str = "data_directory" ;
190- const DUCKDB_ATTACH_DATABASES_PARAM : & str = "attach_databases" ;
191- const DUCKDB_SETTING_MEMORY_LIMIT : & str = "memory_limit" ;
192- const DUCKDB_SETTING_TEMP_DIRECTORY : & str = "temp_directory" ;
193- const DUCKDB_SETTING_PRESERVE_INSERTION_ORDER : & str = "preserve_insertion_order" ;
194-
195198impl DuckDBTableProviderFactory {
196199 #[ must_use]
197200 pub fn new ( access_mode : AccessMode ) -> Self {
@@ -200,6 +203,7 @@ impl DuckDBTableProviderFactory {
200203 instances : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
201204 unsupported_type_action : UnsupportedTypeAction :: Error ,
202205 dialect : Arc :: new ( DuckDBDialect :: new ( ) ) ,
206+ settings_registry : DuckDBSettingsRegistry :: new ( ) ,
203207 }
204208 }
205209
@@ -218,6 +222,22 @@ impl DuckDBTableProviderFactory {
218222 self
219223 }
220224
225+ #[ must_use]
226+ pub fn with_settings_registry ( mut self , settings_registry : DuckDBSettingsRegistry ) -> Self {
227+ self . settings_registry = settings_registry;
228+ self
229+ }
230+
231+ #[ must_use]
232+ pub fn settings_registry ( & self ) -> & DuckDBSettingsRegistry {
233+ & self . settings_registry
234+ }
235+
236+ #[ must_use]
237+ pub fn settings_registry_mut ( & mut self ) -> & mut DuckDBSettingsRegistry {
238+ & mut self . settings_registry
239+ }
240+
221241 #[ must_use]
222242 pub fn attach_databases ( & self , options : & HashMap < String , String > ) -> Vec < Arc < str > > {
223243 options
@@ -390,6 +410,13 @@ impl TableProviderFactory for DuckDBTableProviderFactory {
390410 Mode :: Memory => pool. clone ( ) ,
391411 } ;
392412
413+ // Get local DuckDB SET statements to use as setup queries on the pool
414+ let local_settings = self
415+ . settings_registry
416+ . get_setting_statements ( & options, DuckDBSettingScope :: Local ) ;
417+
418+ let read_pool = read_pool. with_connection_setup_queries ( local_settings) ;
419+
393420 let schema: SchemaRef = Arc :: new ( cmd. schema . as_ref ( ) . into ( ) ) ;
394421
395422 let table_definition =
@@ -407,18 +434,18 @@ impl TableProviderFactory for DuckDBTableProviderFactory {
407434
408435 let dyn_pool: Arc < DynDuckDbConnectionPool > = Arc :: new ( read_pool) ;
409436
410- if let Some ( memory_limit) = options. get ( DUCKDB_SETTING_MEMORY_LIMIT ) {
411- apply_memory_limit ( & dyn_pool, memory_limit) . await ?;
412- }
413-
414- if let Some ( temp_directory) = options. get ( DUCKDB_SETTING_TEMP_DIRECTORY ) {
415- apply_temp_directory ( & dyn_pool, temp_directory) . await ?;
416- }
437+ let db_conn = dyn_pool. connect ( ) . await ?;
438+ let Some ( conn) = db_conn. as_sync ( ) else {
439+ return Err ( DataFusionError :: External ( Box :: new (
440+ Error :: DbConnectionError {
441+ source : "Failed to get sync DuckDbConnection using DbConnection" . into ( ) ,
442+ } ,
443+ ) ) ) ;
444+ } ;
417445
418- if let Some ( preserve_insertion_order) = options. get ( DUCKDB_SETTING_PRESERVE_INSERTION_ORDER )
419- {
420- apply_preserve_insertion_order ( & dyn_pool, preserve_insertion_order) . await ?;
421- }
446+ // Apply DuckDB global settings
447+ self . settings_registry
448+ . apply_settings ( conn, & options, DuckDBSettingScope :: Global ) ?;
422449
423450 let read_provider = Arc :: new ( DuckDBTable :: new_with_schema (
424451 & dyn_pool,
@@ -615,71 +642,6 @@ fn create_table_function_view_name(table_reference: &TableReference) -> TableRef
615642 TableReference :: from ( & tbl_ref_view)
616643}
617644
618- async fn apply_memory_limit (
619- pool : & Arc < DynDuckDbConnectionPool > ,
620- memory_limit : & str ,
621- ) -> DataFusionResult < ( ) > {
622- tracing:: debug!( "Setting DuckDB memory limit to {memory_limit}" ) ;
623-
624- if let Err ( err) = byte_unit:: Byte :: parse_str ( memory_limit, true ) {
625- return Err ( to_datafusion_error ( Error :: UnableToParseMemoryLimit {
626- value : memory_limit. to_string ( ) ,
627- source : err,
628- } ) ) ;
629- }
630-
631- let db_conn = pool. connect ( ) . await ?;
632- let Some ( conn) = db_conn. as_sync ( ) else {
633- // should never happen
634- return Err ( to_datafusion_error ( Error :: DbConnectionError {
635- source : "Failed to get sync DuckDbConnection using DbConnection" . into ( ) ,
636- } ) ) ;
637- } ;
638- conn. execute (
639- & format ! ( "SET {DUCKDB_SETTING_MEMORY_LIMIT} = '{memory_limit}'" ) ,
640- & [ ] ,
641- ) ?;
642- Ok ( ( ) )
643- }
644-
645- async fn apply_temp_directory (
646- pool : & Arc < DynDuckDbConnectionPool > ,
647- temp_directory : & str ,
648- ) -> DataFusionResult < ( ) > {
649- tracing:: debug!( "Setting DuckDB temp directory to {temp_directory}" ) ;
650-
651- let db_conn = pool. connect ( ) . await ?;
652- let Some ( conn) = db_conn. as_sync ( ) else {
653- return Err ( to_datafusion_error ( Error :: DbConnectionError {
654- source : "Failed to get sync DuckDbConnection using DbConnection" . into ( ) ,
655- } ) ) ;
656- } ;
657- conn. execute (
658- & format ! ( "SET {DUCKDB_SETTING_TEMP_DIRECTORY} = '{temp_directory}'" ) ,
659- & [ ] ,
660- ) ?;
661- Ok ( ( ) )
662- }
663-
664- async fn apply_preserve_insertion_order (
665- pool : & Arc < DynDuckDbConnectionPool > ,
666- preserve_insertion_order : & str ,
667- ) -> DataFusionResult < ( ) > {
668- tracing:: debug!( "Setting DuckDB preserve insertion order to {preserve_insertion_order}" ) ;
669-
670- let db_conn = pool. connect ( ) . await ?;
671- let Some ( conn) = db_conn. as_sync ( ) else {
672- return Err ( to_datafusion_error ( Error :: DbConnectionError {
673- source : "Failed to get sync DuckDbConnection using DbConnection" . into ( ) ,
674- } ) ) ;
675- } ;
676- conn. execute (
677- & format ! ( "SET {DUCKDB_SETTING_PRESERVE_INSERTION_ORDER} = {preserve_insertion_order}" ) ,
678- & [ ] ,
679- ) ?;
680- Ok ( ( ) )
681- }
682-
683645pub ( crate ) fn make_initial_table (
684646 table_definition : Arc < TableDefinition > ,
685647 pool : & Arc < DuckDbConnectionPool > ,
0 commit comments