Skip to content

Commit 57a3f90

Browse files
committed
refactor(query): generalize hash-partitioned writes
1 parent 6fa47f8 commit 57a3f90

7 files changed

Lines changed: 223 additions & 96 deletions

File tree

src/query/service/src/interpreters/interpreter_insert.rs

Lines changed: 80 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ use databend_common_sql::Visibility;
4848
use databend_common_sql::binder::ConstraintExprBinder;
4949
use databend_common_sql::executor::physical_plans::FragmentKind;
5050
use databend_common_sql::executor::physical_plans::MutationKind;
51-
use databend_common_sql::executor::physical_plans::SortDesc;
5251
use databend_common_sql::plans::Insert;
5352
use databend_common_sql::plans::InsertInputSource;
5453
use databend_common_sql::plans::InsertValue;
@@ -74,14 +73,12 @@ use crate::physical_plans::EvalScalar;
7473
use crate::physical_plans::Exchange;
7574
use crate::physical_plans::IPhysicalPlan;
7675
use crate::physical_plans::PAIMON_ROUTE_KEY_NAME;
77-
use crate::physical_plans::PaimonWritePrepare;
7876
use crate::physical_plans::PaimonWriteRoute;
7977
use crate::physical_plans::PhysicalPlan;
8078
use crate::physical_plans::PhysicalPlanBuilder;
8179
use crate::physical_plans::PhysicalPlanCast;
8280
use crate::physical_plans::PhysicalPlanMeta;
83-
use crate::physical_plans::Sort;
84-
use crate::physical_plans::SortStep;
81+
use crate::physical_plans::TableWritePrepare;
8582
use crate::pipelines::PipelineBuildResult;
8683
use crate::pipelines::PipelineBuilder;
8784
use crate::pipelines::RawValueSource;
@@ -131,6 +128,25 @@ fn wrap_paimon_write_distribution(
131128
}))
132129
}
133130

