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 aligned_schema_version = request.aligned_schema_version.map(|v| v.schema_version);
413 let Some(Body::ArrowIpc(request)) = request.body else {
414 return Ok(vec![]);
415 };
416
417 let decoder_timer = metrics::CONVERT_REGION_BULK_REQUEST
418 .with_label_values(&["decode"])
419 .start_timer();
420 let mut decoder =
421 FlightDecoder::try_from_schema_bytes(&request.schema).context(FlightCodecSnafu)?;
422 let payload = decoder
423 .try_decode_record_batch(&request.data_header, &request.payload)
424 .context(FlightCodecSnafu)?;
425 decoder_timer.observe_duration();
426 Ok(vec![(
427 region_id,
428 RegionRequest::BulkInserts(RegionBulkInsertsRequest {
429 region_id,
430 payload,
431 raw_data: request,
432 partition_expr_version,
433 aligned_schema_version,
434 }),
435 )])
436}
437
438fn make_region_apply_staging_manifest(
439 api::v1::region::ApplyStagingManifestRequest {
440 region_id,
441 partition_expr,
442 central_region_id,
443 manifest_path,
444 }: api::v1::region::ApplyStagingManifestRequest,
445) -> Result<Vec<(RegionId, RegionRequest)>> {
446 let region_id = region_id.into();
447 Ok(vec![(
448 region_id,
449 RegionRequest::ApplyStagingManifest(ApplyStagingManifestRequest {
450 partition_expr,
451 central_region_id: central_region_id.into(),
452 manifest_path,
453 }),
454 )])
455}
456
457#[derive(Debug)]
459pub struct RegionPutRequest {
460 pub rows: Rows,
462 pub hint: Option<WriteHint>,
464 pub partition_expr_version: Option<u64>,
466}
467
468#[derive(Debug)]
469pub struct RegionReadRequest {
470 pub request: ScanRequest,
471}
472
473#[derive(Debug)]
475pub struct RegionDeleteRequest {
476 pub rows: Rows,
480 pub hint: Option<WriteHint>,
482 pub partition_expr_version: Option<u64>,
484}
485
486#[derive(Debug, Clone)]
487pub struct RegionCreateRequest {
488 pub engine: String,
490 pub column_metadatas: Vec<ColumnMetadata>,
492 pub primary_key: Vec<ColumnId>,
494 pub options: HashMap<String, String>,
496 pub table_dir: String,
498 pub path_type: PathType,
500 pub partition_expr_json: Option<String>,
503}
504
505impl RegionCreateRequest {
506 pub fn validate(&self) -> Result<()> {
508 ensure!(
510 self.column_metadatas
511 .iter()
512 .any(|x| x.semantic_type == SemanticType::Timestamp),
513 InvalidRegionRequestSnafu {
514 region_id: RegionId::new(0, 0),
515 err: "missing timestamp column in create region request".to_string(),
516 }
517 );
518
519 let mut column_id_to_indices = HashMap::with_capacity(self.column_metadatas.len());
521 for (i, c) in self.column_metadatas.iter().enumerate() {
522 if let Some(previous) = column_id_to_indices.insert(c.column_id, i) {
523 return InvalidRegionRequestSnafu {
524 region_id: RegionId::new(0, 0),
525 err: format!(
526 "duplicate column id {} (at position {} and {}) in create region request",
527 c.column_id, previous, i
528 ),
529 }
530 .fail();
531 }
532 }
533
534 for column_id in &self.primary_key {
536 ensure!(
537 column_id_to_indices.contains_key(column_id),
538 InvalidRegionRequestSnafu {
539 region_id: RegionId::new(0, 0),
540 err: format!(
541 "missing primary key column {} in create region request",
542 column_id
543 ),
544 }
545 );
546 }
547
548 Ok(())
549 }
550
551 pub fn is_physical_table(&self) -> bool {
553 self.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
554 }
555}
556
557#[derive(Debug, Clone)]
558pub struct RegionDropRequest {
559 pub fast_path: bool,
562
563 pub force: bool,
566
567 pub partial_drop: bool,
570}
571
572#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
574#[serde(default)]
575pub struct RegionRequirements {
576 pub object_storage: bool,
578}
579
580impl RegionRequirements {
581 pub fn empty() -> Self {
583 Self::default()
584 }
585
586 pub fn object_storage() -> Self {
588 Self {
589 object_storage: true,
590 }
591 }
592}
593
594#[derive(Debug, Clone)]
596pub struct RegionOpenRequest {
597 pub engine: String,
599 pub table_dir: String,
601 pub path_type: PathType,
603 pub options: HashMap<String, String>,
605 pub skip_wal_replay: bool,
607 pub checkpoint: Option<ReplayCheckpoint>,
609 pub requirements: RegionRequirements,
611}
612
613#[derive(Debug, Clone, Copy, PartialEq, Eq)]
614pub struct ReplayCheckpoint {
615 pub entry_id: u64,
616 pub metadata_entry_id: Option<u64>,
617}
618
619impl RegionOpenRequest {
620 pub fn is_physical_table(&self) -> bool {
622 self.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
623 }
624}
625
626#[derive(Debug)]
628pub struct RegionCloseRequest {}
629
630#[derive(Debug, PartialEq, Eq, Clone)]
632pub struct RegionAlterRequest {
633 pub kind: AlterKind,
635}
636
637impl RegionAlterRequest {
638 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
640 self.kind.validate(metadata)?;
641
642 Ok(())
643 }
644
645 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
649 debug_assert!(self.validate(metadata).is_ok());
650 self.kind.need_alter(metadata)
651 }
652}
653
654impl TryFrom<AlterRequest> for RegionAlterRequest {
655 type Error = MetadataError;
656
657 fn try_from(value: AlterRequest) -> Result<Self> {
658 let kind = value.kind.context(InvalidRawRegionRequestSnafu {
659 err: "missing kind in AlterRequest",
660 })?;
661
662 let kind = AlterKind::try_from(kind)?;
663 Ok(RegionAlterRequest { kind })
664 }
665}
666
667#[derive(Debug, PartialEq, Eq, Clone, AsRefStr)]
669pub enum AlterKind {
670 AddColumns {
672 columns: Vec<AddColumn>,
674 },
675 DropColumns {
677 names: Vec<String>,
679 },
680 ModifyColumnTypes {
682 columns: Vec<ModifyColumnType>,
684 },
685 SetRegionOptions { options: Vec<SetRegionOption> },
687 UnsetRegionOptions { keys: Vec<UnsetRegionOption> },
689 SetIndexes { options: Vec<SetIndexOption> },
691 UnsetIndexes { options: Vec<UnsetIndexOption> },
693 DropDefaults {
695 names: Vec<String>,
697 },
698 SetDefaults {
700 columns: Vec<SetDefault>,
702 },
703 SyncColumns {
705 column_metadatas: Vec<ColumnMetadata>,
706 },
707}
708#[derive(Debug, PartialEq, Eq, Clone)]
709pub struct SetDefault {
710 pub name: String,
711 pub default_constraint: Vec<u8>,
712}
713
714#[derive(Debug, PartialEq, Eq, Clone)]
715pub enum SetIndexOption {
716 Fulltext {
717 column_name: String,
718 options: FulltextOptions,
719 },
720 Inverted {
721 column_name: String,
722 },
723 Skipping {
724 column_name: String,
725 options: SkippingIndexOptions,
726 },
727}
728
729impl SetIndexOption {
730 pub fn column_name(&self) -> &String {
732 match self {
733 SetIndexOption::Fulltext { column_name, .. } => column_name,
734 SetIndexOption::Inverted { column_name } => column_name,
735 SetIndexOption::Skipping { column_name, .. } => column_name,
736 }
737 }
738
739 pub fn is_fulltext(&self) -> bool {
741 match self {
742 SetIndexOption::Fulltext { .. } => true,
743 SetIndexOption::Inverted { .. } => false,
744 SetIndexOption::Skipping { .. } => false,
745 }
746 }
747}
748
749impl TryFrom<v1::SetIndex> for SetIndexOption {
750 type Error = MetadataError;
751
752 fn try_from(value: v1::SetIndex) -> Result<Self> {
753 let option = value.options.context(InvalidRawRegionRequestSnafu {
754 err: "missing options in SetIndex",
755 })?;
756
757 let opt = match option {
758 v1::set_index::Options::Fulltext(x) => SetIndexOption::Fulltext {
759 column_name: x.column_name.clone(),
760 options: FulltextOptions::new(
761 x.enable,
762 as_fulltext_option_analyzer(
763 Analyzer::try_from(x.analyzer).context(DecodeProtoSnafu)?,
764 ),
765 x.case_sensitive,
766 as_fulltext_option_backend(
767 PbFulltextBackend::try_from(x.backend).context(DecodeProtoSnafu)?,
768 ),
769 x.granularity as u32,
770 x.false_positive_rate,
771 )
772 .context(InvalidIndexOptionSnafu)?,
773 },
774 v1::set_index::Options::Inverted(i) => SetIndexOption::Inverted {
775 column_name: i.column_name,
776 },
777 v1::set_index::Options::Skipping(s) => SetIndexOption::Skipping {
778 column_name: s.column_name,
779 options: SkippingIndexOptions::new(
780 s.granularity as u32,
781 s.false_positive_rate,
782 as_skipping_index_type(
783 PbSkippingIndexType::try_from(s.skipping_index_type)
784 .context(DecodeProtoSnafu)?,
785 ),
786 )
787 .context(InvalidIndexOptionSnafu)?,
788 },
789 };
790
791 Ok(opt)
792 }
793}
794
795#[derive(Debug, PartialEq, Eq, Clone)]
796pub enum UnsetIndexOption {
797 Fulltext { column_name: String },
798 Inverted { column_name: String },
799 Skipping { column_name: String },
800}
801
802impl UnsetIndexOption {
803 pub fn column_name(&self) -> &String {
804 match self {
805 UnsetIndexOption::Fulltext { column_name } => column_name,
806 UnsetIndexOption::Inverted { column_name } => column_name,
807 UnsetIndexOption::Skipping { column_name } => column_name,
808 }
809 }
810
811 pub fn is_fulltext(&self) -> bool {
812 match self {
813 UnsetIndexOption::Fulltext { .. } => true,
814 UnsetIndexOption::Inverted { .. } => false,
815 UnsetIndexOption::Skipping { .. } => false,
816 }
817 }
818}
819
820impl TryFrom<v1::UnsetIndex> for UnsetIndexOption {
821 type Error = MetadataError;
822
823 fn try_from(value: v1::UnsetIndex) -> Result<Self> {
824 let option = value.options.context(InvalidRawRegionRequestSnafu {
825 err: "missing options in UnsetIndex",
826 })?;
827
828 let opt = match option {
829 v1::unset_index::Options::Fulltext(f) => UnsetIndexOption::Fulltext {
830 column_name: f.column_name,
831 },
832 v1::unset_index::Options::Inverted(i) => UnsetIndexOption::Inverted {
833 column_name: i.column_name,
834 },
835 v1::unset_index::Options::Skipping(s) => UnsetIndexOption::Skipping {
836 column_name: s.column_name,
837 },
838 };
839
840 Ok(opt)
841 }
842}
843
844impl AlterKind {
845 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
849 match self {
850 AlterKind::AddColumns { columns } => {
851 for col_to_add in columns {
852 col_to_add.validate(metadata)?;
853 }
854 }
855 AlterKind::DropColumns { names } => {
856 for name in names {
857 Self::validate_column_to_drop(name, metadata)?;
858 }
859 }
860 AlterKind::ModifyColumnTypes { columns } => {
861 for col_to_change in columns {
862 col_to_change.validate(metadata)?;
863 }
864 }
865 AlterKind::SetRegionOptions { .. } => {}
866 AlterKind::UnsetRegionOptions { .. } => {}
867 AlterKind::SetIndexes { options } => {
868 for option in options {
869 Self::validate_column_alter_index_option(
870 option.column_name(),
871 metadata,
872 option.is_fulltext(),
873 )?;
874 }
875 }
876 AlterKind::UnsetIndexes { options } => {
877 for option in options {
878 Self::validate_column_alter_index_option(
879 option.column_name(),
880 metadata,
881 option.is_fulltext(),
882 )?;
883 }
884 }
885 AlterKind::DropDefaults { names } => {
886 names
887 .iter()
888 .try_for_each(|name| Self::validate_column_existence(name, metadata))?;
889 }
890 AlterKind::SetDefaults { columns } => {
891 columns
892 .iter()
893 .try_for_each(|col| Self::validate_column_existence(&col.name, metadata))?;
894 }
895 AlterKind::SyncColumns { column_metadatas } => {
896 let new_primary_keys = column_metadatas
897 .iter()
898 .filter(|c| c.semantic_type == SemanticType::Tag)
899 .map(|c| (c.column_schema.name.as_str(), c.column_id))
900 .collect::<HashMap<_, _>>();
901
902 let old_primary_keys = metadata
903 .column_metadatas
904 .iter()
905 .filter(|c| c.semantic_type == SemanticType::Tag)
906 .map(|c| (c.column_schema.name.as_str(), c.column_id));
907
908 for (name, id) in old_primary_keys {
909 let primary_key =
910 new_primary_keys
911 .get(name)
912 .with_context(|| InvalidRegionRequestSnafu {
913 region_id: metadata.region_id,
914 err: format!("column {} is not a primary key", name),
915 })?;
916
917 ensure!(
918 *primary_key == id,
919 InvalidRegionRequestSnafu {
920 region_id: metadata.region_id,
921 err: format!(
922 "column with same name {} has different id, existing: {}, got: {}",
923 name, id, primary_key
924 ),
925 }
926 );
927 }
928
929 let new_ts_column = column_metadatas
930 .iter()
931 .find(|c| c.semantic_type == SemanticType::Timestamp)
932 .map(|c| (c.column_schema.name.as_str(), c.column_id))
933 .context(InvalidRegionRequestSnafu {
934 region_id: metadata.region_id,
935 err: "timestamp column not found",
936 })?;
937
938 let old_ts_column = metadata
940 .column_metadatas
941 .iter()
942 .find(|c| c.semantic_type == SemanticType::Timestamp)
943 .map(|c| (c.column_schema.name.as_str(), c.column_id))
944 .unwrap();
945
946 ensure!(
947 new_ts_column == old_ts_column,
948 InvalidRegionRequestSnafu {
949 region_id: metadata.region_id,
950 err: format!(
951 "timestamp column {} has different id, existing: {}, got: {}",
952 old_ts_column.0, old_ts_column.1, new_ts_column.1
953 ),
954 }
955 );
956 }
957 }
958 Ok(())
959 }
960
961 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
963 debug_assert!(self.validate(metadata).is_ok());
964 match self {
965 AlterKind::AddColumns { columns } => columns
966 .iter()
967 .any(|col_to_add| col_to_add.need_alter(metadata)),
968 AlterKind::DropColumns { names } => names
969 .iter()
970 .any(|name| metadata.column_by_name(name).is_some()),
971 AlterKind::ModifyColumnTypes { columns } => columns
972 .iter()
973 .any(|col_to_change| col_to_change.need_alter(metadata)),
974 AlterKind::SetRegionOptions { .. } => true,
975 AlterKind::UnsetRegionOptions { .. } => true,
976 AlterKind::SetIndexes { options, .. } => options
977 .iter()
978 .any(|option| metadata.column_by_name(option.column_name()).is_some()),
979 AlterKind::UnsetIndexes { options } => options
980 .iter()
981 .any(|option| metadata.column_by_name(option.column_name()).is_some()),
982 AlterKind::DropDefaults { names } => names
983 .iter()
984 .any(|name| metadata.column_by_name(name).is_some()),
985
986 AlterKind::SetDefaults { columns } => columns
987 .iter()
988 .any(|x| metadata.column_by_name(&x.name).is_some()),
989 AlterKind::SyncColumns { column_metadatas } => {
990 metadata.column_metadatas != *column_metadatas
991 }
992 }
993 }
994
995 fn validate_column_to_drop(name: &str, metadata: &RegionMetadata) -> Result<()> {
997 let Some(column) = metadata.column_by_name(name) else {
998 return Ok(());
999 };
1000 ensure!(
1001 column.semantic_type == SemanticType::Field,
1002 InvalidRegionRequestSnafu {
1003 region_id: metadata.region_id,
1004 err: format!("column {} is not a field and could not be dropped", name),
1005 }
1006 );
1007 Ok(())
1008 }
1009
1010 fn validate_column_alter_index_option(
1012 column_name: &String,
1013 metadata: &RegionMetadata,
1014 is_fulltext: bool,
1015 ) -> Result<()> {
1016 let column = metadata
1017 .column_by_name(column_name)
1018 .context(InvalidRegionRequestSnafu {
1019 region_id: metadata.region_id,
1020 err: format!("column {} not found", column_name),
1021 })?;
1022
1023 if is_fulltext {
1024 ensure!(
1025 column.column_schema.data_type.is_string(),
1026 InvalidRegionRequestSnafu {
1027 region_id: metadata.region_id,
1028 err: format!(
1029 "cannot change alter index options for non-string column {}",
1030 column_name
1031 ),
1032 }
1033 );
1034 }
1035
1036 Ok(())
1037 }
1038
1039 fn validate_column_existence(column_name: &String, metadata: &RegionMetadata) -> Result<()> {
1041 metadata
1042 .column_by_name(column_name)
1043 .context(InvalidRegionRequestSnafu {
1044 region_id: metadata.region_id,
1045 err: format!("column {} not found", column_name),
1046 })?;
1047
1048 Ok(())
1049 }
1050}
1051
1052impl TryFrom<alter_request::Kind> for AlterKind {
1053 type Error = MetadataError;
1054
1055 fn try_from(kind: alter_request::Kind) -> Result<Self> {
1056 let alter_kind = match kind {
1057 alter_request::Kind::AddColumns(x) => {
1058 let columns = x
1059 .add_columns
1060 .into_iter()
1061 .map(|x| x.try_into())
1062 .collect::<Result<Vec<_>>>()?;
1063 AlterKind::AddColumns { columns }
1064 }
1065 alter_request::Kind::ModifyColumnTypes(x) => {
1066 let columns = x
1067 .modify_column_types
1068 .into_iter()
1069 .map(|x| x.into())
1070 .collect::<Vec<_>>();
1071 AlterKind::ModifyColumnTypes { columns }
1072 }
1073 alter_request::Kind::DropColumns(x) => {
1074 let names = x.drop_columns.into_iter().map(|x| x.name).collect();
1075 AlterKind::DropColumns { names }
1076 }
1077 alter_request::Kind::SetTableOptions(options) => AlterKind::SetRegionOptions {
1078 options: options
1079 .table_options
1080 .iter()
1081 .map(TryFrom::try_from)
1082 .collect::<Result<Vec<_>>>()?,
1083 },
1084 alter_request::Kind::UnsetTableOptions(options) => AlterKind::UnsetRegionOptions {
1085 keys: options
1086 .keys
1087 .iter()
1088 .map(|key| UnsetRegionOption::try_from(key.as_str()))
1089 .collect::<Result<Vec<_>>>()?,
1090 },
1091 alter_request::Kind::SetIndex(o) => AlterKind::SetIndexes {
1092 options: vec![SetIndexOption::try_from(o)?],
1093 },
1094 alter_request::Kind::UnsetIndex(o) => AlterKind::UnsetIndexes {
1095 options: vec![UnsetIndexOption::try_from(o)?],
1096 },
1097 alter_request::Kind::SetIndexes(o) => AlterKind::SetIndexes {
1098 options: o
1099 .set_indexes
1100 .into_iter()
1101 .map(SetIndexOption::try_from)
1102 .collect::<Result<Vec<_>>>()?,
1103 },
1104 alter_request::Kind::UnsetIndexes(o) => AlterKind::UnsetIndexes {
1105 options: o
1106 .unset_indexes
1107 .into_iter()
1108 .map(UnsetIndexOption::try_from)
1109 .collect::<Result<Vec<_>>>()?,
1110 },
1111 alter_request::Kind::DropDefaults(x) => AlterKind::DropDefaults {
1112 names: x.drop_defaults.into_iter().map(|x| x.column_name).collect(),
1113 },
1114 alter_request::Kind::SetDefaults(x) => AlterKind::SetDefaults {
1115 columns: x
1116 .set_defaults
1117 .into_iter()
1118 .map(|x| {
1119 Ok(SetDefault {
1120 name: x.column_name,
1121 default_constraint: x.default_constraint.clone(),
1122 })
1123 })
1124 .collect::<Result<Vec<_>>>()?,
1125 },
1126 alter_request::Kind::SyncColumns(x) => AlterKind::SyncColumns {
1127 column_metadatas: x
1128 .column_defs
1129 .into_iter()
1130 .map(ColumnMetadata::try_from_column_def)
1131 .collect::<Result<Vec<_>>>()?,
1132 },
1133 };
1134
1135 Ok(alter_kind)
1136 }
1137}
1138
1139#[derive(Debug, PartialEq, Eq, Clone)]
1141pub struct AddColumn {
1142 pub column_metadata: ColumnMetadata,
1144 pub location: Option<AddColumnLocation>,
1147}
1148
1149impl AddColumn {
1150 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
1155 ensure!(
1156 self.column_metadata.column_schema.is_nullable()
1157 || self
1158 .column_metadata
1159 .column_schema
1160 .default_constraint()
1161 .is_some(),
1162 InvalidRegionRequestSnafu {
1163 region_id: metadata.region_id,
1164 err: format!(
1165 "no default value for column {}",
1166 self.column_metadata.column_schema.name
1167 ),
1168 }
1169 );
1170
1171 if let Some(existing_column) =
1172 metadata.column_by_name(&self.column_metadata.column_schema.name)
1173 {
1174 ensure!(
1176 *existing_column == self.column_metadata,
1177 InvalidRegionRequestSnafu {
1178 region_id: metadata.region_id,
1179 err: format!(
1180 "column {} already exists with different metadata, existing: {:?}, got: {:?}",
1181 self.column_metadata.column_schema.name,
1182 existing_column,
1183 self.column_metadata,
1184 ),
1185 }
1186 );
1187 ensure!(
1188 self.location.is_none(),
1189 InvalidRegionRequestSnafu {
1190 region_id: metadata.region_id,
1191 err: format!(
1192 "column {} already exists, but location is specified",
1193 self.column_metadata.column_schema.name
1194 ),
1195 }
1196 );
1197 }
1198
1199 if let Some(existing_column) = metadata.column_by_id(self.column_metadata.column_id) {
1200 ensure!(
1202 existing_column.column_schema.name == self.column_metadata.column_schema.name,
1203 InvalidRegionRequestSnafu {
1204 region_id: metadata.region_id,
1205 err: format!(
1206 "column id {} already exists with different name {}",
1207 self.column_metadata.column_id, existing_column.column_schema.name
1208 ),
1209 }
1210 );
1211 }
1212
1213 Ok(())
1214 }
1215
1216 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
1218 debug_assert!(self.validate(metadata).is_ok());
1219 metadata
1220 .column_by_name(&self.column_metadata.column_schema.name)
1221 .is_none()
1222 }
1223}
1224
1225impl TryFrom<v1::region::AddColumn> for AddColumn {
1226 type Error = MetadataError;
1227
1228 fn try_from(add_column: v1::region::AddColumn) -> Result<Self> {
1229 let column_def = add_column
1230 .column_def
1231 .context(InvalidRawRegionRequestSnafu {
1232 err: "missing column_def in AddColumn",
1233 })?;
1234
1235 let column_metadata = ColumnMetadata::try_from_column_def(column_def)?;
1236 let location = add_column
1237 .location
1238 .map(AddColumnLocation::try_from)
1239 .transpose()?;
1240
1241 Ok(AddColumn {
1242 column_metadata,
1243 location,
1244 })
1245 }
1246}
1247
1248#[derive(Debug, PartialEq, Eq, Clone)]
1250pub enum AddColumnLocation {
1251 First,
1253 After {
1255 column_name: String,
1257 },
1258}
1259
1260impl TryFrom<v1::AddColumnLocation> for AddColumnLocation {
1261 type Error = MetadataError;
1262
1263 fn try_from(location: v1::AddColumnLocation) -> Result<Self> {
1264 let location_type = LocationType::try_from(location.location_type)
1265 .map_err(|e| InvalidRawRegionRequestSnafu { err: e.to_string() }.build())?;
1266 let add_column_location = match location_type {
1267 LocationType::First => AddColumnLocation::First,
1268 LocationType::After => AddColumnLocation::After {
1269 column_name: location.after_column_name,
1270 },
1271 };
1272
1273 Ok(add_column_location)
1274 }
1275}
1276
1277#[derive(Debug, PartialEq, Eq, Clone)]
1279pub struct ModifyColumnType {
1280 pub column_name: String,
1282 pub target_type: ConcreteDataType,
1284}
1285
1286impl ModifyColumnType {
1287 pub fn validate(&self, metadata: &RegionMetadata) -> Result<()> {
1289 let column_meta = metadata
1290 .column_by_name(&self.column_name)
1291 .with_context(|| InvalidRegionRequestSnafu {
1292 region_id: metadata.region_id,
1293 err: format!("column {} not found", self.column_name),
1294 })?;
1295
1296 ensure!(
1297 matches!(column_meta.semantic_type, SemanticType::Field),
1298 InvalidRegionRequestSnafu {
1299 region_id: metadata.region_id,
1300 err: "'timestamp' or 'tag' column cannot change type".to_string()
1301 }
1302 );
1303 ensure!(
1304 column_meta
1305 .column_schema
1306 .data_type
1307 .can_arrow_type_cast_to(&self.target_type),
1308 InvalidRegionRequestSnafu {
1309 region_id: metadata.region_id,
1310 err: format!(
1311 "column '{}' cannot be cast automatically to type '{}'",
1312 self.column_name, self.target_type
1313 ),
1314 }
1315 );
1316
1317 Ok(())
1318 }
1319
1320 pub fn need_alter(&self, metadata: &RegionMetadata) -> bool {
1322 debug_assert!(self.validate(metadata).is_ok());
1323 metadata.column_by_name(&self.column_name).is_some()
1324 }
1325}
1326
1327impl From<v1::ModifyColumnType> for ModifyColumnType {
1328 fn from(modify_column_type: v1::ModifyColumnType) -> Self {
1329 let target_type = ColumnDataTypeWrapper::new(
1330 modify_column_type.target_type(),
1331 modify_column_type.target_type_extension,
1332 )
1333 .into();
1334
1335 ModifyColumnType {
1336 column_name: modify_column_type.column_name,
1337 target_type,
1338 }
1339 }
1340}
1341
1342#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
1343pub enum SetRegionOption {
1344 Ttl(Option<TimeToLive>),
1345 Twsc(String, String),
1347 Format(String),
1349 AppendMode(bool),
1351}
1352
1353impl TryFrom<&PbOption> for SetRegionOption {
1354 type Error = MetadataError;
1355
1356 fn try_from(value: &PbOption) -> std::result::Result<Self, Self::Error> {
1357 let PbOption { key, value } = value;
1358 match key.as_str() {
1359 TTL_KEY => {
1360 let ttl = TimeToLive::from_humantime_or_str(value)
1361 .map_err(|_| InvalidSetRegionOptionRequestSnafu { key, value }.build())?;
1362
1363 Ok(Self::Ttl(Some(ttl)))
1364 }
1365 TWCS_TRIGGER_FILE_NUM | TWCS_MAX_OUTPUT_FILE_SIZE | TWCS_TIME_WINDOW => {
1366 Ok(Self::Twsc(key.clone(), value.clone()))
1367 }
1368 SST_FORMAT_KEY => Ok(Self::Format(value.clone())),
1369 APPEND_MODE_KEY => {
1370 let append_mode = value
1371 .parse::<bool>()
1372 .map_err(|_| InvalidSetRegionOptionRequestSnafu { key, value }.build())?;
1373 Ok(Self::AppendMode(append_mode))
1374 }
1375 _ => InvalidSetRegionOptionRequestSnafu { key, value }.fail(),
1376 }
1377 }
1378}
1379
1380impl From<&UnsetRegionOption> for SetRegionOption {
1381 fn from(unset_option: &UnsetRegionOption) -> Self {
1382 match unset_option {
1383 UnsetRegionOption::TwcsTriggerFileNum => {
1384 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1385 }
1386 UnsetRegionOption::TwcsMaxOutputFileSize => {
1387 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1388 }
1389 UnsetRegionOption::TwcsTimeWindow => {
1390 SetRegionOption::Twsc(unset_option.to_string(), String::new())
1391 }
1392 UnsetRegionOption::Ttl => SetRegionOption::Ttl(Default::default()),
1393 }
1394 }
1395}
1396
1397impl TryFrom<&str> for UnsetRegionOption {
1398 type Error = MetadataError;
1399
1400 fn try_from(key: &str) -> Result<Self> {
1401 match key.to_ascii_lowercase().as_str() {
1402 TTL_KEY => Ok(Self::Ttl),
1403 TWCS_TRIGGER_FILE_NUM => Ok(Self::TwcsTriggerFileNum),
1404 TWCS_MAX_OUTPUT_FILE_SIZE => Ok(Self::TwcsMaxOutputFileSize),
1405 TWCS_TIME_WINDOW => Ok(Self::TwcsTimeWindow),
1406 _ => InvalidUnsetRegionOptionRequestSnafu { key }.fail(),
1407 }
1408 }
1409}
1410
1411#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
1412pub enum UnsetRegionOption {
1413 TwcsTriggerFileNum,
1414 TwcsMaxOutputFileSize,
1415 TwcsTimeWindow,
1416 Ttl,
1417}
1418
1419impl UnsetRegionOption {
1420 pub fn as_str(&self) -> &str {
1421 match self {
1422 Self::Ttl => TTL_KEY,
1423 Self::TwcsTriggerFileNum => TWCS_TRIGGER_FILE_NUM,
1424 Self::TwcsMaxOutputFileSize => TWCS_MAX_OUTPUT_FILE_SIZE,
1425 Self::TwcsTimeWindow => TWCS_TIME_WINDOW,
1426 }
1427 }
1428}
1429
1430impl Display for UnsetRegionOption {
1431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1432 write!(f, "{}", self.as_str())
1433 }
1434}
1435
1436#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1437pub enum RegionFlushReason {
1438 RegionMigration,
1440 Repartition,
1442 RemoteWalPrune,
1444 Closing,
1446 Downgrading,
1448}
1449
1450#[derive(Debug, Clone, Default)]
1451pub struct RegionFlushRequest {
1452 pub row_group_size: Option<usize>,
1453 pub reason: Option<RegionFlushReason>,
1454}
1455
1456#[derive(Debug)]
1457pub struct RegionCompactRequest {
1458 pub options: compact_request::Options,
1459 pub parallelism: Option<u32>,
1460}
1461
1462impl Default for RegionCompactRequest {
1463 fn default() -> Self {
1464 Self {
1465 options: compact_request::Options::Regular(Default::default()),
1467 parallelism: None,
1468 }
1469 }
1470}
1471
1472#[derive(Debug, Clone, Default)]
1473pub struct RegionBuildIndexRequest {}
1474
1475#[derive(Debug)]
1477pub enum RegionTruncateRequest {
1478 All,
1480 ByTimeRanges {
1481 time_ranges: Vec<(Timestamp, Timestamp)>,
1485 },
1486}
1487
1488#[derive(Debug, Clone, Copy, Default)]
1493pub struct RegionCatchupRequest {
1494 pub set_writable: bool,
1496 pub entry_id: Option<entry::Id>,
1499 pub metadata_entry_id: Option<entry::Id>,
1503 pub location_id: Option<u64>,
1505 pub checkpoint: Option<ReplayCheckpoint>,
1507}
1508
1509#[derive(Debug, Clone)]
1510pub struct RegionBulkInsertsRequest {
1511 pub region_id: RegionId,
1512 pub payload: DfRecordBatch,
1513 pub raw_data: ArrowIpc,
1514 pub partition_expr_version: Option<u64>,
1515 pub aligned_schema_version: Option<u64>,
1516}
1517
1518impl RegionBulkInsertsRequest {
1519 pub fn estimated_size(&self) -> usize {
1520 self.payload.get_array_memory_size()
1521 }
1522}
1523
1524#[derive(Debug, Clone, PartialEq, Eq)]
1530pub enum StagingPartitionDirective {
1531 UpdatePartitionExpr(String),
1532 RejectAllWrites,
1533}
1534
1535impl StagingPartitionDirective {
1536 pub fn partition_expr(&self) -> Option<&str> {
1538 match self {
1539 Self::UpdatePartitionExpr(expr) => Some(expr),
1540 Self::RejectAllWrites => None,
1541 }
1542 }
1543}
1544
1545#[derive(Debug, Clone)]
1546pub struct EnterStagingRequest {
1547 pub partition_directive: StagingPartitionDirective,
1549}
1550
1551impl EnterStagingRequest {
1552 pub fn with_partition_expr(partition_expr: String) -> Self {
1554 Self {
1555 partition_directive: StagingPartitionDirective::UpdatePartitionExpr(partition_expr),
1556 }
1557 }
1558}
1559
1560#[derive(Debug, Clone)]
1579pub struct ApplyStagingManifestRequest {
1580 pub partition_expr: String,
1582 pub central_region_id: RegionId,
1584 pub manifest_path: String,
1587}
1588
1589impl fmt::Display for RegionRequest {
1590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1591 match self {
1592 RegionRequest::Put(_) => write!(f, "Put"),
1593 RegionRequest::Delete(_) => write!(f, "Delete"),
1594 RegionRequest::Create(_) => write!(f, "Create"),
1595 RegionRequest::Drop(_) => write!(f, "Drop"),
1596 RegionRequest::Open(_) => write!(f, "Open"),
1597 RegionRequest::Close(_) => write!(f, "Close"),
1598 RegionRequest::Alter(_) => write!(f, "Alter"),
1599 RegionRequest::Flush(_) => write!(f, "Flush"),
1600 RegionRequest::Compact(_) => write!(f, "Compact"),
1601 RegionRequest::BuildIndex(_) => write!(f, "BuildIndex"),
1602 RegionRequest::Truncate(_) => write!(f, "Truncate"),
1603 RegionRequest::Catchup(_) => write!(f, "Catchup"),
1604 RegionRequest::BulkInserts(_) => write!(f, "BulkInserts"),
1605 RegionRequest::EnterStaging(_) => write!(f, "EnterStaging"),
1606 RegionRequest::ApplyStagingManifest(_) => write!(f, "ApplyStagingManifest"),
1607 }
1608 }
1609}
1610
1611#[cfg(test)]
1612mod tests {
1613
1614 use api::v1::region::RegionColumnDef;
1615 use api::v1::{ColumnDataType, ColumnDef};
1616 use datatypes::prelude::ConcreteDataType;
1617 use datatypes::schema::{ColumnSchema, FulltextAnalyzer, FulltextBackend};
1618
1619 use super::*;
1620 use crate::metadata::RegionMetadataBuilder;
1621
1622 #[test]
1623 fn test_from_proto_location() {
1624 let proto_location = v1::AddColumnLocation {
1625 location_type: LocationType::First as i32,
1626 after_column_name: String::default(),
1627 };
1628 let location = AddColumnLocation::try_from(proto_location).unwrap();
1629 assert_eq!(location, AddColumnLocation::First);
1630
1631 let proto_location = v1::AddColumnLocation {
1632 location_type: 10,
1633 after_column_name: String::default(),
1634 };
1635 AddColumnLocation::try_from(proto_location).unwrap_err();
1636
1637 let proto_location = v1::AddColumnLocation {
1638 location_type: LocationType::After as i32,
1639 after_column_name: "a".to_string(),
1640 };
1641 let location = AddColumnLocation::try_from(proto_location).unwrap();
1642 assert_eq!(
1643 location,
1644 AddColumnLocation::After {
1645 column_name: "a".to_string()
1646 }
1647 );
1648 }
1649
1650 #[test]
1651 fn test_from_none_proto_add_column() {
1652 AddColumn::try_from(v1::region::AddColumn {
1653 column_def: None,
1654 location: None,
1655 })
1656 .unwrap_err();
1657 }
1658
1659 #[test]
1660 fn test_from_proto_alter_request() {
1661 RegionAlterRequest::try_from(AlterRequest {
1662 region_id: 0,
1663 schema_version: 1,
1664 kind: None,
1665 })
1666 .unwrap_err();
1667
1668 let request = RegionAlterRequest::try_from(AlterRequest {
1669 region_id: 0,
1670 schema_version: 1,
1671 kind: Some(alter_request::Kind::AddColumns(v1::region::AddColumns {
1672 add_columns: vec![v1::region::AddColumn {
1673 column_def: Some(RegionColumnDef {
1674 column_def: Some(ColumnDef {
1675 name: "a".to_string(),
1676 data_type: ColumnDataType::String as i32,
1677 is_nullable: true,
1678 default_constraint: vec![],
1679 semantic_type: SemanticType::Field as i32,
1680 comment: String::new(),
1681 ..Default::default()
1682 }),
1683 column_id: 1,
1684 }),
1685 location: Some(v1::AddColumnLocation {
1686 location_type: LocationType::First as i32,
1687 after_column_name: String::default(),
1688 }),
1689 }],
1690 })),
1691 })
1692 .unwrap();
1693
1694 assert_eq!(
1695 request,
1696 RegionAlterRequest {
1697 kind: AlterKind::AddColumns {
1698 columns: vec![AddColumn {
1699 column_metadata: ColumnMetadata {
1700 column_schema: ColumnSchema::new(
1701 "a",
1702 ConcreteDataType::string_datatype(),
1703 true,
1704 ),
1705 semantic_type: SemanticType::Field,
1706 column_id: 1,
1707 },
1708 location: Some(AddColumnLocation::First),
1709 }]
1710 },
1711 }
1712 );
1713 }
1714
1715 fn new_metadata() -> RegionMetadata {
1718 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1719 builder
1720 .push_column_metadata(ColumnMetadata {
1721 column_schema: ColumnSchema::new(
1722 "ts",
1723 ConcreteDataType::timestamp_millisecond_datatype(),
1724 false,
1725 ),
1726 semantic_type: SemanticType::Timestamp,
1727 column_id: 1,
1728 })
1729 .push_column_metadata(ColumnMetadata {
1730 column_schema: ColumnSchema::new(
1731 "tag_0",
1732 ConcreteDataType::string_datatype(),
1733 true,
1734 ),
1735 semantic_type: SemanticType::Tag,
1736 column_id: 2,
1737 })
1738 .push_column_metadata(ColumnMetadata {
1739 column_schema: ColumnSchema::new(
1740 "field_0",
1741 ConcreteDataType::string_datatype(),
1742 true,
1743 ),
1744 semantic_type: SemanticType::Field,
1745 column_id: 3,
1746 })
1747 .push_column_metadata(ColumnMetadata {
1748 column_schema: ColumnSchema::new(
1749 "field_1",
1750 ConcreteDataType::boolean_datatype(),
1751 true,
1752 ),
1753 semantic_type: SemanticType::Field,
1754 column_id: 4,
1755 })
1756 .primary_key(vec![2]);
1757 builder.build().unwrap()
1758 }
1759
1760 #[test]
1761 fn test_add_column_validate() {
1762 let metadata = new_metadata();
1763 let add_column = AddColumn {
1764 column_metadata: ColumnMetadata {
1765 column_schema: ColumnSchema::new(
1766 "tag_1",
1767 ConcreteDataType::string_datatype(),
1768 true,
1769 ),
1770 semantic_type: SemanticType::Tag,
1771 column_id: 5,
1772 },
1773 location: None,
1774 };
1775 add_column.validate(&metadata).unwrap();
1776 assert!(add_column.need_alter(&metadata));
1777
1778 AddColumn {
1780 column_metadata: ColumnMetadata {
1781 column_schema: ColumnSchema::new(
1782 "tag_1",
1783 ConcreteDataType::string_datatype(),
1784 false,
1785 ),
1786 semantic_type: SemanticType::Tag,
1787 column_id: 5,
1788 },
1789 location: None,
1790 }
1791 .validate(&metadata)
1792 .unwrap_err();
1793
1794 let add_column = AddColumn {
1796 column_metadata: ColumnMetadata {
1797 column_schema: ColumnSchema::new(
1798 "tag_0",
1799 ConcreteDataType::string_datatype(),
1800 true,
1801 ),
1802 semantic_type: SemanticType::Tag,
1803 column_id: 2,
1804 },
1805 location: None,
1806 };
1807 add_column.validate(&metadata).unwrap();
1808 assert!(!add_column.need_alter(&metadata));
1809 }
1810
1811 #[test]
1812 fn test_add_duplicate_columns() {
1813 let kind = AlterKind::AddColumns {
1814 columns: vec![
1815 AddColumn {
1816 column_metadata: ColumnMetadata {
1817 column_schema: ColumnSchema::new(
1818 "tag_1",
1819 ConcreteDataType::string_datatype(),
1820 true,
1821 ),
1822 semantic_type: SemanticType::Tag,
1823 column_id: 5,
1824 },
1825 location: None,
1826 },
1827 AddColumn {
1828 column_metadata: ColumnMetadata {
1829 column_schema: ColumnSchema::new(
1830 "tag_1",
1831 ConcreteDataType::string_datatype(),
1832 true,
1833 ),
1834 semantic_type: SemanticType::Field,
1835 column_id: 6,
1836 },
1837 location: None,
1838 },
1839 ],
1840 };
1841 let metadata = new_metadata();
1842 kind.validate(&metadata).unwrap();
1843 assert!(kind.need_alter(&metadata));
1844 }
1845
1846 #[test]
1847 fn test_add_existing_column_different_metadata() {
1848 let metadata = new_metadata();
1849
1850 let kind = AlterKind::AddColumns {
1852 columns: vec![AddColumn {
1853 column_metadata: ColumnMetadata {
1854 column_schema: ColumnSchema::new(
1855 "tag_0",
1856 ConcreteDataType::string_datatype(),
1857 true,
1858 ),
1859 semantic_type: SemanticType::Tag,
1860 column_id: 4,
1861 },
1862 location: None,
1863 }],
1864 };
1865 kind.validate(&metadata).unwrap_err();
1866
1867 let kind = AlterKind::AddColumns {
1869 columns: vec![AddColumn {
1870 column_metadata: ColumnMetadata {
1871 column_schema: ColumnSchema::new(
1872 "tag_0",
1873 ConcreteDataType::int64_datatype(),
1874 true,
1875 ),
1876 semantic_type: SemanticType::Tag,
1877 column_id: 2,
1878 },
1879 location: None,
1880 }],
1881 };
1882 kind.validate(&metadata).unwrap_err();
1883
1884 let kind = AlterKind::AddColumns {
1886 columns: vec![AddColumn {
1887 column_metadata: ColumnMetadata {
1888 column_schema: ColumnSchema::new(
1889 "tag_1",
1890 ConcreteDataType::string_datatype(),
1891 true,
1892 ),
1893 semantic_type: SemanticType::Tag,
1894 column_id: 2,
1895 },
1896 location: None,
1897 }],
1898 };
1899 kind.validate(&metadata).unwrap_err();
1900 }
1901
1902 #[test]
1903 fn test_add_existing_column_with_location() {
1904 let metadata = new_metadata();
1905 let kind = AlterKind::AddColumns {
1906 columns: vec![AddColumn {
1907 column_metadata: ColumnMetadata {
1908 column_schema: ColumnSchema::new(
1909 "tag_0",
1910 ConcreteDataType::string_datatype(),
1911 true,
1912 ),
1913 semantic_type: SemanticType::Tag,
1914 column_id: 2,
1915 },
1916 location: Some(AddColumnLocation::First),
1917 }],
1918 };
1919 kind.validate(&metadata).unwrap_err();
1920 }
1921
1922 #[test]
1923 fn test_validate_drop_column() {
1924 let metadata = new_metadata();
1925 let kind = AlterKind::DropColumns {
1926 names: vec!["xxxx".to_string()],
1927 };
1928 kind.validate(&metadata).unwrap();
1929 assert!(!kind.need_alter(&metadata));
1930
1931 AlterKind::DropColumns {
1932 names: vec!["tag_0".to_string()],
1933 }
1934 .validate(&metadata)
1935 .unwrap_err();
1936
1937 let kind = AlterKind::DropColumns {
1938 names: vec!["field_0".to_string()],
1939 };
1940 kind.validate(&metadata).unwrap();
1941 assert!(kind.need_alter(&metadata));
1942 }
1943
1944 #[test]
1945 fn test_validate_modify_column_type() {
1946 let metadata = new_metadata();
1947 AlterKind::ModifyColumnTypes {
1948 columns: vec![ModifyColumnType {
1949 column_name: "xxxx".to_string(),
1950 target_type: ConcreteDataType::string_datatype(),
1951 }],
1952 }
1953 .validate(&metadata)
1954 .unwrap_err();
1955
1956 AlterKind::ModifyColumnTypes {
1957 columns: vec![ModifyColumnType {
1958 column_name: "field_1".to_string(),
1959 target_type: ConcreteDataType::date_datatype(),
1960 }],
1961 }
1962 .validate(&metadata)
1963 .unwrap_err();
1964
1965 AlterKind::ModifyColumnTypes {
1966 columns: vec![ModifyColumnType {
1967 column_name: "ts".to_string(),
1968 target_type: ConcreteDataType::date_datatype(),
1969 }],
1970 }
1971 .validate(&metadata)
1972 .unwrap_err();
1973
1974 AlterKind::ModifyColumnTypes {
1975 columns: vec![ModifyColumnType {
1976 column_name: "tag_0".to_string(),
1977 target_type: ConcreteDataType::date_datatype(),
1978 }],
1979 }
1980 .validate(&metadata)
1981 .unwrap_err();
1982
1983 let kind = AlterKind::ModifyColumnTypes {
1984 columns: vec![ModifyColumnType {
1985 column_name: "field_0".to_string(),
1986 target_type: ConcreteDataType::int32_datatype(),
1987 }],
1988 };
1989 kind.validate(&metadata).unwrap();
1990 assert!(kind.need_alter(&metadata));
1991 }
1992
1993 #[test]
1994 fn test_validate_add_columns() {
1995 let kind = AlterKind::AddColumns {
1996 columns: vec![
1997 AddColumn {
1998 column_metadata: ColumnMetadata {
1999 column_schema: ColumnSchema::new(
2000 "tag_1",
2001 ConcreteDataType::string_datatype(),
2002 true,
2003 ),
2004 semantic_type: SemanticType::Tag,
2005 column_id: 5,
2006 },
2007 location: None,
2008 },
2009 AddColumn {
2010 column_metadata: ColumnMetadata {
2011 column_schema: ColumnSchema::new(
2012 "field_2",
2013 ConcreteDataType::string_datatype(),
2014 true,
2015 ),
2016 semantic_type: SemanticType::Field,
2017 column_id: 6,
2018 },
2019 location: None,
2020 },
2021 ],
2022 };
2023 let request = RegionAlterRequest { kind };
2024 let mut metadata = new_metadata();
2025 metadata.schema_version = 1;
2026 request.validate(&metadata).unwrap();
2027 }
2028
2029 #[test]
2030 fn test_validate_create_region() {
2031 let column_metadatas = vec![
2032 ColumnMetadata {
2033 column_schema: ColumnSchema::new(
2034 "ts",
2035 ConcreteDataType::timestamp_millisecond_datatype(),
2036 false,
2037 ),
2038 semantic_type: SemanticType::Timestamp,
2039 column_id: 1,
2040 },
2041 ColumnMetadata {
2042 column_schema: ColumnSchema::new(
2043 "tag_0",
2044 ConcreteDataType::string_datatype(),
2045 true,
2046 ),
2047 semantic_type: SemanticType::Tag,
2048 column_id: 2,
2049 },
2050 ColumnMetadata {
2051 column_schema: ColumnSchema::new(
2052 "field_0",
2053 ConcreteDataType::string_datatype(),
2054 true,
2055 ),
2056 semantic_type: SemanticType::Field,
2057 column_id: 3,
2058 },
2059 ];
2060 let create = RegionCreateRequest {
2061 engine: "mito".to_string(),
2062 column_metadatas,
2063 primary_key: vec![3, 4],
2064 options: HashMap::new(),
2065 table_dir: "path".to_string(),
2066 path_type: PathType::Bare,
2067 partition_expr_json: Some("".to_string()),
2068 };
2069
2070 assert!(create.validate().is_err());
2071 }
2072
2073 #[test]
2074 fn test_validate_modify_column_fulltext_options() {
2075 let kind = AlterKind::SetIndexes {
2076 options: vec![SetIndexOption::Fulltext {
2077 column_name: "tag_0".to_string(),
2078 options: FulltextOptions::new_unchecked(
2079 true,
2080 FulltextAnalyzer::Chinese,
2081 false,
2082 FulltextBackend::Bloom,
2083 1000,
2084 0.01,
2085 ),
2086 }],
2087 };
2088 let request = RegionAlterRequest { kind };
2089 let mut metadata = new_metadata();
2090 metadata.schema_version = 1;
2091 request.validate(&metadata).unwrap();
2092
2093 let kind = AlterKind::UnsetIndexes {
2094 options: vec![UnsetIndexOption::Fulltext {
2095 column_name: "tag_0".to_string(),
2096 }],
2097 };
2098 let request = RegionAlterRequest { kind };
2099 let mut metadata = new_metadata();
2100 metadata.schema_version = 1;
2101 request.validate(&metadata).unwrap();
2102 }
2103
2104 #[test]
2105 fn test_validate_sync_columns() {
2106 let metadata = new_metadata();
2107 let kind = AlterKind::SyncColumns {
2108 column_metadatas: vec![
2109 ColumnMetadata {
2110 column_schema: ColumnSchema::new(
2111 "tag_1",
2112 ConcreteDataType::string_datatype(),
2113 true,
2114 ),
2115 semantic_type: SemanticType::Tag,
2116 column_id: 5,
2117 },
2118 ColumnMetadata {
2119 column_schema: ColumnSchema::new(
2120 "field_2",
2121 ConcreteDataType::string_datatype(),
2122 true,
2123 ),
2124 semantic_type: SemanticType::Field,
2125 column_id: 6,
2126 },
2127 ],
2128 };
2129 let err = kind.validate(&metadata).unwrap_err();
2130 assert!(err.to_string().contains("not a primary key"));
2131
2132 let mut column_metadatas_with_different_ts_column = metadata.column_metadatas.clone();
2134 let ts_column = column_metadatas_with_different_ts_column
2135 .iter_mut()
2136 .find(|c| c.semantic_type == SemanticType::Timestamp)
2137 .unwrap();
2138 ts_column.column_schema.name = "ts1".to_string();
2139
2140 let kind = AlterKind::SyncColumns {
2141 column_metadatas: column_metadatas_with_different_ts_column,
2142 };
2143 let err = kind.validate(&metadata).unwrap_err();
2144 assert!(
2145 err.to_string()
2146 .contains("timestamp column ts has different id")
2147 );
2148
2149 let mut column_metadatas_with_different_pk_column = metadata.column_metadatas.clone();
2151 let pk_column = column_metadatas_with_different_pk_column
2152 .iter_mut()
2153 .find(|c| c.column_schema.name == "tag_0")
2154 .unwrap();
2155 pk_column.column_id = 100;
2156 let kind = AlterKind::SyncColumns {
2157 column_metadatas: column_metadatas_with_different_pk_column,
2158 };
2159 let err = kind.validate(&metadata).unwrap_err();
2160 assert!(
2161 err.to_string()
2162 .contains("column with same name tag_0 has different id")
2163 );
2164
2165 let mut column_metadatas_with_new_field_column = metadata.column_metadatas.clone();
2167 column_metadatas_with_new_field_column.push(ColumnMetadata {
2168 column_schema: ColumnSchema::new("field_2", ConcreteDataType::string_datatype(), true),
2169 semantic_type: SemanticType::Field,
2170 column_id: 4,
2171 });
2172 let kind = AlterKind::SyncColumns {
2173 column_metadatas: column_metadatas_with_new_field_column,
2174 };
2175 kind.validate(&metadata).unwrap();
2176 }
2177
2178 #[test]
2179 fn test_cast_path_type_to_primitive() {
2180 assert_eq!(PathType::Bare as u8, 0);
2181 assert_eq!(PathType::Data as u8, 1);
2182 assert_eq!(PathType::Metadata as u8, 2);
2183 assert_eq!(
2184 PathType::try_from(PathType::Bare as u8).unwrap(),
2185 PathType::Bare
2186 );
2187 assert_eq!(
2188 PathType::try_from(PathType::Data as u8).unwrap(),
2189 PathType::Data
2190 );
2191 assert_eq!(
2192 PathType::try_from(PathType::Metadata as u8).unwrap(),
2193 PathType::Metadata
2194 );
2195 }
2196}