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