131+
fn prepare_table_write_input(
132+
input: PhysicalPlan,
133+
table: &Arc<dyn Table>,
134+
select_schema: DataSchemaRef,
135+
select_column_bindings: Vec<ColumnBinding>,
136+
insert_schema: DataSchemaRef,
137+
cast_needed: bool,
138+
) -> PhysicalPlan {
139+
PhysicalPlan::new(TableWritePrepare {
140+
meta: PhysicalPlanMeta::new("TableWritePrepare"),
141+
input,
142+
table_info: table.get_table_info().clone(),
143+
insert_schema,
144+
select_schema,
145+
select_column_bindings,
146+
cast_needed,
147+
})
148+
}
149+
134150
/// Build the INSERT SELECT physical plan (including Paimon route/exchange when needed).
135151
pub fn build_insert_select_physical_plan(
136152
select_plan: PhysicalPlan,
@@ -139,24 +155,25 @@ pub fn build_insert_select_physical_plan(
139155
insert_schema: DataSchemaRef,
140156
table: Arc<dyn Table>,
141157
cast_needed: bool,
158+
input_prepared: bool,
142159
table_meta_timestamps: TableMetaTimestamps,
143160
distributed: bool,
144161
) -> Result<PhysicalPlan> {
145162
let table_info = table.get_table_info().clone();
146163
let needs_pk_route = is_fixed_bucket_primary_key(&table)?;
147164

148165
let prepare_and_route = |input: PhysicalPlan| -> Result<PhysicalPlan> {
149-
let prepared = PhysicalPlan::new(PaimonWritePrepare {
150-
meta: PhysicalPlanMeta::new("PaimonWritePrepare"),
166+
let prepared = prepare_table_write_input(
151167
input,
152-
table_info: table_info.clone(),
153-
insert_schema: insert_schema.clone(),
154-
select_schema: select_schema.clone(),
155-
select_column_bindings: select_column_bindings.clone(),
168+
&table,
169+
select_schema.clone(),
170+
select_column_bindings.clone(),
171+
insert_schema.clone(),
156172
cast_needed,
157-
});
173+
);
158174
wrap_paimon_write_distribution(prepared, table.clone())
159175
};
176+
let input_prepared = input_prepared || needs_pk_route;
160177

161178
if table.support_distributed_insert()
162179
&& let Some(exchange) = Exchange::from_physical_plan(&select_plan)
@@ -173,7 +190,7 @@ pub fn build_insert_select_physical_plan(
173190
select_column_bindings,
174191
insert_schema,
175192
cast_needed,
176-
input_prepared: needs_pk_route,
193+
input_prepared,
177194
table_meta_timestamps,
178195
meta: PhysicalPlanMeta::new("DistributedInsertSelect"),
179196
});
@@ -198,12 +215,12 @@ pub fn build_insert_select_physical_plan(
198215
select_column_bindings,
199216
insert_schema,
200217
cast_needed,
201-
input_prepared: needs_pk_route,
218+
input_prepared,
202219
table_meta_timestamps,
203220
meta: PhysicalPlanMeta::new("DistributedInsertSelect"),
204221
});
205-
// VALUES / local select has no top Merge; synthesize one for PK so commit metas gather.
206-
if needs_pk_route && distributed {
222+
// VALUES / local select has no top Merge; synthesize one so commit metas gather.
223+
if input_prepared && distributed {
207224
Ok(PhysicalPlan::new(Exchange {
208225
input: insert,
209226
kind: FragmentKind::Merge,
@@ -289,45 +306,40 @@ impl InsertInterpreter {
289306
&self,
290307
input: PhysicalPlan,
291308
table: &Arc<dyn Table>,
309+
select_schema: &DataSchemaRef,
292310
insert_schema: &DataSchemaRef,
293311
select_column_bindings: &[ColumnBinding],
294312
metadata: &MetadataRef,
295313
cast_needed: bool,
296-
) -> Result<PhysicalPlan> {
314+
) -> Result<(PhysicalPlan, bool)> {
297315
let Ok(fuse_table) = FuseTable::try_from_table(table.as_ref()) else {
298-
return Ok(input);
316+
return Ok((input, false));
299317
};
300-
if cast_needed || !fuse_table.use_hash_write_distribution() {
301-
return Ok(input);
318+
if !fuse_table.use_hash_write_distribution() {
319+
return Ok((input, false));
302320
}
303321

304322
let Some(partition_info) = fuse_table.partition_pruning_info(self.ctx.clone()) else {
305-
return Ok(input);
323+
return Ok((input, false));
306324
};
325+
let input = prepare_table_write_input(
326+
input,
327+
table,
328+
select_schema.clone(),
329+
select_column_bindings.to_vec(),
330+
insert_schema.clone(),
331+
cast_needed,
332+
);
307333
let input_schema = input.output_schema()?;
308334
let mut partition_exprs = Vec::with_capacity(partition_info.partition_keys.len());
309335
for partition_key in partition_info.partition_keys {
310336
let expr = partition_key.as_expr(&BUILTIN_FUNCTIONS);
311-
if expr
312-
.column_refs()
313-
.keys()
314-
.any(|name| insert_schema.index_of(name).is_err())
315-
{
316-
return Ok(input);
317-
}
318-
partition_exprs.push(expr.project_column_ref(|name| {
319-
let position = insert_schema.index_of(name)?;
320-
let binding = select_column_bindings.get(position).ok_or_else(|| {
321-
ErrorCode::Internal("INSERT SELECT column binding is missing")
322-
})?;
323-
input_schema.index_of(&binding.index.to_string())
324-
})?);
337+
partition_exprs.push(expr.project_column_ref(|name| input_schema.index_of(name))?);
325338
}
326339

327340
let input_columns = input_schema.num_fields();
328341
let mut eval_exprs = Vec::with_capacity(partition_exprs.len());
329342
let mut hash_keys = Vec::with_capacity(partition_exprs.len());
330-
let mut order_by = Vec::with_capacity(partition_exprs.len());
331343
for (index, expr) in partition_exprs.into_iter().enumerate() {
332344
let data_type = expr.data_type().clone();
333345
let display_name = format!("partition_key_{index}");
@@ -341,12 +353,6 @@ impl InsertInterpreter {
341353
data_type,
342354
display_name: display_name.clone(),
343355
});
344-
order_by.push(SortDesc {
345-
asc: true,
346-
nulls_first: false,
347-
order_by: symbol,
348-
display_name,
349-
});
350356
}
351357

352358
let projections: BTreeSet<_> = (0..input_columns + eval_exprs.len()).collect();
@@ -359,17 +365,7 @@ impl InsertInterpreter {
359365
ignore_exchange: false,
360366
allow_adjust_parallelism: true,
361367
});
362-
Ok(PhysicalPlan::new(Sort {
363-
meta: PhysicalPlanMeta::new("Sort"),
364-
input: exchange,
365-
order_by,
366-
limit: None,
367-
step: SortStep::Single,
368-
pre_projection: None,
369-
broadcast_id: None,
370-
enable_fixed_rows: self.ctx.get_settings().get_enable_fixed_rows_sort()?,
371-
stat_info: None,
372-
}))
368+
Ok((exchange, true))
373369
}
374370
}
375371

@@ -455,6 +451,7 @@ impl Interpreter for InsertInterpreter {
455451
insert_schema,
456452
table.clone(),
457453
false,
454+
false,
458455
table_meta_timestamps,
459456
self.ctx.get_cluster().get_nodes().len() > 1,
460457
)?;
@@ -547,39 +544,48 @@ impl Interpreter for InsertInterpreter {
547544

548545
let update_stream_meta = dml_build_update_stream_req(self.ctx.clone()).await?;
549546

547+
let select_schema = plan.schema();
550548
let insert_schema = self.plan.dest_schema();
551549
let cast_needed = self.check_schema_cast(plan)?;
552-
let select_plan = if table1.support_distributed_insert()
553-
&& let Some(exchange) = Exchange::from_physical_plan(&select_plan)
554-
{
555-
let input = self.build_hash_distributed_input(
556-
exchange.input.clone(),
557-
&table1,
558-
&insert_schema,
559-
&select_column_bindings,
560-
metadata,
561-
cast_needed,
562-
)?;
563-
exchange.derive(vec![input])
550+
let distributed = self.ctx.get_cluster().get_nodes().len() > 1;
551+
let (select_plan, input_prepared) = if distributed {
552+
if table1.support_distributed_insert()
553+
&& let Some(exchange) = Exchange::from_physical_plan(&select_plan)
554+
{
555+
let (input, input_prepared) = self.build_hash_distributed_input(
556+
exchange.input.clone(),
557+
&table1,
558+
&select_schema,
559+
&insert_schema,
560+
&select_column_bindings,
561+
metadata,
562+
cast_needed,
563+
)?;
564+
(exchange.derive(vec![input]), input_prepared)
565+
} else {
566+
self.build_hash_distributed_input(
567+
select_plan,
568+
&table1,
569+
&select_schema,
570+
&insert_schema,
571+
&select_column_bindings,
572+
metadata,
573+
cast_needed,
574+
)?
575+
}
564576
} else {
565-
self.build_hash_distributed_input(
566-
select_plan,
567-
&table1,
568-
&insert_schema,
569-
&select_column_bindings,
570-
metadata,
571-
cast_needed,
572-
)?
577+
(select_plan, false)
573578
};
574579
let mut insert_select_plan = build_insert_select_physical_plan(
575580
select_plan,
576-
plan.schema(),
581+
select_schema,
577582
select_column_bindings,
578583
insert_schema,
579584
table1,
580585
cast_needed,
586+
input_prepared,
581587
table_meta_timestamps,
582-
self.ctx.get_cluster().get_nodes().len() > 1,
588+
distributed,
583589
)?;
584590

585591
insert_select_plan.adjust_plan_id(&mut 0);

src/query/service/src/physical_plans/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ mod physical_mutation_into_organize;
4343
mod physical_mutation_into_split;
4444
mod physical_mutation_manipulate;
4545
mod physical_mutation_source;
46-
mod physical_paimon_write_prepare;
4746
mod physical_paimon_write_route;
4847
mod physical_project_set;
4948
mod physical_r_cte_scan;
@@ -56,6 +55,7 @@ mod physical_row_fetch;
5655
mod physical_sort;
5756
mod physical_spatial_join;
5857
mod physical_table_scan;
58+
mod physical_table_write_prepare;
5959
mod physical_udf;
6060
mod physical_union_all;
6161
mod physical_window;
@@ -95,7 +95,6 @@ pub use physical_mutation_into_organize::MutationOrganize;
9595
pub use physical_mutation_into_split::MutationSplit;
9696
pub use physical_mutation_manipulate::MutationManipulate;
9797
pub use physical_mutation_source::*;
98-
pub use physical_paimon_write_prepare::PaimonWritePrepare;
9998
pub use physical_paimon_write_route::PAIMON_ROUTE_KEY_NAME;
10099
pub use physical_paimon_write_route::PaimonWriteRoute;
101100
pub use physical_project_set::ProjectSet;
@@ -111,6 +110,7 @@ pub use physical_sort::Sort;
111110
pub use physical_sort::SortStep;
112111
pub use physical_spatial_join::PhysicalSpatialJoin;
113112
pub use physical_table_scan::TableScan;
113+
pub use physical_table_write_prepare::TableWritePrepare;
114114
pub use physical_udf::UdfFunctionDesc;
115115
pub use physical_union_all::UnionAll;
116116
pub use physical_window::*;

src/query/service/src/physical_plans/physical_distributed_insert_select.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::any::Any;
1616

1717
use databend_common_catalog::plan::DataSourcePlan;
1818
use databend_common_exception::Result;
19+
use databend_common_expression::DataSchema;
1920
use databend_common_expression::DataSchemaRef;
2021
use databend_common_meta_app::schema::TableInfo;
2122
use databend_common_pipeline_transforms::TransformPipelineHelper;
@@ -96,6 +97,9 @@ impl IPhysicalPlan for DistributedInsertSelect {
9697

9798
let select_schema = &self.select_schema;
9899
let insert_schema = &self.insert_schema;
100+
let table = builder
101+
.ctx
102+
.build_table_by_table_info(&self.table_info, None)?;
99103
// should render result for select
100104
if !self.input_prepared {
101105
PipelineBuilder::build_result_projection(
@@ -106,7 +110,9 @@ impl IPhysicalPlan for DistributedInsertSelect {
106110
false,
107111
)?;
108112
} else {
109-
let projection = (0..insert_schema.num_fields()).collect::<Vec<_>>();
113+
let table_schema = table.schema().remove_virtual_computed_fields();
114+
let prepared_schema = DataSchema::from(&table_schema);
115+
let projection = (0..prepared_schema.num_fields()).collect::<Vec<_>>();
110116
let num_input_columns = self.input.output_schema()?.num_fields();
111117
builder.main_pipeline.add_transformer(|| {
112118
CompoundBlockOperator::new(
@@ -129,10 +135,6 @@ impl IPhysicalPlan for DistributedInsertSelect {
129135
})?;
130136
}
131137

132-
let table = builder
133-
.ctx
134-
.build_table_by_table_info(&self.table_info, None)?;
135-
136138
let source_schema = insert_schema;
137139
if !self.input_prepared {
138140
PipelineBuilder::fill_and_reorder_columns(
@@ -142,6 +144,11 @@ impl IPhysicalPlan for DistributedInsertSelect {
142144
source_schema.clone(),
143145
)?;
144146
}
147+
PipelineBuilder::build_table_write_layout(
148+
builder.ctx.clone(),
149+
&mut builder.main_pipeline,
150+
table.clone(),
151+
)?;
145152

146153
table.append_data(
147154
builder.ctx.clone(),

0 commit comments

Comments
 (0)