1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use chrono::{DateTime, Utc};
19use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
20use common_macro::ToMetaBuilder;
21use common_query::AddColumnLocation;
22use datafusion_expr::TableProviderFilterPushDown;
23use datatypes::error::time_index_not_widening_error;
24pub use datatypes::error::{Error as ConvertError, Result as ConvertResult};
25use datatypes::schema::{
26 ColumnSchema, FulltextOptions, Schema, SchemaBuilder, SchemaRef, SkippingIndexOptions,
27};
28use derive_builder::Builder;
29use serde::{Deserialize, Deserializer, Serialize};
30use snafu::{OptionExt, ResultExt, ensure};
31use store_api::metric_engine_consts::PHYSICAL_TABLE_METADATA_KEY;
32use store_api::mito_engine_options::{
33 APPEND_MODE_KEY, AUTO_FLUSH_INTERVAL_KEY, COMPACTION_TYPE, COMPACTION_TYPE_TWCS,
34 MAX_ROW_GROUP_ROW_COUNT, MERGE_MODE_KEY, PRESERVE_ROW_SEQUENCE, SKIP_WAL_KEY, SST_FORMAT_KEY,
35 TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM, TWCS_TRIGGER_FILE_NUM,
36};
37use store_api::region_request::{SetRegionOption, UnsetRegionOption};
38use store_api::storage::{ColumnDescriptor, ColumnDescriptorBuilder, ColumnId};
39
40use crate::error::{self, Result};
41use crate::requests::{
42 AddColumnRequest, AlterKind, AnnotationContext, AnnotationFamily, AnnotationValidationError,
43 ModifyColumnTypeRequest, REPARTITION_COLUMN_HINT_KEY, REPARTITION_PARTITION_NUM_HINT_KEY,
44 SetDefaultRequest, SetIndexOption, TableOptions, UnsetIndexOption, has_stable_string_form,
45 parse_entity_columns, parse_entity_option_key, validate_and_normalize_annotation,
46 validate_annotation_keys,
47};
48use crate::table_reference::TableReference;
49
50pub type TableId = u32;
51pub type TableVersion = u64;
52
53#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
56pub enum FilterPushDownType {
57 Unsupported,
59 Inexact,
64 Exact,
68}
69
70impl From<TableProviderFilterPushDown> for FilterPushDownType {
71 fn from(value: TableProviderFilterPushDown) -> Self {
72 match value {
73 TableProviderFilterPushDown::Unsupported => FilterPushDownType::Unsupported,
74 TableProviderFilterPushDown::Inexact => FilterPushDownType::Inexact,
75 TableProviderFilterPushDown::Exact => FilterPushDownType::Exact,
76 }
77 }
78}
79
80impl From<FilterPushDownType> for TableProviderFilterPushDown {
81 fn from(value: FilterPushDownType) -> Self {
82 match value {
83 FilterPushDownType::Unsupported => TableProviderFilterPushDown::Unsupported,
84 FilterPushDownType::Inexact => TableProviderFilterPushDown::Inexact,
85 FilterPushDownType::Exact => TableProviderFilterPushDown::Exact,
86 }
87 }
88}
89
90#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
92pub enum TableType {
93 Base,
95 View,
97 Temporary,
99}
100
101impl std::fmt::Display for TableType {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 TableType::Base => f.write_str("BASE TABLE"),
105 TableType::Temporary => f.write_str("TEMPORARY"),
106 TableType::View => f.write_str("VIEW"),
107 }
108 }
109}
110
111impl From<TableType> for datafusion::datasource::TableType {
112 fn from(t: TableType) -> datafusion::datasource::TableType {
113 match t {
114 TableType::Base => datafusion::datasource::TableType::Base,
115 TableType::View => datafusion::datasource::TableType::View,
116 TableType::Temporary => datafusion::datasource::TableType::Temporary,
117 }
118 }
119}
120
121#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Default)]
123pub struct TableIdent {
124 pub table_id: TableId,
126 pub version: TableVersion,
129}
130
131#[derive(Clone, Debug, Builder, PartialEq, Eq, ToMetaBuilder, Serialize)]
135#[builder(pattern = "mutable", custom_constructor)]
136pub struct TableMeta {
137 pub schema: SchemaRef,
138 pub primary_key_indices: Vec<usize>,
141 #[builder(default = "self.default_value_indices()?")]
142 pub value_indices: Vec<usize>,
143 #[builder(default, setter(into))]
144 pub engine: String,
145 pub next_column_id: ColumnId,
146 #[builder(default)]
148 pub options: TableOptions,
149 #[builder(default = "Utc::now()")]
150 pub created_on: DateTime<Utc>,
151 #[builder(default = "self.default_updated_on()")]
152 pub updated_on: DateTime<Utc>,
153 #[builder(default = "Vec::new()")]
154 pub partition_key_indices: Vec<usize>,
155 #[builder(default = "Vec::new()")]
156 pub column_ids: Vec<ColumnId>,
157}
158
159impl TableMeta {
160 pub fn empty() -> Self {
161 Self {
162 schema: Arc::new(Schema::new(vec![])),
163 primary_key_indices: vec![],
164 value_indices: vec![],
165 engine: "".to_string(),
166 next_column_id: 0,
167 options: TableOptions::default(),
168 created_on: Utc::now(),
169 updated_on: Utc::now(),
170 partition_key_indices: vec![],
171 column_ids: vec![],
172 }
173 }
174}
175
176impl<'de> Deserialize<'de> for TableMeta {
177 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
178 where
179 D: Deserializer<'de>,
180 {
181 #[derive(Deserialize)]
182 struct RawTableMeta {
183 schema: SchemaRef,
184 primary_key_indices: Vec<usize>,
185 value_indices: Vec<usize>,
186 engine: String,
187 next_column_id: ColumnId,
188 options: TableOptions,
189 created_on: DateTime<Utc>,
190 updated_on: Option<DateTime<Utc>>,
191 #[serde(default)]
192 partition_key_indices: Vec<usize>,
193 #[serde(default)]
194 column_ids: Vec<ColumnId>,
195 }
196
197 let RawTableMeta {
198 schema,
199 primary_key_indices,
200 value_indices,
201 engine,
202 next_column_id,
203 options,
204 created_on,
205 updated_on,
206 partition_key_indices,
207 column_ids,
208 } = RawTableMeta::deserialize(deserializer)?;
209
210 Ok(Self {
211 schema,
212 primary_key_indices,
213 value_indices,
214 engine,
215 next_column_id,
216 options,
217 created_on,
218 updated_on: updated_on.unwrap_or(created_on),
219 partition_key_indices,
220 column_ids,
221 })
222 }
223}
224
225impl TableMetaBuilder {
226 #[cfg(any(test, feature = "testing"))]
228 pub fn empty() -> Self {
229 Self {
230 schema: None,
231 primary_key_indices: None,
232 value_indices: None,
233 engine: None,
234 next_column_id: None,
235 options: None,
236 created_on: None,
237 updated_on: None,
238 partition_key_indices: None,
239 column_ids: None,
240 }
241 }
242}
243
244impl TableMetaBuilder {
245 fn default_value_indices(&self) -> std::result::Result<Vec<usize>, String> {
246 match (&self.primary_key_indices, &self.schema) {
247 (Some(v), Some(schema)) => {
248 let column_schemas = schema.column_schemas();
249 Ok((0..column_schemas.len())
250 .filter(|idx| !v.contains(idx))
251 .collect())
252 }
253 _ => Err("Missing primary_key_indices or schema to create value_indices".to_string()),
254 }
255 }
256
257 fn default_updated_on(&self) -> DateTime<Utc> {
258 self.created_on.unwrap_or_default()
259 }
260
261 pub fn new_external_table() -> Self {
262 Self {
263 schema: None,
264 primary_key_indices: Some(Vec::new()),
265 value_indices: Some(Vec::new()),
266 engine: None,
267 next_column_id: Some(0),
268 options: None,
269 created_on: None,
270 updated_on: None,
271 partition_key_indices: None,
272 column_ids: None,
273 }
274 }
275}
276
277struct SplitResult<'a> {
279 columns_at_first: Vec<&'a AddColumnRequest>,
281 columns_at_after: HashMap<String, Vec<&'a AddColumnRequest>>,
283 columns_at_last: Vec<&'a AddColumnRequest>,
285 column_names: Vec<String>,
287}
288
289impl TableMeta {
290 pub fn row_key_column_names(&self) -> impl Iterator<Item = &String> {
291 let columns_schemas = &self.schema.column_schemas();
292 self.primary_key_indices
293 .iter()
294 .map(|idx| &columns_schemas[*idx].name)
295 }
296
297 pub fn field_column_names(&self) -> impl Iterator<Item = &String> {
298 let columns_schemas = self.schema.column_schemas();
300 let primary_key_indices = &self.primary_key_indices;
301 columns_schemas
302 .iter()
303 .enumerate()
304 .filter(|(i, cs)| !primary_key_indices.contains(i) && !cs.is_time_index())
305 .map(|(_, cs)| &cs.name)
306 }
307
308 pub fn partition_column_names(&self) -> impl Iterator<Item = &String> {
309 let columns_schemas = &self.schema.column_schemas();
310 self.partition_key_indices
311 .iter()
312 .map(|idx| &columns_schemas[*idx].name)
313 }
314
315 pub fn partition_columns(&self) -> impl Iterator<Item = &ColumnSchema> {
316 self.partition_key_indices
317 .iter()
318 .map(|idx| &self.schema.column_schemas()[*idx])
319 }
320
321 pub fn builder_with_alter_kind(
325 &self,
326 table_name: &str,
327 alter_kind: &AlterKind,
328 ) -> Result<TableMetaBuilder> {
329 let mut builder = match alter_kind {
330 AlterKind::AddColumns { columns } => self.add_columns(table_name, columns),
331 AlterKind::DropColumns { names } => self.remove_columns(table_name, names),
332 AlterKind::ModifyColumnTypes { columns } => {
333 self.modify_column_types(table_name, columns)
334 }
335 AlterKind::RenameTable { .. } => Ok(self.new_meta_builder()),
337 AlterKind::SetTableOptions { options } => self.set_table_options(options),
338 AlterKind::UnsetTableOptions { keys } => self.unset_table_options(keys),
339 AlterKind::SetAnnotations { family, options } => {
340 self.set_annotations(table_name, *family, options)
341 }
342 AlterKind::UnsetAnnotations { family, keys } => {
343 self.unset_annotations(table_name, *family, keys)
344 }
345 AlterKind::SetIndexes { options } => self.set_indexes(table_name, options),
346 AlterKind::UnsetIndexes { options } => self.unset_indexes(table_name, options),
347 AlterKind::DropDefaults { names } => self.drop_defaults(table_name, names),
348 AlterKind::SetDefaults { defaults } => self.set_defaults(table_name, defaults),
349 }?;
350 let _ = builder.updated_on(Utc::now());
351 Ok(builder)
352 }
353
354 fn set_table_options(&self, requests: &[SetRegionOption]) -> Result<TableMetaBuilder> {
356 let mut new_options = self.options.clone();
357
358 for request in requests {
359 match request {
360 SetRegionOption::WriteBufferSize(new_write_buffer_size) => {
361 new_options.write_buffer_size = *new_write_buffer_size;
362 }
363 SetRegionOption::Ttl(new_ttl) => {
364 new_options.ttl = *new_ttl;
365 }
366 SetRegionOption::Twsc(key, value) => {
367 let persisted_key = if matches!(
368 key.as_str(),
369 TWCS_TRIGGER_FILE_NUM | TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM
370 ) {
371 new_options.extra_options.remove(TWCS_TRIGGER_FILE_NUM);
372 new_options
373 .extra_options
374 .remove(TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM);
375 TWCS_TRIGGER_FILE_NUM
376 } else {
377 key
378 };
379 if !value.is_empty() {
380 new_options
381 .extra_options
382 .insert(persisted_key.to_string(), value.clone());
383 new_options.extra_options.insert(
385 COMPACTION_TYPE.to_string(),
386 COMPACTION_TYPE_TWCS.to_string(),
387 );
388 } else {
389 new_options.extra_options.remove(persisted_key);
391 }
392 }
393 SetRegionOption::Format(value) => {
394 new_options
395 .extra_options
396 .insert(SST_FORMAT_KEY.to_string(), value.clone());
397 }
398 SetRegionOption::AppendMode(value) => {
399 new_options
400 .extra_options
401 .insert(APPEND_MODE_KEY.to_string(), value.to_string());
402 if *value {
403 new_options.extra_options.remove(MERGE_MODE_KEY);
404 }
405 }
406 SetRegionOption::AutoFlushInterval(new_interval) => {
407 if let Some(interval) = new_interval {
408 new_options.extra_options.insert(
409 AUTO_FLUSH_INTERVAL_KEY.to_string(),
410 humantime::format_duration(*interval).to_string(),
411 );
412 } else {
413 new_options.extra_options.remove(AUTO_FLUSH_INTERVAL_KEY);
414 }
415 }
416 SetRegionOption::MaxRowGroupRowCount(row_count) => {
417 if let Some(row_count) = row_count {
418 new_options
419 .extra_options
420 .insert(MAX_ROW_GROUP_ROW_COUNT.to_string(), row_count.to_string());
421 } else {
422 new_options.extra_options.remove(MAX_ROW_GROUP_ROW_COUNT);
423 }
424 }
425 SetRegionOption::PreserveRowSequence(preserve) => {
426 if *preserve {
427 new_options
428 .extra_options
429 .insert(PRESERVE_ROW_SEQUENCE.to_string(), preserve.to_string());
430 } else {
431 new_options.extra_options.remove(PRESERVE_ROW_SEQUENCE);
432 }
433 }
434 SetRegionOption::SkipWal(skip_wal) => {
435 new_options.skip_wal = *skip_wal;
436 new_options
439 .extra_options
440 .insert(SKIP_WAL_KEY.to_string(), skip_wal.to_string());
441 }
442 }
443 }
444 let mut builder = self.new_meta_builder();
445 builder.options(new_options);
446
447 Ok(builder)
448 }
449
450 fn unset_table_options(&self, requests: &[UnsetRegionOption]) -> Result<TableMetaBuilder> {
451 let requests = requests.iter().map(Into::into).collect::<Vec<_>>();
452 self.set_table_options(&requests)
453 }
454
455 fn set_annotations(
460 &self,
461 table_name: &str,
462 family: AnnotationFamily,
463 options: &[(String, String)],
464 ) -> Result<TableMetaBuilder> {
465 validate_annotation_keys(options.iter().map(|(key, _)| key.as_str())).map_err(|err| {
466 error::InvalidAlterRequestSnafu {
467 table: table_name,
468 err: err.to_string(),
469 }
470 .build()
471 })?;
472 let cx = AnnotationContext {
473 schema: &self.schema,
474 partition_key_indices: &self.partition_key_indices,
475 };
476 let mut new_options = self.options.clone();
477 for (key, value) in options {
478 ensure!(
479 AnnotationFamily::of_key(key) == Some(family),
480 error::InvalidAlterRequestSnafu {
481 table: table_name,
482 err: format!(
483 "`{key}` is outside the `{}` annotation namespace",
484 family.namespace()
485 ),
486 }
487 );
488 let checked = validate_and_normalize_annotation(family, &cx, key, value).map_err(
489 |e| match e {
490 AnnotationValidationError::ColumnNotFound { column } => {
491 error::ColumnNotExistsSnafu {
492 column_name: column,
493 table_name,
494 }
495 .build()
496 }
497 other => error::InvalidAlterRequestSnafu {
498 table: table_name,
499 err: other.to_string(),
500 }
501 .build(),
502 },
503 )?;
504 new_options.extra_options.insert(key.clone(), checked);
505 }
506 let mut builder = self.new_meta_builder();
507 builder.options(new_options);
508 Ok(builder)
509 }
510
511 fn unset_annotations(
515 &self,
516 table_name: &str,
517 family: AnnotationFamily,
518 keys: &[String],
519 ) -> Result<TableMetaBuilder> {
520 validate_annotation_keys(keys.iter().map(String::as_str)).map_err(|err| {
521 error::InvalidAlterRequestSnafu {
522 table: table_name,
523 err: err.to_string(),
524 }
525 .build()
526 })?;
527 let mut new_options = self.options.clone();
528 for key in keys {
529 ensure!(
530 AnnotationFamily::of_key(key) == Some(family),
531 error::InvalidAlterRequestSnafu {
532 table: table_name,
533 err: format!(
534 "`{key}` is outside the `{}` annotation namespace",
535 family.namespace()
536 ),
537 }
538 );
539 new_options.extra_options.remove(key);
540 }
541 let mut builder = self.new_meta_builder();
542 builder.options(new_options);
543 Ok(builder)
544 }
545
546 fn set_indexes(
547 &self,
548 table_name: &str,
549 requests: &[SetIndexOption],
550 ) -> Result<TableMetaBuilder> {
551 let table_schema = &self.schema;
552 let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
553 for request in requests {
554 let column_name = request.column_name();
555 table_schema
556 .column_index_by_name(column_name)
557 .with_context(|| error::ColumnNotExistsSnafu {
558 column_name,
559 table_name,
560 })?;
561 set_index_options
562 .entry(column_name)
563 .or_default()
564 .push(request);
565 }
566
567 let mut meta_builder = self.new_meta_builder();
568 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
569 for mut column in table_schema.column_schemas().iter().cloned() {
570 if let Some(request) = set_index_options.get(column.name.as_str()) {
571 for request in request {
572 self.set_index(&mut column, request)?;
573 }
574 }
575 columns.push(column);
576 }
577
578 let mut builder = SchemaBuilder::try_from_columns(columns)
579 .with_context(|_| error::SchemaBuildSnafu {
580 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
581 })?
582 .version(table_schema.version() + 1);
583
584 for (k, v) in table_schema.metadata().iter() {
585 builder = builder.add_metadata(k, v);
586 }
587
588 let new_schema = builder.build().with_context(|_| {
589 let column_names = requests
590 .iter()
591 .map(|request| request.column_name())
592 .collect::<Vec<_>>();
593 error::SchemaBuildSnafu {
594 msg: format!(
595 "Table {table_name} cannot set index options with columns {column_names:?}",
596 ),
597 }
598 })?;
599 let _ = meta_builder
600 .schema(Arc::new(new_schema))
601 .primary_key_indices(self.primary_key_indices.clone());
602
603 Ok(meta_builder)
604 }
605
606 fn unset_indexes(
607 &self,
608 table_name: &str,
609 requests: &[UnsetIndexOption],
610 ) -> Result<TableMetaBuilder> {
611 let table_schema = &self.schema;
612 let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
613 for request in requests {
614 let column_name = request.column_name();
615 table_schema
616 .column_index_by_name(column_name)
617 .with_context(|| error::ColumnNotExistsSnafu {
618 column_name,
619 table_name,
620 })?;
621 set_index_options
622 .entry(column_name)
623 .or_default()
624 .push(request);
625 }
626
627 let mut meta_builder = self.new_meta_builder();
628 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
629 for mut column in table_schema.column_schemas().iter().cloned() {
630 if let Some(request) = set_index_options.get(column.name.as_str()) {
631 for request in request {
632 self.unset_index(&mut column, request)?;
633 }
634 }
635 columns.push(column);
636 }
637
638 let mut builder = SchemaBuilder::try_from_columns(columns)
639 .with_context(|_| error::SchemaBuildSnafu {
640 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
641 })?
642 .version(table_schema.version() + 1);
643
644 for (k, v) in table_schema.metadata().iter() {
645 builder = builder.add_metadata(k, v);
646 }
647
648 let new_schema = builder.build().with_context(|_| {
649 let column_names = requests
650 .iter()
651 .map(|request| request.column_name())
652 .collect::<Vec<_>>();
653 error::SchemaBuildSnafu {
654 msg: format!(
655 "Table {table_name} cannot set index options with columns {column_names:?}",
656 ),
657 }
658 })?;
659 let _ = meta_builder
660 .schema(Arc::new(new_schema))
661 .primary_key_indices(self.primary_key_indices.clone());
662
663 Ok(meta_builder)
664 }
665
666 fn set_index(&self, column_schema: &mut ColumnSchema, request: &SetIndexOption) -> Result<()> {
667 match request {
668 SetIndexOption::Fulltext {
669 column_name,
670 options,
671 } => {
672 ensure!(
673 column_schema.data_type.is_string(),
674 error::InvalidColumnOptionSnafu {
675 column_name,
676 msg: "FULLTEXT index only supports string type",
677 }
678 );
679
680 let current_fulltext_options = column_schema
681 .fulltext_options()
682 .context(error::SetFulltextOptionsSnafu { column_name })?;
683 set_column_fulltext_options(
684 column_schema,
685 column_name,
686 options,
687 current_fulltext_options,
688 )?;
689 }
690 SetIndexOption::Inverted { column_name } => {
691 debug_assert_eq!(column_schema.name, *column_name);
692 column_schema.set_inverted_index(true);
693 }
694 SetIndexOption::Skipping {
695 column_name,
696 options,
697 } => {
698 set_column_skipping_index_options(column_schema, column_name, options)?;
699 }
700 }
701
702 Ok(())
703 }
704
705 fn unset_index(
706 &self,
707 column_schema: &mut ColumnSchema,
708 request: &UnsetIndexOption,
709 ) -> Result<()> {
710 match request {
711 UnsetIndexOption::Fulltext { column_name } => {
712 let current_fulltext_options = column_schema
713 .fulltext_options()
714 .context(error::SetFulltextOptionsSnafu { column_name })?;
715 unset_column_fulltext_options(
716 column_schema,
717 column_name,
718 current_fulltext_options.clone(),
719 )?
720 }
721 UnsetIndexOption::Inverted { .. } => {
722 column_schema.set_inverted_index(false);
723 }
724 UnsetIndexOption::Skipping { column_name } => {
725 unset_column_skipping_index_options(column_schema, column_name)?;
726 }
727 }
728
729 Ok(())
730 }
731
732 pub fn alloc_new_column(
737 &mut self,
738 table_name: &str,
739 new_column: &ColumnSchema,
740 ) -> Result<ColumnDescriptor> {
741 let desc = ColumnDescriptorBuilder::new(
742 self.next_column_id as ColumnId,
743 &new_column.name,
744 new_column.data_type.clone(),
745 )
746 .is_nullable(new_column.is_nullable())
747 .default_constraint(new_column.default_constraint().cloned())
748 .build()
749 .context(error::BuildColumnDescriptorSnafu {
750 table_name,
751 column_name: &new_column.name,
752 })?;
753
754 self.next_column_id += 1;
756
757 Ok(desc)
758 }
759
760 fn new_meta_builder(&self) -> TableMetaBuilder {
762 let mut builder = TableMetaBuilder::from(self);
763 builder.value_indices = None;
765 builder
766 }
767
768 fn add_columns(
770 &self,
771 table_name: &str,
772 requests: &[AddColumnRequest],
773 ) -> Result<TableMetaBuilder> {
774 let table_schema = &self.schema;
775 let mut meta_builder = self.new_meta_builder();
776 let original_primary_key_indices: HashSet<&usize> =
777 self.primary_key_indices.iter().collect();
778
779 let mut names = HashSet::with_capacity(requests.len());
780 let mut new_columns = Vec::with_capacity(requests.len());
781 for col_to_add in requests {
782 if let Some(column_schema) =
783 table_schema.column_schema_by_name(&col_to_add.column_schema.name)
784 {
785 ensure!(
787 col_to_add.add_if_not_exists,
788 error::ColumnExistsSnafu {
789 table_name,
790 column_name: &col_to_add.column_schema.name
791 },
792 );
793
794 ensure!(
796 column_schema.data_type == col_to_add.column_schema.data_type,
797 error::InvalidAlterRequestSnafu {
798 table: table_name,
799 err: format!(
800 "column {} already exists with different type {:?}",
801 col_to_add.column_schema.name, column_schema.data_type,
802 ),
803 }
804 );
805 } else {
806 ensure!(
809 names.insert(&col_to_add.column_schema.name),
810 error::InvalidAlterRequestSnafu {
811 table: table_name,
812 err: format!(
813 "add column {} more than once",
814 col_to_add.column_schema.name
815 ),
816 }
817 );
818
819 ensure!(
820 col_to_add.column_schema.is_nullable()
821 || col_to_add.column_schema.default_constraint().is_some(),
822 error::InvalidAlterRequestSnafu {
823 table: table_name,
824 err: format!(
825 "no default value for column {}",
826 col_to_add.column_schema.name
827 ),
828 },
829 );
830
831 if !has_stable_string_form(&col_to_add.column_schema.data_type)
835 && let Some(key) =
836 entity_option_referencing(&self.options, &col_to_add.column_schema.name)
837 {
838 return error::InvalidAlterRequestSnafu {
839 table: table_name,
840 err: format!(
841 "column `{}` is referenced by entity option `{key}` and must \
842 keep a type that renders as a string, got `{}`",
843 col_to_add.column_schema.name, col_to_add.column_schema.data_type
844 ),
845 }
846 .fail();
847 }
848
849 new_columns.push(col_to_add.clone());
850 }
851 }
852 let requests = &new_columns[..];
853
854 let SplitResult {
855 columns_at_first,
856 columns_at_after,
857 columns_at_last,
858 column_names,
859 } = self.split_requests_by_column_location(table_name, requests)?;
860 let mut primary_key_indices = Vec::with_capacity(self.primary_key_indices.len());
861 let mut columns = Vec::with_capacity(table_schema.num_columns() + requests.len());
862 columns_at_first.iter().rev().for_each(|request| {
864 if request.is_key {
865 primary_key_indices.push(columns.len());
867 }
868 columns.push(request.column_schema.clone());
869 });
870 for (index, column_schema) in table_schema.column_schemas().iter().enumerate() {
872 if original_primary_key_indices.contains(&index) {
873 primary_key_indices.push(columns.len());
874 }
875 columns.push(column_schema.clone());
876 if let Some(requests) = columns_at_after.get(&column_schema.name) {
877 requests.iter().rev().for_each(|request| {
878 if request.is_key {
879 primary_key_indices.push(columns.len());
881 }
882 columns.push(request.column_schema.clone());
883 });
884 }
885 }
886 columns_at_last.iter().for_each(|request| {
888 if request.is_key {
889 primary_key_indices.push(columns.len());
891 }
892 columns.push(request.column_schema.clone());
893 });
894
895 let mut builder = SchemaBuilder::try_from(columns)
896 .with_context(|_| error::SchemaBuildSnafu {
897 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
898 })?
899 .version(table_schema.version() + 1);
901 for (k, v) in table_schema.metadata().iter() {
902 builder = builder.add_metadata(k, v);
903 }
904 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
905 msg: format!("Table {table_name} cannot add new columns {column_names:?}"),
906 })?;
907
908 let partition_key_indices = self
909 .partition_key_indices
910 .iter()
911 .map(|idx| table_schema.column_name_by_index(*idx))
912 .map(|name| new_schema.column_index_by_name(name).unwrap())
914 .collect();
915
916 let _ = meta_builder
918 .schema(Arc::new(new_schema))
919 .primary_key_indices(primary_key_indices)
920 .partition_key_indices(partition_key_indices);
921
922 Ok(meta_builder)
923 }
924
925 fn remove_columns(
926 &self,
927 table_name: &str,
928 column_names: &[String],
929 ) -> Result<TableMetaBuilder> {
930 let table_schema = &self.schema;
931 let column_names: HashSet<_> = column_names.iter().collect();
932 let mut meta_builder = self.new_meta_builder();
933
934 let timestamp_index = table_schema.timestamp_index();
935 for column_name in &column_names {
937 if let Some(index) = table_schema.column_index_by_name(column_name) {
938 ensure!(
941 !self.primary_key_indices.contains(&index),
942 error::RemoveColumnInIndexSnafu {
943 column_name: *column_name,
944 table_name,
945 }
946 );
947
948 ensure!(
949 !self.partition_key_indices.contains(&index),
950 error::RemovePartitionColumnSnafu {
951 column_name: *column_name,
952 table_name,
953 }
954 );
955
956 if let Some(ts_index) = timestamp_index {
957 ensure!(
959 index != ts_index,
960 error::RemoveColumnInIndexSnafu {
961 column_name: table_schema.column_name_by_index(ts_index),
962 table_name,
963 }
964 );
965 }
966 } else {
967 return error::ColumnNotExistsSnafu {
968 column_name: *column_name,
969 table_name,
970 }
971 .fail()?;
972 }
973 }
974
975 let columns: Vec<_> = table_schema
977 .column_schemas()
978 .iter()
979 .filter(|column_schema| !column_names.contains(&column_schema.name))
980 .cloned()
981 .collect();
982
983 let mut builder = SchemaBuilder::try_from_columns(columns)
984 .with_context(|_| error::SchemaBuildSnafu {
985 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
986 })?
987 .version(table_schema.version() + 1);
989 for (k, v) in table_schema.metadata().iter() {
990 builder = builder.add_metadata(k, v);
991 }
992 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
993 msg: format!("Table {table_name} cannot add remove columns {column_names:?}"),
994 })?;
995
996 let primary_key_indices = self
998 .primary_key_indices
999 .iter()
1000 .map(|idx| table_schema.column_name_by_index(*idx))
1001 .map(|name| new_schema.column_index_by_name(name).unwrap())
1003 .collect();
1004
1005 let partition_key_indices = self
1006 .partition_key_indices
1007 .iter()
1008 .map(|idx| table_schema.column_name_by_index(*idx))
1009 .map(|name| new_schema.column_index_by_name(name).unwrap())
1011 .collect();
1012
1013 let _ = meta_builder
1014 .schema(Arc::new(new_schema))
1015 .primary_key_indices(primary_key_indices)
1016 .partition_key_indices(partition_key_indices);
1017
1018 Ok(meta_builder)
1019 }
1020
1021 fn modify_column_types(
1022 &self,
1023 table_name: &str,
1024 requests: &[ModifyColumnTypeRequest],
1025 ) -> Result<TableMetaBuilder> {
1026 let table_schema = &self.schema;
1027 let mut meta_builder = self.new_meta_builder();
1028
1029 let mut modify_column_types = HashMap::with_capacity(requests.len());
1030 let timestamp_index = table_schema.timestamp_index();
1031
1032 for col_to_change in requests {
1033 let change_column_name = &col_to_change.column_name;
1034
1035 let index = table_schema
1036 .column_index_by_name(change_column_name)
1037 .with_context(|| error::ColumnNotExistsSnafu {
1038 column_name: change_column_name,
1039 table_name,
1040 })?;
1041
1042 if !has_stable_string_form(&col_to_change.target_type)
1048 && let Some(key) = entity_option_referencing(&self.options, change_column_name)
1049 {
1050 return error::InvalidAlterRequestSnafu {
1051 table: table_name,
1052 err: format!(
1053 "column `{change_column_name}` is referenced by entity option \
1054 `{key}` and must keep a type that renders as a string, got `{}`",
1055 col_to_change.target_type
1056 ),
1057 }
1058 .fail();
1059 }
1060
1061 let column = &table_schema.column_schemas()[index];
1062
1063 ensure!(
1064 !self.primary_key_indices.contains(&index),
1065 error::InvalidAlterRequestSnafu {
1066 table: table_name,
1067 err: format!(
1068 "Not allowed to change primary key index column '{}'",
1069 column.name
1070 )
1071 }
1072 );
1073
1074 let is_time_index = timestamp_index == Some(index);
1075 if is_time_index {
1076 ensure!(
1080 column
1081 .data_type
1082 .is_timestamp_unit_widening_to(&col_to_change.target_type),
1083 error::InvalidAlterRequestSnafu {
1084 table: table_name,
1085 err: time_index_not_widening_error(
1086 &column.name,
1087 &column.data_type,
1088 &col_to_change.target_type,
1089 ),
1090 }
1091 );
1092 } else {
1093 ensure!(
1094 column
1095 .data_type
1096 .can_arrow_type_cast_to(&col_to_change.target_type),
1097 error::InvalidAlterRequestSnafu {
1098 table: table_name,
1099 err: format!(
1100 "column '{}' cannot be cast automatically to type '{}'",
1101 col_to_change.column_name, col_to_change.target_type,
1102 ),
1103 }
1104 );
1105
1106 ensure!(
1107 column.is_nullable(),
1108 error::InvalidAlterRequestSnafu {
1109 table: table_name,
1110 err: format!(
1111 "column '{}' must be nullable to ensure safe conversion.",
1112 col_to_change.column_name,
1113 ),
1114 }
1115 );
1116 }
1117 ensure!(
1118 modify_column_types
1119 .insert(&col_to_change.column_name, col_to_change)
1120 .is_none(),
1121 error::InvalidAlterRequestSnafu {
1122 table: table_name,
1123 err: format!(
1124 "change column datatype {} more than once",
1125 col_to_change.column_name
1126 ),
1127 }
1128 );
1129 }
1130 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
1133 for mut column in table_schema.column_schemas().iter().cloned() {
1134 if let Some(change_column) = modify_column_types.get(&column.name) {
1135 column.data_type = change_column.target_type.clone();
1136 let new_default = if let Some(default_value) = column.default_constraint() {
1137 Some(
1138 default_value
1139 .cast_to_datatype(&change_column.target_type)
1140 .with_context(|_| error::CastDefaultValueSnafu {
1141 reason: format!(
1142 "Failed to cast default value from {:?} to type {:?}",
1143 default_value, &change_column.target_type
1144 ),
1145 })?,
1146 )
1147 } else {
1148 None
1149 };
1150 column = column
1151 .clone()
1152 .with_default_constraint(new_default.clone())
1153 .with_context(|_| error::CastDefaultValueSnafu {
1154 reason: format!("Failed to set new default: {:?}", new_default),
1155 })?;
1156 }
1157 columns.push(column)
1158 }
1159
1160 let mut builder = SchemaBuilder::try_from_columns(columns)
1161 .with_context(|_| error::SchemaBuildSnafu {
1162 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1163 })?
1164 .version(table_schema.version() + 1);
1166 for (k, v) in table_schema.metadata().iter() {
1167 builder = builder.add_metadata(k, v);
1168 }
1169 let new_schema = builder.build().with_context(|_| {
1170 let column_names: Vec<_> = requests
1171 .iter()
1172 .map(|request| &request.column_name)
1173 .collect();
1174
1175 error::SchemaBuildSnafu {
1176 msg: format!(
1177 "Table {table_name} cannot change datatype with columns {column_names:?}"
1178 ),
1179 }
1180 })?;
1181
1182 let _ = meta_builder
1183 .schema(Arc::new(new_schema))
1184 .primary_key_indices(self.primary_key_indices.clone());
1185
1186 Ok(meta_builder)
1187 }
1188
1189 fn split_requests_by_column_location<'a>(
1191 &self,
1192 table_name: &str,
1193 requests: &'a [AddColumnRequest],
1194 ) -> Result<SplitResult<'a>> {
1195 let table_schema = &self.schema;
1196 let mut columns_at_first = Vec::new();
1197 let mut columns_at_after = HashMap::new();
1198 let mut columns_at_last = Vec::new();
1199 let mut column_names = Vec::with_capacity(requests.len());
1200 for request in requests {
1201 let column_name = &request.column_schema.name;
1203 column_names.push(column_name.clone());
1204 ensure!(
1205 table_schema.column_schema_by_name(column_name).is_none(),
1206 error::ColumnExistsSnafu {
1207 column_name,
1208 table_name,
1209 }
1210 );
1211 match request.location.as_ref() {
1212 Some(AddColumnLocation::First) => {
1213 columns_at_first.push(request);
1214 }
1215 Some(AddColumnLocation::After { column_name }) => {
1216 ensure!(
1217 table_schema.column_schema_by_name(column_name).is_some(),
1218 error::ColumnNotExistsSnafu {
1219 column_name,
1220 table_name,
1221 }
1222 );
1223 columns_at_after
1224 .entry(column_name.clone())
1225 .or_insert(Vec::new())
1226 .push(request);
1227 }
1228 None => {
1229 columns_at_last.push(request);
1230 }
1231 }
1232 }
1233 Ok(SplitResult {
1234 columns_at_first,
1235 columns_at_after,
1236 columns_at_last,
1237 column_names,
1238 })
1239 }
1240
1241 fn drop_defaults(&self, table_name: &str, column_names: &[String]) -> Result<TableMetaBuilder> {
1242 let table_schema = &self.schema;
1243 let mut meta_builder = self.new_meta_builder();
1244 let mut columns = Vec::with_capacity(table_schema.num_columns());
1245 for column_schema in table_schema.column_schemas() {
1246 if let Some(name) = column_names.iter().find(|s| **s == column_schema.name) {
1247 ensure!(
1249 column_schema.default_constraint().is_some(),
1250 error::InvalidAlterRequestSnafu {
1251 table: table_name,
1252 err: format!("column {name} does not have a default value"),
1253 }
1254 );
1255 if !column_schema.is_nullable() {
1256 return error::InvalidAlterRequestSnafu {
1257 table: table_name,
1258 err: format!(
1259 "column {name} is not nullable and `default` cannot be dropped",
1260 ),
1261 }
1262 .fail();
1263 }
1264 let new_column_schema = column_schema.clone();
1265 let new_column_schema = new_column_schema
1266 .with_default_constraint(None)
1267 .with_context(|_| error::SchemaBuildSnafu {
1268 msg: format!("Table {table_name} cannot drop default values"),
1269 })?;
1270 columns.push(new_column_schema);
1271 } else {
1272 columns.push(column_schema.clone());
1273 }
1274 }
1275
1276 let mut builder = SchemaBuilder::try_from_columns(columns)
1277 .with_context(|_| error::SchemaBuildSnafu {
1278 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1279 })?
1280 .version(table_schema.version() + 1);
1282 for (k, v) in table_schema.metadata().iter() {
1283 builder = builder.add_metadata(k, v);
1284 }
1285 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1286 msg: format!("Table {table_name} cannot drop default values"),
1287 })?;
1288
1289 let _ = meta_builder.schema(Arc::new(new_schema));
1290
1291 Ok(meta_builder)
1292 }
1293
1294 fn set_defaults(
1295 &self,
1296 table_name: &str,
1297 set_defaults: &[SetDefaultRequest],
1298 ) -> Result<TableMetaBuilder> {
1299 let table_schema = &self.schema;
1300 let mut meta_builder = self.new_meta_builder();
1301 let mut columns = Vec::with_capacity(table_schema.num_columns());
1302 for column_schema in table_schema.column_schemas() {
1303 if let Some(set_default) = set_defaults
1304 .iter()
1305 .find(|s| s.column_name == column_schema.name)
1306 {
1307 let new_column_schema = column_schema.clone();
1308 let new_column_schema = new_column_schema
1309 .with_default_constraint(set_default.default_constraint.clone())
1310 .with_context(|_| error::SchemaBuildSnafu {
1311 msg: format!("Table {table_name} cannot set default values"),
1312 })?;
1313 columns.push(new_column_schema);
1314 } else {
1315 columns.push(column_schema.clone());
1316 }
1317 }
1318
1319 let mut builder = SchemaBuilder::try_from_columns(columns)
1320 .with_context(|_| error::SchemaBuildSnafu {
1321 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1322 })?
1323 .version(table_schema.version() + 1);
1325 for (k, v) in table_schema.metadata().iter() {
1326 builder = builder.add_metadata(k, v);
1327 }
1328 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1329 msg: format!("Table {table_name} cannot set default values"),
1330 })?;
1331
1332 let _ = meta_builder.schema(Arc::new(new_schema));
1333
1334 Ok(meta_builder)
1335 }
1336}
1337
1338#[derive(Clone, Debug, PartialEq, Eq, Builder, Serialize, Deserialize)]
1339#[builder(pattern = "owned")]
1340pub struct TableInfo {
1341 #[builder(default, setter(into))]
1343 pub ident: TableIdent,
1344 #[builder(setter(into))]
1346 pub name: String,
1347 #[builder(default, setter(into))]
1349 pub desc: Option<String>,
1350 #[builder(default = "DEFAULT_CATALOG_NAME.to_string()", setter(into))]
1351 pub catalog_name: String,
1352 #[builder(default = "DEFAULT_SCHEMA_NAME.to_string()", setter(into))]
1353 pub schema_name: String,
1354 pub meta: TableMeta,
1355 #[builder(default = "TableType::Base")]
1356 pub table_type: TableType,
1357}
1358
1359pub type TableInfoRef = Arc<TableInfo>;
1360
1361impl TableInfo {
1362 pub fn table_id(&self) -> TableId {
1363 self.ident.table_id
1364 }
1365
1366 pub fn full_table_name(&self) -> String {
1368 common_catalog::format_full_table_name(&self.catalog_name, &self.schema_name, &self.name)
1369 }
1370
1371 pub fn get_db_string(&self) -> String {
1372 common_catalog::build_db_string(&self.catalog_name, &self.schema_name)
1373 }
1374
1375 pub fn is_physical_table(&self) -> bool {
1377 self.meta
1378 .options
1379 .extra_options
1380 .contains_key(PHYSICAL_TABLE_METADATA_KEY)
1381 }
1382
1383 pub fn is_ttl_instant_table(&self) -> bool {
1385 self.meta
1386 .options
1387 .ttl
1388 .map(|t| t.is_instant())
1389 .unwrap_or(false)
1390 }
1391}
1392
1393impl TableInfoBuilder {
1394 pub fn new<S: Into<String>>(name: S, meta: TableMeta) -> Self {
1395 Self {
1396 name: Some(name.into()),
1397 meta: Some(meta),
1398 ..Default::default()
1399 }
1400 }
1401
1402 pub fn table_id(mut self, id: TableId) -> Self {
1403 let ident = self.ident.get_or_insert_with(TableIdent::default);
1404 ident.table_id = id;
1405 self
1406 }
1407
1408 pub fn table_version(mut self, version: TableVersion) -> Self {
1409 let ident = self.ident.get_or_insert_with(TableIdent::default);
1410 ident.version = version;
1411 self
1412 }
1413}
1414
1415impl TableIdent {
1416 pub fn new(table_id: TableId) -> Self {
1417 Self {
1418 table_id,
1419 version: 0,
1420 }
1421 }
1422}
1423
1424impl From<TableId> for TableIdent {
1425 fn from(table_id: TableId) -> Self {
1426 Self::new(table_id)
1427 }
1428}
1429
1430impl TableInfo {
1431 pub fn name_to_ids(&self) -> Option<HashMap<String, ColumnId>> {
1435 let column_schemas = self.meta.schema.column_schemas();
1436 if self.meta.column_ids.len() != column_schemas.len() {
1437 None
1438 } else {
1439 Some(
1440 self.meta
1441 .column_ids
1442 .iter()
1443 .enumerate()
1444 .map(|(index, id)| (column_schemas[index].name.clone(), *id))
1445 .collect(),
1446 )
1447 }
1448 }
1449
1450 pub fn sort_columns(&mut self) {
1452 let column_schemas = self.meta.schema.column_schemas();
1453 let primary_keys = self
1454 .meta
1455 .primary_key_indices
1456 .iter()
1457 .map(|index| column_schemas[*index].name.clone())
1458 .collect::<HashSet<_>>();
1459
1460 let name_to_ids = self.name_to_ids().unwrap_or_default();
1461 let mut column_schemas = column_schemas.to_vec();
1462 column_schemas.sort_unstable_by(|a, b| a.name.cmp(&b.name));
1463
1464 let mut primary_key_indices = Vec::with_capacity(primary_keys.len());
1466 let mut value_indices = Vec::with_capacity(column_schemas.len() - primary_keys.len());
1467 let mut column_ids = Vec::with_capacity(column_schemas.len());
1468 for (index, column_schema) in column_schemas.iter().enumerate() {
1469 if primary_keys.contains(&column_schema.name) {
1470 primary_key_indices.push(index);
1471 } else {
1472 value_indices.push(index);
1473 }
1474 if let Some(id) = name_to_ids.get(&column_schema.name) {
1475 column_ids.push(*id);
1476 }
1477 }
1478
1479 self.meta.schema = Arc::new(Schema::new_with_version(
1481 column_schemas,
1482 self.meta.schema.version(),
1483 ));
1484 self.meta.primary_key_indices = primary_key_indices;
1485 self.meta.value_indices = value_indices;
1486 self.meta.column_ids = column_ids;
1487 }
1488
1489 pub fn to_region_options(&self) -> HashMap<String, String> {
1493 let mut options = HashMap::from(&self.meta.options);
1494 options.remove(REPARTITION_COLUMN_HINT_KEY);
1495 options.remove(REPARTITION_PARTITION_NUM_HINT_KEY);
1496 options
1497 }
1498
1499 pub fn table_ref(&self) -> TableReference<'_> {
1501 TableReference::full(
1502 self.catalog_name.as_str(),
1503 self.schema_name.as_str(),
1504 self.name.as_str(),
1505 )
1506 }
1507}
1508
1509fn entity_option_referencing<'a>(options: &'a TableOptions, column: &str) -> Option<&'a str> {
1510 options.extra_options.iter().find_map(|(key, value)| {
1511 (parse_entity_option_key(key).is_some()
1512 && parse_entity_columns(value).iter().any(|c| c == column))
1513 .then_some(key.as_str())
1514 })
1515}
1516
1517fn set_column_fulltext_options(
1526 column_schema: &mut ColumnSchema,
1527 column_name: &str,
1528 options: &FulltextOptions,
1529 current_options: Option<FulltextOptions>,
1530) -> Result<()> {
1531 if let Some(current_options) = current_options {
1532 ensure!(
1533 current_options.analyzer == options.analyzer
1534 && current_options.case_sensitive == options.case_sensitive,
1535 error::InvalidColumnOptionSnafu {
1536 column_name,
1537 msg: format!(
1538 "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1539 current_options.analyzer, current_options.case_sensitive
1540 ),
1541 }
1542 );
1543 }
1544
1545 column_schema
1546 .set_fulltext_options(options)
1547 .context(error::SetFulltextOptionsSnafu { column_name })?;
1548
1549 Ok(())
1550}
1551
1552fn unset_column_fulltext_options(
1553 column_schema: &mut ColumnSchema,
1554 column_name: &str,
1555 current_options: Option<FulltextOptions>,
1556) -> Result<()> {
1557 ensure!(
1558 current_options
1559 .as_ref()
1560 .is_some_and(|options| options.enable),
1561 error::InvalidColumnOptionSnafu {
1562 column_name,
1563 msg: "FULLTEXT index already disabled".to_string(),
1564 }
1565 );
1566
1567 let mut options = current_options.unwrap();
1568 options.enable = false;
1569 column_schema
1570 .set_fulltext_options(&options)
1571 .context(error::SetFulltextOptionsSnafu { column_name })?;
1572
1573 Ok(())
1574}
1575
1576fn set_column_skipping_index_options(
1577 column_schema: &mut ColumnSchema,
1578 column_name: &str,
1579 options: &SkippingIndexOptions,
1580) -> Result<()> {
1581 column_schema
1582 .set_skipping_options(options)
1583 .context(error::SetSkippingOptionsSnafu { column_name })?;
1584
1585 Ok(())
1586}
1587
1588fn unset_column_skipping_index_options(
1589 column_schema: &mut ColumnSchema,
1590 column_name: &str,
1591) -> Result<()> {
1592 column_schema
1593 .unset_skipping_options()
1594 .context(error::UnsetSkippingOptionsSnafu { column_name })?;
1595 Ok(())
1596}
1597
1598#[cfg(test)]
1599mod tests {
1600 use std::assert_matches;
1601
1602 use common_error::ext::ErrorExt;
1603 use common_error::status_code::StatusCode;
1604 use datatypes::data_type::ConcreteDataType;
1605 use datatypes::schema::{
1606 ColumnSchema, FulltextAnalyzer, FulltextBackend, Schema, SchemaBuilder,
1607 };
1608
1609 use super::*;
1610 use crate::Error;
1611
1612 fn new_test_schema() -> Schema {
1614 let column_schemas = vec![
1615 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
1616 ColumnSchema::new(
1617 "ts",
1618 ConcreteDataType::timestamp_millisecond_datatype(),
1619 false,
1620 )
1621 .with_time_index(true),
1622 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
1623 ];
1624 SchemaBuilder::try_from(column_schemas)
1625 .unwrap()
1626 .version(123)
1627 .build()
1628 .unwrap()
1629 }
1630
1631 fn add_columns_to_meta(meta: &TableMeta) -> TableMeta {
1632 let new_tag = ColumnSchema::new("my_tag", ConcreteDataType::string_datatype(), true);
1633 let new_field = ColumnSchema::new("my_field", ConcreteDataType::string_datatype(), true);
1634 let alter_kind = AlterKind::AddColumns {
1635 columns: vec![
1636 AddColumnRequest {
1637 column_schema: new_tag,
1638 is_key: true,
1639 location: None,
1640 add_if_not_exists: false,
1641 },
1642 AddColumnRequest {
1643 column_schema: new_field,
1644 is_key: false,
1645 location: None,
1646 add_if_not_exists: false,
1647 },
1648 ],
1649 };
1650
1651 let builder = meta
1652 .builder_with_alter_kind("my_table", &alter_kind)
1653 .unwrap();
1654 builder.build().unwrap()
1655 }
1656
1657 fn add_columns_to_meta_with_location(meta: &TableMeta) -> TableMeta {
1658 let new_tag = ColumnSchema::new("my_tag_first", ConcreteDataType::string_datatype(), true);
1659 let new_field = ColumnSchema::new(
1660 "my_field_after_ts",
1661 ConcreteDataType::string_datatype(),
1662 true,
1663 );
1664 let yet_another_field = ColumnSchema::new(
1665 "yet_another_field_after_ts",
1666 ConcreteDataType::int64_datatype(),
1667 true,
1668 );
1669 let alter_kind = AlterKind::AddColumns {
1670 columns: vec![
1671 AddColumnRequest {
1672 column_schema: new_tag,
1673 is_key: true,
1674 location: Some(AddColumnLocation::First),
1675 add_if_not_exists: false,
1676 },
1677 AddColumnRequest {
1678 column_schema: new_field,
1679 is_key: false,
1680 location: Some(AddColumnLocation::After {
1681 column_name: "ts".to_string(),
1682 }),
1683 add_if_not_exists: false,
1684 },
1685 AddColumnRequest {
1686 column_schema: yet_another_field,
1687 is_key: true,
1688 location: Some(AddColumnLocation::After {
1689 column_name: "ts".to_string(),
1690 }),
1691 add_if_not_exists: false,
1692 },
1693 ],
1694 };
1695
1696 let builder = meta
1697 .builder_with_alter_kind("my_table", &alter_kind)
1698 .unwrap();
1699 builder.build().unwrap()
1700 }
1701
1702 #[test]
1703 fn test_modify_time_index_column_type() {
1704 let schema = Arc::new(new_test_schema());
1705 let meta = TableMetaBuilder::empty()
1706 .schema(schema)
1707 .primary_key_indices(vec![0])
1708 .engine("engine")
1709 .next_column_id(3)
1710 .build()
1711 .unwrap();
1712
1713 let alter_kind = AlterKind::ModifyColumnTypes {
1715 columns: vec![ModifyColumnTypeRequest {
1716 column_name: "ts".to_string(),
1717 target_type: ConcreteDataType::timestamp_microsecond_datatype(),
1718 }],
1719 };
1720 let new_meta = meta
1721 .builder_with_alter_kind("my_table", &alter_kind)
1722 .unwrap()
1723 .build()
1724 .unwrap();
1725 let ts_column = new_meta.schema.column_schema_by_name("ts").unwrap();
1726 assert_eq!(
1727 ConcreteDataType::timestamp_microsecond_datatype(),
1728 ts_column.data_type
1729 );
1730 assert!(ts_column.is_time_index());
1731 assert!(!ts_column.is_nullable());
1732 assert_eq!(new_meta.schema.version(), 124);
1733 assert_eq!(&[0], &new_meta.primary_key_indices[..]);
1734
1735 for target in [
1738 ConcreteDataType::timestamp_second_datatype(),
1739 ConcreteDataType::timestamp_millisecond_datatype(),
1740 ConcreteDataType::string_datatype(),
1741 ] {
1742 let alter_kind = AlterKind::ModifyColumnTypes {
1743 columns: vec![ModifyColumnTypeRequest {
1744 column_name: "ts".to_string(),
1745 target_type: target.clone(),
1746 }],
1747 };
1748 let res = meta.builder_with_alter_kind("my_table", &alter_kind);
1749 assert!(res.is_err(), "expected rejection for {target}");
1750 }
1751 }
1752
1753 #[test]
1754 fn test_add_columns() {
1755 let schema = Arc::new(new_test_schema());
1756 let meta = TableMetaBuilder::empty()
1757 .schema(schema)
1758 .primary_key_indices(vec![0])
1759 .engine("engine")
1760 .next_column_id(3)
1761 .build()
1762 .unwrap();
1763
1764 let new_meta = add_columns_to_meta(&meta);
1765 let names: Vec<String> = new_meta
1766 .schema
1767 .column_schemas()
1768 .iter()
1769 .map(|column_schema| column_schema.name.clone())
1770 .collect();
1771 assert_eq!(&["col1", "ts", "col2", "my_tag", "my_field"], &names[..]);
1772 assert_eq!(&[0, 3], &new_meta.primary_key_indices[..]);
1773 assert_eq!(&[1, 2, 4], &new_meta.value_indices[..]);
1774 }
1775
1776 #[test]
1777 fn test_set_append_mode_true_clears_merge_mode_option() {
1778 let schema = Arc::new(new_test_schema());
1779 let mut table_options = TableOptions::default();
1780 table_options
1781 .extra_options
1782 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1783 let meta = TableMetaBuilder::empty()
1784 .schema(schema)
1785 .primary_key_indices(vec![0])
1786 .engine("engine")
1787 .next_column_id(3)
1788 .options(table_options)
1789 .build()
1790 .unwrap();
1791
1792 let alter_kind = AlterKind::SetTableOptions {
1793 options: vec![SetRegionOption::AppendMode(true)],
1794 };
1795 let new_meta = meta
1796 .builder_with_alter_kind("my_table", &alter_kind)
1797 .unwrap()
1798 .build()
1799 .unwrap();
1800
1801 assert_eq!(
1802 Some("true"),
1803 new_meta
1804 .options
1805 .extra_options
1806 .get(APPEND_MODE_KEY)
1807 .map(String::as_str)
1808 );
1809 assert!(!new_meta.options.extra_options.contains_key(MERGE_MODE_KEY));
1810 }
1811
1812 #[test]
1813 fn test_set_append_mode_false_keeps_merge_mode_option() {
1814 let schema = Arc::new(new_test_schema());
1815 let mut table_options = TableOptions::default();
1816 table_options
1817 .extra_options
1818 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1819 let meta = TableMetaBuilder::empty()
1820 .schema(schema)
1821 .primary_key_indices(vec![0])
1822 .engine("engine")
1823 .next_column_id(3)
1824 .options(table_options)
1825 .build()
1826 .unwrap();
1827
1828 let alter_kind = AlterKind::SetTableOptions {
1829 options: vec![SetRegionOption::AppendMode(false)],
1830 };
1831 let new_meta = meta
1832 .builder_with_alter_kind("my_table", &alter_kind)
1833 .unwrap()
1834 .build()
1835 .unwrap();
1836
1837 assert_eq!(
1838 Some("false"),
1839 new_meta
1840 .options
1841 .extra_options
1842 .get(APPEND_MODE_KEY)
1843 .map(String::as_str)
1844 );
1845 assert_eq!(
1846 Some("last_non_null"),
1847 new_meta
1848 .options
1849 .extra_options
1850 .get(MERGE_MODE_KEY)
1851 .map(String::as_str)
1852 );
1853 }
1854
1855 #[test]
1856 fn test_set_preserve_row_sequence_option() {
1857 let schema = Arc::new(new_test_schema());
1858 let meta = TableMetaBuilder::empty()
1859 .schema(schema)
1860 .primary_key_indices(vec![0])
1861 .engine("engine")
1862 .next_column_id(3)
1863 .build()
1864 .unwrap();
1865
1866 let apply = |meta: &TableMeta, kind: &AlterKind| {
1867 meta.builder_with_alter_kind("my_table", kind)
1868 .unwrap()
1869 .build()
1870 .unwrap()
1871 };
1872
1873 let with_true = apply(
1874 &meta,
1875 &AlterKind::SetTableOptions {
1876 options: vec![SetRegionOption::PreserveRowSequence(true)],
1877 },
1878 );
1879 assert_eq!(
1880 Some("true"),
1881 with_true
1882 .options
1883 .extra_options
1884 .get(PRESERVE_ROW_SEQUENCE)
1885 .map(String::as_str)
1886 );
1887
1888 let set_false = apply(
1889 &with_true,
1890 &AlterKind::SetTableOptions {
1891 options: vec![SetRegionOption::PreserveRowSequence(false)],
1892 },
1893 );
1894 assert!(
1895 !set_false
1896 .options
1897 .extra_options
1898 .contains_key(PRESERVE_ROW_SEQUENCE)
1899 );
1900
1901 let unset = apply(
1902 &with_true,
1903 &AlterKind::UnsetTableOptions {
1904 keys: vec![UnsetRegionOption::PreserveRowSequence],
1905 },
1906 );
1907 assert!(
1908 !unset
1909 .options
1910 .extra_options
1911 .contains_key(PRESERVE_ROW_SEQUENCE)
1912 );
1913 }
1914
1915 #[test]
1916 fn test_set_skip_wal_updates_typed_and_extra_options() {
1917 let mut meta = TableMetaBuilder::empty()
1918 .schema(Arc::new(new_test_schema()))
1919 .primary_key_indices(vec![0])
1920 .engine("engine")
1921 .next_column_id(3)
1922 .build()
1923 .unwrap();
1924 meta.options
1925 .extra_options
1926 .insert(SKIP_WAL_KEY.to_string(), false.to_string());
1927
1928 let alter_kind = AlterKind::SetTableOptions {
1929 options: vec![SetRegionOption::SkipWal(true)],
1930 };
1931 let new_meta = meta
1932 .builder_with_alter_kind("my_table", &alter_kind)
1933 .unwrap()
1934 .build()
1935 .unwrap();
1936
1937 assert!(new_meta.options.skip_wal);
1938 assert_eq!(
1939 Some("true"),
1940 new_meta
1941 .options
1942 .extra_options
1943 .get(SKIP_WAL_KEY)
1944 .map(String::as_str)
1945 );
1946
1947 let alter_kind = AlterKind::SetTableOptions {
1948 options: vec![SetRegionOption::SkipWal(false)],
1949 };
1950 let new_meta = new_meta
1951 .builder_with_alter_kind("my_table", &alter_kind)
1952 .unwrap()
1953 .build()
1954 .unwrap();
1955
1956 assert!(!new_meta.options.skip_wal);
1957 assert_eq!(
1958 Some("false"),
1959 new_meta
1960 .options
1961 .extra_options
1962 .get(SKIP_WAL_KEY)
1963 .map(String::as_str)
1964 );
1965 }
1966
1967 #[test]
1968 fn test_set_unset_repartition_hints_together() {
1969 let meta = TableMetaBuilder::empty()
1970 .schema(Arc::new(new_test_schema()))
1971 .primary_key_indices(vec![0])
1972 .engine("engine")
1973 .next_column_id(3)
1974 .build()
1975 .unwrap();
1976 let options = vec![
1977 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string()),
1978 (
1979 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
1980 "10".to_string(),
1981 ),
1982 ];
1983 for reverse in [false, true] {
1984 let mut options = options.clone();
1985 if reverse {
1986 options.reverse();
1987 }
1988 let updated = meta
1989 .builder_with_alter_kind(
1990 "t",
1991 &AlterKind::SetAnnotations {
1992 family: AnnotationFamily::RepartitionHint,
1993 options: options.clone(),
1994 },
1995 )
1996 .unwrap()
1997 .build()
1998 .unwrap();
1999 assert_eq!(
2000 updated.options.extra_options,
2001 options.iter().cloned().collect()
2002 );
2003 for (key, value) in [
2004 (REPARTITION_COLUMN_HINT_KEY, "missing"),
2005 (REPARTITION_PARTITION_NUM_HINT_KEY, "0"),
2006 ] {
2007 let invalid = options
2008 .iter()
2009 .map(|(k, v)| {
2010 (
2011 k.clone(),
2012 if k == key {
2013 value.to_string()
2014 } else {
2015 v.clone()
2016 },
2017 )
2018 })
2019 .collect();
2020 assert!(
2021 updated
2022 .builder_with_alter_kind(
2023 "t",
2024 &AlterKind::SetAnnotations {
2025 family: AnnotationFamily::RepartitionHint,
2026 options: invalid,
2027 }
2028 )
2029 .is_err()
2030 );
2031 assert_eq!(
2032 updated.options.extra_options,
2033 options.iter().cloned().collect()
2034 );
2035 }
2036 let cleared = updated
2037 .builder_with_alter_kind(
2038 "t",
2039 &AlterKind::UnsetAnnotations {
2040 family: AnnotationFamily::RepartitionHint,
2041 keys: options.into_iter().map(|(key, _)| key).collect(),
2042 },
2043 )
2044 .unwrap()
2045 .build()
2046 .unwrap();
2047 assert!(cleared.options.extra_options.is_empty());
2048 }
2049 }
2050
2051 #[test]
2052 fn test_repartition_partition_num_hint() {
2053 for partition_key_indices in [vec![], vec![0]] {
2054 let mut meta = TableMetaBuilder::empty()
2055 .schema(Arc::new(new_test_schema()))
2056 .primary_key_indices(vec![0])
2057 .partition_key_indices(partition_key_indices)
2058 .engine("engine")
2059 .next_column_id(3)
2060 .build()
2061 .unwrap();
2062 for value in ["", " ", "0", "-1", "1.5", "abc", "4294967296"] {
2063 let err = meta
2064 .builder_with_alter_kind(
2065 "t",
2066 &AlterKind::SetAnnotations {
2067 family: AnnotationFamily::RepartitionHint,
2068 options: vec![(
2069 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2070 value.to_string(),
2071 )],
2072 },
2073 )
2074 .err()
2075 .unwrap();
2076 assert!(
2077 err.to_string().contains("expects a positive integer"),
2078 "{err}"
2079 );
2080 }
2081 for (value, expected) in [(" 8 ", "8"), ("1", "1"), ("4294967295", "4294967295")] {
2082 meta = meta
2083 .builder_with_alter_kind(
2084 "t",
2085 &AlterKind::SetAnnotations {
2086 family: AnnotationFamily::RepartitionHint,
2087 options: vec![(
2088 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2089 value.to_string(),
2090 )],
2091 },
2092 )
2093 .unwrap()
2094 .build()
2095 .unwrap();
2096 assert_eq!(
2097 meta.options.extra_options,
2098 HashMap::from([(
2099 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2100 expected.to_string()
2101 )])
2102 );
2103 }
2104 for _ in 0..2 {
2105 meta = meta
2106 .builder_with_alter_kind(
2107 "t",
2108 &AlterKind::UnsetAnnotations {
2109 family: AnnotationFamily::RepartitionHint,
2110 keys: vec![REPARTITION_PARTITION_NUM_HINT_KEY.to_string()],
2111 },
2112 )
2113 .unwrap()
2114 .build()
2115 .unwrap();
2116 assert!(meta.options.extra_options.is_empty());
2117 }
2118 }
2119 }
2120
2121 #[test]
2122 fn test_set_repartition_column_hint() {
2123 let meta = TableMetaBuilder::empty()
2124 .schema(Arc::new(new_test_schema()))
2125 .primary_key_indices(vec![0])
2126 .engine("engine")
2127 .next_column_id(3)
2128 .build()
2129 .unwrap();
2130
2131 let alter_kind = AlterKind::SetAnnotations {
2132 family: AnnotationFamily::RepartitionHint,
2133 options: vec![(
2134 REPARTITION_COLUMN_HINT_KEY.to_string(),
2135 " col1 ".to_string(),
2136 )],
2137 };
2138 let new_meta = meta
2139 .builder_with_alter_kind("my_table", &alter_kind)
2140 .unwrap()
2141 .build()
2142 .unwrap();
2143
2144 assert_eq!(
2145 Some("col1"),
2146 new_meta
2147 .options
2148 .extra_options
2149 .get(REPARTITION_COLUMN_HINT_KEY)
2150 .map(String::as_str)
2151 );
2152 }
2153
2154 #[test]
2155 fn test_set_repartition_column_hint_rejects_empty_column() {
2156 let meta = TableMetaBuilder::empty()
2157 .schema(Arc::new(new_test_schema()))
2158 .primary_key_indices(vec![0])
2159 .engine("engine")
2160 .next_column_id(3)
2161 .build()
2162 .unwrap();
2163
2164 let alter_kind = AlterKind::SetAnnotations {
2165 family: AnnotationFamily::RepartitionHint,
2166 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), " ".to_string())],
2167 };
2168 let err = meta
2169 .builder_with_alter_kind("my_table", &alter_kind)
2170 .err()
2171 .unwrap();
2172
2173 assert!(
2174 err.to_string()
2175 .contains("repartition.column.hint expects exactly one column name")
2176 );
2177 }
2178
2179 #[test]
2180 fn test_set_repartition_column_hint_rejects_multiple_columns() {
2181 let meta = TableMetaBuilder::empty()
2182 .schema(Arc::new(new_test_schema()))
2183 .primary_key_indices(vec![0])
2184 .engine("engine")
2185 .next_column_id(3)
2186 .build()
2187 .unwrap();
2188
2189 let alter_kind = AlterKind::SetAnnotations {
2190 family: AnnotationFamily::RepartitionHint,
2191 options: vec![(
2192 REPARTITION_COLUMN_HINT_KEY.to_string(),
2193 "col1,col2".to_string(),
2194 )],
2195 };
2196 let err = meta
2197 .builder_with_alter_kind("my_table", &alter_kind)
2198 .err()
2199 .unwrap();
2200
2201 assert!(
2202 err.to_string()
2203 .contains("repartition.column.hint expects exactly one column name")
2204 );
2205 }
2206
2207 #[test]
2208 fn test_set_repartition_column_hint_rejects_missing_column() {
2209 let meta = TableMetaBuilder::empty()
2210 .schema(Arc::new(new_test_schema()))
2211 .primary_key_indices(vec![0])
2212 .engine("engine")
2213 .next_column_id(3)
2214 .build()
2215 .unwrap();
2216
2217 let alter_kind = AlterKind::SetAnnotations {
2218 family: AnnotationFamily::RepartitionHint,
2219 options: vec![(
2220 REPARTITION_COLUMN_HINT_KEY.to_string(),
2221 "missing".to_string(),
2222 )],
2223 };
2224 let err = meta
2225 .builder_with_alter_kind("my_table", &alter_kind)
2226 .err()
2227 .unwrap();
2228
2229 assert!(err.to_string().contains("Column missing not exists"));
2230 }
2231
2232 #[test]
2233 fn test_set_repartition_column_hint_rejects_time_index_column() {
2234 let meta = TableMetaBuilder::empty()
2235 .schema(Arc::new(new_test_schema()))
2236 .primary_key_indices(vec![0])
2237 .engine("engine")
2238 .next_column_id(3)
2239 .build()
2240 .unwrap();
2241
2242 let alter_kind = AlterKind::SetAnnotations {
2243 family: AnnotationFamily::RepartitionHint,
2244 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), "ts".to_string())],
2245 };
2246 let err = meta
2247 .builder_with_alter_kind("my_table", &alter_kind)
2248 .err()
2249 .unwrap();
2250
2251 assert!(
2252 err.to_string()
2253 .contains("cannot set repartition.column.hint to the time index column")
2254 );
2255 }
2256
2257 #[test]
2258 fn test_set_repartition_column_hint_rejects_partitioned_table() {
2259 let meta = TableMetaBuilder::empty()
2260 .schema(Arc::new(new_test_schema()))
2261 .primary_key_indices(vec![0])
2262 .engine("engine")
2263 .next_column_id(3)
2264 .partition_key_indices(vec![0])
2265 .build()
2266 .unwrap();
2267
2268 let alter_kind = AlterKind::SetAnnotations {
2269 family: AnnotationFamily::RepartitionHint,
2270 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string())],
2271 };
2272 let err = meta
2273 .builder_with_alter_kind("my_table", &alter_kind)
2274 .err()
2275 .unwrap();
2276
2277 assert!(
2278 err.to_string()
2279 .contains("cannot set repartition.column.hint on a table with partition metadata")
2280 );
2281 }
2282
2283 #[test]
2284 fn test_unset_repartition_column_hint() {
2285 let mut table_options = TableOptions::default();
2286 table_options
2287 .extra_options
2288 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
2289 let meta = TableMetaBuilder::empty()
2290 .schema(Arc::new(new_test_schema()))
2291 .primary_key_indices(vec![0])
2292 .engine("engine")
2293 .next_column_id(3)
2294 .options(table_options)
2295 .build()
2296 .unwrap();
2297
2298 let new_meta = meta
2299 .builder_with_alter_kind(
2300 "my_table",
2301 &AlterKind::UnsetAnnotations {
2302 family: AnnotationFamily::RepartitionHint,
2303 keys: vec![REPARTITION_COLUMN_HINT_KEY.to_string()],
2304 },
2305 )
2306 .unwrap()
2307 .build()
2308 .unwrap();
2309
2310 assert!(
2311 !new_meta
2312 .options
2313 .extra_options
2314 .contains_key(REPARTITION_COLUMN_HINT_KEY)
2315 );
2316 }
2317
2318 #[test]
2319 fn test_repartition_hints_are_not_region_options() {
2320 let mut table_options = TableOptions::default();
2321 table_options
2322 .extra_options
2323 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
2324 table_options.extra_options.insert(
2325 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2326 "8".to_string(),
2327 );
2328 let table_info = TableInfoBuilder::default()
2329 .table_id(1)
2330 .table_version(0)
2331 .name("my_table")
2332 .catalog_name(DEFAULT_CATALOG_NAME)
2333 .schema_name(DEFAULT_SCHEMA_NAME)
2334 .meta(
2335 TableMetaBuilder::empty()
2336 .schema(Arc::new(new_test_schema()))
2337 .primary_key_indices(vec![0])
2338 .engine("engine")
2339 .next_column_id(3)
2340 .options(table_options)
2341 .build()
2342 .unwrap(),
2343 )
2344 .build()
2345 .unwrap();
2346
2347 assert!(
2348 !table_info
2349 .to_region_options()
2350 .contains_key(REPARTITION_PARTITION_NUM_HINT_KEY)
2351 );
2352 assert!(
2353 !table_info
2354 .to_region_options()
2355 .contains_key(REPARTITION_COLUMN_HINT_KEY)
2356 );
2357 }
2358
2359 #[test]
2360 fn test_set_auto_flush_interval() {
2361 let schema = Arc::new(new_test_schema());
2362 let table_options = TableOptions::default();
2363 let meta = TableMetaBuilder::empty()
2364 .schema(schema)
2365 .primary_key_indices(vec![0])
2366 .engine("engine")
2367 .next_column_id(3)
2368 .options(table_options)
2369 .build()
2370 .unwrap();
2371
2372 let alter_kind = AlterKind::SetTableOptions {
2373 options: vec![SetRegionOption::AutoFlushInterval(Some(
2374 std::time::Duration::from_secs(300),
2375 ))],
2376 };
2377 let new_meta = meta
2378 .builder_with_alter_kind("my_table", &alter_kind)
2379 .unwrap()
2380 .build()
2381 .unwrap();
2382
2383 assert_eq!(
2384 Some("5m"),
2385 new_meta
2386 .options
2387 .extra_options
2388 .get(AUTO_FLUSH_INTERVAL_KEY)
2389 .map(String::as_str)
2390 );
2391 }
2392
2393 #[test]
2394 fn test_set_auto_flush_interval_none_removes_existing() {
2395 let schema = Arc::new(new_test_schema());
2396 let mut table_options = TableOptions::default();
2397 table_options
2398 .extra_options
2399 .insert(AUTO_FLUSH_INTERVAL_KEY.to_string(), "5m".to_string());
2400 let meta = TableMetaBuilder::empty()
2401 .schema(schema)
2402 .primary_key_indices(vec![0])
2403 .engine("engine")
2404 .next_column_id(3)
2405 .options(table_options)
2406 .build()
2407 .unwrap();
2408
2409 let alter_kind = AlterKind::SetTableOptions {
2410 options: vec![SetRegionOption::AutoFlushInterval(None)],
2411 };
2412 let new_meta = meta
2413 .builder_with_alter_kind("my_table", &alter_kind)
2414 .unwrap()
2415 .build()
2416 .unwrap();
2417
2418 assert!(
2419 !new_meta
2420 .options
2421 .extra_options
2422 .contains_key(AUTO_FLUSH_INTERVAL_KEY)
2423 );
2424 }
2425
2426 #[test]
2427 fn test_set_twcs_trigger_persists_legacy_key() {
2428 for key in [TWCS_TRIGGER_FILE_NUM, TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM] {
2429 let mut table_options = TableOptions::default();
2430 table_options.extra_options.insert(
2431 TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM.to_string(),
2432 "4".to_string(),
2433 );
2434 let meta = TableMetaBuilder::empty()
2435 .schema(Arc::new(new_test_schema()))
2436 .primary_key_indices(vec![0])
2437 .engine("engine")
2438 .next_column_id(3)
2439 .options(table_options)
2440 .build()
2441 .unwrap();
2442 let alter_kind = AlterKind::SetTableOptions {
2443 options: vec![SetRegionOption::Twsc(key.to_string(), "8".to_string())],
2444 };
2445
2446 let new_meta = meta
2447 .builder_with_alter_kind("my_table", &alter_kind)
2448 .unwrap()
2449 .build()
2450 .unwrap();
2451
2452 assert_eq!(
2453 Some("8"),
2454 new_meta
2455 .options
2456 .extra_options
2457 .get(TWCS_TRIGGER_FILE_NUM)
2458 .map(String::as_str)
2459 );
2460 assert!(
2461 !new_meta
2462 .options
2463 .extra_options
2464 .contains_key(TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM)
2465 );
2466 }
2467 }
2468
2469 #[test]
2470 fn test_set_and_unset_max_row_group_row_count() {
2471 let meta = TableMetaBuilder::empty()
2472 .schema(Arc::new(new_test_schema()))
2473 .primary_key_indices(vec![0])
2474 .engine("engine")
2475 .next_column_id(3)
2476 .options(TableOptions::default())
2477 .build()
2478 .unwrap();
2479
2480 let alter_kind = AlterKind::SetTableOptions {
2481 options: vec![SetRegionOption::MaxRowGroupRowCount(Some(512))],
2482 };
2483 let new_meta = meta
2484 .builder_with_alter_kind("my_table", &alter_kind)
2485 .unwrap()
2486 .build()
2487 .unwrap();
2488 assert_eq!(
2489 Some("512"),
2490 new_meta
2491 .options
2492 .extra_options
2493 .get(MAX_ROW_GROUP_ROW_COUNT)
2494 .map(String::as_str)
2495 );
2496
2497 let alter_kind = AlterKind::UnsetTableOptions {
2498 keys: vec![UnsetRegionOption::MaxRowGroupRowCount],
2499 };
2500 let new_meta = new_meta
2501 .builder_with_alter_kind("my_table", &alter_kind)
2502 .unwrap()
2503 .build()
2504 .unwrap();
2505 assert!(
2506 !new_meta
2507 .options
2508 .extra_options
2509 .contains_key(MAX_ROW_GROUP_ROW_COUNT)
2510 );
2511 }
2512
2513 #[test]
2514 fn test_add_columns_multiple_times() {
2515 let schema = Arc::new(new_test_schema());
2516 let meta = TableMetaBuilder::empty()
2517 .schema(schema)
2518 .primary_key_indices(vec![0])
2519 .engine("engine")
2520 .next_column_id(3)
2521 .build()
2522 .unwrap();
2523
2524 let alter_kind = AlterKind::AddColumns {
2525 columns: vec![
2526 AddColumnRequest {
2527 column_schema: ColumnSchema::new(
2528 "col3",
2529 ConcreteDataType::int32_datatype(),
2530 true,
2531 ),
2532 is_key: true,
2533 location: None,
2534 add_if_not_exists: true,
2535 },
2536 AddColumnRequest {
2537 column_schema: ColumnSchema::new(
2538 "col3",
2539 ConcreteDataType::int32_datatype(),
2540 true,
2541 ),
2542 is_key: true,
2543 location: None,
2544 add_if_not_exists: true,
2545 },
2546 ],
2547 };
2548 let err = meta
2549 .builder_with_alter_kind("my_table", &alter_kind)
2550 .err()
2551 .unwrap();
2552 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2553 }
2554
2555 #[test]
2556 fn test_remove_columns() {
2557 let schema = Arc::new(new_test_schema());
2558 let meta = TableMetaBuilder::empty()
2559 .schema(schema.clone())
2560 .primary_key_indices(vec![0])
2561 .engine("engine")
2562 .next_column_id(3)
2563 .build()
2564 .unwrap();
2565 let meta = add_columns_to_meta(&meta);
2567
2568 let alter_kind = AlterKind::DropColumns {
2569 names: vec![String::from("col2"), String::from("my_field")],
2570 };
2571 let new_meta = meta
2572 .builder_with_alter_kind("my_table", &alter_kind)
2573 .unwrap()
2574 .build()
2575 .unwrap();
2576
2577 let names: Vec<String> = new_meta
2578 .schema
2579 .column_schemas()
2580 .iter()
2581 .map(|column_schema| column_schema.name.clone())
2582 .collect();
2583 assert_eq!(&["col1", "ts", "my_tag"], &names[..]);
2584 assert_eq!(&[0, 2], &new_meta.primary_key_indices[..]);
2585 assert_eq!(&[1], &new_meta.value_indices[..]);
2586 assert_eq!(
2587 schema.timestamp_column(),
2588 new_meta.schema.timestamp_column()
2589 );
2590 }
2591
2592 #[test]
2593 fn test_remove_multiple_columns_before_timestamp() {
2594 let column_schemas = vec![
2595 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2596 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
2597 ColumnSchema::new("col3", ConcreteDataType::int32_datatype(), true),
2598 ColumnSchema::new(
2599 "ts",
2600 ConcreteDataType::timestamp_millisecond_datatype(),
2601 false,
2602 )
2603 .with_time_index(true),
2604 ];
2605 let schema = Arc::new(
2606 SchemaBuilder::try_from(column_schemas)
2607 .unwrap()
2608 .version(123)
2609 .build()
2610 .unwrap(),
2611 );
2612 let meta = TableMetaBuilder::empty()
2613 .schema(schema.clone())
2614 .primary_key_indices(vec![1])
2615 .engine("engine")
2616 .next_column_id(4)
2617 .build()
2618 .unwrap();
2619
2620 let alter_kind = AlterKind::DropColumns {
2622 names: vec![String::from("col3"), String::from("col1")],
2623 };
2624 let new_meta = meta
2625 .builder_with_alter_kind("my_table", &alter_kind)
2626 .unwrap()
2627 .build()
2628 .unwrap();
2629
2630 let names: Vec<String> = new_meta
2631 .schema
2632 .column_schemas()
2633 .iter()
2634 .map(|column_schema| column_schema.name.clone())
2635 .collect();
2636 assert_eq!(&["col2", "ts"], &names[..]);
2637 assert_eq!(&[0], &new_meta.primary_key_indices[..]);
2638 assert_eq!(&[1], &new_meta.value_indices[..]);
2639 assert_eq!(
2640 schema.timestamp_column(),
2641 new_meta.schema.timestamp_column()
2642 );
2643 }
2644
2645 #[test]
2646 fn test_add_existing_column() {
2647 let schema = Arc::new(new_test_schema());
2648 let meta = TableMetaBuilder::empty()
2649 .schema(schema)
2650 .primary_key_indices(vec![0])
2651 .engine("engine")
2652 .next_column_id(3)
2653 .build()
2654 .unwrap();
2655
2656 let alter_kind = AlterKind::AddColumns {
2657 columns: vec![AddColumnRequest {
2658 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2659 is_key: false,
2660 location: None,
2661 add_if_not_exists: false,
2662 }],
2663 };
2664
2665 let err = meta
2666 .builder_with_alter_kind("my_table", &alter_kind)
2667 .err()
2668 .unwrap();
2669 assert_eq!(StatusCode::TableColumnExists, err.status_code());
2670
2671 let alter_kind = AlterKind::AddColumns {
2673 columns: vec![AddColumnRequest {
2674 column_schema: ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2675 is_key: true,
2676 location: None,
2677 add_if_not_exists: true,
2678 }],
2679 };
2680 let new_meta = meta
2681 .builder_with_alter_kind("my_table", &alter_kind)
2682 .unwrap()
2683 .build()
2684 .unwrap();
2685 assert_eq!(
2686 meta.schema.column_schemas(),
2687 new_meta.schema.column_schemas()
2688 );
2689 assert_eq!(meta.schema.version() + 1, new_meta.schema.version());
2690 }
2691
2692 #[test]
2693 fn test_add_different_type_column() {
2694 let schema = Arc::new(new_test_schema());
2695 let meta = TableMetaBuilder::empty()
2696 .schema(schema)
2697 .primary_key_indices(vec![0])
2698 .engine("engine")
2699 .next_column_id(3)
2700 .build()
2701 .unwrap();
2702
2703 let alter_kind = AlterKind::AddColumns {
2705 columns: vec![AddColumnRequest {
2706 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2707 is_key: false,
2708 location: None,
2709 add_if_not_exists: true,
2710 }],
2711 };
2712 let err = meta
2713 .builder_with_alter_kind("my_table", &alter_kind)
2714 .err()
2715 .unwrap();
2716 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2717 }
2718
2719 #[test]
2720 fn test_add_invalid_column() {
2721 let schema = Arc::new(new_test_schema());
2722 let meta = TableMetaBuilder::empty()
2723 .schema(schema)
2724 .primary_key_indices(vec![0])
2725 .engine("engine")
2726 .next_column_id(3)
2727 .build()
2728 .unwrap();
2729
2730 let alter_kind = AlterKind::AddColumns {
2732 columns: vec![AddColumnRequest {
2733 column_schema: ColumnSchema::new(
2734 "weny",
2735 ConcreteDataType::string_datatype(),
2736 false,
2737 ),
2738 is_key: false,
2739 location: None,
2740 add_if_not_exists: false,
2741 }],
2742 };
2743
2744 let err = meta
2745 .builder_with_alter_kind("my_table", &alter_kind)
2746 .err()
2747 .unwrap();
2748 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2749 }
2750
2751 #[test]
2752 fn test_remove_unknown_column() {
2753 let schema = Arc::new(new_test_schema());
2754 let meta = TableMetaBuilder::empty()
2755 .schema(schema)
2756 .primary_key_indices(vec![0])
2757 .engine("engine")
2758 .next_column_id(3)
2759 .build()
2760 .unwrap();
2761
2762 let alter_kind = AlterKind::DropColumns {
2763 names: vec![String::from("unknown")],
2764 };
2765
2766 let err = meta
2767 .builder_with_alter_kind("my_table", &alter_kind)
2768 .err()
2769 .unwrap();
2770 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2771 }
2772
2773 #[test]
2774 fn test_change_unknown_column_data_type() {
2775 let schema = Arc::new(new_test_schema());
2776 let meta = TableMetaBuilder::empty()
2777 .schema(schema)
2778 .primary_key_indices(vec![0])
2779 .engine("engine")
2780 .next_column_id(3)
2781 .build()
2782 .unwrap();
2783
2784 let alter_kind = AlterKind::ModifyColumnTypes {
2785 columns: vec![ModifyColumnTypeRequest {
2786 column_name: "unknown".to_string(),
2787 target_type: ConcreteDataType::string_datatype(),
2788 }],
2789 };
2790
2791 let err = meta
2792 .builder_with_alter_kind("my_table", &alter_kind)
2793 .err()
2794 .unwrap();
2795 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2796 }
2797
2798 #[test]
2799 fn test_remove_key_column() {
2800 let schema = Arc::new(new_test_schema());
2801 let meta = TableMetaBuilder::empty()
2802 .schema(schema)
2803 .primary_key_indices(vec![0])
2804 .engine("engine")
2805 .next_column_id(3)
2806 .build()
2807 .unwrap();
2808
2809 let alter_kind = AlterKind::DropColumns {
2811 names: vec![String::from("col1")],
2812 };
2813
2814 let err = meta
2815 .builder_with_alter_kind("my_table", &alter_kind)
2816 .err()
2817 .unwrap();
2818 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2819
2820 let alter_kind = AlterKind::DropColumns {
2822 names: vec![String::from("ts")],
2823 };
2824
2825 let err = meta
2826 .builder_with_alter_kind("my_table", &alter_kind)
2827 .err()
2828 .unwrap();
2829 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2830 }
2831
2832 #[test]
2833 fn test_remove_partition_column() {
2834 let schema = Arc::new(new_test_schema());
2835 let meta = TableMetaBuilder::empty()
2836 .schema(schema)
2837 .primary_key_indices(vec![])
2838 .partition_key_indices(vec![0])
2839 .engine("engine")
2840 .next_column_id(3)
2841 .build()
2842 .unwrap();
2843 let alter_kind = AlterKind::DropColumns {
2845 names: vec![String::from("col1")],
2846 };
2847
2848 let err = meta
2849 .builder_with_alter_kind("my_table", &alter_kind)
2850 .err()
2851 .unwrap();
2852 assert_matches!(err, Error::RemovePartitionColumn { .. });
2853 }
2854
2855 #[test]
2856 fn test_change_key_column_data_type() {
2857 let schema = Arc::new(new_test_schema());
2858 let meta = TableMetaBuilder::empty()
2859 .schema(schema)
2860 .primary_key_indices(vec![0])
2861 .engine("engine")
2862 .next_column_id(3)
2863 .build()
2864 .unwrap();
2865
2866 let alter_kind = AlterKind::ModifyColumnTypes {
2868 columns: vec![ModifyColumnTypeRequest {
2869 column_name: "col1".to_string(),
2870 target_type: ConcreteDataType::string_datatype(),
2871 }],
2872 };
2873
2874 let err = meta
2875 .builder_with_alter_kind("my_table", &alter_kind)
2876 .err()
2877 .unwrap();
2878 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2879
2880 let alter_kind = AlterKind::ModifyColumnTypes {
2882 columns: vec![ModifyColumnTypeRequest {
2883 column_name: "ts".to_string(),
2884 target_type: ConcreteDataType::string_datatype(),
2885 }],
2886 };
2887
2888 let err = meta
2889 .builder_with_alter_kind("my_table", &alter_kind)
2890 .err()
2891 .unwrap();
2892 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2893 }
2894
2895 #[test]
2896 fn test_alloc_new_column() {
2897 let schema = Arc::new(new_test_schema());
2898 let mut meta = TableMetaBuilder::empty()
2899 .schema(schema)
2900 .primary_key_indices(vec![0])
2901 .engine("engine")
2902 .next_column_id(3)
2903 .build()
2904 .unwrap();
2905 assert_eq!(3, meta.next_column_id);
2906
2907 let column_schema = ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true);
2908 let desc = meta.alloc_new_column("test_table", &column_schema).unwrap();
2909
2910 assert_eq!(4, meta.next_column_id);
2911 assert_eq!(column_schema.name, desc.name);
2912 }
2913
2914 #[test]
2915 fn test_add_columns_with_location() {
2916 let schema = Arc::new(new_test_schema());
2917 let meta = TableMetaBuilder::empty()
2918 .schema(schema)
2919 .primary_key_indices(vec![0])
2920 .partition_key_indices(vec![0, 2])
2922 .engine("engine")
2923 .next_column_id(3)
2924 .build()
2925 .unwrap();
2926
2927 let new_meta = add_columns_to_meta_with_location(&meta);
2928 let names: Vec<String> = new_meta
2929 .schema
2930 .column_schemas()
2931 .iter()
2932 .map(|column_schema| column_schema.name.clone())
2933 .collect();
2934 assert_eq!(
2935 &[
2936 "my_tag_first", "col1", "ts", "yet_another_field_after_ts", "my_field_after_ts", "col2", ],
2943 &names[..]
2944 );
2945 assert_eq!(&[0, 1, 3], &new_meta.primary_key_indices[..]);
2946 assert_eq!(&[2, 4, 5], &new_meta.value_indices[..]);
2947 assert_eq!(&[1, 5], &new_meta.partition_key_indices[..]);
2948 }
2949
2950 #[test]
2951 fn test_modify_column_fulltext_options() {
2952 let schema = Arc::new(new_test_schema());
2953 let meta = TableMetaBuilder::empty()
2954 .schema(schema)
2955 .primary_key_indices(vec![0])
2956 .engine("engine")
2957 .next_column_id(3)
2958 .build()
2959 .unwrap();
2960
2961 let alter_kind = AlterKind::SetIndexes {
2962 options: vec![SetIndexOption::Fulltext {
2963 column_name: "col1".to_string(),
2964 options: FulltextOptions::default(),
2965 }],
2966 };
2967 let err = meta
2968 .builder_with_alter_kind("my_table", &alter_kind)
2969 .err()
2970 .unwrap();
2971 assert_eq!(
2972 "Invalid column option, column name: col1, error: FULLTEXT index only supports string type",
2973 err.to_string()
2974 );
2975
2976 let new_meta = add_columns_to_meta_with_location(&meta);
2978 let alter_kind = AlterKind::SetIndexes {
2979 options: vec![SetIndexOption::Fulltext {
2980 column_name: "my_tag_first".to_string(),
2981 options: FulltextOptions::new_unchecked(
2982 true,
2983 FulltextAnalyzer::Chinese,
2984 true,
2985 FulltextBackend::Bloom,
2986 1000,
2987 0.01,
2988 ),
2989 }],
2990 };
2991 let new_meta = new_meta
2992 .builder_with_alter_kind("my_table", &alter_kind)
2993 .unwrap()
2994 .build()
2995 .unwrap();
2996 let column_schema = new_meta
2997 .schema
2998 .column_schema_by_name("my_tag_first")
2999 .unwrap();
3000 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
3001 assert!(fulltext_options.enable);
3002 assert_eq!(
3003 datatypes::schema::FulltextAnalyzer::Chinese,
3004 fulltext_options.analyzer
3005 );
3006 assert!(fulltext_options.case_sensitive);
3007
3008 let alter_kind = AlterKind::UnsetIndexes {
3009 options: vec![UnsetIndexOption::Fulltext {
3010 column_name: "my_tag_first".to_string(),
3011 }],
3012 };
3013 let new_meta = new_meta
3014 .builder_with_alter_kind("my_table", &alter_kind)
3015 .unwrap()
3016 .build()
3017 .unwrap();
3018 let column_schema = new_meta
3019 .schema
3020 .column_schema_by_name("my_tag_first")
3021 .unwrap();
3022 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
3023 assert!(!fulltext_options.enable);
3024 }
3025
3026 #[test]
3027 fn test_table_info_serde_compatibility() {
3028 let serialized = r#"{"ident":{"table_id":1024,"version":1},"name":"foo","desc":"my table","catalog_name":"greptime","schema_name":"public","meta":{"schema":{"column_schemas":[{"name":"col1","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"ts","data_type":{"Timestamp":{"Millisecond":null}},"is_nullable":false,"is_time_index":true,"default_constraint":null,"metadata":{"greptime:time_index":"true"}},{"name":"col2","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}}],"timestamp_index":1,"version":123},"primary_key_indices":[0],"value_indices":[1,2],"engine":"mito","next_column_id":3,"options":{"write_buffer_size":null,"ttl":"1h","skip_wal":false,"extra_options":{}},"created_on":"-262143-01-01T00:00:00Z","updated_on":"+262142-12-31T23:59:59.999999999Z","partition_key_indices":[2],"column_ids":[0,1,2]},"table_type":"Base"}"#;
3061
3062 let actual: TableInfo = serde_json::from_str(serialized).unwrap();
3063 let expected = TableInfo {
3064 ident: TableIdent {
3065 table_id: 1024,
3066 version: 1,
3067 },
3068 name: "foo".to_string(),
3069 desc: Some("my table".to_string()),
3070 catalog_name: "greptime".to_string(),
3071 schema_name: "public".to_string(),
3072 meta: TableMeta {
3073 schema: Arc::new(new_test_schema()),
3074 primary_key_indices: vec![0],
3075 value_indices: vec![1, 2],
3076 engine: "mito".to_string(),
3077 next_column_id: 3,
3078 options: TableOptions {
3079 ttl: Some(common_time::TimeToLive::Duration(
3080 std::time::Duration::from_secs(3600),
3081 )),
3082 ..Default::default()
3083 },
3084 created_on: DateTime::<Utc>::MIN_UTC,
3085 updated_on: DateTime::<Utc>::MAX_UTC,
3086 partition_key_indices: vec![2],
3087 column_ids: vec![0, 1, 2],
3088 },
3089 table_type: TableType::Base,
3090 };
3091 assert_eq!(actual, expected);
3092 }
3093
3094 use crate::requests::{SEMANTIC_METRIC_UNIT, SEMANTIC_SIGNAL_TYPE};
3095
3096 fn semantic_test_meta() -> TableMeta {
3099 let column_schemas = vec![
3100 ColumnSchema::new("host", ConcreteDataType::string_datatype(), true),
3101 ColumnSchema::new(
3102 "ts",
3103 ConcreteDataType::timestamp_millisecond_datatype(),
3104 false,
3105 )
3106 .with_time_index(true),
3107 ColumnSchema::new("payload", ConcreteDataType::binary_datatype(), true),
3108 ColumnSchema::new("service", ConcreteDataType::string_datatype(), true),
3109 ColumnSchema::new("note", ConcreteDataType::string_datatype(), true),
3110 ];
3111 let schema = Arc::new(
3112 SchemaBuilder::try_from(column_schemas)
3113 .unwrap()
3114 .build()
3115 .unwrap(),
3116 );
3117 TableMetaBuilder::empty()
3118 .schema(schema)
3119 .primary_key_indices(vec![0])
3120 .engine("engine")
3121 .next_column_id(5)
3122 .build()
3123 .unwrap()
3124 }
3125
3126 #[test]
3127 fn test_set_semantic_annotations() {
3128 let meta = semantic_test_meta();
3129 let alter_kind = AlterKind::SetAnnotations {
3131 family: AnnotationFamily::Semantic,
3132 options: vec![
3133 (SEMANTIC_SIGNAL_TYPE.to_string(), "trace".to_string()),
3134 (
3135 "greptime.semantic.entity.service.id".to_string(),
3136 "service".to_string(),
3137 ),
3138 ],
3139 };
3140 let new_meta = meta
3141 .builder_with_alter_kind("my_table", &alter_kind)
3142 .unwrap()
3143 .build()
3144 .unwrap();
3145 assert_eq!(
3146 new_meta.options.extra_options.get(SEMANTIC_SIGNAL_TYPE),
3147 Some(&"trace".to_string())
3148 );
3149 assert_eq!(
3150 new_meta
3151 .options
3152 .extra_options
3153 .get("greptime.semantic.entity.service.id"),
3154 Some(&"service".to_string())
3155 );
3156 }
3157
3158 #[test]
3159 fn test_repartition_hint_batch_rejects_duplicate_keys() {
3160 let meta = TableMetaBuilder::empty()
3161 .schema(Arc::new(new_test_schema()))
3162 .primary_key_indices(vec![0])
3163 .engine("engine")
3164 .next_column_id(3)
3165 .build()
3166 .unwrap();
3167
3168 let dup_set = AlterKind::SetAnnotations {
3171 family: AnnotationFamily::RepartitionHint,
3172 options: vec![
3173 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string()),
3174 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col2".to_string()),
3175 ],
3176 };
3177 let err = meta
3178 .builder_with_alter_kind("my_table", &dup_set)
3179 .err()
3180 .unwrap();
3181 assert!(
3182 err.to_string().contains("duplicate repartition hint keys"),
3183 "{err}"
3184 );
3185
3186 let dup_unset = AlterKind::UnsetAnnotations {
3187 family: AnnotationFamily::RepartitionHint,
3188 keys: vec![
3189 REPARTITION_COLUMN_HINT_KEY.to_string(),
3190 REPARTITION_COLUMN_HINT_KEY.to_string(),
3191 ],
3192 };
3193 let err = meta
3194 .builder_with_alter_kind("my_table", &dup_unset)
3195 .err()
3196 .unwrap();
3197 assert!(
3198 err.to_string().contains("duplicate repartition hint keys"),
3199 "{err}"
3200 );
3201 }
3202
3203 #[test]
3204 fn test_set_semantic_annotations_rejects_invalid() {
3205 let meta = semantic_test_meta();
3206 let cases = [
3207 (
3208 "greptime.semantic.unknown_key",
3209 "x",
3210 "unknown semantic option",
3211 ),
3212 (SEMANTIC_SIGNAL_TYPE, "garbage", "invalid value"),
3213 (
3214 "greptime.semantic.entity.host.id",
3215 "no_such_column",
3216 "no_such_column",
3217 ),
3218 (
3219 "greptime.semantic.entity.host.id",
3220 "payload",
3221 "cannot render as a string",
3222 ),
3223 ];
3224 for (key, value, needle) in cases {
3225 let alter_kind = AlterKind::SetAnnotations {
3226 family: AnnotationFamily::Semantic,
3227 options: vec![(key.to_string(), value.to_string())],
3228 };
3229 let err = meta
3230 .builder_with_alter_kind("my_table", &alter_kind)
3231 .err()
3232 .unwrap();
3233 assert!(
3234 err.to_string().contains(needle),
3235 "key `{key}`: unexpected error `{err}`"
3236 );
3237 }
3238
3239 let missing = meta
3242 .builder_with_alter_kind(
3243 "my_table",
3244 &AlterKind::SetAnnotations {
3245 family: AnnotationFamily::Semantic,
3246 options: vec![(
3247 "greptime.semantic.entity.host.id".to_string(),
3248 "no_such_column".to_string(),
3249 )],
3250 },
3251 )
3252 .err()
3253 .unwrap();
3254 assert_eq!(
3255 common_error::status_code::StatusCode::TableColumnNotFound,
3256 common_error::ext::ErrorExt::status_code(&missing)
3257 );
3258 }
3259
3260 #[test]
3261 fn test_unset_semantic_annotations() {
3262 let mut meta = semantic_test_meta();
3263 meta.options
3264 .extra_options
3265 .insert(SEMANTIC_SIGNAL_TYPE.to_string(), "trace".to_string());
3266 meta.options
3268 .extra_options
3269 .insert("greptime.semantic.future_key".to_string(), "x".to_string());
3270
3271 let alter_kind = AlterKind::UnsetAnnotations {
3272 family: AnnotationFamily::Semantic,
3273 keys: vec![
3274 SEMANTIC_SIGNAL_TYPE.to_string(),
3275 "greptime.semantic.future_key".to_string(),
3276 SEMANTIC_METRIC_UNIT.to_string(),
3278 ],
3279 };
3280 let new_meta = meta
3281 .builder_with_alter_kind("my_table", &alter_kind)
3282 .unwrap()
3283 .build()
3284 .unwrap();
3285 assert!(
3286 !new_meta
3287 .options
3288 .extra_options
3289 .contains_key(SEMANTIC_SIGNAL_TYPE)
3290 );
3291 assert!(
3292 !new_meta
3293 .options
3294 .extra_options
3295 .contains_key("greptime.semantic.future_key")
3296 );
3297
3298 let outside = AlterKind::UnsetAnnotations {
3299 family: AnnotationFamily::Semantic,
3300 keys: vec!["ttl".to_string()],
3301 };
3302 let err = meta
3303 .builder_with_alter_kind("my_table", &outside)
3304 .err()
3305 .unwrap();
3306 assert!(err.to_string().contains("annotation namespace"), "{err}");
3307 }
3308
3309 #[test]
3310 fn test_modify_entity_column_type_keeps_string_form() {
3311 let mut meta = semantic_test_meta();
3312 meta.options.extra_options.insert(
3313 "greptime.semantic.entity.service.id".to_string(),
3314 "service".to_string(),
3315 );
3316
3317 let alter_kind = AlterKind::ModifyColumnTypes {
3318 columns: vec![ModifyColumnTypeRequest {
3319 column_name: "service".to_string(),
3320 target_type: ConcreteDataType::binary_datatype(),
3321 }],
3322 };
3323 let err = meta
3324 .builder_with_alter_kind("my_table", &alter_kind)
3325 .err()
3326 .unwrap();
3327 assert!(
3328 err.to_string()
3329 .contains("must keep a type that renders as a string"),
3330 "{err}"
3331 );
3332
3333 let alter_kind = AlterKind::ModifyColumnTypes {
3335 columns: vec![ModifyColumnTypeRequest {
3336 column_name: "note".to_string(),
3337 target_type: ConcreteDataType::binary_datatype(),
3338 }],
3339 };
3340 meta.builder_with_alter_kind("my_table", &alter_kind)
3341 .unwrap();
3342 }
3343
3344 #[test]
3345 fn test_stale_entity_declaration_guards_readd_and_reports_missing_column() {
3346 let mut meta = semantic_test_meta();
3348 meta.options.extra_options.insert(
3349 "greptime.semantic.entity.service.id".to_string(),
3350 "gone".to_string(),
3351 );
3352
3353 let modify = AlterKind::ModifyColumnTypes {
3356 columns: vec![ModifyColumnTypeRequest {
3357 column_name: "gone".to_string(),
3358 target_type: ConcreteDataType::binary_datatype(),
3359 }],
3360 };
3361 let err = meta
3362 .builder_with_alter_kind("my_table", &modify)
3363 .err()
3364 .unwrap();
3365 assert_eq!(
3366 common_error::status_code::StatusCode::TableColumnNotFound,
3367 common_error::ext::ErrorExt::status_code(&err)
3368 );
3369
3370 let add = |ty: ConcreteDataType| AlterKind::AddColumns {
3373 columns: vec![AddColumnRequest {
3374 column_schema: ColumnSchema::new("gone", ty, true),
3375 is_key: false,
3376 location: None,
3377 add_if_not_exists: false,
3378 }],
3379 };
3380 let err = meta
3381 .builder_with_alter_kind("my_table", &add(ConcreteDataType::binary_datatype()))
3382 .err()
3383 .unwrap();
3384 assert!(
3385 err.to_string()
3386 .contains("must keep a type that renders as a string"),
3387 "{err}"
3388 );
3389
3390 meta.builder_with_alter_kind("my_table", &add(ConcreteDataType::string_datatype()))
3392 .unwrap();
3393 }
3394}