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;
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 IndexBuildFailed(IndexBuildFailed),
1118 IndexBuildRetry(BuildIndexRequest),
1120 CompactionFinished(CompactionFinished),
1122 CompactionFailed(CompactionFailed),
1124 Truncate(TruncateResult),
1126 DiscardUnflushed(DiscardUnflushedResult),
1128 RegionChange(RegionChangeResult),
1130 RegionEdit(RegionEditResult),
1132 EnterStaging(EnterStagingResult),
1134 CopyRegionFromFinished(CopyRegionFromFinished),
1136}
1137
1138#[derive(Debug)]
1140pub(crate) struct FlushFinished {
1141 pub(crate) region_id: RegionId,
1143 pub(crate) flush_reason: FlushReason,
1145 pub(crate) flushed_entry_id: EntryId,
1147 pub(crate) senders: Vec<OutputTx>,
1149 pub(crate) _timer: HistogramTimer,
1151 pub(crate) edit: RegionEdit,
1153 pub(crate) unindexed_files: Vec<FileId>,
1155 pub(crate) memtables_to_remove: SmallVec<[MemtableId; 2]>,
1157 pub(crate) is_staging: bool,
1159}
1160
1161impl FlushFinished {
1162 pub(crate) fn on_success(self) {
1164 for sender in self.senders {
1165 sender.send(Ok(0));
1166 }
1167 }
1168}
1169
1170impl OnFailure for FlushFinished {
1171 fn on_failure(&mut self, err: Error) {
1172 let err = Arc::new(err);
1173 for sender in self.senders.drain(..) {
1174 sender.send(Err(err.clone()).context(FlushRegionSnafu {
1175 region_id: self.region_id,
1176 }));
1177 }
1178 }
1179}
1180
1181#[derive(Debug)]
1183pub(crate) struct FlushFailed {
1184 pub(crate) err: Arc<Error>,
1186}
1187
1188impl FlushFailed {
1189 pub(crate) fn is_cancelled(&self) -> bool {
1191 matches!(self.err.as_ref(), Error::FlushCancelled { .. })
1192 }
1193}
1194
1195#[derive(Debug)]
1196pub(crate) struct IndexBuildFinished {
1197 pub(crate) manifest_version: ManifestVersion,
1198 pub(crate) file_meta: FileMeta,
1199}
1200
1201#[derive(Debug)]
1203pub(crate) struct IndexBuildStopped {
1204 pub(crate) file_id: FileId,
1205}
1206
1207#[derive(Debug)]
1209pub(crate) struct IndexBuildFailed {
1210 pub(crate) err: Arc<Error>,
1211}
1212
1213#[derive(Debug)]
1215pub(crate) enum CompactionUnitNotification {
1216 Finished {
1218 plan_id: u64,
1219 result: std::result::Result<RegionEdit, Arc<Error>>,
1220 applied: tokio::sync::oneshot::Sender<()>,
1221 },
1222 Released {
1224 plan_id: u64,
1226 },
1227}
1228
1229#[derive(Debug)]
1231pub(crate) struct CompactionFinished {
1232 pub(crate) region_id: RegionId,
1234 pub(crate) execution: CompactionExecution,
1236 pub(crate) senders: Vec<OutputTx>,
1238 pub(crate) start_time: Instant,
1240 pub(crate) edit: RegionEdit,
1242}
1243
1244impl CompactionFinished {
1245 pub fn on_success(self) {
1246 COMPACTION_ELAPSED_TOTAL
1248 .with_label_values(&["remote"])
1249 .observe(self.start_time.elapsed().as_secs_f64());
1250
1251 for sender in self.senders {
1252 sender.send(Ok(0));
1253 }
1254 info!("Successfully compacted region: {}", self.region_id);
1255 }
1256}
1257
1258impl OnFailure for CompactionFinished {
1259 fn on_failure(&mut self, err: Error) {
1261 let err = Arc::new(err);
1262 for sender in self.senders.drain(..) {
1263 sender.send(Err(err.clone()).context(CompactRegionSnafu {
1264 region_id: self.region_id,
1265 }));
1266 }
1267 }
1268}
1269
1270#[derive(Debug)]
1272pub(crate) struct CompactionFailed {
1273 pub(crate) region_id: RegionId,
1274 pub(crate) execution: CompactionExecution,
1276 pub(crate) err: Arc<Error>,
1278}
1279
1280#[derive(Debug)]
1282pub(crate) struct TruncateResult {
1283 pub(crate) region_id: RegionId,
1285 pub(crate) sender: OptionOutputTx,
1287 pub(crate) result: Result<()>,
1289 pub(crate) kind: TruncateKind,
1290}
1291
1292#[derive(Debug)]
1294pub(crate) struct DiscardUnflushedResult {
1295 pub(crate) region_id: RegionId,
1297 pub(crate) sender: OptionOutputTx,
1299 pub(crate) result: Result<()>,
1301 pub(crate) discarded_entry_id: EntryId,
1303 pub(crate) discarded_sequence: SequenceNumber,
1305 pub(crate) discarded_rows: u64,
1307 pub(crate) discarded_bytes: u64,
1309}
1310
1311#[derive(Debug)]
1313pub(crate) struct RegionChangeResult {
1314 pub(crate) region_id: RegionId,
1316 pub(crate) new_meta: RegionMetadataRef,
1318 pub(crate) sender: OptionOutputTx,
1320 pub(crate) result: Result<()>,
1322 pub(crate) need_index: bool,
1324 pub(crate) new_options: Option<RegionOptions>,
1326}
1327
1328#[derive(Debug)]
1330pub(crate) struct EnterStagingResult {
1331 pub(crate) region_id: RegionId,
1333 pub(crate) partition_directive: StagingPartitionDirective,
1335 pub(crate) sender: OptionOutputTx,
1337 pub(crate) result: Result<()>,
1339}
1340
1341#[derive(Debug)]
1342pub(crate) struct CopyRegionFromFinished {
1343 pub(crate) region_id: RegionId,
1345 pub(crate) edit: RegionEdit,
1347 pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1349}
1350
1351#[derive(Debug, Default)]
1352pub(crate) struct Waiters(SmallVec<[Sender<Result<()>>; 1]>);
1353
1354impl Waiters {
1355 pub(crate) fn one(waiter: Sender<Result<()>>) -> Self {
1356 let mut waiters = SmallVec::new();
1357 waiters.push(waiter);
1358 Self(waiters)
1359 }
1360
1361 pub(crate) fn reply_with<F: Fn() -> Result<()>>(self, f: F) {
1362 for tx in self.0 {
1363 let _ = tx.send(f());
1364 }
1365 }
1366
1367 pub(crate) fn merge(&mut self, other: Self) {
1368 self.0.extend(other.0);
1369 }
1370}
1371
1372#[derive(Debug)]
1374pub(crate) struct RegionEditRequest {
1375 pub(crate) region_id: RegionId,
1376 pub(crate) edit: RegionEdit,
1377 pub(crate) preload_sst_cache: bool,
1379 pub(crate) waiters: Waiters,
1381}
1382
1383impl RegionEditRequest {
1384 pub(crate) fn new(
1385 region_id: RegionId,
1386 edit: RegionEdit,
1387 preload_sst_cache: bool,
1388 waiter: Sender<Result<()>>,
1389 ) -> Self {
1390 Self {
1391 region_id,
1392 edit,
1393 preload_sst_cache,
1394 waiters: Waiters::one(waiter),
1395 }
1396 }
1397}
1398
1399#[derive(Debug)]
1401pub(crate) struct RegionEditResult {
1402 pub(crate) region_id: RegionId,
1404 pub(crate) waiters: Waiters,
1406 pub(crate) edit: RegionEdit,
1408 pub(crate) result: std::result::Result<(), Arc<Error>>,
1410 pub(crate) update_region_state: bool,
1412 pub(crate) is_staging: bool,
1414}
1415
1416#[derive(Debug)]
1417pub(crate) struct BuildIndexRequest {
1418 pub(crate) region_id: RegionId,
1419 pub(crate) build_type: IndexBuildType,
1420 pub(crate) file_metas: Vec<FileMeta>,
1422}
1423
1424#[derive(Debug)]
1425pub(crate) struct RegionSyncRequest {
1426 pub(crate) region_id: RegionId,
1427 pub(crate) manifest_version: ManifestVersion,
1428 pub(crate) sender: Sender<Result<(ManifestVersion, bool)>>,
1430}
1431
1432#[derive(Debug)]
1433pub(crate) struct RemapManifestsRequest {
1434 pub(crate) region_id: RegionId,
1436 pub(crate) input_regions: Vec<RegionId>,
1438 pub(crate) region_mapping: HashMap<RegionId, Vec<RegionId>>,
1440 pub(crate) new_partition_exprs: HashMap<RegionId, PartitionExpr>,
1442 pub(crate) sender: Sender<Result<HashMap<RegionId, String>>>,
1446}
1447
1448#[derive(Debug)]
1449pub(crate) struct CopyRegionFromRequest {
1450 pub(crate) region_id: RegionId,
1452 pub(crate) source_region_id: RegionId,
1454 pub(crate) parallelism: usize,
1456 pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462 use api::v1::value::ValueData;
1463 use api::v1::{Row, SemanticType};
1464 use datatypes::prelude::ConcreteDataType;
1465 use datatypes::schema::ColumnDefaultConstraint;
1466 use mito_codec::test_util::i64_value;
1467 use store_api::metadata::RegionMetadataBuilder;
1468 use tokio::sync::oneshot;
1469
1470 use super::*;
1471 use crate::error::Error;
1472 use crate::test_util::ts_ms_value;
1473
1474 fn new_column_schema(
1475 name: &str,
1476 data_type: ColumnDataType,
1477 semantic_type: SemanticType,
1478 ) -> ColumnSchema {
1479 ColumnSchema {
1480 column_name: name.to_string(),
1481 datatype: data_type as i32,
1482 semantic_type: semantic_type as i32,
1483 ..Default::default()
1484 }
1485 }
1486
1487 fn check_invalid_request(err: &Error, expect: &str) {
1488 if let Error::InvalidRequest {
1489 region_id: _,
1490 reason,
1491 location: _,
1492 } = err
1493 {
1494 assert_eq!(reason, expect);
1495 } else {
1496 panic!("Unexpected error {err}")
1497 }
1498 }
1499
1500 fn waiter() -> (Sender<Result<()>>, Receiver<Result<()>>) {
1501 oneshot::channel()
1502 }
1503
1504 fn assert_waiter_ok(rx: &mut Receiver<Result<()>>) {
1505 rx.try_recv().unwrap().unwrap();
1506 }
1507
1508 #[test]
1509 fn test_waiters_reply_with_single_waiter() {
1510 let (tx, mut rx) = waiter();
1511 Waiters::one(tx).reply_with(|| Ok(()));
1512 assert_waiter_ok(&mut rx);
1513 }
1514
1515 #[test]
1516 fn test_waiters_reply_with_many_waiters() {
1517 let (tx1, mut rx1) = waiter();
1518 let (tx2, mut rx2) = waiter();
1519 let (tx3, mut rx3) = waiter();
1520
1521 let waiters = Waiters(vec![tx1, tx2, tx3].into());
1522 waiters.reply_with(|| Ok(()));
1523
1524 assert_waiter_ok(&mut rx1);
1525 assert_waiter_ok(&mut rx2);
1526 assert_waiter_ok(&mut rx3);
1527 }
1528
1529 #[test]
1530 fn test_waiters_merge() {
1531 let (tx1, mut rx1) = waiter();
1532 let (tx2, mut rx2) = waiter();
1533 let (tx3, mut rx3) = waiter();
1534 let (tx4, mut rx4) = waiter();
1535
1536 let mut waiters = Waiters::one(tx1);
1537 waiters.merge(Waiters::one(tx2));
1538 waiters.merge(Waiters(vec![tx3, tx4].into()));
1539 assert_eq!(4, waiters.0.len());
1540
1541 waiters.reply_with(|| Ok(()));
1542
1543 assert_waiter_ok(&mut rx1);
1544 assert_waiter_ok(&mut rx2);
1545 assert_waiter_ok(&mut rx3);
1546 assert_waiter_ok(&mut rx4);
1547 }
1548
1549 #[test]
1550 fn test_write_request_duplicate_column() {
1551 let rows = Rows {
1552 schema: vec![
1553 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1554 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1555 ],
1556 rows: vec![],
1557 };
1558
1559 let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1560 check_invalid_request(&err, "duplicate column c0");
1561 }
1562
1563 #[test]
1564 fn test_valid_write_request() {
1565 let rows = Rows {
1566 schema: vec![
1567 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1568 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1569 ],
1570 rows: vec![Row {
1571 values: vec![i64_value(1), i64_value(2)],
1572 }],
1573 };
1574
1575 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1576 assert_eq!(0, request.column_index_by_name("c0").unwrap());
1577 assert_eq!(1, request.column_index_by_name("c1").unwrap());
1578 assert_eq!(None, request.column_index_by_name("c2"));
1579 }
1580
1581 #[test]
1582 fn test_write_request_column_num() {
1583 let rows = Rows {
1584 schema: vec![
1585 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1586 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1587 ],
1588 rows: vec![Row {
1589 values: vec![i64_value(1), i64_value(2), i64_value(3)],
1590 }],
1591 };
1592
1593 let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1594 check_invalid_request(&err, "row has 3 columns but schema has 2");
1595 }
1596
1597 fn new_region_metadata() -> RegionMetadata {
1598 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1599 builder
1600 .push_column_metadata(ColumnMetadata {
1601 column_schema: datatypes::schema::ColumnSchema::new(
1602 "ts",
1603 ConcreteDataType::timestamp_millisecond_datatype(),
1604 false,
1605 ),
1606 semantic_type: SemanticType::Timestamp,
1607 column_id: 1,
1608 })
1609 .push_column_metadata(ColumnMetadata {
1610 column_schema: datatypes::schema::ColumnSchema::new(
1611 "k0",
1612 ConcreteDataType::int64_datatype(),
1613 true,
1614 ),
1615 semantic_type: SemanticType::Tag,
1616 column_id: 2,
1617 })
1618 .primary_key(vec![2]);
1619 builder.build().unwrap()
1620 }
1621
1622 #[test]
1623 fn test_check_schema() {
1624 let rows = Rows {
1625 schema: vec![
1626 new_column_schema(
1627 "ts",
1628 ColumnDataType::TimestampMillisecond,
1629 SemanticType::Timestamp,
1630 ),
1631 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1632 ],
1633 rows: vec![Row {
1634 values: vec![ts_ms_value(1), i64_value(2)],
1635 }],
1636 };
1637 let metadata = new_region_metadata();
1638
1639 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1640 request.check_schema(&metadata).unwrap();
1641 }
1642
1643 #[test]
1644 fn test_column_type() {
1645 let rows = Rows {
1646 schema: vec![
1647 new_column_schema("ts", ColumnDataType::Int64, SemanticType::Timestamp),
1648 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1649 ],
1650 rows: vec![Row {
1651 values: vec![i64_value(1), i64_value(2)],
1652 }],
1653 };
1654 let metadata = new_region_metadata();
1655
1656 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1657 let err = request.check_schema(&metadata).unwrap_err();
1658 check_invalid_request(
1659 &err,
1660 "column ts expect type Timestamp(Millisecond(TimestampMillisecondType)), given: INT64(4)",
1661 );
1662 }
1663
1664 #[test]
1665 fn test_semantic_type() {
1666 let rows = Rows {
1667 schema: vec![
1668 new_column_schema(
1669 "ts",
1670 ColumnDataType::TimestampMillisecond,
1671 SemanticType::Tag,
1672 ),
1673 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1674 ],
1675 rows: vec![Row {
1676 values: vec![ts_ms_value(1), i64_value(2)],
1677 }],
1678 };
1679 let metadata = new_region_metadata();
1680
1681 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1682 let err = request.check_schema(&metadata).unwrap_err();
1683 check_invalid_request(&err, "column ts has semantic type Timestamp, given: TAG(0)");
1684 }
1685
1686 #[test]
1687 fn test_column_nullable() {
1688 let rows = Rows {
1689 schema: vec![
1690 new_column_schema(
1691 "ts",
1692 ColumnDataType::TimestampMillisecond,
1693 SemanticType::Timestamp,
1694 ),
1695 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1696 ],
1697 rows: vec![Row {
1698 values: vec![Value { value_data: None }, i64_value(2)],
1699 }],
1700 };
1701 let metadata = new_region_metadata();
1702
1703 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1704 let err = request.check_schema(&metadata).unwrap_err();
1705 check_invalid_request(&err, "column ts is not null but input has null");
1706 }
1707
1708 #[test]
1709 fn test_column_default() {
1710 let rows = Rows {
1711 schema: vec![new_column_schema(
1712 "k0",
1713 ColumnDataType::Int64,
1714 SemanticType::Tag,
1715 )],
1716 rows: vec![Row {
1717 values: vec![i64_value(1)],
1718 }],
1719 };
1720 let metadata = new_region_metadata();
1721
1722 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1723 let err = request.check_schema(&metadata).unwrap_err();
1724 check_invalid_request(&err, "missing column ts");
1725 }
1726
1727 #[test]
1728 fn test_unknown_column() {
1729 let rows = Rows {
1730 schema: vec![
1731 new_column_schema(
1732 "ts",
1733 ColumnDataType::TimestampMillisecond,
1734 SemanticType::Timestamp,
1735 ),
1736 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1737 new_column_schema("k1", ColumnDataType::Int64, SemanticType::Tag),
1738 ],
1739 rows: vec![Row {
1740 values: vec![ts_ms_value(1), i64_value(2), i64_value(3)],
1741 }],
1742 };
1743 let metadata = new_region_metadata();
1744
1745 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1746 let err = request.check_schema(&metadata).unwrap_err();
1747 check_invalid_request(&err, r#"unknown columns: ["k1"]"#);
1748 }
1749
1750 #[test]
1751 fn test_fill_impure_columns_err() {
1752 let rows = Rows {
1753 schema: vec![new_column_schema(
1754 "k0",
1755 ColumnDataType::Int64,
1756 SemanticType::Tag,
1757 )],
1758 rows: vec![Row {
1759 values: vec![i64_value(1)],
1760 }],
1761 };
1762 let metadata = {
1763 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1764 builder
1765 .push_column_metadata(ColumnMetadata {
1766 column_schema: datatypes::schema::ColumnSchema::new(
1767 "ts",
1768 ConcreteDataType::timestamp_millisecond_datatype(),
1769 false,
1770 )
1771 .with_default_constraint(Some(ColumnDefaultConstraint::Function(
1772 "now()".to_string(),
1773 )))
1774 .unwrap(),
1775 semantic_type: SemanticType::Timestamp,
1776 column_id: 1,
1777 })
1778 .push_column_metadata(ColumnMetadata {
1779 column_schema: datatypes::schema::ColumnSchema::new(
1780 "k0",
1781 ConcreteDataType::int64_datatype(),
1782 true,
1783 ),
1784 semantic_type: SemanticType::Tag,
1785 column_id: 2,
1786 })
1787 .primary_key(vec![2]);
1788 builder.build().unwrap()
1789 };
1790
1791 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1792 let err = request.check_schema(&metadata).unwrap_err();
1793 assert!(err.is_fill_default());
1794 assert!(
1795 request
1796 .fill_missing_columns(&metadata)
1797 .unwrap_err()
1798 .to_string()
1799 .contains("unexpected impure default value with region_id")
1800 );
1801 }
1802
1803 #[test]
1804 fn test_fill_missing_columns() {
1805 let rows = Rows {
1806 schema: vec![new_column_schema(
1807 "ts",
1808 ColumnDataType::TimestampMillisecond,
1809 SemanticType::Timestamp,
1810 )],
1811 rows: vec![Row {
1812 values: vec![ts_ms_value(1)],
1813 }],
1814 };
1815 let metadata = new_region_metadata();
1816
1817 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1818 let err = request.check_schema(&metadata).unwrap_err();
1819 assert!(err.is_fill_default());
1820 request.fill_missing_columns(&metadata).unwrap();
1821
1822 let expect_rows = Rows {
1823 schema: vec![new_column_schema(
1824 "ts",
1825 ColumnDataType::TimestampMillisecond,
1826 SemanticType::Timestamp,
1827 )],
1828 rows: vec![Row {
1829 values: vec![ts_ms_value(1)],
1830 }],
1831 };
1832 assert_eq!(expect_rows, request.rows);
1833 }
1834
1835 fn builder_with_ts_tag() -> RegionMetadataBuilder {
1836 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1837 builder
1838 .push_column_metadata(ColumnMetadata {
1839 column_schema: datatypes::schema::ColumnSchema::new(
1840 "ts",
1841 ConcreteDataType::timestamp_millisecond_datatype(),
1842 false,
1843 ),
1844 semantic_type: SemanticType::Timestamp,
1845 column_id: 1,
1846 })
1847 .push_column_metadata(ColumnMetadata {
1848 column_schema: datatypes::schema::ColumnSchema::new(
1849 "k0",
1850 ConcreteDataType::int64_datatype(),
1851 true,
1852 ),
1853 semantic_type: SemanticType::Tag,
1854 column_id: 2,
1855 })
1856 .primary_key(vec![2]);
1857 builder
1858 }
1859
1860 fn region_metadata_two_fields() -> RegionMetadata {
1861 let mut builder = builder_with_ts_tag();
1862 builder
1863 .push_column_metadata(ColumnMetadata {
1864 column_schema: datatypes::schema::ColumnSchema::new(
1865 "f0",
1866 ConcreteDataType::int64_datatype(),
1867 true,
1868 ),
1869 semantic_type: SemanticType::Field,
1870 column_id: 3,
1871 })
1872 .push_column_metadata(ColumnMetadata {
1874 column_schema: datatypes::schema::ColumnSchema::new(
1875 "f1",
1876 ConcreteDataType::int64_datatype(),
1877 false,
1878 )
1879 .with_default_constraint(Some(ColumnDefaultConstraint::Value(
1880 datatypes::value::Value::Int64(100),
1881 )))
1882 .unwrap(),
1883 semantic_type: SemanticType::Field,
1884 column_id: 4,
1885 });
1886 builder.build().unwrap()
1887 }
1888
1889 #[test]
1890 fn test_fill_missing_for_delete() {
1891 let rows = Rows {
1892 schema: vec![new_column_schema(
1893 "ts",
1894 ColumnDataType::TimestampMillisecond,
1895 SemanticType::Timestamp,
1896 )],
1897 rows: vec![Row {
1898 values: vec![ts_ms_value(1)],
1899 }],
1900 };
1901 let metadata = region_metadata_two_fields();
1902
1903 let mut request =
1904 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1905 let err = request.check_schema(&metadata).unwrap_err();
1906 check_invalid_request(&err, "delete requests need column k0");
1907 let err = request.fill_missing_columns(&metadata).unwrap_err();
1908 check_invalid_request(&err, "delete requests need column k0");
1909
1910 let rows = Rows {
1911 schema: vec![
1912 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1913 new_column_schema(
1914 "ts",
1915 ColumnDataType::TimestampMillisecond,
1916 SemanticType::Timestamp,
1917 ),
1918 ],
1919 rows: vec![Row {
1920 values: vec![i64_value(100), ts_ms_value(1)],
1921 }],
1922 };
1923 let mut request =
1924 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1925 let err = request.check_schema(&metadata).unwrap_err();
1926 assert!(err.is_fill_default());
1927 request.fill_missing_columns(&metadata).unwrap();
1928
1929 let expect_rows = Rows {
1930 schema: vec![
1931 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1932 new_column_schema(
1933 "ts",
1934 ColumnDataType::TimestampMillisecond,
1935 SemanticType::Timestamp,
1936 ),
1937 new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
1938 ],
1939 rows: vec![Row {
1941 values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
1942 }],
1943 };
1944 assert_eq!(expect_rows, request.rows);
1945 }
1946
1947 #[test]
1948 fn test_fill_missing_without_default_in_delete() {
1949 let mut builder = builder_with_ts_tag();
1950 builder
1951 .push_column_metadata(ColumnMetadata {
1953 column_schema: datatypes::schema::ColumnSchema::new(
1954 "f0",
1955 ConcreteDataType::int64_datatype(),
1956 true,
1957 ),
1958 semantic_type: SemanticType::Field,
1959 column_id: 3,
1960 })
1961 .push_column_metadata(ColumnMetadata {
1963 column_schema: datatypes::schema::ColumnSchema::new(
1964 "f1",
1965 ConcreteDataType::int64_datatype(),
1966 false,
1967 ),
1968 semantic_type: SemanticType::Field,
1969 column_id: 4,
1970 });
1971 let metadata = builder.build().unwrap();
1972
1973 let rows = Rows {
1974 schema: vec![
1975 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1976 new_column_schema(
1977 "ts",
1978 ColumnDataType::TimestampMillisecond,
1979 SemanticType::Timestamp,
1980 ),
1981 ],
1982 rows: vec![Row {
1984 values: vec![i64_value(100), ts_ms_value(1)],
1985 }],
1986 };
1987 let mut request =
1988 WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1989 let err = request.check_schema(&metadata).unwrap_err();
1990 assert!(err.is_fill_default());
1991 request.fill_missing_columns(&metadata).unwrap();
1992
1993 let expect_rows = Rows {
1994 schema: vec![
1995 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1996 new_column_schema(
1997 "ts",
1998 ColumnDataType::TimestampMillisecond,
1999 SemanticType::Timestamp,
2000 ),
2001 new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
2002 ],
2003 rows: vec![Row {
2005 values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
2006 }],
2007 };
2008 assert_eq!(expect_rows, request.rows);
2009 }
2010
2011 #[test]
2012 fn test_no_default() {
2013 let rows = Rows {
2014 schema: vec![new_column_schema(
2015 "k0",
2016 ColumnDataType::Int64,
2017 SemanticType::Tag,
2018 )],
2019 rows: vec![Row {
2020 values: vec![i64_value(1)],
2021 }],
2022 };
2023 let metadata = new_region_metadata();
2024
2025 let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2026 let err = request.fill_missing_columns(&metadata).unwrap_err();
2027 check_invalid_request(&err, "column ts does not have default value");
2028 }
2029
2030 #[test]
2031 fn test_missing_and_invalid() {
2032 let rows = Rows {
2034 schema: vec![
2035 new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
2036 new_column_schema(
2037 "ts",
2038 ColumnDataType::TimestampMillisecond,
2039 SemanticType::Timestamp,
2040 ),
2041 new_column_schema("f1", ColumnDataType::String, SemanticType::Field),
2042 ],
2043 rows: vec![Row {
2044 values: vec![
2045 i64_value(100),
2046 ts_ms_value(1),
2047 Value {
2048 value_data: Some(ValueData::StringValue("xxxxx".to_string())),
2049 },
2050 ],
2051 }],
2052 };
2053 let metadata = region_metadata_two_fields();
2054
2055 let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2056 let err = request.check_schema(&metadata).unwrap_err();
2057 check_invalid_request(
2058 &err,
2059 "column f1 expect type Int64(Int64Type), given: STRING(12)",
2060 );
2061 }
2062
2063 #[test]
2064 fn test_delete_request_defaults_to_writing_wal() {
2065 let (request, _receiver) = WorkerRequest::try_from_region_request(
2066 RegionId::new(1, 1),
2067 RegionRequest::Delete(store_api::region_request::RegionDeleteRequest {
2068 rows: Rows::default(),
2069 hint: None,
2070 partition_expr_version: None,
2071 }),
2072 None,
2073 )
2074 .unwrap();
2075 let WorkerRequest::Write(request) = request else {
2076 panic!("expected a write request");
2077 };
2078 assert_eq!(request.request.op_type, OpType::Delete);
2079 assert!(!request.request.skip_wal);
2080 }
2081
2082 #[test]
2083 fn test_write_request_metadata() {
2084 let rows = Rows {
2085 schema: vec![
2086 new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
2087 new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
2088 ],
2089 rows: vec![Row {
2090 values: vec![i64_value(1), i64_value(2)],
2091 }],
2092 };
2093
2094 let metadata = Arc::new(new_region_metadata());
2095 let request = WriteRequest::new(
2096 RegionId::new(1, 1),
2097 OpType::Put,
2098 rows,
2099 Some(metadata.clone()),
2100 )
2101 .unwrap();
2102
2103 assert!(request.region_metadata.is_some());
2104 assert_eq!(
2105 request.region_metadata.unwrap().region_id,
2106 RegionId::new(1, 1)
2107 );
2108 }
2109}