1use std::collections::HashMap;
18use std::sync::Arc;
19use std::time::Instant;
20
21use api::helper::{
22 ColumnDataTypeWrapper, is_column_type_value_eq, is_semantic_type_eq, proto_value_type,
23 proto_value_type_match,
24};
25use api::v1::column_def::options_from_column_schema;
26use api::v1::{ColumnDataType, ColumnSchema, OpType, Rows, SemanticType, Value, WriteHint};
27use common_telemetry::info;
28use datatypes::arrow::array::{Array, ArrayRef};
29use datatypes::arrow::datatypes::DataType as ArrowDataType;
30use datatypes::arrow::record_batch::RecordBatch;
31use datatypes::prelude::DataType;
32use partition::expr::PartitionExpr;
33use prometheus::HistogramTimer;
34use prost::Message;
35use smallvec::SmallVec;
36use snafu::{OptionExt, ResultExt, ensure};
37use store_api::ManifestVersion;
38use store_api::codec::{PrimaryKeyEncoding, infer_primary_key_encoding_from_hint};
39use store_api::metadata::{ColumnMetadata, RegionMetadata, RegionMetadataRef};
40use store_api::region_engine::{
41 MitoCopyRegionFromResponse, SetRegionRoleStateResponse, SettableRegionRoleState,
42};
43use store_api::region_request::{
44 AffectedRows, ApplyStagingManifestRequest, EnterStagingRequest, RegionAlterRequest,
45 RegionBuildIndexRequest, RegionBulkInsertsRequest, RegionCatchupRequest, RegionCleanUpRequest,
46 RegionCloseRequest, RegionCompactRequest, RegionCreateRequest, RegionDropRequest,
47 RegionFlushRequest, RegionOpenRequest, RegionRequest, RegionTruncateRequest,
48 StagingPartitionDirective,
49};
50use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
51use store_api::storage::{FileId, RegionId, SequenceNumber};
52use tokio::sync::oneshot::{self, Receiver, Sender};
53
54use crate::compaction::{CompactionExecution, CompactionPickFinished};
55use crate::error::{
56 CompactRegionSnafu, ConvertColumnDataTypeSnafu, CreateDefaultSnafu, Error, FillDefaultSnafu,
57 FlushRegionSnafu, InvalidPartitionExprSnafu, InvalidRequestSnafu, MissingPartitionExprSnafu,
58 Result, UnexpectedSnafu,
59};
60use crate::flush::FlushReason;
61use crate::manifest::action::{RegionEdit, TruncateKind};
62use crate::memtable::MemtableId;
63use crate::memtable::bulk::part::BulkPart;
64use crate::metrics::COMPACTION_ELAPSED_TOTAL;
65use crate::region::MitoRegionRef;
66use crate::region::options::RegionOptions;
67use crate::sst::file::FileMeta;
68use crate::sst::index::{IndexBuildType, ResultMpscSender};
69use crate::wal::EntryId;
70use crate::wal::entry_distributor::WalEntryReceiver;
71
72#[derive(Debug)]
74pub struct WriteRequest {
75 pub region_id: RegionId,
77 pub op_type: OpType,
79 pub rows: Rows,
81 pub name_to_index: HashMap<String, usize>,
83 pub has_null: Vec<bool>,
85 pub skip_wal: bool,
87 pub hint: Option<WriteHint>,
89 pub(crate) region_metadata: Option<RegionMetadataRef>,
91 pub partition_expr_version: Option<u64>,
93}
94
95impl WriteRequest {
96 pub fn new(
100 region_id: RegionId,
101 op_type: OpType,
102 rows: Rows,
103 region_metadata: Option<RegionMetadataRef>,
104 ) -> Result<WriteRequest> {
105 let mut name_to_index = HashMap::with_capacity(rows.schema.len());
106 for (index, column) in rows.schema.iter().enumerate() {
107 ensure!(
108 name_to_index
109 .insert(column.column_name.clone(), index)
110 .is_none(),
111 InvalidRequestSnafu {
112 region_id,
113 reason: format!("duplicate column {}", column.column_name),
114 }
115 );
116 }
117
118 let mut has_null = vec![false; rows.schema.len()];
119 for row in &rows.rows {
120 ensure!(
121 row.values.len() == rows.schema.len(),
122 InvalidRequestSnafu {
123 region_id,
124 reason: format!(
125 "row has {} columns but schema has {}",
126 row.values.len(),
127 rows.schema.len()
128 ),
129 }
130 );
131
132 for (i, (value, column_schema)) in row.values.iter().zip(&rows.schema).enumerate() {
133 validate_proto_value(region_id, value, column_schema)?;
134
135 if value.value_data.is_none() {
136 has_null[i] = true;
137 }
138 }
139 }
140
141 Ok(WriteRequest {
142 region_id,
143 op_type,
144 rows,
145 name_to_index,
146 has_null,
147 hint: None,
148 skip_wal: false,
149 region_metadata,
150 partition_expr_version: None,
151 })
152 }
153
154 pub fn with_skip_wal(mut self, skip_wal: bool) -> Self {
156 self.skip_wal = skip_wal;
157 self
158 }
159
160 pub fn with_hint(mut self, hint: Option<WriteHint>) -> Self {
162 self.hint = hint;
163 self
164 }
165
166 pub fn with_partition_expr_version(mut self, partition_expr_version: Option<u64>) -> Self {
167 self.partition_expr_version = partition_expr_version;
168 self
169 }
170
171 pub fn primary_key_encoding(&self) -> PrimaryKeyEncoding {
173 infer_primary_key_encoding_from_hint(self.hint.as_ref())
174 }
175
176 pub(crate) fn estimated_size(&self) -> usize {
178 let row_size = self
179 .rows
180 .rows
181 .first()
182 .map(|row| row.encoded_len())
183 .unwrap_or(0);
184 row_size * self.rows.rows.len()
185 }
186
187 pub fn column_index_by_name(&self, name: &str) -> Option<usize> {
189 self.name_to_index.get(name).copied()
190 }
191
192 pub(crate) fn check_schema(&self, metadata: &RegionMetadata) -> Result<()> {
197 debug_assert_eq!(self.region_id, metadata.region_id);
198
199 let region_id = self.region_id;
200 let rows_columns = self
202 .rows
203 .schema
204 .iter()
205 .map(|schema| {
206 let column = RowColumn {
207 request: self,
208 schema,
209 };
210 (schema.column_name.as_str(), column)
211 })
212 .collect();
213 let need_fill_default =
214 check_request_columns(region_id, metadata, rows_columns, |column| {
215 self.check_missing_column(column)
216 })?;
217
218 ensure!(!need_fill_default, FillDefaultSnafu { region_id });
220
221 Ok(())
222 }
223
224 pub(crate) fn fill_missing_columns(&mut self, metadata: &RegionMetadata) -> Result<()> {
229 debug_assert_eq!(self.region_id, metadata.region_id);
230
231 let mut columns_to_fill = vec![];
232 for column in &metadata.column_metadatas {
233 if !self.name_to_index.contains_key(&column.column_schema.name) {
234 columns_to_fill.push(column);
235 }
236 }
237 self.fill_columns(columns_to_fill)?;
238
239 Ok(())
240 }
241
242 pub(crate) fn maybe_fill_missing_columns(&mut self, metadata: &RegionMetadata) -> Result<()> {
244 let num_columns = self.name_to_index.len();
247 if self.rows.schema.len() > num_columns {
248 self.rows.schema.truncate(num_columns);
249 for row in &mut self.rows.rows {
250 row.values.truncate(num_columns);
251 }
252 }
253 if let Err(e) = self.check_schema(metadata) {
254 if e.is_fill_default() {
255 self.fill_missing_columns(metadata)?;
259 } else {
260 return Err(e);
261 }
262 }
263
264 Ok(())
265 }
266
267 fn fill_columns(&mut self, columns: Vec<&ColumnMetadata>) -> Result<()> {
269 let mut default_values = Vec::with_capacity(columns.len());
270 let mut columns_to_fill = Vec::with_capacity(columns.len());
271 for column in columns {
272 let default_value = self.column_default_value(column)?;
273 if default_value.value_data.is_some() {
274 default_values.push(default_value);
275 columns_to_fill.push(column);
276 }
277 }
278
279 for row in &mut self.rows.rows {
280 row.values.extend(default_values.iter().cloned());
281 }
282
283 for column in columns_to_fill {
284 let (datatype, datatype_ext) =
285 ColumnDataTypeWrapper::try_from(column.column_schema.data_type.clone())
286 .with_context(|_| ConvertColumnDataTypeSnafu {
287 reason: format!(
288 "no protobuf type for column {} ({:?})",
289 column.column_schema.name, column.column_schema.data_type
290 ),
291 })?
292 .to_parts();
293 self.rows.schema.push(ColumnSchema {
294 column_name: column.column_schema.name.clone(),
295 datatype: datatype as i32,
296 semantic_type: column.semantic_type as i32,
297 datatype_extension: datatype_ext,
298 options: options_from_column_schema(&column.column_schema),
299 });
300 }
301
302 Ok(())
303 }
304
305 fn check_missing_column(&self, column: &ColumnMetadata) -> Result<()> {
307 if self.op_type == OpType::Delete {
308 if column.semantic_type == SemanticType::Field {
309 return Ok(());
312 } else {
313 return InvalidRequestSnafu {
314 region_id: self.region_id,
315 reason: format!("delete requests need column {}", column.column_schema.name),
316 }
317 .fail();
318 }
319 }
320
321 check_column_can_be_filled(self.region_id, column)
323 }
324
325 fn column_default_value(&self, column: &ColumnMetadata) -> Result<Value> {
327 let default_value = match self.op_type {
328 OpType::Delete => {
329 ensure!(
330 column.semantic_type == SemanticType::Field,
331 InvalidRequestSnafu {
332 region_id: self.region_id,
333 reason: format!(
334 "delete requests need column {}",
335 column.column_schema.name
336 ),
337 }
338 );
339
340 if column.column_schema.is_nullable() {
345 datatypes::value::Value::Null
346 } else {
347 column.column_schema.data_type.default_value()
348 }
349 }
350 OpType::Put => {
351 if column.column_schema.is_default_impure() {
353 UnexpectedSnafu {
354 reason: format!(
355 "unexpected impure default value with region_id: {}, column: {}, default_value: {:?}",
356 self.region_id,
357 column.column_schema.name,
358 column.column_schema.default_constraint(),
359 ),
360 }
361 .fail()?
362 }
363 column
364 .column_schema
365 .create_default()
366 .context(CreateDefaultSnafu {
367 region_id: self.region_id,
368 column: &column.column_schema.name,
369 })?
370 .with_context(|| InvalidRequestSnafu {
372 region_id: self.region_id,
373 reason: format!(
374 "column {} does not have default value",
375 column.column_schema.name
376 ),
377 })?
378 }
379 };
380
381 Ok(api::helper::to_grpc_value(default_value))
383 }
384}
385
386trait RequestColumn {
388 fn has_type_of(&self, column: &ColumnMetadata) -> bool;
390
391 fn type_name(&self) -> String;
393
394 fn semantic_type(&self) -> Option<i32>;
396
397 fn has_null(&self) -> bool;
399}
400
401fn check_request_columns<C: RequestColumn>(
407 region_id: RegionId,
408 metadata: &RegionMetadata,
409 mut columns: HashMap<&str, C>,
410 mut check_missing: impl FnMut(&ColumnMetadata) -> Result<()>,
411) -> Result<bool> {
412 let mut has_missing = false;
413 for column in &metadata.column_metadatas {
414 let Some(input_col) = columns.remove(column.column_schema.name.as_str()) else {
415 check_missing(column)?;
416 has_missing = true;
417 continue;
418 };
419 ensure!(
421 input_col.has_type_of(column),
422 InvalidRequestSnafu {
423 region_id,
424 reason: format!(
425 "column {} expect type {:?}, given: {}",
426 column.column_schema.name,
427 column.column_schema.data_type,
428 input_col.type_name(),
429 )
430 }
431 );
432
433 if let Some(semantic_type) = input_col.semantic_type() {
435 ensure!(
436 is_semantic_type_eq(semantic_type, column.semantic_type),
437 InvalidRequestSnafu {
438 region_id,
439 reason: format!(
440 "column {} has semantic type {:?}, given: {}({})",
441 column.column_schema.name,
442 column.semantic_type,
443 api::v1::SemanticType::try_from(semantic_type)
444 .map(|v| v.as_str_name())
445 .unwrap_or("Unknown"),
446 semantic_type
447 ),
448 }
449 );
450 }
451
452 ensure!(
454 !input_col.has_null() || column.column_schema.is_nullable(),
455 InvalidRequestSnafu {
456 region_id,
457 reason: format!(
458 "column {} is not null but input has null",
459 column.column_schema.name
460 ),
461 }
462 );
463 }
464
465 if !columns.is_empty() {
467 let names: Vec<_> = columns.into_keys().collect();
468 return InvalidRequestSnafu {
469 region_id,
470 reason: format!("unknown columns: {:?}", names),
471 }
472 .fail();
473 }
474
475 Ok(has_missing)
476}
477
478fn check_column_can_be_filled(region_id: RegionId, column: &ColumnMetadata) -> Result<()> {
481 ensure!(
482 column.column_schema.is_nullable() || column.column_schema.default_constraint().is_some(),
483 InvalidRequestSnafu {
484 region_id,
485 reason: format!("missing column {}", column.column_schema.name),
486 }
487 );
488 Ok(())
489}
490
491struct RowColumn<'a> {
493 request: &'a WriteRequest,
494 schema: &'a ColumnSchema,
495}
496
497impl RequestColumn for RowColumn<'_> {
498 fn has_type_of(&self, column: &ColumnMetadata) -> bool {
499 is_column_type_value_eq(
500 self.schema.datatype,
501 self.schema.datatype_extension.clone(),
502 &column.column_schema.data_type,
503 )
504 }
505
506 fn type_name(&self) -> String {
507 format!(
508 "{}({})",
509 ColumnDataType::try_from(self.schema.datatype)
510 .map(|v| v.as_str_name())
511 .unwrap_or("Unknown"),
512 self.schema.datatype,
513 )
514 }
515
516 fn semantic_type(&self) -> Option<i32> {
517 Some(self.schema.semantic_type)
518 }
519
520 fn has_null(&self) -> bool {
521 self.request.has_null[self.request.name_to_index[&self.schema.column_name]]
523 }
524}
525
526struct BulkColumn<'a> {
528 array: &'a ArrayRef,
529}
530
531impl RequestColumn for BulkColumn<'_> {
532 fn has_type_of(&self, column: &ColumnMetadata) -> bool {
533 let actual = self.array.data_type();
534 let data_type = &column.column_schema.data_type;
535 if column.semantic_type == SemanticType::Tag && data_type.is_string() {
537 return match actual {
538 ArrowDataType::Dictionary(key, value) => key.is_integer() && value.is_string(),
539 actual => actual.is_string(),
540 };
541 }
542 *actual == data_type.as_arrow_type()
543 }
544
545 fn type_name(&self) -> String {
546 self.array.data_type().to_string()
547 }
548
549 fn semantic_type(&self) -> Option<i32> {
550 None
551 }
552
553 fn has_null(&self) -> bool {
554 self.array.logical_null_count() > 0
555 }
556}
557
558pub fn check_bulk_insert_batch(metadata: &RegionMetadata, batch: &RecordBatch) -> Result<bool> {
566 let region_id = metadata.region_id;
567 let sparse = metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse;
568 let mut primary_key = None;
569 let mut columns = HashMap::with_capacity(batch.num_columns());
570 for (field, array) in batch.schema_ref().fields().iter().zip(batch.columns()) {
571 let duplicate = if sparse && field.name() == PRIMARY_KEY_COLUMN_NAME {
572 primary_key.replace(array).is_some()
573 } else {
574 columns
575 .insert(field.name().as_str(), BulkColumn { array })
576 .is_some()
577 };
578 ensure!(
579 !duplicate,
580 InvalidRequestSnafu {
581 region_id,
582 reason: format!("duplicate column {}", field.name()),
583 }
584 );
585 }
586 if sparse {
587 let primary_key = primary_key.with_context(|| InvalidRequestSnafu {
588 region_id,
589 reason: format!("missing column {PRIMARY_KEY_COLUMN_NAME}"),
590 })?;
591 let type_matches = match primary_key.data_type() {
593 ArrowDataType::Binary => true,
594 ArrowDataType::Dictionary(key, value) => {
595 key.is_integer() && **value == ArrowDataType::Binary
596 }
597 _ => false,
598 };
599 ensure!(
600 type_matches && primary_key.logical_null_count() == 0,
601 InvalidRequestSnafu {
602 region_id,
603 reason: format!(
604 "column {PRIMARY_KEY_COLUMN_NAME} must be binary without nulls, given: {}",
605 primary_key.data_type()
606 ),
607 }
608 );
609 }
610 let mut needs_fill = false;
611 check_request_columns(region_id, metadata, columns, |column| {
612 if sparse && column.semantic_type == SemanticType::Tag {
613 Ok(())
614 } else {
615 needs_fill = true;
616 check_column_can_be_filled(region_id, column)
617 }
618 })?;
619 Ok(needs_fill)
620}
621
622pub(crate) fn validate_proto_value(
624 region_id: RegionId,
625 value: &Value,
626 column_schema: &ColumnSchema,
627) -> Result<()> {
628 if let Some(value_type) = proto_value_type(value) {
629 let column_type = ColumnDataType::try_from(column_schema.datatype).map_err(|_| {
630 InvalidRequestSnafu {
631 region_id,
632 reason: format!(
633 "column {} has unknown type {}",
634 column_schema.column_name, column_schema.datatype
635 ),
636 }
637 .build()
638 })?;
639 ensure!(
640 proto_value_type_match(column_type, value_type),
641 InvalidRequestSnafu {
642 region_id,
643 reason: format!(
644 "value has type {:?}, but column {} has type {:?}({})",
645 value_type, column_schema.column_name, column_type, column_schema.datatype,
646 ),
647 }
648 );
649 }
650
651 Ok(())
652}
653
654#[derive(Debug)]
656pub struct OutputTx(Sender<Result<AffectedRows>>);
657
658impl OutputTx {
659 pub(crate) fn new(sender: Sender<Result<AffectedRows>>) -> OutputTx {
661 OutputTx(sender)
662 }
663
664 pub(crate) fn send(self, result: Result<AffectedRows>) {
666 let _ = self.0.send(result);
668 }
669}
670
671#[derive(Debug)]
673pub(crate) struct OptionOutputTx(Option<OutputTx>);
674
675impl OptionOutputTx {
676 pub(crate) fn new(sender: Option<OutputTx>) -> OptionOutputTx {
678 OptionOutputTx(sender)
679 }
680
681 pub(crate) fn none() -> OptionOutputTx {
683 OptionOutputTx(None)
684 }
685
686 pub(crate) fn send_mut(&mut self, result: Result<AffectedRows>) {
688 if let Some(sender) = self.0.take() {
689 sender.send(result);
690 }
691 }
692
693 pub(crate) fn send(mut self, result: Result<AffectedRows>) {
695 if let Some(sender) = self.0.take() {
696 sender.send(result);
697 }
698 }
699
700 pub(crate) fn take_inner(&mut self) -> Option<OutputTx> {
702 self.0.take()
703 }
704}
705
706impl From<Sender<Result<AffectedRows>>> for OptionOutputTx {
707 fn from(sender: Sender<Result<AffectedRows>>) -> Self {
708 Self::new(Some(OutputTx::new(sender)))
709 }
710}
711
712impl OnFailure for OptionOutputTx {
713 fn on_failure(&mut self, err: Error) {
714 self.send_mut(Err(err));
715 }
716}
717
718pub(crate) trait OnFailure {
720 fn on_failure(&mut self, err: Error);
722}
723
724#[derive(Debug)]
726pub(crate) struct SenderWriteRequest {
727 pub(crate) sender: OptionOutputTx,
729 pub(crate) request: WriteRequest,
730}
731
732pub(crate) struct SenderBulkRequest {
733 pub(crate) skip_wal: bool,
734 pub(crate) sender: OptionOutputTx,
735 pub(crate) region_id: RegionId,
736 pub(crate) request: BulkPart,
737 pub(crate) region_metadata: Option<RegionMetadataRef>,
738 pub(crate) partition_expr_version: Option<u64>,
739}
740
741#[derive(Debug)]
742pub(crate) struct BulkInsertRequest {
743 pub(crate) metadata: Option<RegionMetadataRef>,
744 pub(crate) request: RegionBulkInsertsRequest,
745 pub(crate) sender: OptionOutputTx,
746}
747
748#[derive(Debug)]
750pub(crate) struct WorkerRequestWithTime {
751 pub(crate) request: WorkerRequest,
752 pub(crate) created_at: Instant,
753}
754
755impl WorkerRequestWithTime {
756 pub(crate) fn new(request: WorkerRequest) -> Self {
757 Self {
758 request,
759 created_at: Instant::now(),
760 }
761 }
762}
763
764#[derive(Debug)]
766pub(crate) enum WorkerRequest {
767 Write(SenderWriteRequest),
769
770 BatchWrite(Vec<SenderWriteRequest>),
772
773 Ddl(SenderDdlRequest),
775
776 Background {
778 region_id: RegionId,
780 notify: BackgroundNotify,
782 },
783
784 SetRegionRoleStateGracefully {
786 region_id: RegionId,
788 region_role_state: SettableRegionRoleState,
790 sender: Sender<SetRegionRoleStateResponse>,
792 },
793
794 Stop,
796
797 EditRegion(RegionEditRequest),
799
800 SyncRegion(RegionSyncRequest),
802
803 BulkInserts(BulkInsertRequest),
805
806 RemapManifests(RemapManifestsRequest),
808
809 CopyRegionFrom(CopyRegionFromRequest),
811}
812
813impl WorkerRequest {
814 pub(crate) fn new_open_region_request(
816 region_id: RegionId,
817 request: RegionOpenRequest,
818 entry_receiver: Option<WalEntryReceiver>,
819 ) -> (WorkerRequest, Receiver<Result<AffectedRows>>) {
820 let (sender, receiver) = oneshot::channel();
821
822 let worker_request = WorkerRequest::Ddl(SenderDdlRequest {
823 region_id,
824 sender: sender.into(),
825 request: DdlRequest::Open((request, entry_receiver)),
826 });
827
828 (worker_request, receiver)
829 }
830
831 pub(crate) fn new_catchup_region_request(
833 region_id: RegionId,
834 request: RegionCatchupRequest,
835 entry_receiver: Option<WalEntryReceiver>,
836 ) -> (WorkerRequest, Receiver<Result<AffectedRows>>) {
837 let (sender, receiver) = oneshot::channel();
838 let worker_request = WorkerRequest::Ddl(SenderDdlRequest {
839 region_id,
840 sender: sender.into(),
841 request: DdlRequest::Catchup((request, entry_receiver)),
842 });
843 (worker_request, receiver)
844 }
845
846 pub(crate) fn try_from_region_request(
848 region_id: RegionId,
849 value: RegionRequest,
850 region_metadata: Option<RegionMetadataRef>,
851 ) -> Result<(WorkerRequest, Receiver<Result<AffectedRows>>)> {
852 let (sender, receiver) = oneshot::channel();
853 let worker_request = match value {
854 RegionRequest::Put(v) => {
855 let mut write_request =
856 WriteRequest::new(region_id, OpType::Put, v.rows, region_metadata.clone())?
857 .with_hint(v.hint)
858 .with_skip_wal(v.skip_wal)
859 .with_partition_expr_version(v.partition_expr_version);
860 if write_request.primary_key_encoding() == PrimaryKeyEncoding::Dense
861 && let Some(region_metadata) = ®ion_metadata
862 {
863 write_request.maybe_fill_missing_columns(region_metadata)?;
864 }
865 WorkerRequest::Write(SenderWriteRequest {
866 sender: sender.into(),
867 request: write_request,
868 })
869 }
870 RegionRequest::Delete(v) => {
871 let mut write_request =
872 WriteRequest::new(region_id, OpType::Delete, v.rows, region_metadata.clone())?
873 .with_hint(v.hint)
874 .with_partition_expr_version(v.partition_expr_version);
875 if write_request.primary_key_encoding() == PrimaryKeyEncoding::Dense
876 && let Some(region_metadata) = ®ion_metadata
877 {
878 write_request.maybe_fill_missing_columns(region_metadata)?;
879 }
880 WorkerRequest::Write(SenderWriteRequest {
881 sender: sender.into(),
882 request: write_request,
883 })
884 }
885 RegionRequest::Create(v) => WorkerRequest::Ddl(SenderDdlRequest {
886 region_id,
887 sender: sender.into(),
888 request: DdlRequest::Create(v),
889 }),
890 RegionRequest::Drop(v) => WorkerRequest::Ddl(SenderDdlRequest {
891 region_id,
892 sender: sender.into(),
893 request: DdlRequest::Drop(v),
894 }),
895 RegionRequest::Open(v) => WorkerRequest::Ddl(SenderDdlRequest {
896 region_id,
897 sender: sender.into(),
898 request: DdlRequest::Open((v, None)),
899 }),
900 RegionRequest::CleanUp(v) => WorkerRequest::Ddl(SenderDdlRequest {
901 region_id,
902 sender: sender.into(),
903 request: DdlRequest::OfflineCleanup(v),
904 }),
905 RegionRequest::Close(v) => WorkerRequest::Ddl(SenderDdlRequest {
906 region_id,
907 sender: sender.into(),
908 request: DdlRequest::Close(v),
909 }),
910 RegionRequest::Alter(v) => WorkerRequest::Ddl(SenderDdlRequest {
911 region_id,
912 sender: sender.into(),
913 request: DdlRequest::Alter(v),
914 }),
915 RegionRequest::Flush(v) => WorkerRequest::Ddl(SenderDdlRequest {
916 region_id,
917 sender: sender.into(),
918 request: DdlRequest::Flush(v),
919 }),
920 RegionRequest::Compact(v) => WorkerRequest::Ddl(SenderDdlRequest {
921 region_id,
922 sender: sender.into(),
923 request: DdlRequest::Compact(v),
924 }),
925 RegionRequest::BuildIndex(v) => WorkerRequest::Ddl(SenderDdlRequest {
926 region_id,
927 sender: sender.into(),
928 request: DdlRequest::BuildIndex(v),
929 }),
930 RegionRequest::Truncate(v) => WorkerRequest::Ddl(SenderDdlRequest {
931 region_id,
932 sender: sender.into(),
933 request: DdlRequest::Truncate(v),
934 }),
935 RegionRequest::Catchup(v) => WorkerRequest::Ddl(SenderDdlRequest {
936 region_id,
937 sender: sender.into(),
938 request: DdlRequest::Catchup((v, None)),
939 }),
940 RegionRequest::EnterStaging(v) => WorkerRequest::Ddl(SenderDdlRequest {
941 region_id,
942 sender: sender.into(),
943 request: DdlRequest::EnterStaging(v),
944 }),
945 RegionRequest::BulkInserts(mut region_bulk_inserts_request) => {
946 if let Some(region_metadata) = ®ion_metadata
948 && region_bulk_inserts_request.payload.num_rows() > 0
949 && check_bulk_insert_batch(
950 region_metadata,
951 ®ion_bulk_inserts_request.payload,
952 )?
953 {
954 region_bulk_inserts_request.aligned_schema_version = None;
956 }
957 WorkerRequest::BulkInserts(BulkInsertRequest {
958 metadata: region_metadata,
959 sender: sender.into(),
960 request: region_bulk_inserts_request,
961 })
962 }
963 RegionRequest::ApplyStagingManifest(v) => WorkerRequest::Ddl(SenderDdlRequest {
964 region_id,
965 sender: sender.into(),
966 request: DdlRequest::ApplyStagingManifest(v),
967 }),
968 };
969
970 Ok((worker_request, receiver))
971 }
972
973 pub(crate) fn new_set_readonly_gracefully(
974 region_id: RegionId,
975 region_role_state: SettableRegionRoleState,
976 ) -> (WorkerRequest, Receiver<SetRegionRoleStateResponse>) {
977 let (sender, receiver) = oneshot::channel();
978
979 (
980 WorkerRequest::SetRegionRoleStateGracefully {
981 region_id,
982 region_role_state,
983 sender,
984 },
985 receiver,
986 )
987 }
988
989 pub(crate) fn new_sync_region_request(
990 region_id: RegionId,
991 manifest_version: ManifestVersion,
992 ) -> (WorkerRequest, Receiver<Result<(ManifestVersion, bool)>>) {
993 let (sender, receiver) = oneshot::channel();
994 (
995 WorkerRequest::SyncRegion(RegionSyncRequest {
996 region_id,
997 manifest_version,
998 sender,
999 }),
1000 receiver,
1001 )
1002 }
1003
1004 #[allow(clippy::type_complexity)]
1011 pub(crate) fn try_from_remap_manifests_request(
1012 store_api::region_engine::RemapManifestsRequest {
1013 region_id,
1014 input_regions,
1015 region_mapping,
1016 new_partition_exprs,
1017 }: store_api::region_engine::RemapManifestsRequest,
1018 ) -> Result<(WorkerRequest, Receiver<Result<HashMap<RegionId, String>>>)> {
1019 let (sender, receiver) = oneshot::channel();
1020 let new_partition_exprs = new_partition_exprs
1021 .into_iter()
1022 .map(|(k, v)| {
1023 Ok((
1024 k,
1025 PartitionExpr::from_json_str(&v)
1026 .context(InvalidPartitionExprSnafu { expr: v })?
1027 .context(MissingPartitionExprSnafu { region_id: k })?,
1028 ))
1029 })
1030 .collect::<Result<HashMap<_, _>>>()?;
1031
1032 let request = RemapManifestsRequest {
1033 region_id,
1034 input_regions,
1035 region_mapping,
1036 new_partition_exprs,
1037 sender,
1038 };
1039
1040 Ok((WorkerRequest::RemapManifests(request), receiver))
1041 }
1042
1043 pub(crate) fn try_from_copy_region_from_request(
1045 region_id: RegionId,
1046 store_api::region_engine::MitoCopyRegionFromRequest {
1047 source_region_id,
1048 parallelism,
1049 }: store_api::region_engine::MitoCopyRegionFromRequest,
1050 ) -> Result<(WorkerRequest, Receiver<Result<MitoCopyRegionFromResponse>>)> {
1051 let (sender, receiver) = oneshot::channel();
1052 let request = CopyRegionFromRequest {
1053 region_id,
1054 source_region_id,
1055 parallelism,
1056 sender,
1057 };
1058 Ok((WorkerRequest::CopyRegionFrom(request), receiver))
1059 }
1060}
1061
1062#[derive(Debug)]
1064pub(crate) enum DdlRequest {
1065 Create(RegionCreateRequest),
1066 Drop(RegionDropRequest),
1067 Open((RegionOpenRequest, Option<WalEntryReceiver>)),
1068 OfflineCleanup(RegionCleanUpRequest),
1069 Close(RegionCloseRequest),
1070 Alter(RegionAlterRequest),
1071 Flush(RegionFlushRequest),
1072 Compact(RegionCompactRequest),
1073 BuildIndex(RegionBuildIndexRequest),
1074 Truncate(RegionTruncateRequest),
1075 Catchup((RegionCatchupRequest, Option<WalEntryReceiver>)),
1076 EnterStaging(EnterStagingRequest),
1077 ApplyStagingManifest(ApplyStagingManifestRequest),
1078}
1079
1080#[derive(Debug)]
1082pub(crate) struct SenderDdlRequest {
1083 pub(crate) region_id: RegionId,
1085 pub(crate) sender: OptionOutputTx,
1087 pub(crate) request: DdlRequest,
1089}
1090
1091#[derive(Debug)]
1093pub(crate) enum BackgroundNotify {
1094 RegionOpened {
1096 region: MitoRegionRef,
1097 registered: Sender<()>,
1098 },
1099 CompactionUnit(CompactionUnitNotification),
1101 CompactionDdlComplete {
1103 generation: u64,
1104 request_index: usize,
1105 },
1106 CompactionPickFinished(CompactionPickFinished),
1108 FlushFinished(FlushFinished),
1110 FlushFailed(FlushFailed),
1112 IndexBuildFinished(IndexBuildFinished),
1114 IndexBuildStopped(IndexBuildStopped),
1116 IndexBuildRetry(BuildIndexRequest),
1118 CompactionFinished(CompactionFinished),
1120 CompactionFailed(CompactionFailed),
1122 Truncate(TruncateResult),
1124 DiscardUnflushed(DiscardUnflushedResult),
1126 RegionChange(RegionChangeResult),
1128 RegionEdit(RegionEditResult),
1130 EnterStaging(EnterStagingResult),
1132 CopyRegionFromFinished(CopyRegionFromFinished),
1134}
1135
1136#[derive(Debug)]
1138pub(crate) struct FlushFinished {
1139 pub(crate) region_id: RegionId,
1141 pub(crate) flush_reason: FlushReason,
1143 pub(crate) flushed_entry_id: EntryId,
1145 pub(crate) senders: Vec<OutputTx>,
1147 pub(crate) _timer: HistogramTimer,
1149 pub(crate) edit: RegionEdit,
1151 pub(crate) memtables_to_remove: SmallVec<[MemtableId; 2]>,
1153 pub(crate) is_staging: bool,
1155}
1156
1157impl FlushFinished {
1158 pub(crate) fn on_success(self) {
1160 for sender in self.senders {
1161 sender.send(Ok(0));
1162 }
1163 }
1164}
1165
1166impl OnFailure for FlushFinished {
1167 fn on_failure(&mut self, err: Error) {
1168 let err = Arc::new(err);
1169 for sender in self.senders.drain(..) {
1170 sender.send(Err(err.clone()).context(FlushRegionSnafu {
1171 region_id: self.region_id,
1172 }));
1173 }
1174 }
1175}
1176
1177#[derive(Debug)]
1179pub(crate) struct FlushFailed {
1180 pub(crate) err: Arc<Error>,
1182}
1183
1184impl FlushFailed {
1185 pub(crate) fn is_cancelled(&self) -> bool {
1187 matches!(self.err.as_ref(), Error::FlushCancelled { .. })
1188 }
1189}
1190
1191#[derive(Debug)]
1192pub(crate) struct IndexBuildFinished {
1193 pub(crate) manifest_version: ManifestVersion,
1194 pub(crate) file_meta: FileMeta,
1195 pub(crate) result_senders: Vec<ResultMpscSender>,
1197}
1198
1199#[derive(Debug)]
1201pub(crate) struct IndexBuildStopped {
1202 pub(crate) file_id: FileId,
1203}
1204
1205#[derive(Debug)]
1207pub(crate) enum CompactionUnitNotification {
1208 Finished {
1210 plan_id: u64,
1211 result: std::result::Result<RegionEdit, Arc<Error>>,
1212 applied: tokio::sync::oneshot::Sender<()>,
1213 },
1214 Released {
1216 plan_id: u64,
1218 },
1219}
1220
1221#[derive(Debug)]
1223pub(crate) struct CompactionFinished {
1224 pub(crate) region_id: RegionId,
1226 pub(crate) execution: CompactionExecution,
1228 pub(crate) senders: Vec<OutputTx>,
1230 pub(crate) start_time: Instant,
1232 pub(crate) edit: RegionEdit,
1234}
1235
1236impl CompactionFinished {
1237 pub fn on_success(self) {
1238 COMPACTION_ELAPSED_TOTAL
1240 .with_label_values(&["remote"])
1241 .observe(self.start_time.elapsed().as_secs_f64());
1242
1243 for sender in self.senders {
1244 sender.send(Ok(0));
1245 }
1246 info!("Successfully compacted region: {}", self.region_id);
1247 }
1248}
1249
1250impl OnFailure for CompactionFinished {
1251 fn on_failure(&mut self, err: Error) {
1253 let err = Arc::new(err);
1254 for sender in self.senders.drain(..) {
1255 sender.send(Err(err.clone()).context(CompactRegionSnafu {
1256 region_id: self.region_id,
1257 }));
1258 }
1259 }
1260}
1261
1262#[derive(Debug)]
1264pub(crate) struct CompactionFailed {
1265 pub(crate) region_id: RegionId,
1266 pub(crate) execution: CompactionExecution,
1268 pub(crate) err: Arc<Error>,
1270}
1271
1272#[derive(Debug)]
1274pub(crate) struct TruncateResult {
1275 pub(crate) region_id: RegionId,
1277 pub(crate) sender: OptionOutputTx,
1279 pub(crate) result: Result<()>,
1281 pub(crate) kind: TruncateKind,
1282}
1283
1284#[derive(Debug)]
1286pub(crate) struct DiscardUnflushedResult {
1287 pub(crate) region_id: RegionId,
1289 pub(crate) sender: OptionOutputTx,
1291 pub(crate) result: Result<()>,
1293 pub(crate) discarded_entry_id: EntryId,
1295 pub(crate) discarded_sequence: SequenceNumber,
1297 pub(crate) discarded_rows: u64,
1299 pub(crate) discarded_bytes: u64,
1301}
1302
1303#[derive(Debug)]
1305pub(crate) struct RegionChangeResult {
1306 pub(crate) region_id: RegionId,
1308 pub(crate) new_meta: RegionMetadataRef,
1310 pub(crate) sender: OptionOutputTx,
1312 pub(crate) result: Result<()>,
1314 pub(crate) need_index: bool,
1316 pub(crate) new_options: Option<RegionOptions>,
1318}
1319
1320#[derive(Debug)]
1322pub(crate) struct EnterStagingResult {
1323 pub(crate) region_id: RegionId,
1325 pub(crate) partition_directive: StagingPartitionDirective,
1327 pub(crate) sender: OptionOutputTx,
1329 pub(crate) result: Result<()>,
1331}
1332
1333#[derive(Debug)]
1334pub(crate) struct CopyRegionFromFinished {
1335 pub(crate) region_id: RegionId,
1337 pub(crate) edit: RegionEdit,
1339 pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1341}
1342
1343#[derive(Debug, Default)]
1344pub(crate) struct Waiters(SmallVec<[Sender<Result<()>>; 1]>);
1345
1346impl Waiters {
1347 pub(crate) fn one(waiter: Sender<Result<()>>) -> Self {
1348 let mut waiters = SmallVec::new();
1349 waiters.push(waiter);
1350 Self(waiters)
1351 }
1352
1353 pub(crate) fn reply_with<F: Fn() -> Result<()>>(self, f: F) {
1354 for tx in self.0 {
1355 let _ = tx.send(f());
1356 }
1357 }
1358
1359 pub(crate) fn merge(&mut self, other: Self) {
1360 self.0.extend(other.0);
1361 }
1362}
1363
1364#[derive(Debug)]
1366pub(crate) struct RegionEditRequest {
1367 pub(crate) region_id: RegionId,
1368 pub(crate) edit: RegionEdit,
1369 pub(crate) preload_sst_cache: bool,
1371 pub(crate) waiters: Waiters,
1373}
1374
1375impl RegionEditRequest {
1376 pub(crate) fn new(
1377 region_id: RegionId,
1378 edit: RegionEdit,
1379 preload_sst_cache: bool,
1380 waiter: Sender<Result<()>>,
1381 ) -> Self {
1382 Self {
1383 region_id,
1384 edit,
1385 preload_sst_cache,
1386 waiters: Waiters::one(waiter),
1387 }
1388 }
1389}
1390
1391#[derive(Debug)]
1393pub(crate) struct RegionEditResult {
1394 pub(crate) region_id: RegionId,
1396 pub(crate) waiters: Waiters,
1398 pub(crate) edit: RegionEdit,
1400 pub(crate) result: std::result::Result<(), Arc<Error>>,
1402 pub(crate) update_region_state: bool,
1404 pub(crate) is_staging: bool,
1406}
1407
1408#[derive(Debug)]
1409pub(crate) struct BuildIndexRequest {
1410 pub(crate) region_id: RegionId,
1411 pub(crate) build_type: IndexBuildType,
1412 pub(crate) file_metas: Vec<FileMeta>,
1414 pub(crate) result_senders: Vec<ResultMpscSender>,
1416}
1417
1418#[derive(Debug)]
1419pub(crate) struct RegionSyncRequest {
1420 pub(crate) region_id: RegionId,
1421 pub(crate) manifest_version: ManifestVersion,
1422 pub(crate) sender: Sender<Result<(ManifestVersion, bool)>>,
1424}
1425
1426#[derive(Debug)]
1427pub(crate) struct RemapManifestsRequest {
1428 pub(crate) region_id: RegionId,
1430 pub(crate) input_regions: Vec<RegionId>,
1432 pub(crate) region_mapping: HashMap<RegionId, Vec<RegionId>>,
1434 pub(crate) new_partition_exprs: HashMap<RegionId, PartitionExpr>,
1436 pub(crate) sender: Sender<Result<HashMap<RegionId, String>>>,
1440}
1441
1442#[derive(Debug)]
1443pub(crate) struct CopyRegionFromRequest {
1444 pub(crate) region_id: RegionId,
1446 pub(crate) source_region_id: RegionId,
1448 pub(crate) parallelism: usize,
1450 pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1452}
1453
1454#[cfg(test)]
1455mod tests {
1456 use api::v1::value::ValueData;
1457 use api::v1::{Row, SemanticType};
1458 use datatypes::prelude::ConcreteDataType;
1459 use datatypes::schema::ColumnDefaultConstraint;
1460 use mito_codec::test_util::i64_value;
1461 use store_api::metadata::RegionMetadataBuilder;
1462 use tokio::sync::oneshot;
1463
1464 use super::*;
1465 use crate::error::Error;
1466 use crate::test_util::ts_ms_value;
1467
1468 fn new_column_schema(
1469 name: &str,
1470 data_type: ColumnDataType,
1471 semantic_type: SemanticType,
1472 ) -> ColumnSchema {
1473 ColumnSchema {
1474 column_name: name.to_string(),
1475 datatype: data_type as i32,
1476 semantic_type: semantic_type as i32,
1477 ..Default::default()
1478 }
1479 }
1480
1481 fn check_invalid_request(err: &Error, expect: &str) {
1482 if let Error::InvalidRequest {
1483 region_id: _,
1484 reason,
1485 location: _,
1486 } = err
1487 {
1488 assert_eq!(reason, expect);
1489 } else {
1490 panic!("Unexpected error {err}")
1491 }
1492 }
1493
1494 fn waiter() -> (Sender<Result<()>>, Receiver<Result<()>>) {
1495 oneshot::channel()
1496 }
1497
1498 fn assert_waiter_ok(rx: &mut Receiver<Result<()>>) {
1499 rx.try_recv().unwrap().unwrap();
1500 }
1501
1502 #[test]
1503 fn test_waiters_reply_with_single_waiter() {
1504 let (tx, mut rx) = waiter();
1505 Waiters::one(tx).reply_with(|| Ok(()));
1506 assert_waiter_ok(&mut rx);
1507 }
1508
1509 #[test]
1510 fn test_waiters_reply_with_many_waiters() {
1511 let (tx1, mut rx1) = waiter();
1512 let (tx2, mut rx2) = waiter();
1513 let (tx3, mut rx3) = waiter();
1514
1515 let waiters = Waiters(vec![tx1, tx2, tx3].into());
1516 waiters.reply_with(|| Ok(()));
1517
1518 assert_waiter_ok(&mut rx1);
1519 assert_waiter_ok(&mut rx2);
1520 assert_waiter_ok(&mut rx3);
1521 }
1522
1523 #[test]
1524 fn test_waiters_merge() {
1525 let (tx1, mut rx1) = waiter();
1526 let (tx2, mut rx2) = waiter();
1527 let (tx3, mut rx3) = waiter();
1528 let (tx4, mut rx4) = waiter();
1529
1530 let mut waiters = Waiters::one(tx1);
1531 waiters.merge(Waiters::one(tx2));
1532 waiters.merge(Waiters(vec![tx3, tx4].into()));
1533 assert_eq!(4, waiters.0.len());
1534
1535 waiters.reply_with(|| Ok(()));
1536
1537 assert_waiter_ok(&mut rx1);
1538 assert_waiter_ok(&mut rx2);
1539 assert_waiter_ok(&mut rx3);
1540 assert_waiter_ok(&mut rx4);
1541 }
1542
1543 #[test]
1544 fn test_write_request_duplicate_column() {
1545 let rows = Rows {
1546 schema: vec![
1547 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1548 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1549 ],
1550 rows: vec![],
1551 };
1552
1553 let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1554 check_invalid_request(&err, "duplicate column c0");
1555 }
1556
1557 #[test]
1558 fn test_valid_write_request() {
1559 let rows = Rows {
1560 schema: vec![
1561 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1562 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1563 ],
1564 rows: vec![Row {
1565 values: vec![i64_value(1), i64_value(2)],
1566 }],
1567 };
1568
1569 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1570 assert_eq!(0, request.column_index_by_name("c0").unwrap());
1571 assert_eq!(1, request.column_index_by_name("c1").unwrap());
1572 assert_eq!(None, request.column_index_by_name("c2"));
1573 }
1574
1575 #[test]
1576 fn test_write_request_column_num() {
1577 let rows = Rows {
1578 schema: vec![
1579 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1580 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1581 ],
1582 rows: vec![Row {
1583 values: vec![i64_value(1), i64_value(2), i64_value(3)],
1584 }],
1585 };
1586
1587 let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1588 check_invalid_request(&err, "row has 3 columns but schema has 2");
1589 }
1590
1591 fn new_region_metadata() -> RegionMetadata {
1592 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1593 builder
1594 .push_column_metadata(ColumnMetadata {
1595 column_schema: datatypes::schema::ColumnSchema::new(
1596 "ts",
1597 ConcreteDataType::timestamp_millisecond_datatype(),
1598 false,
1599 ),
1600 semantic_type: SemanticType::Timestamp,
1601 column_id: 1,
1602 })
1603 .push_column_metadata(ColumnMetadata {
1604 column_schema: datatypes::schema::ColumnSchema::new(
1605 "k0",
1606 ConcreteDataType::int64_datatype(),
1607 true,
1608 ),
1609 semantic_type: SemanticType::Tag,
1610 column_id: 2,
1611 })
1612 .primary_key(vec![2]);
1613 builder.build().unwrap()
1614 }
1615
1616 #[test]
1617 fn test_check_schema() {
1618 let rows = Rows {
1619 schema: vec![
1620 new_column_schema(
1621 "ts",
1622 ColumnDataType::TimestampMillisecond,
1623 SemanticType::Timestamp,
1624 ),
1625 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1626 ],
1627 rows: vec![Row {
1628 values: vec![ts_ms_value(1), i64_value(2)],
1629 }],
1630 };
1631 let metadata = new_region_metadata();
1632
1633 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1634 request.check_schema(&metadata).unwrap();
1635 }
1636
1637 #[test]
1638 fn test_column_type() {
1639 let rows = Rows {
1640 schema: vec![
1641 new_column_schema("ts", ColumnDataType::Int64, SemanticType::Timestamp),
1642 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1643 ],
1644 rows: vec![Row {
1645 values: vec![i64_value(1), i64_value(2)],
1646 }],
1647 };
1648 let metadata = new_region_metadata();
1649
1650 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1651 let err = request.check_schema(&metadata).unwrap_err();
1652 check_invalid_request(
1653 &err,
1654 "column ts expect type Timestamp(Millisecond(TimestampMillisecondType)), given: INT64(4)",
1655 );
1656 }
1657
1658 #[test]
1659 fn test_semantic_type() {
1660 let rows = Rows {
1661 schema: vec![
1662 new_column_schema(
1663 "ts",
1664 ColumnDataType::TimestampMillisecond,
1665 SemanticType::Tag,
1666 ),
1667 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1668 ],
1669 rows: vec![Row {
1670 values: vec![ts_ms_value(1), i64_value(2)],
1671 }],
1672 };
1673 let metadata = new_region_metadata();
1674
1675 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1676 let err = request.check_schema(&metadata).unwrap_err();
1677 check_invalid_request(&err, "column ts has semantic type Timestamp, given: TAG(0)");
1678 }
1679
1680 #[test]
1681 fn test_column_nullable() {
1682 let rows = Rows {
1683 schema: vec![
1684 new_column_schema(
1685 "ts",
1686 ColumnDataType::TimestampMillisecond,
1687 SemanticType::Timestamp,
1688 ),
1689 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1690 ],
1691 rows: vec![Row {
1692 values: vec![Value { value_data: None }, i64_value(2)],
1693 }],
1694 };
1695 let metadata = new_region_metadata();
1696
1697 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1698 let err = request.check_schema(&metadata).unwrap_err();
1699 check_invalid_request(&err, "column ts is not null but input has null");
1700 }
1701
1702 #[test]
1703 fn test_column_default() {
1704 let rows = Rows {
1705 schema: vec![new_column_schema(
1706 "k0",
1707 ColumnDataType::Int64,
1708 SemanticType::Tag,
1709 )],
1710 rows: vec![Row {
1711 values: vec![i64_value(1)],
1712 }],
1713 };
1714 let metadata = new_region_metadata();
1715
1716 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1717 let err = request.check_schema(&metadata).unwrap_err();
1718 check_invalid_request(&err, "missing column ts");
1719 }
1720
1721 #[test]
1722 fn test_unknown_column() {
1723 let rows = Rows {
1724 schema: vec![
1725 new_column_schema(
1726 "ts",
1727 ColumnDataType::TimestampMillisecond,
1728 SemanticType::Timestamp,
1729 ),
1730 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1731 new_column_schema("k1", ColumnDataType::Int64, SemanticType::Tag),
1732 ],
1733 rows: vec![Row {
1734 values: vec![ts_ms_value(1), i64_value(2), i64_value(3)],
1735 }],
1736 };
1737 let metadata = new_region_metadata();
1738
1739 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1740 let err = request.check_schema(&metadata).unwrap_err();
1741 check_invalid_request(&err, r#"unknown columns: ["k1"]"#);
1742 }
1743
1744 #[test]
1745 fn test_fill_impure_columns_err() {
1746 let rows = Rows {
1747 schema: vec![new_column_schema(
1748 "k0",
1749 ColumnDataType::Int64,
1750 SemanticType::Tag,
1751 )],
1752 rows: vec![Row {
1753 values: vec![i64_value(1)],
1754 }],
1755 };
1756 let metadata = {
1757 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1758 builder
1759 .push_column_metadata(ColumnMetadata {
1760 column_schema: datatypes::schema::ColumnSchema::new(
1761 "ts",
1762 ConcreteDataType::timestamp_millisecond_datatype(),
1763 false,
1764 )
1765 .with_default_constraint(Some(ColumnDefaultConstraint::Function(
1766 "now()".to_string(),
1767 )))
1768 .unwrap(),
1769 semantic_type: SemanticType::Timestamp,
1770 column_id: 1,
1771 })
1772 .push_column_metadata(ColumnMetadata {
1773 column_schema: datatypes::schema::ColumnSchema::new(
1774 "k0",
1775 ConcreteDataType::int64_datatype(),
1776 true,
1777 ),
1778 semantic_type: SemanticType::Tag,
1779 column_id: 2,
1780 })
1781 .primary_key(vec![2]);
1782 builder.build().unwrap()
1783 };
1784
1785 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1786 let err = request.check_schema(&metadata).unwrap_err();
1787 assert!(err.is_fill_default());
1788 assert!(
1789 request
1790 .fill_missing_columns(&metadata)
1791 .unwrap_err()
1792 .to_string()
1793 .contains("unexpected impure default value with region_id")
1794 );
1795 }
1796
1797 #[test]
1798 fn test_fill_missing_columns() {
1799 let rows = Rows {
1800 schema: vec![new_column_schema(
1801 "ts",
1802 ColumnDataType::TimestampMillisecond,
1803 SemanticType::Timestamp,
1804 )],
1805 rows: vec![Row {
1806 values: vec![ts_ms_value(1)],
1807 }],
1808 };
1809 let metadata = new_region_metadata();
1810
1811 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1812 let err = request.check_schema(&metadata).unwrap_err();
1813 assert!(err.is_fill_default());
1814 request.fill_missing_columns(&metadata).unwrap();
1815
1816 let expect_rows = Rows {
1817 schema: vec![new_column_schema(
1818 "ts",
1819 ColumnDataType::TimestampMillisecond,
1820 SemanticType::Timestamp,
1821 )],
1822 rows: vec![Row {
1823 values: vec![ts_ms_value(1)],
1824 }],
1825 };
1826 assert_eq!(expect_rows, request.rows);
1827 }
1828
1829 fn builder_with_ts_tag() -> RegionMetadataBuilder {
1830 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1831 builder
1832 .push_column_metadata(ColumnMetadata {
1833 column_schema: datatypes::schema::ColumnSchema::new(
1834 "ts",
1835 ConcreteDataType::timestamp_millisecond_datatype(),
1836 false,
1837 ),
1838 semantic_type: SemanticType::Timestamp,
1839 column_id: 1,
1840 })
1841 .push_column_metadata(ColumnMetadata {
1842 column_schema: datatypes::schema::ColumnSchema::new(
1843 "k0",
1844 ConcreteDataType::int64_datatype(),
1845 true,
1846 ),
1847 semantic_type: SemanticType::Tag,
1848 column_id: 2,
1849 })
1850 .primary_key(vec![2]);
1851 builder
1852 }
1853
1854 fn region_metadata_two_fields() -> RegionMetadata {
1855 let mut builder = builder_with_ts_tag();
1856 builder
1857 .push_column_metadata(ColumnMetadata {
1858 column_schema: datatypes::schema::ColumnSchema::new(
1859 "f0",
1860 ConcreteDataType::int64_datatype(),
1861 true,
1862 ),
1863 semantic_type: SemanticType::Field,
1864 column_id: 3,
1865 })
1866 .push_column_metadata(ColumnMetadata {
1868 column_schema: datatypes::schema::ColumnSchema::new(
1869 "f1",
1870 ConcreteDataType::int64_datatype(),
1871 false,
1872 )
1873 .with_default_constraint(Some(ColumnDefaultConstraint::Value(
1874 datatypes::value::Value::Int64(100),
1875 )))
1876 .unwrap(),
1877 semantic_type: SemanticType::Field,
1878 column_id: 4,
1879 });
1880 builder.build().unwrap()
1881 }
1882
1883 #[test]
1884 fn test_fill_missing_for_delete() {
1885 let rows = Rows {
1886 schema: vec![new_column_schema(
1887 "ts",
1888 ColumnDataType::TimestampMillisecond,
1889 SemanticType::Timestamp,
1890 )],
1891 rows: vec![Row {
1892 values: vec![ts_ms_value(1)],
1893 }],
1894 };
1895 let metadata = region_metadata_two_fields();
1896
1897 let mut request =
1898 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1899 let err = request.check_schema(&metadata).unwrap_err();
1900 check_invalid_request(&err, "delete requests need column k0");
1901 let err = request.fill_missing_columns(&metadata).unwrap_err();
1902 check_invalid_request(&err, "delete requests need column k0");
1903
1904 let rows = Rows {
1905 schema: vec![
1906 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1907 new_column_schema(
1908 "ts",
1909 ColumnDataType::TimestampMillisecond,
1910 SemanticType::Timestamp,
1911 ),
1912 ],
1913 rows: vec![Row {
1914 values: vec![i64_value(100), ts_ms_value(1)],
1915 }],
1916 };
1917 let mut request =
1918 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1919 let err = request.check_schema(&metadata).unwrap_err();
1920 assert!(err.is_fill_default());
1921 request.fill_missing_columns(&metadata).unwrap();
1922
1923 let expect_rows = Rows {
1924 schema: vec![
1925 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1926 new_column_schema(
1927 "ts",
1928 ColumnDataType::TimestampMillisecond,
1929 SemanticType::Timestamp,
1930 ),
1931 new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
1932 ],
1933 rows: vec![Row {
1935 values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
1936 }],
1937 };
1938 assert_eq!(expect_rows, request.rows);
1939 }
1940
1941 #[test]
1942 fn test_fill_missing_without_default_in_delete() {
1943 let mut builder = builder_with_ts_tag();
1944 builder
1945 .push_column_metadata(ColumnMetadata {
1947 column_schema: datatypes::schema::ColumnSchema::new(
1948 "f0",
1949 ConcreteDataType::int64_datatype(),
1950 true,
1951 ),
1952 semantic_type: SemanticType::Field,
1953 column_id: 3,
1954 })
1955 .push_column_metadata(ColumnMetadata {
1957 column_schema: datatypes::schema::ColumnSchema::new(
1958 "f1",
1959 ConcreteDataType::int64_datatype(),
1960 false,
1961 ),
1962 semantic_type: SemanticType::Field,
1963 column_id: 4,
1964 });
1965 let metadata = builder.build().unwrap();
1966
1967 let rows = Rows {
1968 schema: vec![
1969 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1970 new_column_schema(
1971 "ts",
1972 ColumnDataType::TimestampMillisecond,
1973 SemanticType::Timestamp,
1974 ),
1975 ],
1976 rows: vec![Row {
1978 values: vec![i64_value(100), ts_ms_value(1)],
1979 }],
1980 };
1981 let mut request =
1982 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1983 let err = request.check_schema(&metadata).unwrap_err();
1984 assert!(err.is_fill_default());
1985 request.fill_missing_columns(&metadata).unwrap();
1986
1987 let expect_rows = Rows {
1988 schema: vec![
1989 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1990 new_column_schema(
1991 "ts",
1992 ColumnDataType::TimestampMillisecond,
1993 SemanticType::Timestamp,
1994 ),
1995 new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
1996 ],
1997 rows: vec![Row {
1999 values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
2000 }],
2001 };
2002 assert_eq!(expect_rows, request.rows);
2003 }
2004
2005 #[test]
2006 fn test_no_default() {
2007 let rows = Rows {
2008 schema: vec![new_column_schema(
2009 "k0",
2010 ColumnDataType::Int64,
2011 SemanticType::Tag,
2012 )],
2013 rows: vec![Row {
2014 values: vec![i64_value(1)],
2015 }],
2016 };
2017 let metadata = new_region_metadata();
2018
2019 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2020 let err = request.fill_missing_columns(&metadata).unwrap_err();
2021 check_invalid_request(&err, "column ts does not have default value");
2022 }
2023
2024 #[test]
2025 fn test_missing_and_invalid() {
2026 let rows = Rows {
2028 schema: vec![
2029 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
2030 new_column_schema(
2031 "ts",
2032 ColumnDataType::TimestampMillisecond,
2033 SemanticType::Timestamp,
2034 ),
2035 new_column_schema("f1", ColumnDataType::String, SemanticType::Field),
2036 ],
2037 rows: vec![Row {
2038 values: vec![
2039 i64_value(100),
2040 ts_ms_value(1),
2041 Value {
2042 value_data: Some(ValueData::StringValue("xxxxx".to_string())),
2043 },
2044 ],
2045 }],
2046 };
2047 let metadata = region_metadata_two_fields();
2048
2049 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2050 let err = request.check_schema(&metadata).unwrap_err();
2051 check_invalid_request(
2052 &err,
2053 "column f1 expect type Int64(Int64Type), given: STRING(12)",
2054 );
2055 }
2056
2057 #[test]
2058 fn test_delete_request_defaults_to_writing_wal() {
2059 let (request, _receiver) = WorkerRequest::try_from_region_request(
2060 RegionId::new(1, 1),
2061 RegionRequest::Delete(store_api::region_request::RegionDeleteRequest {
2062 rows: Rows::default(),
2063 hint: None,
2064 partition_expr_version: None,
2065 }),
2066 None,
2067 )
2068 .unwrap();
2069 let WorkerRequest::Write(request) = request else {
2070 panic!("expected a write request");
2071 };
2072 assert_eq!(request.request.op_type, OpType::Delete);
2073 assert!(!request.request.skip_wal);
2074 }
2075
2076 #[test]
2077 fn test_write_request_metadata() {
2078 let rows = Rows {
2079 schema: vec![
2080 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
2081 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
2082 ],
2083 rows: vec![Row {
2084 values: vec![i64_value(1), i64_value(2)],
2085 }],
2086 };
2087
2088 let metadata = Arc::new(new_region_metadata());
2089 let request = WriteRequest::new(
2090 RegionId::new(1, 1),
2091 OpType::Put,
2092 rows,
2093 Some(metadata.clone()),
2094 )
2095 .unwrap();
2096
2097 assert!(request.region_metadata.is_some());
2098 assert_eq!(
2099 request.region_metadata.unwrap().region_id,
2100 RegionId::new(1, 1)
2101 );
2102 }
2103}