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