@@ -28,6 +28,13 @@ use datafusion::{
28
28
execution:: TaskContext ,
29
29
physical_expr:: PhysicalSortExpr ,
30
30
physical_plan:: {
31
+ aggregates:: { AggregateExec , AggregateMode } ,
32
+ coalesce_batches:: CoalesceBatchesExec ,
33
+ coalesce_partitions:: CoalescePartitionsExec ,
34
+ displayable,
35
+ filter:: FilterExec ,
36
+ projection:: ProjectionExec ,
37
+ repartition:: RepartitionExec ,
31
38
DisplayAs , DisplayFormatType , ExecutionPlan , Partitioning , RecordBatchStream ,
32
39
SendableRecordBatchStream as DfSendableRecordBatchStream , Statistics ,
33
40
} ,
@@ -111,33 +118,94 @@ impl DisplayAs for UnresolvedPartitionedScan {
111
118
/// related nodes to execute.
112
119
#[ derive( Debug ) ]
113
120
pub struct ResolvedPartitionedScan {
114
- pub remote_executor : Arc < dyn RemotePhysicalPlanExecutor > ,
115
- pub remote_exec_plans : Vec < ( TableIdentifier , Arc < dyn ExecutionPlan > ) > ,
121
+ pub remote_exec_ctx : Arc < RemoteExecContext > ,
122
+ pub pushdown_continue : bool ,
116
123
}
117
124
118
125
impl ResolvedPartitionedScan {
119
- pub fn extend_remote_exec_plans (
126
+ pub fn new (
127
+ remote_executor : Arc < dyn RemotePhysicalPlanExecutor > ,
128
+ remote_exec_plans : Vec < ( TableIdentifier , Arc < dyn ExecutionPlan > ) > ,
129
+ ) -> Self {
130
+ let remote_exec_ctx = Arc :: new ( RemoteExecContext {
131
+ executor : remote_executor,
132
+ plans : remote_exec_plans,
133
+ } ) ;
134
+
135
+ Self {
136
+ remote_exec_ctx,
137
+ pushdown_continue : true ,
138
+ }
139
+ }
140
+
141
+ pub fn pushdown_finished ( & self ) -> Arc < dyn ExecutionPlan > {
142
+ Arc :: new ( Self {
143
+ remote_exec_ctx : self . remote_exec_ctx . clone ( ) ,
144
+ pushdown_continue : false ,
145
+ } )
146
+ }
147
+
148
+ pub fn try_to_push_down_more (
120
149
& self ,
121
- extended_node : Arc < dyn ExecutionPlan > ,
122
- ) -> DfResult < Arc < ResolvedPartitionedScan > > {
150
+ cur_node : Arc < dyn ExecutionPlan > ,
151
+ ) -> DfResult < Arc < dyn ExecutionPlan > > {
152
+ // Can not push more...
153
+ if !self . pushdown_continue {
154
+ return cur_node. with_new_children ( vec ! [ self . pushdown_finished( ) ] ) ;
155
+ }
156
+
157
+ // Push down more, and when occur the terminated push down able node, we need to
158
+ // set `can_push_down_more` false.
159
+ let pushdown_status = Self :: maybe_a_pushdown_node ( cur_node. clone ( ) ) ;
160
+ let ( node, can_push_down_more) = match pushdown_status {
161
+ PushDownStatus :: Continue ( node) => ( node, true ) ,
162
+ PushDownStatus :: Terminated ( node) => ( node, false ) ,
163
+ PushDownStatus :: Unable => {
164
+ let partitioned_scan = self . pushdown_finished ( ) ;
165
+ return cur_node. with_new_children ( vec ! [ partitioned_scan] ) ;
166
+ }
167
+ } ;
168
+
123
169
let new_plans = self
124
- . remote_exec_plans
170
+ . remote_exec_ctx
171
+ . plans
125
172
. iter ( )
126
173
. map ( |( table, plan) | {
127
- extended_node
128
- . clone ( )
174
+ node. clone ( )
129
175
. with_new_children ( vec ! [ plan. clone( ) ] )
130
176
. map ( |extended_plan| ( table. clone ( ) , extended_plan) )
131
177
} )
132
178
. collect :: < DfResult < Vec < _ > > > ( ) ?;
133
179
180
+ let remote_exec_ctx = Arc :: new ( RemoteExecContext {
181
+ executor : self . remote_exec_ctx . executor . clone ( ) ,
182
+ plans : new_plans,
183
+ } ) ;
134
184
let plan = ResolvedPartitionedScan {
135
- remote_executor : self . remote_executor . clone ( ) ,
136
- remote_exec_plans : new_plans ,
185
+ remote_exec_ctx ,
186
+ pushdown_continue : can_push_down_more ,
137
187
} ;
138
188
139
189
Ok ( Arc :: new ( plan) )
140
190
}
191
+
192
+ #[ inline]
193
+ pub fn maybe_a_pushdown_node ( plan : Arc < dyn ExecutionPlan > ) -> PushDownStatus {
194
+ PushDownStatus :: new ( plan)
195
+ }
196
+
197
+ /// `ResolvedPartitionedScan` can be executable after satisfying followings:
198
+ /// + The pushdown searching process is finished.
199
+ #[ inline]
200
+ fn is_executable ( & self ) -> bool {
201
+ !self . pushdown_continue
202
+ }
203
+ }
204
+
205
+ #[ derive( Debug ) ]
206
+ pub struct RemoteExecContext {
207
+ executor : Arc < dyn RemotePhysicalPlanExecutor > ,
208
+ plans : Vec < ( TableIdentifier , Arc < dyn ExecutionPlan > ) > ,
141
209
}
142
210
143
211
impl ExecutionPlan for ResolvedPartitionedScan {
@@ -146,31 +214,36 @@ impl ExecutionPlan for ResolvedPartitionedScan {
146
214
}
147
215
148
216
fn schema ( & self ) -> ArrowSchemaRef {
149
- self . remote_exec_plans
217
+ self . remote_exec_ctx
218
+ . plans
150
219
. first ( )
151
220
. expect ( "remote_exec_plans should not be empty" )
152
221
. 1
153
222
. schema ( )
154
223
}
155
224
156
225
fn output_partitioning ( & self ) -> Partitioning {
157
- Partitioning :: UnknownPartitioning ( self . remote_exec_plans . len ( ) )
226
+ Partitioning :: UnknownPartitioning ( self . remote_exec_ctx . plans . len ( ) )
158
227
}
159
228
160
229
fn output_ordering ( & self ) -> Option < & [ PhysicalSortExpr ] > {
161
230
None
162
231
}
163
232
164
233
fn children ( & self ) -> Vec < Arc < dyn ExecutionPlan > > {
165
- vec ! [ ]
234
+ self . remote_exec_ctx
235
+ . plans
236
+ . iter ( )
237
+ . map ( |( _, plan) | plan. clone ( ) )
238
+ . collect ( )
166
239
}
167
240
168
241
fn with_new_children (
169
242
self : Arc < Self > ,
170
243
_children : Vec < Arc < dyn ExecutionPlan > > ,
171
244
) -> DfResult < Arc < dyn ExecutionPlan > > {
172
245
Err ( DataFusionError :: Internal (
173
- "UnresolvedPartitionedScan should not have children" . to_string ( ) ,
246
+ "UnresolvedPartitionedScan can't be built directly from new children" . to_string ( ) ,
174
247
) )
175
248
}
176
249
@@ -179,11 +252,19 @@ impl ExecutionPlan for ResolvedPartitionedScan {
179
252
partition : usize ,
180
253
context : Arc < TaskContext > ,
181
254
) -> DfResult < DfSendableRecordBatchStream > {
182
- let ( sub_table, plan) = & self . remote_exec_plans [ partition] ;
255
+ if !self . is_executable ( ) {
256
+ return Err ( DataFusionError :: Internal ( format ! (
257
+ "partitioned scan is still inexecutable, plan:{}" ,
258
+ displayable( self ) . indent( true )
259
+ ) ) ) ;
260
+ }
261
+
262
+ let ( sub_table, plan) = & self . remote_exec_ctx . plans [ partition] ;
183
263
184
264
// Send plan for remote execution.
185
265
let stream_future =
186
- self . remote_executor
266
+ self . remote_exec_ctx
267
+ . executor
187
268
. execute ( sub_table. clone ( ) , & context, plan. clone ( ) ) ?;
188
269
let record_stream = PartitionedScanStream :: new ( stream_future, plan. schema ( ) ) ;
189
270
@@ -280,15 +361,18 @@ pub(crate) enum StreamState {
280
361
Polling ( DfSendableRecordBatchStream ) ,
281
362
}
282
363
283
- // TODO: make display for the plan more pretty.
284
364
impl DisplayAs for ResolvedPartitionedScan {
285
- fn fmt_as ( & self , _t : DisplayFormatType , f : & mut fmt:: Formatter ) -> fmt:: Result {
286
- write ! (
287
- f,
288
- "ResolvedPartitionedScan: remote_exec_plans:{:?}, partition_count={}" ,
289
- self . remote_exec_plans,
290
- self . output_partitioning( ) . partition_count( ) ,
291
- )
365
+ fn fmt_as ( & self , t : DisplayFormatType , f : & mut fmt:: Formatter ) -> fmt:: Result {
366
+ match t {
367
+ DisplayFormatType :: Default | DisplayFormatType :: Verbose => {
368
+ write ! (
369
+ f,
370
+ "ResolvedPartitionedScan: pushing_down:{}, partition_count:{}" ,
371
+ self . pushdown_continue,
372
+ self . remote_exec_ctx. plans. len( )
373
+ )
374
+ }
375
+ }
292
376
}
293
377
}
294
378
@@ -352,7 +436,7 @@ impl DisplayAs for UnresolvedSubTableScan {
352
436
fn fmt_as ( & self , _t : DisplayFormatType , f : & mut fmt:: Formatter ) -> fmt:: Result {
353
437
write ! (
354
438
f,
355
- "UnresolvedSubTableScan: table= {:?}, read_request :{:?}, partition_count= {}" ,
439
+ "UnresolvedSubTableScan: table: {:?}, request :{:?}, partition_count: {}" ,
356
440
self . table,
357
441
self . read_request,
358
442
self . output_partitioning( ) . partition_count( ) ,
@@ -406,6 +490,48 @@ impl TryFrom<UnresolvedSubTableScan> for ceresdbproto::remote_engine::Unresolved
406
490
}
407
491
}
408
492
493
+ /// Pushdown status, including:
494
+ /// + Unable, plan node which can't be pushed down to
495
+ /// `ResolvedPartitionedScan` node.
496
+ /// + Continue, node able to be pushed down to `ResolvedPartitionedScan`, and
497
+ /// the newly generated `ResolvedPartitionedScan` can continue to accept
498
+ /// more pushdown nodes after.
499
+ /// + Terminated, node able to be pushed down to `ResolvedPartitionedScan`,
500
+ /// but the newly generated `ResolvedPartitionedScan` can't accept more
501
+ /// pushdown nodes after.
502
+ pub enum PushDownStatus {
503
+ Unable ,
504
+ Continue ( Arc < dyn ExecutionPlan > ) ,
505
+ Terminated ( Arc < dyn ExecutionPlan > ) ,
506
+ }
507
+
508
+ impl PushDownStatus {
509
+ pub fn new ( plan : Arc < dyn ExecutionPlan > ) -> Self {
510
+ if let Some ( aggr) = plan. as_any ( ) . downcast_ref :: < AggregateExec > ( ) {
511
+ if * aggr. mode ( ) == AggregateMode :: Partial {
512
+ Self :: Terminated ( plan)
513
+ } else {
514
+ Self :: Unable
515
+ }
516
+ } else if plan. as_any ( ) . downcast_ref :: < FilterExec > ( ) . is_some ( )
517
+ || plan. as_any ( ) . downcast_ref :: < ProjectionExec > ( ) . is_some ( )
518
+ || plan. as_any ( ) . downcast_ref :: < RepartitionExec > ( ) . is_some ( )
519
+ || plan
520
+ . as_any ( )
521
+ . downcast_ref :: < CoalescePartitionsExec > ( )
522
+ . is_some ( )
523
+ || plan
524
+ . as_any ( )
525
+ . downcast_ref :: < CoalesceBatchesExec > ( )
526
+ . is_some ( )
527
+ {
528
+ Self :: Continue ( plan)
529
+ } else {
530
+ Self :: Unable
531
+ }
532
+ }
533
+ }
534
+
409
535
#[ cfg( test) ]
410
536
mod test {
411
537
use datafusion:: error:: DataFusionError ;
0 commit comments