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