@@ -245,26 +245,31 @@ impl SessionContext {
245
245
self . state . read ( ) . config . clone ( )
246
246
}
247
247
248
- /// Creates a [`DataFrame`] that will execute a SQL query.
248
+ /// Creates a [`DataFrame`] that executes a SQL query supported by
249
+ /// DataFusion, including DDL (such as `CREATE TABLE`).
249
250
///
250
- /// This method is `async` because queries of type `CREATE EXTERNAL TABLE`
251
- /// might require the schema to be inferred.
251
+ /// You can use [`Self::plan_sql`] and
252
+ /// [`DataFrame::create_physical_plan`] directly if you need read only
253
+ /// query support (no way to create external tables, for example)
254
+ ///
255
+ /// This method is `async` because queries of type `CREATE
256
+ /// EXTERNAL TABLE` might require the schema to be inferred.
252
257
pub async fn sql ( & self , sql : & str ) -> Result < DataFrame > {
253
- let mut statements = DFParser :: parse_sql ( sql) ?;
254
- if statements. len ( ) != 1 {
255
- return Err ( DataFusionError :: NotImplemented (
256
- "The context currently only supports a single SQL statement" . to_string ( ) ,
257
- ) ) ;
258
- }
259
-
260
- // create a query planner
261
- let plan = {
262
- // TODO: Move catalog off SessionState onto SessionContext
263
- let state = self . state . read ( ) ;
264
- let query_planner = SqlToRel :: new ( & * state) ;
265
- query_planner. statement_to_plan ( statements. pop_front ( ) . unwrap ( ) ) ?
266
- } ;
258
+ let plan = self . plan_sql ( sql) ?;
259
+ self . dataframe ( plan) . await
260
+ }
267
261
262
+ /// Creates a [`DataFrame`] that will execute the specified
263
+ /// LogicalPlan, including DDL (such as `CREATE TABLE`).
264
+ /// Use [`Self::dataframe_without_ddl`] if you do not want
265
+ /// to support DDL statements.
266
+ ///
267
+ /// Any DDL statements are executed during this function (not when
268
+ /// the [`DataFrame`] is evaluated)
269
+ ///
270
+ /// This method is `async` because queries of type `CREATE EXTERNAL TABLE`
271
+ /// might require the schema to be inferred by performing I/O.
272
+ pub async fn dataframe ( & self , plan : LogicalPlan ) -> Result < DataFrame > {
268
273
match plan {
269
274
LogicalPlan :: CreateExternalTable ( cmd) => {
270
275
self . create_external_table ( & cmd) . await
@@ -492,6 +497,15 @@ impl SessionContext {
492
497
}
493
498
}
494
499
500
+ /// Creates a [`DataFrame`] that will execute the specified
501
+ /// LogicalPlan, but will error if the plans represent DDL such as
502
+ /// `CREATE TABLE`
503
+ ///
504
+ /// Use [`Self::dataframe`] to run plans with DDL
505
+ pub fn dataframe_without_ddl ( & self , plan : LogicalPlan ) -> Result < DataFrame > {
506
+ Ok ( DataFrame :: new ( self . state ( ) , plan) )
507
+ }
508
+
495
509
// return an empty dataframe
496
510
fn return_empty_dataframe ( & self ) -> Result < DataFrame > {
497
511
let plan = LogicalPlanBuilder :: empty ( false ) . build ( ) ?;
@@ -559,11 +573,9 @@ impl SessionContext {
559
573
}
560
574
Ok ( false )
561
575
}
562
- /// Creates a logical plan.
563
- ///
564
- /// This function is intended for internal use and should not be called directly.
565
- #[ deprecated( note = "Use SessionContext::sql which snapshots the SessionState" ) ]
566
- pub fn create_logical_plan ( & self , sql : & str ) -> Result < LogicalPlan > {
576
+
577
+ /// Creates a [`LogicalPlan`] from a SQL query.
578
+ pub fn plan_sql ( & self , sql : & str ) -> Result < LogicalPlan > {
567
579
let mut statements = DFParser :: parse_sql ( sql) ?;
568
580
569
581
if statements. len ( ) != 1 {
@@ -1000,32 +1012,24 @@ impl SessionContext {
1000
1012
}
1001
1013
1002
1014
/// Optimizes the logical plan by applying optimizer rules.
1003
- pub fn optimize ( & self , plan : & LogicalPlan ) -> Result < LogicalPlan > {
1004
- self . state . read ( ) . optimize ( plan)
1015
+ #[ deprecated(
1016
+ note = "Use `SessionContext::dataframe_without_ddl` and `DataFrame::into_optimized_plan`"
1017
+ ) ]
1018
+ pub fn optimize ( & self , plan : LogicalPlan ) -> Result < LogicalPlan > {
1019
+ self . dataframe_without_ddl ( plan) ?. into_optimized_plan ( )
1005
1020
}
1006
1021
1007
- /// Creates a physical plan from a logical plan.
1022
+ /// Creates a physical [`ExecutionPlan`] from a [`LogicalPlan`].
1023
+ #[ deprecated(
1024
+ note = "Use `SessionContext::::dataframe_without_ddl` and `DataFrame::create_physical_plan`"
1025
+ ) ]
1008
1026
pub async fn create_physical_plan (
1009
1027
& self ,
1010
- logical_plan : & LogicalPlan ,
1028
+ logical_plan : LogicalPlan ,
1011
1029
) -> Result < Arc < dyn ExecutionPlan > > {
1012
- let state_cloned = {
1013
- let mut state = self . state . write ( ) ;
1014
- state. execution_props . start_execution ( ) ;
1015
-
1016
- // We need to clone `state` to release the lock that is not `Send`. We could
1017
- // make the lock `Send` by using `tokio::sync::Mutex`, but that would require to
1018
- // propagate async even to the `LogicalPlan` building methods.
1019
- // Cloning `state` here is fine as we then pass it as immutable `&state`, which
1020
- // means that we avoid write consistency issues as the cloned version will not
1021
- // be written to. As for eventual modifications that would be applied to the
1022
- // original state after it has been cloned, they will not be picked up by the
1023
- // clone but that is okay, as it is equivalent to postponing the state update
1024
- // by keeping the lock until the end of the function scope.
1025
- state. clone ( )
1026
- } ;
1027
-
1028
- state_cloned. create_physical_plan ( logical_plan) . await
1030
+ self . dataframe_without_ddl ( logical_plan) ?
1031
+ . create_physical_plan ( )
1032
+ . await
1029
1033
}
1030
1034
1031
1035
/// Executes a query and writes the results to a partitioned CSV file.
0 commit comments