1use std::collections::HashMap;
16use std::fmt::{self, Display};
17
18use api::helper::{ColumnDataTypeWrapper, from_pb_time_ranges};
19use api::v1::add_column_location::LocationType;
20use api::v1::column_def::{
21 as_fulltext_option_analyzer, as_fulltext_option_backend, as_skipping_index_type,
22};
23use api::v1::region::bulk_insert_request::Body;
24use api::v1::region::{
25 AlterRequest, AlterRequests, BuildIndexRequest, BulkInsertRequest, CloseRequest,
26 CompactRequest, CreateRequest, CreateRequests, DeleteRequests, DropRequest, DropRequests,
27 FlushRequest, InsertRequests, OpenRequest, TruncateRequest, alter_request, compact_request,
28 region_request, truncate_request,
29};
30use api::v1::{
31 self, Analyzer, ArrowIpc, FulltextBackend as PbFulltextBackend, Option as PbOption, Rows,
32 SemanticType, SkippingIndexType as PbSkippingIndexType, WriteHint,
33};
34pub use common_base::AffectedRows;
35use common_grpc::flight::FlightDecoder;
36use common_recordbatch::DfRecordBatch;
37use common_time::{TimeToLive, Timestamp};
38use datatypes::prelude::ConcreteDataType;
39use datatypes::schema::{FulltextOptions, SkippingIndexOptions};
40use num_enum::TryFromPrimitive;
41use serde::{Deserialize, Serialize};
42use snafu::{OptionExt, ResultExt, ensure};
43use strum::{AsRefStr, IntoStaticStr};
44
45use crate::logstore::entry;
46use crate::metadata::{
47 ColumnMetadata, ConvertTimeRangesSnafu, DecodeProtoSnafu, FlightCodecSnafu,
48 InvalidIndexOptionSnafu, InvalidRawRegionRequestSnafu, InvalidRegionRequestSnafu,
49 InvalidSetRegionOptionRequestSnafu, InvalidUnsetRegionOptionRequestSnafu, MetadataError,
50 RegionMetadata, Result, UnexpectedSnafu,
51};
52use crate::metric_engine_consts::PHYSICAL_TABLE_METADATA_KEY;
53use crate::metrics;
54use crate::mito_engine_options::{
55 APPEND_MODE_KEY, SST_FORMAT_KEY, TTL_KEY, TWCS_MAX_OUTPUT_FILE_SIZE, TWCS_TIME_WINDOW,
56 TWCS_TRIGGER_FILE_NUM,
57};
58use crate::path_utils::table_dir;
59use crate::storage::{ColumnId, RegionId, ScanRequest};
60
61#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
63#[repr(u8)]
64pub enum PathType {
65 Bare,
69 Data,
73 Metadata,
77}
78
79#[derive(Debug, IntoStaticStr)]
80pub enum BatchRegionDdlRequest {
81 Create(Vec<(RegionId, RegionCreateRequest)>),
82 Drop(Vec<(RegionId, RegionDropRequest)>),
83 Alter(Vec<(RegionId, RegionAlterRequest)>),
84}
85
86impl BatchRegionDdlRequest {
87 pub fn try_from_request_body(body: region_request::Body) -> Result<Option<Self>> {
89 match body {
90 region_request::Body::Creates(creates) => {
91 let requests = creates
92 .requests
93 .into_iter()
94 .map(parse_region_create)
95 .collect::<Result<Vec<_>>>()?;
96 Ok(Some(Self::Create(requests)))
97 }
98 region_request::Body::Drops(drops) => {
99 let requests = drops
100 .requests
101 .into_iter()
102 .map(parse_region_drop)
103 .collect::<Result<Vec<_>>>()?;
104 Ok(Some(Self::Drop(requests)))
105 }
106 region_request::Body::Alters(alters) => {
107 let requests = alters
108 .requests
109 .into_iter()
110 .map(parse_region_alter)
111 .collect::<Result<Vec<_>>>()?;
112 Ok(Some(Self::Alter(requests)))
113 }
114 _ => Ok(None),
115 }
116 }
117
118 pub fn request_type(&self) -> &'static str {
119 self.into()
120 }
121
122 pub fn into_region_requests(self) -> Vec<(RegionId, RegionRequest)> {
123 match self {
124 Self::Create(requests) => requests
125 .into_iter()
126 .map(|(region_id, request)| (region_id, RegionRequest::Create(request)))
127 .collect(),
128 Self::Drop(requests) => requests
129 .into_iter()
130 .map(|(region_id, request)| (region_id, RegionRequest::Drop(request)))
131 .collect(),
132 Self::Alter(requests) => requests
133 .into_iter()
134 .map(|(region_id, request)| (region_id, RegionRequest::Alter(request)))
135 .collect(),
136 }
137 }
138}
139
140#[derive(Debug, IntoStaticStr)]
141pub enum RegionRequest {
142 Put(RegionPutRequest),
143 Delete(RegionDeleteRequest),
144 Create(RegionCreateRequest),
145 Drop(RegionDropRequest),
146 Open(RegionOpenRequest),
147 Close(RegionCloseRequest),
148 Alter(RegionAlterRequest),
149 Flush(RegionFlushRequest),
150 Compact(RegionCompactRequest),
151 BuildIndex(RegionBuildIndexRequest),
152 Truncate(RegionTruncateRequest),
153 Catchup(RegionCatchupRequest),
154 BulkInserts(RegionBulkInsertsRequest),
155 EnterStaging(EnterStagingRequest),
156 ApplyStagingManifest(ApplyStagingManifestRequest),
157}
158
159impl RegionRequest {
160 pub fn try_from_request_body(body: region_request::Body) -> Result<Vec<(RegionId, Self)>> {
163 match body {
164 region_request::Body::Inserts(inserts) => make_region_puts(inserts),
165 region_request::Body::Deletes(deletes) => make_region_deletes(deletes),
166 region_request::Body::Create(create) => make_region_create(create),
167 region_request::Body::Drop(drop) => make_region_drop(drop),
168 region_request::Body::Open(open) => make_region_open(open),
169 region_request::Body::Close(close) => make_region_close(close),
170 region_request::Body::Alter(alter) => make_region_alter(alter),
171 region_request::Body::Flush(flush) => make_region_flush(flush),
172 region_request::Body::Compact(compact) => make_region_compact(compact),
173 region_request::Body::BuildIndex(index) => make_region_build_index(index),
174 region_request::Body::Truncate(truncate) => make_region_truncate(truncate),
175 region_request::Body::Creates(creates) => make_region_creates(creates),
176 region_request::Body::Drops(drops) => make_region_drops(drops),
177 region_request::Body::Alters(alters) => make_region_alters(alters),
178 region_request::Body::BulkInsert(bulk) => make_region_bulk_inserts(bulk),
179 region_request::Body::Sync(_) => UnexpectedSnafu {
180 reason: "Sync request should be handled separately by RegionServer",
181 }
182 .fail(),
183 region_request::Body::ListMetadata(_) => UnexpectedSnafu {
184 reason: "ListMetadata request should be handled separately by RegionServer",
185 }
186 .fail(),
187 region_request::Body::RemoteDynFilter(_) => UnexpectedSnafu {
188 reason: "RemoteDynFilter request should be handled separately by RegionServer",
189 }
190 .fail(),
191 region_request::Body::ApplyStagingManifest(apply) => {
192 make_region_apply_staging_manifest(apply)
193 }
194 }
195 }
196
197 pub fn request_type(&self) -> &'static str {
199 self.into()
200 }
201}
202
203fn make_region_puts(inserts: InsertRequests) -> Result<Vec<(RegionId, RegionRequest)>> {
204 let requests = inserts
205 .requests
206 .into_iter()
207 .filter_map(|r| {
208 let region_id = r.region_id.into();
209 r.rows.map(|rows| {
210 (
211 region_id,
212 RegionRequest::Put(RegionPutRequest {
213 rows,
214 hint: None,
215 partition_expr_version: r.partition_expr_version.map(|v| v.value),
216 }),
217 )
218 })
219 })
220 .collect();
221 Ok(requests)
222}
223
224fn make_region_deletes(deletes: DeleteRequests) -> Result<Vec<(RegionId, RegionRequest)>> {
225 let requests = deletes
226 .requests
227 .into_iter()
228 .filter_map(|r| {
229 let region_id = r.region_id.into();
230 r.rows.map(|rows| {
231 (
232 region_id,
233 RegionRequest::Delete(RegionDeleteRequest {
234 rows,
235 hint: None,
236 partition_expr_version: r.partition_expr_version.map(|v| v.value),
237 }),
238 )
239 })
240 })
241 .collect();
242 Ok(requests)
243}
244
245fn parse_region_create(create: CreateRequest) -> Result<(RegionId, RegionCreateRequest)> {
246 let column_metadatas = create
247 .column_defs
248 .into_iter()
249 .map(ColumnMetadata::try_from_column_def)
250 .collect::<Result<Vec<_>>>()?;
251 let region_id = RegionId::from(create.region_id);
252 let table_dir = table_dir(&create.path, region_id.table_id());
253 let partition_expr_json = create.partition.as_ref().map(|p| p.expression.clone());
254 Ok((
255 region_id,
256 RegionCreateRequest {
257 engine: create.engine,
258 column_metadatas,
259 primary_key: create.primary_key,
260 options: create.options,
261 table_dir,
262 path_type: PathType::Bare,
263 partition_expr_json,
264 },
265 ))
266}
267
268fn make_region_create(create: CreateRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
269 let (region_id, request) = parse_region_create(create)?;
270 Ok(vec![(region_id, RegionRequest::Create(request))])
271}
272
273fn make_region_creates(creates: CreateRequests) -> Result<Vec<(RegionId, RegionRequest)>> {
274 let mut requests = Vec::with_capacity(creates.requests.len());
275 for create in creates.requests {
276 requests.extend(make_region_create(create)?);
277 }
278 Ok(requests)
279}
280
281fn parse_region_drop(drop: DropRequest) -> Result<(RegionId, RegionDropRequest)> {
282 let region_id = drop.region_id.into();
283 Ok((
284 region_id,
285 RegionDropRequest {
286 fast_path: drop.fast_path,
287 force: drop.force,
288 partial_drop: drop.partial_drop,
289 },
290 ))
291}
292
293fn make_region_drop(drop: DropRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
294 let (region_id, request) = parse_region_drop(drop)?;
295 Ok(vec![(region_id, RegionRequest::Drop(request))])
296}
297
298fn make_region_drops(drops: DropRequests) -> Result<Vec<(RegionId, RegionRequest)>> {
299 let mut requests = Vec::with_capacity(drops.requests.len());
300 for drop in drops.requests {
301 requests.extend(make_region_drop(drop)?);
302 }
303 Ok(requests)
304}
305
306fn make_region_open(open: OpenRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
307 let region_id = RegionId::from(open.region_id);
308 let table_dir = table_dir(&open.path, region_id.table_id());
309 Ok(vec![(
310 region_id,
311 RegionRequest::Open(RegionOpenRequest {
312 engine: open.engine,
313 table_dir,
314 path_type: PathType::Bare,
315 options: open.options,
316 skip_wal_replay: false,
317 checkpoint: None,
318 requirements: Default::default(),
319 }),
320 )])
321}
322
323fn make_region_close(close: CloseRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
324 let region_id = close.region_id.into();
325 Ok(vec![(
326 region_id,
327 RegionRequest::Close(RegionCloseRequest {}),
328 )])
329}
330
331fn parse_region_alter(alter: AlterRequest) -> Result<(RegionId, RegionAlterRequest)> {
332 let region_id = alter.region_id.into();
333 let request = RegionAlterRequest::try_from(alter)?;
334 Ok((region_id, request))
335}
336
337fn make_region_alter(alter: AlterRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
338 let (region_id, request) = parse_region_alter(alter)?;
339 Ok(vec![(region_id, RegionRequest::Alter(request))])
340}
341
342fn make_region_alters(alters: AlterRequests) -> Result<Vec<(RegionId, RegionRequest)>> {
343 let mut requests = Vec::with_capacity(alters.requests.len());
344 for alter in alters.requests {
345 requests.extend(make_region_alter(alter)?);
346 }
347 Ok(requests)
348}
349
350fn make_region_flush(flush: FlushRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
351 let region_id = flush.region_id.into();
352 Ok(vec![(
353 region_id,
354 RegionRequest::Flush(RegionFlushRequest::default()),
355 )])
356}
357
358fn make_region_compact(compact: CompactRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
359 let region_id = compact.region_id.into();
360 let options = compact
361 .options
362 .unwrap_or(compact_request::Options::Regular(Default::default()));
363 let parallelism = if compact.parallelism == 0 {
365 None
366 } else {
367 Some(compact.parallelism)
368 };
369 Ok(vec![(
370 region_id,
371 RegionRequest::Compact(RegionCompactRequest {
372 options,
373 parallelism,
374 }),
375 )])
376}
377
378fn make_region_build_index(index: BuildIndexRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
379 let region_id = index.region_id.into();
380 Ok(vec![(
381 region_id,
382 RegionRequest::BuildIndex(RegionBuildIndexRequest {}),
383 )])
384}
385
386fn make_region_truncate(truncate: TruncateRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
387 let region_id = truncate.region_id.into();
388 match truncate.kind {
389 None => InvalidRawRegionRequestSnafu {
390 err: "missing kind in TruncateRequest".to_string(),
391 }
392 .fail(),
393 Some(truncate_request::Kind::All(_)) => Ok(vec![(
394 region_id,
395 RegionRequest::Truncate(RegionTruncateRequest::All),
396 )]),
397 Some(truncate_request::Kind::TimeRanges(time_ranges)) => {
398 let time_ranges = from_pb_time_ranges(time_ranges).context(ConvertTimeRangesSnafu)?;
399
400 Ok(vec![(
401 region_id,
402 RegionRequest::Truncate(RegionTruncateRequest::ByTimeRanges { time_ranges }),
403 )])
404 }
405 }
406}
407
408fn make_region_bulk_inserts(request: BulkInsertRequest) -> Result<Vec<(RegionId, RegionRequest)>> {
410 let region_id = request.region_id.into();
411 let partition_expr_version = request.partition_expr_version.map(|v| v.value);
412 let Some(Body::ArrowIpc(request)) = request.body else {
413 return Ok(vec![]);
414 };
415
416 let decoder_timer = metrics::CONVERT_REGION_BULK_REQUEST
417 .with_label_values(&["decode"])
418 .start_timer();
419 let mut decoder =
420 FlightDecoder::try_from_schema_bytes(&request.schema).context(FlightCodecSnafu)?;
421 let payload = decoder
422 .try_decode_record_batch(&request.data_header, &request.payload)
423 .context(FlightCodecSnafu)?;
424 decoder_timer.observe_duration();
425 Ok(vec![(
426 region_id,
427 RegionRequest::BulkInserts(RegionBulkInsertsRequest {
428 region_id,
429 payload,
430 raw_data: request,
431 partition_expr_version,
432 }),
433 )])
434}
435
436fn make_region_apply_staging_manifest(
437 api::v1::region::ApplyStagingManifestRequest {
438 region_id,
439 partition_expr,
440 central_region_id,
441 manifest_path,
442 }: api::v1::region::ApplyStagingManifestRequest,
443) -> Result<Vec<(RegionId, RegionRequest)>> {
444 let region_id = region_id.into();
445 Ok(vec![(
446 region_id,
447 RegionRequest::ApplyStagingManifest(ApplyStagingManifestRequest {
448 partition_expr,
449 central_region_id: central_region_id.into(),
450 manifest_path,
451 }),
452 )])
453}
454
455#[derive(Debug)]
457pub struct RegionPutRequest {
458 pub rows: Rows,
460 pub hint: Option<WriteHint>,
462 pub partition_expr_version: Option<u64>,
464}
465
466#[derive(Debug)]
467pub struct RegionReadRequest {
468 pub request: ScanRequest,
469}
470
471#[derive(Debug)]
473pub struct RegionDeleteRequest {
474 pub rows: Rows,
478 pub hint: Option<WriteHint>,
480 pub partition_expr_version: Option<u64>,
482}
483
484#[derive(Debug, Clone)]
485pub struct RegionCreateRequest {
486 pub engine: String,
488 pub column_metadatas: Vec<ColumnMetadata>,
490 pub primary_key: Vec<ColumnId>,
492 pub options: HashMap<String, String>,
494 pub table_dir: String,
496 pub path_type: PathType,
498 pub partition_expr_json: Option<String>,
501}
502
503impl RegionCreateRequest {
504 pub fn validate(&self) -> Result<()> {
506 ensure!(
508 self.column_metadatas
509 .iter()
510 .any(|x| x.semantic_type == SemanticType::Timestamp),
511 InvalidRegionRequestSnafu {
512 region_id: RegionId::new(0, 0),
513 err: "missing timestamp column in create region request".to_string(),
514 }
515 );
516
517 let mut column_id_to_indices = HashMap::with_capacity(self.column_metadatas.len());
519 for (i, c) in self.column_metadatas.iter().enumerate() {
520 if let Some(previous) = column_id_to_indices.insert(c.column_id, i) {
521 return InvalidRegionRequestSnafu {
522 region_id: RegionId::new(0, 0),
523 err: format!(
524 "duplicate column id {} (at position {} and {}) in create region request",
525 c.column_id, previous, i
526 ),
527 }
528 .fail();
529 }
530 }
531
532 for column_id in &self.primary_key {
534 ensure!(
535 column_id_to_indices.contains_key(column_id),
536 InvalidRegionRequestSnafu {
537 region_id: RegionId::new(0, 0),
538 err: format!(
539 "missing primary key column {} in create region request",
540 column_id
541 ),
542 }
543 );
544 }
545
546 Ok(())
547 }
548
549 pub fn is_physical_table(&self) -> bool {
551 self.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
552 }
553}
554
555#[derive(Debug, Clone)]
556pub struct RegionDropRequest {
557 pub fast_path: bool,
560
561 pub force: bool,
564
565 pub partial_drop: bool,
568}
569
570#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
572#[serde(default)]
573pub struct RegionRequirements {
574 pub object_storage: bool,
576}
577
578impl RegionRequirements {
579 pub fn empty() -> Self {
581 Self::default()
582 }
583
584 pub fn object_storage() -> Self {
586 Self {
587 object_storage: true,
588 }
589 }
590}
591
592#[derive(Debug, Clone)]
594pub struct RegionOpenRequest {
595 pub engine: String,
597 pub table_dir: String,
599 pub path_type: PathType,
601 pub options: HashMap<String, String>,
603 pub skip_wal_replay: bool,
605 pub checkpoint: Option<ReplayCheckpoint>,
607 pub requirements: RegionRequirements,
609}
610
611#[derive(Debug, Clone, Copy, PartialEq, Eq)]
612pub struct ReplayCheckpoint {
613 pub entry_id: u64,
614 pub metadata_entry_id: Option<u64>,
615}
616
617impl RegionOpenRequest {
618 pub fn is_physical_table(&self) -> bool {
620 self.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
621 }
622}
623
624#[derive(Debug)]
626pub struct RegionCloseRequest {}
627
628#[derive(Debug, PartialEq, Eq, Clone)]
630pub struct RegionAlterRequest {
631 pub kind: AlterKind,
633}
634
635impl RegionAlterRequest {
636 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
638 self.kind.validate(metadata)?;
639
640 Ok(())
641 }
642
643 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
647 debug_assert!(self.validate(metadata).is_ok());
648 self.kind.need_alter(metadata)
649 }
650}
651
652impl TryFrom<AlterRequest> for RegionAlterRequest {
653 type Error = MetadataError;
654
655 fn try_from(value: AlterRequest) -> Result<Self> {
656 let kind = value.kind.context(InvalidRawRegionRequestSnafu {
657 err: "missing kind in AlterRequest",
658 })?;
659
660 let kind = AlterKind::try_from(kind)?;
661 Ok(RegionAlterRequest { kind })
662 }
663}
664
665#[derive(Debug, PartialEq, Eq, Clone, AsRefStr)]
667pub enum AlterKind {
668 AddColumns {
670 columns: Vec<AddColumn>,
672 },
673 DropColumns {
675 names: Vec<String>,
677 },
678 ModifyColumnTypes {
680 columns: Vec<ModifyColumnType>,
682 },
683 SetRegionOptions { options: Vec<SetRegionOption> },
685 UnsetRegionOptions { keys: Vec<UnsetRegionOption> },
687 SetIndexes { options: Vec<SetIndexOption> },
689 UnsetIndexes { options: Vec<UnsetIndexOption> },
691 DropDefaults {
693 names: Vec<String>,
695 },
696 SetDefaults {
698 columns: Vec<SetDefault>,
700 },
701 SyncColumns {
703 column_metadatas: Vec<ColumnMetadata>,
704 },
705}
706#[derive(Debug, PartialEq, Eq, Clone)]
707pub struct SetDefault {
708 pub name: String,
709 pub default_constraint: Vec<u8>,
710}
711
712#[derive(Debug, PartialEq, Eq, Clone)]
713pub enum SetIndexOption {
714 Fulltext {
715 column_name: String,
716 options: FulltextOptions,
717 },
718 Inverted {
719 column_name: String,
720 },
721 Skipping {
722 column_name: String,
723 options: SkippingIndexOptions,
724 },
725}
726
727impl SetIndexOption {
728 pub fn column_name(&self) -> &String {
730 match self {
731 SetIndexOption::Fulltext { column_name, .. } => column_name,
732 SetIndexOption::Inverted { column_name } => column_name,
733 SetIndexOption::Skipping { column_name, .. } => column_name,
734 }
735 }
736
737 pub fn is_fulltext(&self) -> bool {
739 match self {
740 SetIndexOption::Fulltext { .. } => true,
741 SetIndexOption::Inverted { .. } => false,
742 SetIndexOption::Skipping { .. } => false,
743 }
744 }
745}
746
747impl TryFrom<v1::SetIndex> for SetIndexOption {
748 type Error = MetadataError;
749
750 fn try_from(value: v1::SetIndex) -> Result<Self> {
751 let option = value.options.context(InvalidRawRegionRequestSnafu {
752 err: "missing options in SetIndex",
753 })?;
754
755 let opt = match option {
756 v1::set_index::Options::Fulltext(x) => SetIndexOption::Fulltext {
757 column_name: x.column_name.clone(),
758 options: FulltextOptions::new(
759 x.enable,
760 as_fulltext_option_analyzer(
761 Analyzer::try_from(x.analyzer).context(DecodeProtoSnafu)?,
762 ),
763 x.case_sensitive,
764 as_fulltext_option_backend(
765 PbFulltextBackend::try_from(x.backend).context(DecodeProtoSnafu)?,
766 ),
767 x.granularity as u32,
768 x.false_positive_rate,
769 )
770 .context(InvalidIndexOptionSnafu)?,
771 },
772 v1::set_index::Options::Inverted(i) => SetIndexOption::Inverted {
773 column_name: i.column_name,
774 },
775 v1::set_index::Options::Skipping(s) => SetIndexOption::Skipping {
776 column_name: s.column_name,
777 options: SkippingIndexOptions::new(
778 s.granularity as u32,
779 s.false_positive_rate,
780 as_skipping_index_type(
781 PbSkippingIndexType::try_from(s.skipping_index_type)
782 .context(DecodeProtoSnafu)?,
783 ),
784 )
785 .context(InvalidIndexOptionSnafu)?,
786 },
787 };
788
789 Ok(opt)
790 }
791}
792
793#[derive(Debug, PartialEq, Eq, Clone)]
794pub enum UnsetIndexOption {
795 Fulltext { column_name: String },
796 Inverted { column_name: String },
797 Skipping { column_name: String },
798}
799
800impl UnsetIndexOption {
801 pub fn column_name(&self) -> &String {
802 match self {
803 UnsetIndexOption::Fulltext { column_name } => column_name,
804 UnsetIndexOption::Inverted { column_name } => column_name,
805 UnsetIndexOption::Skipping { column_name } => column_name,
806 }
807 }
808
809 pub fn is_fulltext(&self) -> bool {
810 match self {
811 UnsetIndexOption::Fulltext { .. } => true,
812 UnsetIndexOption::Inverted { .. } => false,
813 UnsetIndexOption::Skipping { .. } => false,
814 }
815 }
816}
817
818impl TryFrom<v1::UnsetIndex> for UnsetIndexOption {
819 type Error = MetadataError;
820
821 fn try_from(value: v1::UnsetIndex) -> Result<Self> {
822 let option = value.options.context(InvalidRawRegionRequestSnafu {
823 err: "missing options in UnsetIndex",
824 })?;
825
826 let opt = match option {
827 v1::unset_index::Options::Fulltext(f) => UnsetIndexOption::Fulltext {
828 column_name: f.column_name,
829 },
830 v1::unset_index::Options::Inverted(i) => UnsetIndexOption::Inverted {
831 column_name: i.column_name,
832 },
833 v1::unset_index::Options::Skipping(s) => UnsetIndexOption::Skipping {
834 column_name: s.column_name,
835 },
836 };
837
838 Ok(opt)
839 }
840}
841
842impl AlterKind {
843 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
847 match self {
848 AlterKind::AddColumns { columns } => {
849 for col_to_add in columns {
850 col_to_add.validate(metadata)?;
851 }
852 }
853 AlterKind::DropColumns { names } => {
854 for name in names {
855 Self::validate_column_to_drop(name, metadata)?;
856 }
857 }
858 AlterKind::ModifyColumnTypes { columns } => {
859 for col_to_change in columns {
860 col_to_change.validate(metadata)?;
861 }
862 }
863 AlterKind::SetRegionOptions { .. } => {}
864 AlterKind::UnsetRegionOptions { .. } => {}
865 AlterKind::SetIndexes { options } => {
866 for option in options {
867 Self::validate_column_alter_index_option(
868 option.column_name(),
869 metadata,
870 option.is_fulltext(),
871 )?;
872 }
873 }
874 AlterKind::UnsetIndexes { options } => {
875 for option in options {
876 Self::validate_column_alter_index_option(
877 option.column_name(),
878 metadata,
879 option.is_fulltext(),
880 )?;
881 }
882 }
883 AlterKind::DropDefaults { names } => {
884 names
885 .iter()
886 .try_for_each(|name| Self::validate_column_existence(name, metadata))?;
887 }
888 AlterKind::SetDefaults { columns } => {
889 columns
890 .iter()
891 .try_for_each(|col| Self::validate_column_existence(&col.name, metadata))?;
892 }
893 AlterKind::SyncColumns { column_metadatas } => {
894 let new_primary_keys = column_metadatas
895 .iter()
896 .filter(|c| c.semantic_type == SemanticType::Tag)
897 .map(|c| (c.column_schema.name.as_str(), c.column_id))
898 .collect::<HashMap<_, _>>();
899
900 let old_primary_keys = metadata
901 .column_metadatas
902 .iter()
903 .filter(|c| c.semantic_type == SemanticType::Tag)
904 .map(|c| (c.column_schema.name.as_str(), c.column_id));
905
906 for (name, id) in old_primary_keys {
907 let primary_key =
908 new_primary_keys
909 .get(name)
910 .with_context(|| InvalidRegionRequestSnafu {
911 region_id: metadata.region_id,
912 err: format!("column {} is not a primary key", name),
913 })?;
914
915 ensure!(
916 *primary_key == id,
917 InvalidRegionRequestSnafu {
918 region_id: metadata.region_id,
919 err: format!(
920 "column with same name {} has different id, existing: {}, got: {}",
921 name, id, primary_key
922 ),
923 }
924 );
925 }
926
927 let new_ts_column = column_metadatas
928 .iter()
929 .find(|c| c.semantic_type == SemanticType::Timestamp)
930 .map(|c| (c.column_schema.name.as_str(), c.column_id))
931 .context(InvalidRegionRequestSnafu {
932 region_id: metadata.region_id,
933 err: "timestamp column not found",
934 })?;
935
936 let old_ts_column = metadata
938 .column_metadatas
939 .iter()
940 .find(|c| c.semantic_type == SemanticType::Timestamp)
941 .map(|c| (c.column_schema.name.as_str(), c.column_id))
942 .unwrap();
943
944 ensure!(
945 new_ts_column == old_ts_column,
946 InvalidRegionRequestSnafu {
947 region_id: metadata.region_id,
948 err: format!(
949 "timestamp column {} has different id, existing: {}, got: {}",
950 old_ts_column.0, old_ts_column.1, new_ts_column.1
951 ),
952 }
953 );
954 }
955 }
956 Ok(())
957 }
958
959 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
961 debug_assert!(self.validate(metadata).is_ok());
962 match self {
963 AlterKind::AddColumns { columns } => columns
964 .iter()
965 .any(|col_to_add| col_to_add.need_alter(metadata)),
966 AlterKind::DropColumns { names } => names
967 .iter()
968 .any(|name| metadata.column_by_name(name).is_some()),
969 AlterKind::ModifyColumnTypes { columns } => columns
970 .iter()
971 .any(|col_to_change| col_to_change.need_alter(metadata)),
972 AlterKind::SetRegionOptions { .. } => true,
973 AlterKind::UnsetRegionOptions { .. } => true,
974 AlterKind::SetIndexes { options, .. } => options
975 .iter()
976 .any(|option| metadata.column_by_name(option.column_name()).is_some()),
977 AlterKind::UnsetIndexes { options } => options
978 .iter()
979 .any(|option| metadata.column_by_name(option.column_name()).is_some()),
980 AlterKind::DropDefaults { names } => names
981 .iter()
982 .any(|name| metadata.column_by_name(name).is_some()),
983
984 AlterKind::SetDefaults { columns } => columns
985 .iter()
986 .any(|x| metadata.column_by_name(&x.name).is_some()),
987 AlterKind::SyncColumns { column_metadatas } => {
988 metadata.column_metadatas != *column_metadatas
989 }
990 }
991 }
992
993 fn validate_column_to_drop(name: &str, metadata: &RegionMetadata) -> Result<()> {
995 let Some(column) = metadata.column_by_name(name) else {
996 return Ok(());
997 };
998 ensure!(
999 column.semantic_type == SemanticType::Field,
1000 InvalidRegionRequestSnafu {
1001 region_id: metadata.region_id,
1002 err: format!("column {} is not a field and could not be dropped", name),
1003 }
1004 );
1005 Ok(())
1006 }
1007
1008 fn validate_column_alter_index_option(
1010 column_name: &String,
1011 metadata: &RegionMetadata,
1012 is_fulltext: bool,
1013 ) -> Result<()> {
1014 let column = metadata
1015 .column_by_name(column_name)
1016 .context(InvalidRegionRequestSnafu {
1017 region_id: metadata.region_id,
1018 err: format!("column {} not found", column_name),
1019 })?;
1020
1021 if is_fulltext {
1022 ensure!(
1023 column.column_schema.data_type.is_string(),
1024 InvalidRegionRequestSnafu {
1025 region_id: metadata.region_id,
1026 err: format!(
1027 "cannot change alter index options for non-string column {}",
1028 column_name
1029 ),
1030 }
1031 );
1032 }
1033
1034 Ok(())
1035 }
1036
1037 fn validate_column_existence(column_name: &String, metadata: &RegionMetadata) -> Result<()> {
1039 metadata
1040 .column_by_name(column_name)
1041 .context(InvalidRegionRequestSnafu {
1042 region_id: metadata.region_id,
1043 err: format!("column {} not found", column_name),
1044 })?;
1045
1046 Ok(())
1047 }
1048}
1049
1050impl TryFrom<alter_request::Kind> for AlterKind {
1051 type Error = MetadataError;
1052
1053 fn try_from(kind: alter_request::Kind) -> Result<Self> {
1054 let alter_kind = match kind {
1055 alter_request::Kind::AddColumns(x) => {
1056 let columns = x
1057 .add_columns
1058 .into_iter()
1059 .map(|x| x.try_into())
1060 .collect::<Result<Vec<_>>>()?;
1061 AlterKind::AddColumns { columns }
1062 }
1063 alter_request::Kind::ModifyColumnTypes(x) => {
1064 let columns = x
1065 .modify_column_types
1066 .into_iter()
1067 .map(|x| x.into())
1068 .collect::<Vec<_>>();
1069 AlterKind::ModifyColumnTypes { columns }
1070 }
1071 alter_request::Kind::DropColumns(x) => {
1072 let names = x.drop_columns.into_iter().map(|x| x.name).collect();
1073 AlterKind::DropColumns { names }
1074 }
1075 alter_request::Kind::SetTableOptions(options) => AlterKind::SetRegionOptions {
1076 options: options
1077 .table_options
1078 .iter()
1079 .map(TryFrom::try_from)
1080 .collect::<Result<Vec<_>>>()?,
1081 },
1082 alter_request::Kind::UnsetTableOptions(options) => AlterKind::UnsetRegionOptions {
1083 keys: options
1084 .keys
1085 .iter()
1086 .map(|key| UnsetRegionOption::try_from(key.as_str()))
1087 .collect::<Result<Vec<_>>>()?,
1088 },
1089 alter_request::Kind::SetIndex(o) => AlterKind::SetIndexes {
1090 options: vec![SetIndexOption::try_from(o)?],
1091 },
1092 alter_request::Kind::UnsetIndex(o) => AlterKind::UnsetIndexes {
1093 options: vec![UnsetIndexOption::try_from(o)?],
1094 },
1095 alter_request::Kind::SetIndexes(o) => AlterKind::SetIndexes {
1096 options: o
1097 .set_indexes
1098 .into_iter()
1099 .map(SetIndexOption::try_from)
1100 .collect::<Result<Vec<_>>>()?,
1101 },
1102 alter_request::Kind::UnsetIndexes(o) => AlterKind::UnsetIndexes {
1103 options: o
1104 .unset_indexes
1105 .into_iter()
1106 .map(UnsetIndexOption::try_from)
1107 .collect::<Result<Vec<_>>>()?,
1108 },
1109 alter_request::Kind::DropDefaults(x) => AlterKind::DropDefaults {
1110 names: x.drop_defaults.into_iter().map(|x| x.column_name).collect(),
1111 },
1112 alter_request::Kind::SetDefaults(x) => AlterKind::SetDefaults {
1113 columns: x
1114 .set_defaults
1115 .into_iter()
1116 .map(|x| {
1117 Ok(SetDefault {
1118 name: x.column_name,
1119 default_constraint: x.default_constraint.clone(),
1120 })
1121 })
1122 .collect::<Result<Vec<_>>>()?,
1123 },
1124 alter_request::Kind::SyncColumns(x) => AlterKind::SyncColumns {
1125 column_metadatas: x
1126 .column_defs
1127 .into_iter()
1128 .map(ColumnMetadata::try_from_column_def)
1129 .collect::<Result<Vec<_>>>()?,
1130 },
1131 };
1132
1133 Ok(alter_kind)
1134 }
1135}
1136
1137#[derive(Debug, PartialEq, Eq, Clone)]
1139pub struct AddColumn {
1140 pub column_metadata: ColumnMetadata,
1142 pub location: Option<AddColumnLocation>,
1145}
1146
1147impl AddColumn {
1148 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
1153 ensure!(
1154 self.column_metadata.column_schema.is_nullable()
1155 || self
1156 .column_metadata
1157 .column_schema
1158 .default_constraint()
1159 .is_some(),
1160 InvalidRegionRequestSnafu {
1161 region_id: metadata.region_id,
1162 err: format!(
1163 "no default value for column {}",
1164 self.column_metadata.column_schema.name
1165 ),
1166 }
1167 );
1168
1169 if let Some(existing_column) =
1170 metadata.column_by_name(&self.column_metadata.column_schema.name)
1171 {
1172 ensure!(
1174 *existing_column == self.column_metadata,
1175 InvalidRegionRequestSnafu {
1176 region_id: metadata.region_id,
1177 err: format!(
1178 "column {} already exists with different metadata, existing: {:?}, got: {:?}",
1179 self.column_metadata.column_schema.name,
1180 existing_column,
1181 self.column_metadata,
1182 ),
1183 }
1184 );
1185 ensure!(
1186 self.location.is_none(),
1187 InvalidRegionRequestSnafu {
1188 region_id: metadata.region_id,
1189 err: format!(
1190 "column {} already exists, but location is specified",
1191 self.column_metadata.column_schema.name
1192 ),
1193 }
1194 );
1195 }
1196
1197 if let Some(existing_column) = metadata.column_by_id(self.column_metadata.column_id) {
1198 ensure!(
1200 existing_column.column_schema.name == self.column_metadata.column_schema.name,
1201 InvalidRegionRequestSnafu {
1202 region_id: metadata.region_id,
1203 err: format!(
1204 "column id {} already exists with different name {}",
1205 self.column_metadata.column_id, existing_column.column_schema.name
1206 ),
1207 }
1208 );
1209 }
1210
1211 Ok(())
1212 }
1213
1214 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
1216 debug_assert!(self.validate(metadata).is_ok());
1217 metadata
1218 .column_by_name(&self.column_metadata.column_schema.name)
1219 .is_none()
1220 }
1221}
1222
1223impl TryFrom<v1::region::AddColumn> for AddColumn {
1224 type Error = MetadataError;
1225
1226 fn try_from(add_column: v1::region::AddColumn) -> Result<Self> {
1227 let column_def = add_column
1228 .column_def
1229 .context(InvalidRawRegionRequestSnafu {
1230 err: "missing column_def in AddColumn",
1231 })?;
1232
1233 let column_metadata = ColumnMetadata::try_from_column_def(column_def)?;
1234 let location = add_column
1235 .location
1236 .map(AddColumnLocation::try_from)
1237 .transpose()?;
1238
1239 Ok(AddColumn {
1240 column_metadata,
1241 location,
1242 })
1243 }
1244}
1245
1246#[derive(Debug, PartialEq, Eq, Clone)]
1248pub enum AddColumnLocation {
1249 First,
1251 After {
1253 column_name: String,
1255 },
1256}
1257
1258impl TryFrom<v1::AddColumnLocation> for AddColumnLocation {
1259 type Error = MetadataError;
1260
1261 fn try_from(location: v1::AddColumnLocation) -> Result<Self> {
1262 let location_type = LocationType::try_from(location.location_type)
1263 .map_err(|e| InvalidRawRegionRequestSnafu { err: e.to_string() }.build())?;
1264 let add_column_location = match location_type {
1265 LocationType::First => AddColumnLocation::First,
1266 LocationType::After => AddColumnLocation::After {
1267 column_name: location.after_column_name,
1268 },
1269 };
1270
1271 Ok(add_column_location)
1272 }
1273}
1274
1275#[derive(Debug, PartialEq, Eq, Clone)]
1277pub struct ModifyColumnType {
1278 pub column_name: String,
1280 pub target_type: ConcreteDataType,
1282}
1283
1284impl ModifyColumnType {
1285 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
1287 let column_meta = metadata
1288 .column_by_name(&self.column_name)
1289 .with_context(|| InvalidRegionRequestSnafu {
1290 region_id: metadata.region_id,
1291 err: format!("column {} not found", self.column_name),
1292 })?;
1293
1294 ensure!(
1295 matches!(column_meta.semantic_type, SemanticType::Field),
1296 InvalidRegionRequestSnafu {
1297 region_id: metadata.region_id,
1298 err: "'timestamp' or 'tag' column cannot change type".to_string()
1299 }
1300 );
1301 ensure!(
1302 column_meta
1303 .column_schema
1304 .data_type
1305 .can_arrow_type_cast_to(&self.target_type),
1306 InvalidRegionRequestSnafu {
1307 region_id: metadata.region_id,
1308 err: format!(
1309 "column '{}' cannot be cast automatically to type '{}'",
1310 self.column_name, self.target_type
1311 ),
1312 }
1313 );
1314
1315 Ok(())
1316 }
1317
1318 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
1320 debug_assert!(self.validate(metadata).is_ok());
1321 metadata.column_by_name(&self.column_name).is_some()
1322 }
1323}
1324
1325impl From<v1::ModifyColumnType> for ModifyColumnType {
1326 fn from(modify_column_type: v1::ModifyColumnType) -> Self {
1327 let target_type = ColumnDataTypeWrapper::new(
1328 modify_column_type.target_type(),
1329 modify_column_type.target_type_extension,
1330 )
1331 .into();
1332
1333 ModifyColumnType {
1334 column_name: modify_column_type.column_name,
1335 target_type,
1336 }
1337 }
1338}
1339
1340#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
1341pub enum SetRegionOption {
1342 Ttl(Option<TimeToLive>),
1343 Twsc(String, String),
1345 Format(String),
1347 AppendMode(bool),
1349}
1350
1351impl TryFrom<&PbOption> for SetRegionOption {
1352 type Error = MetadataError;
1353
1354 fn try_from(value: &PbOption) -> std::result::Result<Self, Self::Error> {
1355 let PbOption { key, value } = value;
1356 match key.as_str() {
1357 TTL_KEY => {
1358 let ttl = TimeToLive::from_humantime_or_str(value)
1359 .map_err(|_| InvalidSetRegionOptionRequestSnafu { key, value }.build())?;
1360
1361 Ok(Self::Ttl(Some(ttl)))
1362 }
1363 TWCS_TRIGGER_FILE_NUM | TWCS_MAX_OUTPUT_FILE_SIZE | TWCS_TIME_WINDOW => {
1364 Ok(Self::Twsc(key.clone(), value.clone()))
1365 }
1366 SST_FORMAT_KEY => Ok(Self::Format(value.clone())),
1367 APPEND_MODE_KEY => {
1368 let append_mode = value
1369 .parse::<bool>()
1370 .map_err(|_| InvalidSetRegionOptionRequestSnafu { key, value }.build())?;
1371 Ok(Self::AppendMode(append_mode))
1372 }
1373 _ => InvalidSetRegionOptionRequestSnafu { key, value }.fail(),
1374 }
1375 }
1376}
1377
1378impl From<&UnsetRegionOption> for SetRegionOption {
1379 fn from(unset_option: &UnsetRegionOption) -> Self {
1380 match unset_option {
1381 UnsetRegionOption::TwcsTriggerFileNum => {
1382 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1383 }
1384 UnsetRegionOption::TwcsMaxOutputFileSize => {
1385 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1386 }
1387 UnsetRegionOption::TwcsTimeWindow => {
1388 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1389 }
1390 UnsetRegionOption::Ttl => SetRegionOption::Ttl(Default::default()),
1391 }
1392 }
1393}
1394
1395impl TryFrom<&str> for UnsetRegionOption {
1396 type Error = MetadataError;
1397
1398 fn try_from(key: &str) -> Result<Self> {
1399 match key.to_ascii_lowercase().as_str() {
1400 TTL_KEY => Ok(Self::Ttl),
1401 TWCS_TRIGGER_FILE_NUM => Ok(Self::TwcsTriggerFileNum),
1402 TWCS_MAX_OUTPUT_FILE_SIZE => Ok(Self::TwcsMaxOutputFileSize),
1403 TWCS_TIME_WINDOW => Ok(Self::TwcsTimeWindow),
1404 _ => InvalidUnsetRegionOptionRequestSnafu { key }.fail(),
1405 }
1406 }
1407}
1408
1409#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
1410pub enum UnsetRegionOption {
1411 TwcsTriggerFileNum,
1412 TwcsMaxOutputFileSize,
1413 TwcsTimeWindow,
1414 Ttl,
1415}
1416
1417impl UnsetRegionOption {
1418 pub fn as_str(&self) -> &str {
1419 match self {
1420 Self::Ttl => TTL_KEY,
1421 Self::TwcsTriggerFileNum => TWCS_TRIGGER_FILE_NUM,
1422 Self::TwcsMaxOutputFileSize => TWCS_MAX_OUTPUT_FILE_SIZE,
1423 Self::TwcsTimeWindow => TWCS_TIME_WINDOW,
1424 }
1425 }
1426}
1427
1428impl Display for UnsetRegionOption {
1429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1430 write!(f, "{}", self.as_str())
1431 }
1432}
1433
1434#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1435pub enum RegionFlushReason {
1436 RegionMigration,
1438 Repartition,
1440 RemoteWalPrune,
1442 Closing,
1444 Downgrading,
1446}
1447
1448#[derive(Debug, Clone, Default)]
1449pub struct RegionFlushRequest {
1450 pub row_group_size: Option<usize>,
1451 pub reason: Option<RegionFlushReason>,
1452}
1453
1454#[derive(Debug)]
1455pub struct RegionCompactRequest {
1456 pub options: compact_request::Options,
1457 pub parallelism: Option<u32>,
1458}
1459
1460impl Default for RegionCompactRequest {
1461 fn default() -> Self {
1462 Self {
1463 options: compact_request::Options::Regular(Default::default()),
1465 parallelism: None,
1466 }
1467 }
1468}
1469
1470#[derive(Debug, Clone, Default)]
1471pub struct RegionBuildIndexRequest {}
1472
1473#[derive(Debug)]
1475pub enum RegionTruncateRequest {
1476 All,
1478 ByTimeRanges {
1479 time_ranges: Vec<(Timestamp, Timestamp)>,
1483 },
1484}
1485
1486#[derive(Debug, Clone, Copy, Default)]
1491pub struct RegionCatchupRequest {
1492 pub set_writable: bool,
1494 pub entry_id: Option<entry::Id>,
1497 pub metadata_entry_id: Option<entry::Id>,
1501 pub location_id: Option<u64>,
1503 pub checkpoint: Option<ReplayCheckpoint>,
1505}
1506
1507#[derive(Debug, Clone)]
1508pub struct RegionBulkInsertsRequest {
1509 pub region_id: RegionId,
1510 pub payload: DfRecordBatch,
1511 pub raw_data: ArrowIpc,
1512 pub partition_expr_version: Option<u64>,
1513}
1514
1515impl RegionBulkInsertsRequest {
1516 pub fn estimated_size(&self) -> usize {
1517 self.payload.get_array_memory_size()
1518 }
1519}
1520
1521#[derive(Debug, Clone, PartialEq, Eq)]
1527pub enum StagingPartitionDirective {
1528 UpdatePartitionExpr(String),
1529 RejectAllWrites,
1530}
1531
1532impl StagingPartitionDirective {
1533 pub fn partition_expr(&self) -> Option<&str> {
1535 match self {
1536 Self::UpdatePartitionExpr(expr) => Some(expr),
1537 Self::RejectAllWrites => None,
1538 }
1539 }
1540}
1541
1542#[derive(Debug, Clone)]
1543pub struct EnterStagingRequest {
1544 pub partition_directive: StagingPartitionDirective,
1546}
1547
1548impl EnterStagingRequest {
1549 pub fn with_partition_expr(partition_expr: String) -> Self {
1551 Self {
1552 partition_directive: StagingPartitionDirective::UpdatePartitionExpr(partition_expr),
1553 }
1554 }
1555}
1556
1557#[derive(Debug, Clone)]
1576pub struct ApplyStagingManifestRequest {
1577 pub partition_expr: String,
1579 pub central_region_id: RegionId,
1581 pub manifest_path: String,
1584}
1585
1586impl fmt::Display for RegionRequest {
1587 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1588 match self {
1589 RegionRequest::Put(_) => write!(f, "Put"),
1590 RegionRequest::Delete(_) => write!(f, "Delete"),
1591 RegionRequest::Create(_) => write!(f, "Create"),
1592 RegionRequest::Drop(_) => write!(f, "Drop"),
1593 RegionRequest::Open(_) => write!(f, "Open"),
1594 RegionRequest::Close(_) => write!(f, "Close"),
1595 RegionRequest::Alter(_) => write!(f, "Alter"),
1596 RegionRequest::Flush(_) => write!(f, "Flush"),
1597 RegionRequest::Compact(_) => write!(f, "Compact"),
1598 RegionRequest::BuildIndex(_) => write!(f, "BuildIndex"),
1599 RegionRequest::Truncate(_) => write!(f, "Truncate"),
1600 RegionRequest::Catchup(_) => write!(f, "Catchup"),
1601 RegionRequest::BulkInserts(_) => write!(f, "BulkInserts"),
1602 RegionRequest::EnterStaging(_) => write!(f, "EnterStaging"),
1603 RegionRequest::ApplyStagingManifest(_) => write!(f, "ApplyStagingManifest"),
1604 }
1605 }
1606}
1607
1608#[cfg(test)]
1609mod tests {
1610
1611 use api::v1::region::RegionColumnDef;
1612 use api::v1::{ColumnDataType, ColumnDef};
1613 use datatypes::prelude::ConcreteDataType;
1614 use datatypes::schema::{ColumnSchema, FulltextAnalyzer, FulltextBackend};
1615
1616 use super::*;
1617 use crate::metadata::RegionMetadataBuilder;
1618
1619 #[test]
1620 fn test_from_proto_location() {
1621 let proto_location = v1::AddColumnLocation {
1622 location_type: LocationType::First as i32,
1623 after_column_name: String::default(),
1624 };
1625 let location = AddColumnLocation::try_from(proto_location).unwrap();
1626 assert_eq!(location, AddColumnLocation::First);
1627
1628 let proto_location = v1::AddColumnLocation {
1629 location_type: 10,
1630 after_column_name: String::default(),
1631 };
1632 AddColumnLocation::try_from(proto_location).unwrap_err();
1633
1634 let proto_location = v1::AddColumnLocation {
1635 location_type: LocationType::After as i32,
1636 after_column_name: "a".to_string(),
1637 };
1638 let location = AddColumnLocation::try_from(proto_location).unwrap();
1639 assert_eq!(
1640 location,
1641 AddColumnLocation::After {
1642 column_name: "a".to_string()
1643 }
1644 );
1645 }
1646
1647 #[test]
1648 fn test_from_none_proto_add_column() {
1649 AddColumn::try_from(v1::region::AddColumn {
1650 column_def: None,
1651 location: None,
1652 })
1653 .unwrap_err();
1654 }
1655
1656 #[test]
1657 fn test_from_proto_alter_request() {
1658 RegionAlterRequest::try_from(AlterRequest {
1659 region_id: 0,
1660 schema_version: 1,
1661 kind: None,
1662 })
1663 .unwrap_err();
1664
1665 let request = RegionAlterRequest::try_from(AlterRequest {
1666 region_id: 0,
1667 schema_version: 1,
1668 kind: Some(alter_request::Kind::AddColumns(v1::region::AddColumns {
1669 add_columns: vec![v1::region::AddColumn {
1670 column_def: Some(RegionColumnDef {
1671 column_def: Some(ColumnDef {
1672 name: "a".to_string(),
1673 data_type: ColumnDataType::String as i32,
1674 is_nullable: true,
1675 default_constraint: vec![],
1676 semantic_type: SemanticType::Field as i32,
1677 comment: String::new(),
1678 ..Default::default()
1679 }),
1680 column_id: 1,
1681 }),
1682 location: Some(v1::AddColumnLocation {
1683 location_type: LocationType::First as i32,
1684 after_column_name: String::default(),
1685 }),
1686 }],
1687 })),
1688 })
1689 .unwrap();
1690
1691 assert_eq!(
1692 request,
1693 RegionAlterRequest {
1694 kind: AlterKind::AddColumns {
1695 columns: vec![AddColumn {
1696 column_metadata: ColumnMetadata {
1697 column_schema: ColumnSchema::new(
1698 "a",
1699 ConcreteDataType::string_datatype(),
1700 true,
1701 ),
1702 semantic_type: SemanticType::Field,
1703 column_id: 1,
1704 },
1705 location: Some(AddColumnLocation::First),
1706 }]
1707 },
1708 }
1709 );
1710 }
1711
1712 fn new_metadata() -> RegionMetadata {
1715 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1716 builder
1717 .push_column_metadata(ColumnMetadata {
1718 column_schema: ColumnSchema::new(
1719 "ts",
1720 ConcreteDataType::timestamp_millisecond_datatype(),
1721 false,
1722 ),
1723 semantic_type: SemanticType::Timestamp,
1724 column_id: 1,
1725 })
1726 .push_column_metadata(ColumnMetadata {
1727 column_schema: ColumnSchema::new(
1728 "tag_0",
1729 ConcreteDataType::string_datatype(),
1730 true,
1731 ),
1732 semantic_type: SemanticType::Tag,
1733 column_id: 2,
1734 })
1735 .push_column_metadata(ColumnMetadata {
1736 column_schema: ColumnSchema::new(
1737 "field_0",
1738 ConcreteDataType::string_datatype(),
1739 true,
1740 ),
1741 semantic_type: SemanticType::Field,
1742 column_id: 3,
1743 })
1744 .push_column_metadata(ColumnMetadata {
1745 column_schema: ColumnSchema::new(
1746 "field_1",
1747 ConcreteDataType::boolean_datatype(),
1748 true,
1749 ),
1750 semantic_type: SemanticType::Field,
1751 column_id: 4,
1752 })
1753 .primary_key(vec![2]);
1754 builder.build().unwrap()
1755 }
1756
1757 #[test]
1758 fn test_add_column_validate() {
1759 let metadata = new_metadata();
1760 let add_column = AddColumn {
1761 column_metadata: ColumnMetadata {
1762 column_schema: ColumnSchema::new(
1763 "tag_1",
1764 ConcreteDataType::string_datatype(),
1765 true,
1766 ),
1767 semantic_type: SemanticType::Tag,
1768 column_id: 5,
1769 },
1770 location: None,
1771 };
1772 add_column.validate(&metadata).unwrap();
1773 assert!(add_column.need_alter(&metadata));
1774
1775 AddColumn {
1777 column_metadata: ColumnMetadata {
1778 column_schema: ColumnSchema::new(
1779 "tag_1",
1780 ConcreteDataType::string_datatype(),
1781 false,
1782 ),
1783 semantic_type: SemanticType::Tag,
1784 column_id: 5,
1785 },
1786 location: None,
1787 }
1788 .validate(&metadata)
1789 .unwrap_err();
1790
1791 let add_column = AddColumn {
1793 column_metadata: ColumnMetadata {
1794 column_schema: ColumnSchema::new(
1795 "tag_0",
1796 ConcreteDataType::string_datatype(),
1797 true,
1798 ),
1799 semantic_type: SemanticType::Tag,
1800 column_id: 2,
1801 },
1802 location: None,
1803 };
1804 add_column.validate(&metadata).unwrap();
1805 assert!(!add_column.need_alter(&metadata));
1806 }
1807
1808 #[test]
1809 fn test_add_duplicate_columns() {
1810 let kind = AlterKind::AddColumns {
1811 columns: vec![
1812 AddColumn {
1813 column_metadata: ColumnMetadata {
1814 column_schema: ColumnSchema::new(
1815 "tag_1",
1816 ConcreteDataType::string_datatype(),
1817 true,
1818 ),
1819 semantic_type: SemanticType::Tag,
1820 column_id: 5,
1821 },
1822 location: None,
1823 },
1824 AddColumn {
1825 column_metadata: ColumnMetadata {
1826 column_schema: ColumnSchema::new(
1827 "tag_1",
1828 ConcreteDataType::string_datatype(),
1829 true,
1830 ),
1831 semantic_type: SemanticType::Field,
1832 column_id: 6,
1833 },
1834 location: None,
1835 },
1836 ],
1837 };
1838 let metadata = new_metadata();
1839 kind.validate(&metadata).unwrap();
1840 assert!(kind.need_alter(&metadata));
1841 }
1842
1843 #[test]
1844 fn test_add_existing_column_different_metadata() {
1845 let metadata = new_metadata();
1846
1847 let kind = AlterKind::AddColumns {
1849 columns: vec![AddColumn {
1850 column_metadata: ColumnMetadata {
1851 column_schema: ColumnSchema::new(
1852 "tag_0",
1853 ConcreteDataType::string_datatype(),
1854 true,
1855 ),
1856 semantic_type: SemanticType::Tag,
1857 column_id: 4,
1858 },
1859 location: None,
1860 }],
1861 };
1862 kind.validate(&metadata).unwrap_err();
1863
1864 let kind = AlterKind::AddColumns {
1866 columns: vec![AddColumn {
1867 column_metadata: ColumnMetadata {
1868 column_schema: ColumnSchema::new(
1869 "tag_0",
1870 ConcreteDataType::int64_datatype(),
1871 true,
1872 ),
1873 semantic_type: SemanticType::Tag,
1874 column_id: 2,
1875 },
1876 location: None,
1877 }],
1878 };
1879 kind.validate(&metadata).unwrap_err();
1880
1881 let kind = AlterKind::AddColumns {
1883 columns: vec![AddColumn {
1884 column_metadata: ColumnMetadata {
1885 column_schema: ColumnSchema::new(
1886 "tag_1",
1887 ConcreteDataType::string_datatype(),
1888 true,
1889 ),
1890 semantic_type: SemanticType::Tag,
1891 column_id: 2,
1892 },
1893 location: None,
1894 }],
1895 };
1896 kind.validate(&metadata).unwrap_err();
1897 }
1898
1899 #[test]
1900 fn test_add_existing_column_with_location() {
1901 let metadata = new_metadata();
1902 let kind = AlterKind::AddColumns {
1903 columns: vec![AddColumn {
1904 column_metadata: ColumnMetadata {
1905 column_schema: ColumnSchema::new(
1906 "tag_0",
1907 ConcreteDataType::string_datatype(),
1908 true,
1909 ),
1910 semantic_type: SemanticType::Tag,
1911 column_id: 2,
1912 },
1913 location: Some(AddColumnLocation::First),
1914 }],
1915 };
1916 kind.validate(&metadata).unwrap_err();
1917 }
1918
1919 #[test]
1920 fn test_validate_drop_column() {
1921 let metadata = new_metadata();
1922 let kind = AlterKind::DropColumns {
1923 names: vec!["xxxx".to_string()],
1924 };
1925 kind.validate(&metadata).unwrap();
1926 assert!(!kind.need_alter(&metadata));
1927
1928 AlterKind::DropColumns {
1929 names: vec!["tag_0".to_string()],
1930 }
1931 .validate(&metadata)
1932 .unwrap_err();
1933
1934 let kind = AlterKind::DropColumns {
1935 names: vec!["field_0".to_string()],
1936 };
1937 kind.validate(&metadata).unwrap();
1938 assert!(kind.need_alter(&metadata));
1939 }
1940
1941 #[test]
1942 fn test_validate_modify_column_type() {
1943 let metadata = new_metadata();
1944 AlterKind::ModifyColumnTypes {
1945 columns: vec![ModifyColumnType {
1946 column_name: "xxxx".to_string(),
1947 target_type: ConcreteDataType::string_datatype(),
1948 }],
1949 }
1950 .validate(&metadata)
1951 .unwrap_err();
1952
1953 AlterKind::ModifyColumnTypes {
1954 columns: vec![ModifyColumnType {
1955 column_name: "field_1".to_string(),
1956 target_type: ConcreteDataType::date_datatype(),
1957 }],
1958 }
1959 .validate(&metadata)
1960 .unwrap_err();
1961
1962 AlterKind::ModifyColumnTypes {
1963 columns: vec![ModifyColumnType {
1964 column_name: "ts".to_string(),
1965 target_type: ConcreteDataType::date_datatype(),
1966 }],
1967 }
1968 .validate(&metadata)
1969 .unwrap_err();
1970
1971 AlterKind::ModifyColumnTypes {
1972 columns: vec![ModifyColumnType {
1973 column_name: "tag_0".to_string(),
1974 target_type: ConcreteDataType::date_datatype(),
1975 }],
1976 }
1977 .validate(&metadata)
1978 .unwrap_err();
1979
1980 let kind = AlterKind::ModifyColumnTypes {
1981 columns: vec![ModifyColumnType {
1982 column_name: "field_0".to_string(),
1983 target_type: ConcreteDataType::int32_datatype(),
1984 }],
1985 };
1986 kind.validate(&metadata).unwrap();
1987 assert!(kind.need_alter(&metadata));
1988 }
1989
1990 #[test]
1991 fn test_validate_add_columns() {
1992 let kind = AlterKind::AddColumns {
1993 columns: vec![
1994 AddColumn {
1995 column_metadata: ColumnMetadata {
1996 column_schema: ColumnSchema::new(
1997 "tag_1",
1998 ConcreteDataType::string_datatype(),
1999 true,
2000 ),
2001 semantic_type: SemanticType::Tag,
2002 column_id: 5,
2003 },
2004 location: None,
2005 },
2006 AddColumn {
2007 column_metadata: ColumnMetadata {
2008 column_schema: ColumnSchema::new(
2009 "field_2",
2010 ConcreteDataType::string_datatype(),
2011 true,
2012 ),
2013 semantic_type: SemanticType::Field,
2014 column_id: 6,
2015 },
2016 location: None,
2017 },
2018 ],
2019 };
2020 let request = RegionAlterRequest { kind };
2021 let mut metadata = new_metadata();
2022 metadata.schema_version = 1;
2023 request.validate(&metadata).unwrap();
2024 }
2025
2026 #[test]
2027 fn test_validate_create_region() {
2028 let column_metadatas = vec![
2029 ColumnMetadata {
2030 column_schema: ColumnSchema::new(
2031 "ts",
2032 ConcreteDataType::timestamp_millisecond_datatype(),
2033 false,
2034 ),
2035 semantic_type: SemanticType::Timestamp,
2036 column_id: 1,
2037 },
2038 ColumnMetadata {
2039 column_schema: ColumnSchema::new(
2040 "tag_0",
2041 ConcreteDataType::string_datatype(),
2042 true,
2043 ),
2044 semantic_type: SemanticType::Tag,
2045 column_id: 2,
2046 },
2047 ColumnMetadata {
2048 column_schema: ColumnSchema::new(
2049 "field_0",
2050 ConcreteDataType::string_datatype(),
2051 true,
2052 ),
2053 semantic_type: SemanticType::Field,
2054 column_id: 3,
2055 },
2056 ];
2057 let create = RegionCreateRequest {
2058 engine: "mito".to_string(),
2059 column_metadatas,
2060 primary_key: vec![3, 4],
2061 options: HashMap::new(),
2062 table_dir: "path".to_string(),
2063 path_type: PathType::Bare,
2064 partition_expr_json: Some("".to_string()),
2065 };
2066
2067 assert!(create.validate().is_err());
2068 }
2069
2070 #[test]
2071 fn test_validate_modify_column_fulltext_options() {
2072 let kind = AlterKind::SetIndexes {
2073 options: vec![SetIndexOption::Fulltext {
2074 column_name: "tag_0".to_string(),
2075 options: FulltextOptions::new_unchecked(
2076 true,
2077 FulltextAnalyzer::Chinese,
2078 false,
2079 FulltextBackend::Bloom,
2080 1000,
2081 0.01,
2082 ),
2083 }],
2084 };
2085 let request = RegionAlterRequest { kind };
2086 let mut metadata = new_metadata();
2087 metadata.schema_version = 1;
2088 request.validate(&metadata).unwrap();
2089
2090 let kind = AlterKind::UnsetIndexes {
2091 options: vec![UnsetIndexOption::Fulltext {
2092 column_name: "tag_0".to_string(),
2093 }],
2094 };
2095 let request = RegionAlterRequest { kind };
2096 let mut metadata = new_metadata();
2097 metadata.schema_version = 1;
2098 request.validate(&metadata).unwrap();
2099 }
2100
2101 #[test]
2102 fn test_validate_sync_columns() {
2103 let metadata = new_metadata();
2104 let kind = AlterKind::SyncColumns {
2105 column_metadatas: vec![
2106 ColumnMetadata {
2107 column_schema: ColumnSchema::new(
2108 "tag_1",
2109 ConcreteDataType::string_datatype(),
2110 true,
2111 ),
2112 semantic_type: SemanticType::Tag,
2113 column_id: 5,
2114 },
2115 ColumnMetadata {
2116 column_schema: ColumnSchema::new(
2117 "field_2",
2118 ConcreteDataType::string_datatype(),
2119 true,
2120 ),
2121 semantic_type: SemanticType::Field,
2122 column_id: 6,
2123 },
2124 ],
2125 };
2126 let err = kind.validate(&metadata).unwrap_err();
2127 assert!(err.to_string().contains("not a primary key"));
2128
2129 let mut column_metadatas_with_different_ts_column = metadata.column_metadatas.clone();
2131 let ts_column = column_metadatas_with_different_ts_column
2132 .iter_mut()
2133 .find(|c| c.semantic_type == SemanticType::Timestamp)
2134 .unwrap();
2135 ts_column.column_schema.name = "ts1".to_string();
2136
2137 let kind = AlterKind::SyncColumns {
2138 column_metadatas: column_metadatas_with_different_ts_column,
2139 };
2140 let err = kind.validate(&metadata).unwrap_err();
2141 assert!(
2142 err.to_string()
2143 .contains("timestamp column ts has different id")
2144 );
2145
2146 let mut column_metadatas_with_different_pk_column = metadata.column_metadatas.clone();
2148 let pk_column = column_metadatas_with_different_pk_column
2149 .iter_mut()
2150 .find(|c| c.column_schema.name == "tag_0")
2151 .unwrap();
2152 pk_column.column_id = 100;
2153 let kind = AlterKind::SyncColumns {
2154 column_metadatas: column_metadatas_with_different_pk_column,
2155 };
2156 let err = kind.validate(&metadata).unwrap_err();
2157 assert!(
2158 err.to_string()
2159 .contains("column with same name tag_0 has different id")
2160 );
2161
2162 let mut column_metadatas_with_new_field_column = metadata.column_metadatas.clone();
2164 column_metadatas_with_new_field_column.push(ColumnMetadata {
2165 column_schema: ColumnSchema::new("field_2", ConcreteDataType::string_datatype(), true),
2166 semantic_type: SemanticType::Field,
2167 column_id: 4,
2168 });
2169 let kind = AlterKind::SyncColumns {
2170 column_metadatas: column_metadatas_with_new_field_column,
2171 };
2172 kind.validate(&metadata).unwrap();
2173 }
2174
2175 #[test]
2176 fn test_cast_path_type_to_primitive() {
2177 assert_eq!(PathType::Bare as u8, 0);
2178 assert_eq!(PathType::Data as u8, 1);
2179 assert_eq!(PathType::Metadata as u8, 2);
2180 assert_eq!(
2181 PathType::try_from(PathType::Bare as u8).unwrap(),
2182 PathType::Bare
2183 );
2184 assert_eq!(
2185 PathType::try_from(PathType::Data as u8).unwrap(),
2186 PathType::Data
2187 );
2188 assert_eq!(
2189 PathType::try_from(PathType::Metadata as u8).unwrap(),
2190 PathType::Metadata
2191 );
2192 }
2193}