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