1use std::collections::HashMap;
21use std::fmt::Debug;
22use std::sync::Arc;
23
24use crate::TableProvider;
25
26use arrow::array::{
27 Array, ArrayRef, BooleanArray, RecordBatch as ArrowRecordBatch, UInt64Array,
28};
29use arrow::compute::kernels::zip::zip;
30use arrow::compute::{and, filter_record_batch};
31use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
32use arrow::record_batch::RecordBatch;
33use datafusion_common::error::Result;
34use datafusion_common::tree_node::TreeNodeRecursion;
35use datafusion_common::{Constraints, DFSchema, SchemaExt, not_impl_err, plan_err};
36use datafusion_datasource::memory::{MemSink, MemorySourceConfig};
37use datafusion_datasource::sink::DataSinkExec;
38use datafusion_datasource::source::DataSourceExec;
39use datafusion_expr::dml::InsertOp;
40use datafusion_expr::physical_planning_context::PhysicalPlanningContext;
41use datafusion_expr::{Expr, SortExpr, TableType};
42use datafusion_physical_expr::{
43 LexOrdering, create_physical_expr, create_physical_sort_exprs,
44};
45use datafusion_physical_plan::repartition::RepartitionExec;
46use datafusion_physical_plan::stream::RecordBatchStreamAdapter;
47use datafusion_physical_plan::{
48 ChildrenPropertiesMode, DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning,
49 PhysicalExpr, PlanProperties, ReplaceChildrenOptions, collect_partitioned,
50};
51use datafusion_session::Session;
52
53use async_trait::async_trait;
54use log::debug;
55use parking_lot::Mutex;
56use tokio::sync::RwLock;
57
58pub use datafusion_datasource::memory::PartitionData;
60
61#[derive(Debug)]
66pub struct MemTable {
67 schema: SchemaRef,
68 pub batches: Vec<PartitionData>,
70 constraints: Constraints,
71 column_defaults: HashMap<String, Expr>,
72 pub sort_order: Arc<Mutex<Vec<Vec<SortExpr>>>>,
75}
76
77impl MemTable {
78 pub fn try_new(schema: SchemaRef, partitions: Vec<Vec<RecordBatch>>) -> Result<Self> {
84 if partitions.is_empty() {
85 return plan_err!("No partitions provided, expected at least one partition");
86 }
87
88 for batches in partitions.iter().flatten() {
89 let batches_schema = batches.schema();
90 if !schema.contains(&batches_schema) {
91 debug!(
92 "mem table schema does not contain batches schema. \
93 Target_schema: {schema:?}. Batches Schema: {batches_schema:?}"
94 );
95 return plan_err!("Mismatch between schema and batches");
96 }
97 }
98
99 Ok(Self {
100 schema,
101 batches: partitions
102 .into_iter()
103 .map(|e| Arc::new(RwLock::new(e)))
104 .collect::<Vec<_>>(),
105 constraints: Constraints::default(),
106 column_defaults: HashMap::new(),
107 sort_order: Arc::new(Mutex::new(vec![])),
108 })
109 }
110
111 pub fn with_constraints(mut self, constraints: Constraints) -> Self {
113 self.constraints = constraints;
114 self
115 }
116
117 pub fn with_column_defaults(
119 mut self,
120 column_defaults: HashMap<String, Expr>,
121 ) -> Self {
122 self.column_defaults = column_defaults;
123 self
124 }
125
126 pub fn with_sort_order(self, mut sort_order: Vec<Vec<SortExpr>>) -> Self {
137 std::mem::swap(self.sort_order.lock().as_mut(), &mut sort_order);
138 self
139 }
140
141 pub async fn load(
143 t: Arc<dyn TableProvider>,
144 output_partitions: Option<usize>,
145 state: &dyn Session,
146 ) -> Result<Self> {
147 let schema = t.schema();
148 let constraints = t.constraints().cloned().unwrap_or_default();
149
150 let exec = t.scan(state, None, &[], None).await?;
151 let data = collect_partitioned(exec, state.task_ctx()).await?;
152
153 let data = if let Some(num_partitions) = output_partitions {
155 let source = DataSourceExec::new(Arc::new(MemorySourceConfig::try_new(
156 &data,
157 Arc::clone(&schema),
158 None,
159 )?));
160 let exec = RepartitionExec::try_new(
161 Arc::new(source),
162 Partitioning::RoundRobinBatch(num_partitions),
163 )?;
164 collect_partitioned(Arc::new(exec), state.task_ctx()).await?
165 } else {
166 data
167 };
168
169 MemTable::try_new(schema, data).map(|table| table.with_constraints(constraints))
170 }
171}
172
173#[async_trait]
174impl TableProvider for MemTable {
175 fn schema(&self) -> SchemaRef {
176 Arc::clone(&self.schema)
177 }
178
179 fn constraints(&self) -> Option<&Constraints> {
180 Some(&self.constraints)
181 }
182
183 fn table_type(&self) -> TableType {
184 TableType::Base
185 }
186
187 async fn scan(
188 &self,
189 state: &dyn Session,
190 projection: Option<&Vec<usize>>,
191 _filters: &[Expr],
192 _limit: Option<usize>,
193 ) -> Result<Arc<dyn ExecutionPlan>> {
194 let mut partitions = vec![];
195 for arc_inner_vec in self.batches.iter() {
196 let inner_vec = arc_inner_vec.read().await;
197 partitions.push(inner_vec.clone())
198 }
199
200 let mut source =
201 MemorySourceConfig::try_new(&partitions, self.schema(), projection.cloned())?;
202
203 let show_sizes = state.config_options().explain.show_sizes;
204 source = source.with_show_sizes(show_sizes);
205
206 let sort_order = self.sort_order.lock();
208 if !sort_order.is_empty() {
209 let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
210
211 let eqp = state.execution_props();
212 let mut file_sort_order = vec![];
213 for sort_exprs in sort_order.iter() {
214 let physical_exprs = create_physical_sort_exprs(
215 sort_exprs,
216 &df_schema,
217 eqp,
218 &PhysicalPlanningContext::default(),
219 )?;
220 file_sort_order.extend(LexOrdering::new(physical_exprs));
221 }
222 source = source.try_with_sort_information(file_sort_order)?;
223 }
224
225 Ok(DataSourceExec::from_data_source(source))
226 }
227
228 async fn insert_into(
243 &self,
244 _state: &dyn Session,
245 input: Arc<dyn ExecutionPlan>,
246 insert_op: InsertOp,
247 ) -> Result<Arc<dyn ExecutionPlan>> {
248 *self.sort_order.lock() = vec![];
250
251 self.schema()
254 .logically_equivalent_names_and_types(&input.schema())?;
255
256 if insert_op != InsertOp::Append {
257 return not_impl_err!("{insert_op} not implemented for MemoryTable yet");
258 }
259 let sink = MemSink::try_new(self.batches.clone(), Arc::clone(&self.schema))?;
260 Ok(Arc::new(DataSinkExec::new(input, Arc::new(sink), None)))
261 }
262
263 fn get_column_default(&self, column: &str) -> Option<&Expr> {
264 self.column_defaults.get(column)
265 }
266
267 async fn delete_from(
268 &self,
269 state: &dyn Session,
270 filters: Vec<Expr>,
271 ) -> Result<Arc<dyn ExecutionPlan>> {
272 if self.batches.is_empty() {
274 return Ok(Arc::new(DmlResultExec::new(0)));
275 }
276
277 *self.sort_order.lock() = vec![];
278
279 let mut total_deleted: u64 = 0;
280 let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
281
282 for partition_data in &self.batches {
283 let mut partition = partition_data.write().await;
284 let mut new_batches = Vec::with_capacity(partition.len());
285
286 for batch in partition.iter() {
287 if batch.num_rows() == 0 {
288 continue;
289 }
290
291 let filter_mask = evaluate_filters_to_mask(
293 &filters,
294 batch,
295 &df_schema,
296 state.execution_props(),
297 )?;
298
299 let (delete_count, keep_mask) = match filter_mask {
300 Some(mask) => {
301 let count = mask.iter().filter(|v| v == &Some(true)).count();
303 let keep: BooleanArray =
305 mask.iter().map(|v| Some(v != Some(true))).collect();
306 (count, keep)
307 }
308 None => {
309 (
311 batch.num_rows(),
312 BooleanArray::from(vec![false; batch.num_rows()]),
313 )
314 }
315 };
316
317 total_deleted += delete_count as u64;
318
319 let filtered_batch = filter_record_batch(batch, &keep_mask)?;
320 if filtered_batch.num_rows() > 0 {
321 new_batches.push(filtered_batch);
322 }
323 }
324
325 *partition = new_batches;
326 }
327
328 Ok(Arc::new(DmlResultExec::new(total_deleted)))
329 }
330
331 async fn update(
332 &self,
333 state: &dyn Session,
334 assignments: Vec<(String, Expr)>,
335 filters: Vec<Expr>,
336 ) -> Result<Arc<dyn ExecutionPlan>> {
337 if self.batches.is_empty() {
339 return Ok(Arc::new(DmlResultExec::new(0)));
340 }
341
342 let available_columns: Vec<&str> = self
344 .schema
345 .fields()
346 .iter()
347 .map(|f| f.name().as_str())
348 .collect();
349 for (column_name, _) in &assignments {
350 if self.schema.field_with_name(column_name).is_err() {
351 return plan_err!(
352 "UPDATE failed: column '{}' does not exist. Available columns: {}",
353 column_name,
354 available_columns.join(", ")
355 );
356 }
357 }
358
359 let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
360
361 let physical_assignments: HashMap<String, Arc<dyn PhysicalExpr>> = assignments
363 .iter()
364 .map(|(name, expr)| {
365 let physical_expr = create_physical_expr(
366 expr,
367 &df_schema,
368 state.execution_props(),
369 &PhysicalPlanningContext::default(),
370 )?;
371 Ok((name.clone(), physical_expr))
372 })
373 .collect::<Result<_>>()?;
374
375 *self.sort_order.lock() = vec![];
376
377 let mut total_updated: u64 = 0;
378
379 for partition_data in &self.batches {
380 let mut partition = partition_data.write().await;
381 let mut new_batches = Vec::with_capacity(partition.len());
382
383 for batch in partition.iter() {
384 if batch.num_rows() == 0 {
385 continue;
386 }
387
388 let filter_mask = evaluate_filters_to_mask(
390 &filters,
391 batch,
392 &df_schema,
393 state.execution_props(),
394 )?;
395
396 let (update_count, update_mask) = match filter_mask {
397 Some(mask) => {
398 let count = mask.iter().filter(|v| v == &Some(true)).count();
400 let normalized: BooleanArray =
402 mask.iter().map(|v| Some(v == Some(true))).collect();
403 (count, normalized)
404 }
405 None => {
406 (
408 batch.num_rows(),
409 BooleanArray::from(vec![true; batch.num_rows()]),
410 )
411 }
412 };
413
414 total_updated += update_count as u64;
415
416 if update_count == 0 {
417 new_batches.push(batch.clone());
418 continue;
419 }
420
421 let mut new_columns: Vec<ArrayRef> =
422 Vec::with_capacity(batch.num_columns());
423
424 for field in self.schema.fields() {
425 let column_name = field.name();
426 let original_column =
427 batch.column_by_name(column_name).ok_or_else(|| {
428 datafusion_common::DataFusionError::Internal(format!(
429 "Column '{column_name}' not found in batch"
430 ))
431 })?;
432
433 let new_column = if let Some(physical_expr) =
434 physical_assignments.get(column_name.as_str())
435 {
436 let new_values =
441 physical_expr.evaluate_selection(batch, &update_mask)?;
442 let new_array = new_values.into_array(batch.num_rows())?;
443
444 let new_arr: &dyn Array = new_array.as_ref();
446 let orig_arr: &dyn Array = original_column.as_ref();
447 zip(&update_mask, &new_arr, &orig_arr)?
448 } else {
449 Arc::clone(original_column)
450 };
451
452 new_columns.push(new_column);
453 }
454
455 let updated_batch =
456 ArrowRecordBatch::try_new(Arc::clone(&self.schema), new_columns)?;
457 new_batches.push(updated_batch);
458 }
459
460 *partition = new_batches;
461 }
462
463 Ok(Arc::new(DmlResultExec::new(total_updated)))
464 }
465}
466
467fn evaluate_filters_to_mask(
471 filters: &[Expr],
472 batch: &RecordBatch,
473 df_schema: &DFSchema,
474 execution_props: &datafusion_expr::execution_props::ExecutionProps,
475) -> Result<Option<BooleanArray>> {
476 if filters.is_empty() {
477 return Ok(None);
478 }
479
480 let mut combined_mask: Option<BooleanArray> = None;
481
482 for filter_expr in filters {
483 let physical_expr = create_physical_expr(
484 filter_expr,
485 df_schema,
486 execution_props,
487 &PhysicalPlanningContext::default(),
488 )?;
489
490 let result = physical_expr.evaluate(batch)?;
491 let array = result.into_array(batch.num_rows())?;
492 let bool_array = array
493 .as_any()
494 .downcast_ref::<BooleanArray>()
495 .ok_or_else(|| {
496 datafusion_common::DataFusionError::Internal(
497 "Filter did not evaluate to boolean".to_string(),
498 )
499 })?
500 .clone();
501
502 combined_mask = Some(match combined_mask {
503 Some(existing) => and(&existing, &bool_array)?,
504 None => bool_array,
505 });
506 }
507
508 Ok(combined_mask)
509}
510
511#[derive(Debug)]
513struct DmlResultExec {
514 rows_affected: u64,
515 schema: SchemaRef,
516 properties: Arc<PlanProperties>,
517}
518
519impl DmlResultExec {
520 fn new(rows_affected: u64) -> Self {
521 let schema = Arc::new(Schema::new(vec![Field::new(
522 "count",
523 DataType::UInt64,
524 false,
525 )]));
526
527 let properties = PlanProperties::new(
528 datafusion_physical_expr::EquivalenceProperties::new(Arc::clone(&schema)),
529 Partitioning::UnknownPartitioning(1),
530 datafusion_physical_plan::execution_plan::EmissionType::Final,
531 datafusion_physical_plan::execution_plan::Boundedness::Bounded,
532 );
533
534 Self {
535 rows_affected,
536 schema,
537 properties: Arc::new(properties),
538 }
539 }
540}
541
542impl DisplayAs for DmlResultExec {
543 fn fmt_as(
544 &self,
545 t: DisplayFormatType,
546 f: &mut std::fmt::Formatter,
547 ) -> std::fmt::Result {
548 match t {
549 DisplayFormatType::Default
550 | DisplayFormatType::Verbose
551 | DisplayFormatType::TreeRender => {
552 write!(f, "DmlResultExec: rows_affected={}", self.rows_affected)
553 }
554 }
555 }
556}
557
558impl ExecutionPlan for DmlResultExec {
559 fn name(&self) -> &str {
560 "DmlResultExec"
561 }
562
563 fn schema(&self) -> SchemaRef {
564 Arc::clone(&self.schema)
565 }
566
567 fn properties(&self) -> &Arc<PlanProperties> {
568 &self.properties
569 }
570
571 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
572 vec![]
573 }
574
575 fn replace_children(
576 self: Arc<Self>,
577 _: Vec<Arc<dyn ExecutionPlan>>,
578 _: ReplaceChildrenOptions,
579 ) -> Result<Arc<dyn ExecutionPlan>> {
580 Ok(self)
581 }
582
583 fn with_new_children(
584 self: Arc<Self>,
585 children: Vec<Arc<dyn ExecutionPlan>>,
586 ) -> Result<Arc<dyn ExecutionPlan>> {
587 self.replace_children(
588 children,
589 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
590 )
591 }
592
593 fn execute(
594 &self,
595 _partition: usize,
596 _context: Arc<datafusion_execution::TaskContext>,
597 ) -> Result<datafusion_execution::SendableRecordBatchStream> {
598 let count_array = UInt64Array::from(vec![self.rows_affected]);
600 let batch = ArrowRecordBatch::try_new(
601 Arc::clone(&self.schema),
602 vec![Arc::new(count_array) as ArrayRef],
603 )?;
604
605 let stream = futures::stream::iter(vec![Ok(batch)]);
607 Ok(Box::pin(RecordBatchStreamAdapter::new(
608 Arc::clone(&self.schema),
609 stream,
610 )))
611 }
612
613 fn apply_expressions(
614 &self,
615 _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
616 ) -> Result<TreeNodeRecursion> {
617 Ok(TreeNodeRecursion::Continue)
618 }
619}