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!(
1100 !self.partition_key_indices.contains(&index),
1101 error::InvalidAlterRequestSnafu {
1102 table: table_name,
1103 err: format!("Not allowed to change partition column '{}'", column.name)
1104 }
1105 );
1106 ensure!(
1107 column
1108 .data_type
1109 .can_arrow_type_cast_to(&col_to_change.target_type),
1110 error::InvalidAlterRequestSnafu {
1111 table: table_name,
1112 err: format!(
1113 "column '{}' cannot be cast automatically to type '{}'",
1114 col_to_change.column_name, col_to_change.target_type,
1115 ),
1116 }
1117 );
1118
1119 ensure!(
1120 column.is_nullable(),
1121 error::InvalidAlterRequestSnafu {
1122 table: table_name,
1123 err: format!(
1124 "column '{}' must be nullable to ensure safe conversion.",
1125 col_to_change.column_name,
1126 ),
1127 }
1128 );
1129 }
1130 ensure!(
1131 modify_column_types
1132 .insert(&col_to_change.column_name, col_to_change)
1133 .is_none(),
1134 error::InvalidAlterRequestSnafu {
1135 table: table_name,
1136 err: format!(
1137 "change column datatype {} more than once",
1138 col_to_change.column_name
1139 ),
1140 }
1141 );
1142 }
1143 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
1146 for mut column in table_schema.column_schemas().iter().cloned() {
1147 if let Some(change_column) = modify_column_types.get(&column.name) {
1148 column.data_type = change_column.target_type.clone();
1149 let new_default = if let Some(default_value) = column.default_constraint() {
1150 Some(
1151 default_value
1152 .cast_to_datatype(&change_column.target_type)
1153 .with_context(|_| error::CastDefaultValueSnafu {
1154 reason: format!(
1155 "Failed to cast default value from {:?} to type {:?}",
1156 default_value, change_column.target_type
1157 ),
1158 })?,
1159 )
1160 } else {
1161 None
1162 };
1163 column = column
1164 .clone()
1165 .with_default_constraint(new_default.clone())
1166 .with_context(|_| error::CastDefaultValueSnafu {
1167 reason: format!("Failed to set new default: {:?}", new_default),
1168 })?;
1169 }
1170 columns.push(column)
1171 }
1172
1173 let mut builder = SchemaBuilder::try_from_columns(columns)
1174 .with_context(|_| error::SchemaBuildSnafu {
1175 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1176 })?
1177 .version(table_schema.version() + 1);
1179 for (k, v) in table_schema.metadata().iter() {
1180 builder = builder.add_metadata(k, v);
1181 }
1182 let new_schema = builder.build().with_context(|_| {
1183 let column_names: Vec<_> = requests
1184 .iter()
1185 .map(|request| &request.column_name)
1186 .collect();
1187
1188 error::SchemaBuildSnafu {
1189 msg: format!(
1190 "Table {table_name} cannot change datatype with columns {column_names:?}"
1191 ),
1192 }
1193 })?;
1194
1195 let _ = meta_builder
1196 .schema(Arc::new(new_schema))
1197 .primary_key_indices(self.primary_key_indices.clone());
1198
1199 Ok(meta_builder)
1200 }
1201
1202 fn set_json_settings(
1203 &self,
1204 table_name: &str,
1205 req: &SetJsonSettingsRequest,
1206 ) -> Result<TableMetaBuilder> {
1207 let table_schema = &self.schema;
1208 let idx = table_schema
1209 .column_index_by_name(&req.column_name)
1210 .with_context(|| error::ColumnNotExistsSnafu {
1211 column_name: &req.column_name,
1212 table_name,
1213 })?;
1214 let col = &table_schema.column_schemas()[idx];
1215
1216 ensure!(
1217 !self.primary_key_indices.contains(&idx) && table_schema.timestamp_index() != Some(idx),
1218 error::InvalidAlterRequestSnafu {
1219 table: table_name,
1220 err: format!(
1221 "Not allowed to change JSON settings for key or timestamp column '{}'",
1222 col.name
1223 ),
1224 }
1225 );
1226 ensure!(
1227 col.data_type.is_json2(),
1228 error::InvalidAlterRequestSnafu {
1229 table: table_name,
1230 err: format!("column '{}' is not a JSON2 column", col.name),
1231 }
1232 );
1233
1234 let target_metadata =
1235 json2_metadata_with_updated_settings(col.metadata(), req.settings.clone()).map_err(
1236 |err| {
1237 error::InvalidAlterRequestSnafu {
1238 table: table_name,
1239 err: err.to_string(),
1240 }
1241 .build()
1242 },
1243 )?;
1244
1245 let mut cols = table_schema.column_schemas().to_vec();
1246 *cols[idx].mut_metadata() = target_metadata;
1247
1248 let mut builder = SchemaBuilder::try_from_columns(cols)
1249 .with_context(|_| error::SchemaBuildSnafu {
1250 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1251 })?
1252 .version(table_schema.version() + 1);
1253
1254 for (k, v) in table_schema.metadata().iter() {
1255 builder = builder.add_metadata(k, v);
1256 }
1257
1258 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1259 msg: format!(
1260 "Table {table_name} cannot change JSON settings for column {}",
1261 req.column_name
1262 ),
1263 })?;
1264
1265 let mut meta_builder = self.new_meta_builder();
1266 let _ = meta_builder
1267 .schema(Arc::new(new_schema))
1268 .primary_key_indices(self.primary_key_indices.clone());
1269
1270 Ok(meta_builder)
1271 }
1272
1273 fn split_requests_by_column_location<'a>(
1275 &self,
1276 table_name: &str,
1277 requests: &'a [AddColumnRequest],
1278 ) -> Result<SplitResult<'a>> {
1279 let table_schema = &self.schema;
1280 let mut columns_at_first = Vec::new();
1281 let mut columns_at_after = HashMap::new();
1282 let mut columns_at_last = Vec::new();
1283 let mut column_names = Vec::with_capacity(requests.len());
1284 for request in requests {
1285 let column_name = &request.column_schema.name;
1287 column_names.push(column_name.clone());
1288 ensure!(
1289 table_schema.column_schema_by_name(column_name).is_none(),
1290 error::ColumnExistsSnafu {
1291 column_name,
1292 table_name,
1293 }
1294 );
1295 match request.location.as_ref() {
1296 Some(AddColumnLocation::First) => {
1297 columns_at_first.push(request);
1298 }
1299 Some(AddColumnLocation::After { column_name }) => {
1300 ensure!(
1301 table_schema.column_schema_by_name(column_name).is_some(),
1302 error::ColumnNotExistsSnafu {
1303 column_name,
1304 table_name,
1305 }
1306 );
1307 columns_at_after
1308 .entry(column_name.clone())
1309 .or_insert(Vec::new())
1310 .push(request);
1311 }
1312 None => {
1313 columns_at_last.push(request);
1314 }
1315 }
1316 }
1317 Ok(SplitResult {
1318 columns_at_first,
1319 columns_at_after,
1320 columns_at_last,
1321 column_names,
1322 })
1323 }
1324
1325 fn drop_defaults(&self, table_name: &str, column_names: &[String]) -> Result<TableMetaBuilder> {
1326 let table_schema = &self.schema;
1327 let mut meta_builder = self.new_meta_builder();
1328 let mut columns = Vec::with_capacity(table_schema.num_columns());
1329 for column_schema in table_schema.column_schemas() {
1330 if let Some(name) = column_names.iter().find(|s| **s == column_schema.name) {
1331 ensure!(
1333 column_schema.default_constraint().is_some(),
1334 error::InvalidAlterRequestSnafu {
1335 table: table_name,
1336 err: format!("column {name} does not have a default value"),
1337 }
1338 );
1339 if !column_schema.is_nullable() {
1340 return error::InvalidAlterRequestSnafu {
1341 table: table_name,
1342 err: format!(
1343 "column {name} is not nullable and `default` cannot be dropped",
1344 ),
1345 }
1346 .fail();
1347 }
1348 let new_column_schema = column_schema.clone();
1349 let new_column_schema = new_column_schema
1350 .with_default_constraint(None)
1351 .with_context(|_| error::SchemaBuildSnafu {
1352 msg: format!("Table {table_name} cannot drop default values"),
1353 })?;
1354 columns.push(new_column_schema);
1355 } else {
1356 columns.push(column_schema.clone());
1357 }
1358 }
1359
1360 let mut builder = SchemaBuilder::try_from_columns(columns)
1361 .with_context(|_| error::SchemaBuildSnafu {
1362 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1363 })?
1364 .version(table_schema.version() + 1);
1366 for (k, v) in table_schema.metadata().iter() {
1367 builder = builder.add_metadata(k, v);
1368 }
1369 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1370 msg: format!("Table {table_name} cannot drop default values"),
1371 })?;
1372
1373 let _ = meta_builder.schema(Arc::new(new_schema));
1374
1375 Ok(meta_builder)
1376 }
1377
1378 fn set_defaults(
1379 &self,
1380 table_name: &str,
1381 set_defaults: &[SetDefaultRequest],
1382 ) -> Result<TableMetaBuilder> {
1383 let table_schema = &self.schema;
1384 let mut meta_builder = self.new_meta_builder();
1385 let mut columns = Vec::with_capacity(table_schema.num_columns());
1386 for column_schema in table_schema.column_schemas() {
1387 if let Some(set_default) = set_defaults
1388 .iter()
1389 .find(|s| s.column_name == column_schema.name)
1390 {
1391 let new_column_schema = column_schema.clone();
1392 let new_column_schema = new_column_schema
1393 .with_default_constraint(set_default.default_constraint.clone())
1394 .with_context(|_| error::SchemaBuildSnafu {
1395 msg: format!("Table {table_name} cannot set default values"),
1396 })?;
1397 columns.push(new_column_schema);
1398 } else {
1399 columns.push(column_schema.clone());
1400 }
1401 }
1402
1403 let mut builder = SchemaBuilder::try_from_columns(columns)
1404 .with_context(|_| error::SchemaBuildSnafu {
1405 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1406 })?
1407 .version(table_schema.version() + 1);
1409 for (k, v) in table_schema.metadata().iter() {
1410 builder = builder.add_metadata(k, v);
1411 }
1412 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1413 msg: format!("Table {table_name} cannot set default values"),
1414 })?;
1415
1416 let _ = meta_builder.schema(Arc::new(new_schema));
1417
1418 Ok(meta_builder)
1419 }
1420}
1421
1422#[derive(Clone, Debug, PartialEq, Eq, Builder, Serialize, Deserialize)]
1423#[builder(pattern = "owned")]
1424pub struct TableInfo {
1425 #[builder(default, setter(into))]
1427 pub ident: TableIdent,
1428 #[builder(setter(into))]
1430 pub name: String,
1431 #[builder(default, setter(into))]
1433 pub desc: Option<String>,
1434 #[builder(default = "DEFAULT_CATALOG_NAME.to_string()", setter(into))]
1435 pub catalog_name: String,
1436 #[builder(default = "DEFAULT_SCHEMA_NAME.to_string()", setter(into))]
1437 pub schema_name: String,
1438 pub meta: TableMeta,
1439 #[builder(default = "TableType::Base")]
1440 pub table_type: TableType,
1441}
1442
1443pub type TableInfoRef = Arc<TableInfo>;
1444
1445impl TableInfo {
1446 pub fn table_id(&self) -> TableId {
1447 self.ident.table_id
1448 }
1449
1450 pub fn full_table_name(&self) -> String {
1452 common_catalog::format_full_table_name(&self.catalog_name, &self.schema_name, &self.name)
1453 }
1454
1455 pub fn get_db_string(&self) -> String {
1456 common_catalog::build_db_string(&self.catalog_name, &self.schema_name)
1457 }
1458
1459 pub fn is_physical_table(&self) -> bool {
1461 self.meta
1462 .options
1463 .extra_options
1464 .contains_key(PHYSICAL_TABLE_METADATA_KEY)
1465 }
1466
1467 pub fn is_ttl_instant_table(&self) -> bool {
1469 self.meta
1470 .options
1471 .ttl
1472 .map(|t| t.is_instant())
1473 .unwrap_or(false)
1474 }
1475}
1476
1477impl TableInfoBuilder {
1478 pub fn new<S: Into<String>>(name: S, meta: TableMeta) -> Self {
1479 Self {
1480 name: Some(name.into()),
1481 meta: Some(meta),
1482 ..Default::default()
1483 }
1484 }
1485
1486 pub fn table_id(mut self, id: TableId) -> Self {
1487 let ident = self.ident.get_or_insert_with(TableIdent::default);
1488 ident.table_id = id;
1489 self
1490 }
1491
1492 pub fn table_version(mut self, version: TableVersion) -> Self {
1493 let ident = self.ident.get_or_insert_with(TableIdent::default);
1494 ident.version = version;
1495 self
1496 }
1497}
1498
1499impl TableIdent {
1500 pub fn new(table_id: TableId) -> Self {
1501 Self {
1502 table_id,
1503 version: 0,
1504 }
1505 }
1506}
1507
1508impl From<TableId> for TableIdent {
1509 fn from(table_id: TableId) -> Self {
1510 Self::new(table_id)
1511 }
1512}
1513
1514impl TableInfo {
1515 pub fn name_to_ids(&self) -> Option<HashMap<String, ColumnId>> {
1519 let column_schemas = self.meta.schema.column_schemas();
1520 if self.meta.column_ids.len() != column_schemas.len() {
1521 None
1522 } else {
1523 Some(
1524 self.meta
1525 .column_ids
1526 .iter()
1527 .enumerate()
1528 .map(|(index, id)| (column_schemas[index].name.clone(), *id))
1529 .collect(),
1530 )
1531 }
1532 }
1533
1534 pub fn sort_columns(&mut self) {
1536 let column_schemas = self.meta.schema.column_schemas();
1537 let primary_keys = self
1538 .meta
1539 .primary_key_indices
1540 .iter()
1541 .map(|index| column_schemas[*index].name.clone())
1542 .collect::<HashSet<_>>();
1543
1544 let name_to_ids = self.name_to_ids().unwrap_or_default();
1545 let mut column_schemas = column_schemas.to_vec();
1546 column_schemas.sort_unstable_by(|a, b| a.name.cmp(&b.name));
1547
1548 let mut primary_key_indices = Vec::with_capacity(primary_keys.len());
1550 let mut value_indices = Vec::with_capacity(column_schemas.len() - primary_keys.len());
1551 let mut column_ids = Vec::with_capacity(column_schemas.len());
1552 for (index, column_schema) in column_schemas.iter().enumerate() {
1553 if primary_keys.contains(&column_schema.name) {
1554 primary_key_indices.push(index);
1555 } else {
1556 value_indices.push(index);
1557 }
1558 if let Some(id) = name_to_ids.get(&column_schema.name) {
1559 column_ids.push(*id);
1560 }
1561 }
1562
1563 self.meta.schema = Arc::new(Schema::new_with_version(
1565 column_schemas,
1566 self.meta.schema.version(),
1567 ));
1568 self.meta.primary_key_indices = primary_key_indices;
1569 self.meta.value_indices = value_indices;
1570 self.meta.column_ids = column_ids;
1571 }
1572
1573 pub fn to_region_options(&self) -> HashMap<String, String> {
1577 let mut options = HashMap::from(&self.meta.options);
1578 options.remove(REPARTITION_COLUMN_HINT_KEY);
1579 options.remove(REPARTITION_PARTITION_NUM_HINT_KEY);
1580 options
1581 }
1582
1583 pub fn table_ref(&self) -> TableReference<'_> {
1585 TableReference::full(
1586 self.catalog_name.as_str(),
1587 self.schema_name.as_str(),
1588 self.name.as_str(),
1589 )
1590 }
1591}
1592
1593fn entity_option_referencing<'a>(options: &'a TableOptions, column: &str) -> Option<&'a str> {
1594 options.extra_options.iter().find_map(|(key, value)| {
1595 (parse_entity_option_key(key).is_some()
1596 && parse_entity_columns(value).iter().any(|c| c == column))
1597 .then_some(key.as_str())
1598 })
1599}
1600
1601fn set_column_fulltext_options(
1610 column_schema: &mut ColumnSchema,
1611 column_name: &str,
1612 options: &FulltextOptions,
1613 current_options: Option<FulltextOptions>,
1614) -> Result<()> {
1615 if let Some(current_options) = current_options {
1616 ensure!(
1617 current_options.analyzer == options.analyzer
1618 && current_options.case_sensitive == options.case_sensitive,
1619 error::InvalidColumnOptionSnafu {
1620 column_name,
1621 msg: format!(
1622 "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1623 current_options.analyzer, current_options.case_sensitive
1624 ),
1625 }
1626 );
1627 }
1628
1629 column_schema
1630 .set_fulltext_options(options)
1631 .context(error::SetFulltextOptionsSnafu { column_name })?;
1632
1633 Ok(())
1634}
1635
1636fn unset_column_fulltext_options(
1637 column_schema: &mut ColumnSchema,
1638 column_name: &str,
1639 current_options: Option<FulltextOptions>,
1640) -> Result<()> {
1641 ensure!(
1642 current_options
1643 .as_ref()
1644 .is_some_and(|options| options.enable),
1645 error::InvalidColumnOptionSnafu {
1646 column_name,
1647 msg: "FULLTEXT index already disabled".to_string(),
1648 }
1649 );
1650
1651 let mut options = current_options.unwrap();
1652 options.enable = false;
1653 column_schema
1654 .set_fulltext_options(&options)
1655 .context(error::SetFulltextOptionsSnafu { column_name })?;
1656
1657 Ok(())
1658}
1659
1660fn set_column_skipping_index_options(
1661 column_schema: &mut ColumnSchema,
1662 column_name: &str,
1663 options: &SkippingIndexOptions,
1664) -> Result<()> {
1665 column_schema
1666 .set_skipping_options(options)
1667 .context(error::SetSkippingOptionsSnafu { column_name })?;
1668
1669 Ok(())
1670}
1671
1672fn unset_column_skipping_index_options(
1673 column_schema: &mut ColumnSchema,
1674 column_name: &str,
1675) -> Result<()> {
1676 column_schema
1677 .unset_skipping_options()
1678 .context(error::UnsetSkippingOptionsSnafu { column_name })?;
1679 Ok(())
1680}
1681
1682#[cfg(test)]
1683mod tests {
1684 use std::assert_matches;
1685
1686 use common_error::ext::ErrorExt;
1687 use common_error::status_code::StatusCode;
1688 use datatypes::data_type::ConcreteDataType;
1689 use datatypes::extension::json::{Json2ExtensionType, JsonMetadata};
1690 use datatypes::json::JsonSettings;
1691 use datatypes::schema::{
1692 ColumnSchema, FulltextAnalyzer, FulltextBackend, Schema, SchemaBuilder,
1693 };
1694 use datatypes::types::JsonType;
1695
1696 use super::*;
1697 use crate::Error;
1698
1699 fn new_test_schema() -> Schema {
1701 let column_schemas = vec![
1702 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
1703 ColumnSchema::new(
1704 "ts",
1705 ConcreteDataType::timestamp_millisecond_datatype(),
1706 false,
1707 )
1708 .with_time_index(true),
1709 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
1710 ];
1711 SchemaBuilder::try_from(column_schemas)
1712 .unwrap()
1713 .version(123)
1714 .build()
1715 .unwrap()
1716 }
1717
1718 fn add_columns_to_meta(meta: &TableMeta) -> TableMeta {
1719 let new_tag = ColumnSchema::new("my_tag", ConcreteDataType::string_datatype(), true);
1720 let new_field = ColumnSchema::new("my_field", ConcreteDataType::string_datatype(), true);
1721 let alter_kind = AlterKind::AddColumns {
1722 columns: vec![
1723 AddColumnRequest {
1724 column_schema: new_tag,
1725 is_key: true,
1726 location: None,
1727 add_if_not_exists: false,
1728 },
1729 AddColumnRequest {
1730 column_schema: new_field,
1731 is_key: false,
1732 location: None,
1733 add_if_not_exists: false,
1734 },
1735 ],
1736 };
1737
1738 let builder = meta
1739 .builder_with_alter_kind("my_table", &alter_kind)
1740 .unwrap();
1741 builder.build().unwrap()
1742 }
1743
1744 fn add_columns_to_meta_with_location(meta: &TableMeta) -> TableMeta {
1745 let new_tag = ColumnSchema::new("my_tag_first", ConcreteDataType::string_datatype(), true);
1746 let new_field = ColumnSchema::new(
1747 "my_field_after_ts",
1748 ConcreteDataType::string_datatype(),
1749 true,
1750 );
1751 let yet_another_field = ColumnSchema::new(
1752 "yet_another_field_after_ts",
1753 ConcreteDataType::int64_datatype(),
1754 true,
1755 );
1756 let alter_kind = AlterKind::AddColumns {
1757 columns: vec![
1758 AddColumnRequest {
1759 column_schema: new_tag,
1760 is_key: true,
1761 location: Some(AddColumnLocation::First),
1762 add_if_not_exists: false,
1763 },
1764 AddColumnRequest {
1765 column_schema: new_field,
1766 is_key: false,
1767 location: Some(AddColumnLocation::After {
1768 column_name: "ts".to_string(),
1769 }),
1770 add_if_not_exists: false,
1771 },
1772 AddColumnRequest {
1773 column_schema: yet_another_field,
1774 is_key: true,
1775 location: Some(AddColumnLocation::After {
1776 column_name: "ts".to_string(),
1777 }),
1778 add_if_not_exists: false,
1779 },
1780 ],
1781 };
1782
1783 let builder = meta
1784 .builder_with_alter_kind("my_table", &alter_kind)
1785 .unwrap();
1786 builder.build().unwrap()
1787 }
1788
1789 #[test]
1790 fn test_set_json_settings() {
1791 let schema = Arc::new(
1792 SchemaBuilder::try_from_columns(vec![
1793 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
1794 ColumnSchema::new(
1795 "ts",
1796 ConcreteDataType::timestamp_millisecond_datatype(),
1797 false,
1798 )
1799 .with_time_index(true),
1800 json2_column_schema_v1("payload", JsonSettings::new_v2()),
1801 ])
1802 .unwrap()
1803 .version(123)
1804 .build()
1805 .unwrap(),
1806 );
1807 let meta = TableMetaBuilder::empty()
1808 .schema(schema)
1809 .primary_key_indices(vec![0])
1810 .engine("engine")
1811 .next_column_id(3)
1812 .build()
1813 .unwrap();
1814
1815 let alter_kind = AlterKind::SetJsonSettings {
1816 request: SetJsonSettingsRequest {
1817 column_name: "payload".to_string(),
1818 settings: JsonSettings::try_new(vec![], Some(10)).unwrap(),
1819 },
1820 };
1821 let new_meta = meta
1822 .builder_with_alter_kind("my_table", &alter_kind)
1823 .unwrap()
1824 .build()
1825 .unwrap();
1826
1827 let payload = new_meta.schema.column_schema_by_name("payload").unwrap();
1828 let json_metadata: JsonMetadata =
1829 serde_json::from_str(payload.metadata().get("ARROW:extension:metadata").unwrap())
1830 .unwrap();
1831 assert!(json_metadata.is_version_2());
1832 assert_eq!(
1833 Some(10),
1834 json_metadata.json_settings().max_auto_expanded_paths()
1835 );
1836 assert_eq!(124, new_meta.schema.version());
1837 assert_eq!(meta.primary_key_indices, new_meta.primary_key_indices);
1838 }
1839
1840 fn json2_column_schema_v1(name: &str, settings: JsonSettings) -> ColumnSchema {
1841 let mut column_schema =
1842 ColumnSchema::new(name, ConcreteDataType::Json(JsonType::null()), true);
1843 column_schema.with_extension_type(&Json2ExtensionType::new(Arc::new(
1844 JsonMetadata::new_v1(settings),
1845 )));
1846 column_schema
1847 }
1848
1849 #[test]
1850 fn test_set_json_settings_rejects_non_json2_column() {
1851 let schema = Arc::new(new_test_schema());
1852 let meta = TableMetaBuilder::empty()
1853 .schema(schema)
1854 .primary_key_indices(vec![0])
1855 .engine("engine")
1856 .next_column_id(3)
1857 .build()
1858 .unwrap();
1859 let alter_kind = AlterKind::SetJsonSettings {
1860 request: SetJsonSettingsRequest {
1861 column_name: "col2".to_string(),
1862 settings: JsonSettings::new_v2(),
1863 },
1864 };
1865
1866 let err = match meta.builder_with_alter_kind("my_table", &alter_kind) {
1867 Ok(_) => panic!("expected modifying JSON settings on a non-JSON2 column to fail"),
1868 Err(err) => err,
1869 };
1870 assert!(err.to_string().contains("is not a JSON2 column"));
1871 }
1872
1873 #[test]
1874 fn test_modify_time_index_column_type() {
1875 let schema = Arc::new(new_test_schema());
1876 let meta = TableMetaBuilder::empty()
1877 .schema(schema)
1878 .primary_key_indices(vec![0])
1879 .engine("engine")
1880 .next_column_id(3)
1881 .build()
1882 .unwrap();
1883
1884 let alter_kind = AlterKind::ModifyColumnTypes {
1886 columns: vec![ModifyColumnTypeRequest {
1887 column_name: "ts".to_string(),
1888 target_type: ConcreteDataType::timestamp_microsecond_datatype(),
1889 }],
1890 };
1891 let new_meta = meta
1892 .builder_with_alter_kind("my_table", &alter_kind)
1893 .unwrap()
1894 .build()
1895 .unwrap();
1896 let ts_column = new_meta.schema.column_schema_by_name("ts").unwrap();
1897 assert_eq!(
1898 ConcreteDataType::timestamp_microsecond_datatype(),
1899 ts_column.data_type
1900 );
1901 assert!(ts_column.is_time_index());
1902 assert!(!ts_column.is_nullable());
1903 assert_eq!(new_meta.schema.version(), 124);
1904 assert_eq!(&[0], &new_meta.primary_key_indices[..]);
1905
1906 for target in [
1909 ConcreteDataType::timestamp_second_datatype(),
1910 ConcreteDataType::timestamp_millisecond_datatype(),
1911 ConcreteDataType::string_datatype(),
1912 ] {
1913 let alter_kind = AlterKind::ModifyColumnTypes {
1914 columns: vec![ModifyColumnTypeRequest {
1915 column_name: "ts".to_string(),
1916 target_type: target.clone(),
1917 }],
1918 };
1919 let res = meta.builder_with_alter_kind("my_table", &alter_kind);
1920 assert!(res.is_err(), "expected rejection for {target}");
1921 }
1922 }
1923
1924 #[test]
1925 fn test_modify_partition_column_type() {
1926 let alter_kind = AlterKind::ModifyColumnTypes {
1929 columns: vec![ModifyColumnTypeRequest {
1930 column_name: "col2".to_string(),
1931 target_type: ConcreteDataType::float64_datatype(),
1932 }],
1933 };
1934 for (partition_key_indices, allowed) in [(vec![], true), (vec![2], false)] {
1935 let meta = TableMetaBuilder::empty()
1936 .schema(Arc::new(new_test_schema()))
1937 .primary_key_indices(vec![0])
1938 .partition_key_indices(partition_key_indices)
1939 .engine("engine")
1940 .next_column_id(3)
1941 .build()
1942 .unwrap();
1943 let result = meta.builder_with_alter_kind("my_table", &alter_kind);
1944 assert_eq!(result.is_ok(), allowed);
1945 }
1946
1947 let meta = TableMetaBuilder::empty()
1949 .schema(Arc::new(new_test_schema()))
1950 .primary_key_indices(vec![0])
1951 .partition_key_indices(vec![1])
1952 .engine("engine")
1953 .next_column_id(3)
1954 .build()
1955 .unwrap();
1956 let alter_kind = AlterKind::ModifyColumnTypes {
1957 columns: vec![ModifyColumnTypeRequest {
1958 column_name: "ts".to_string(),
1959 target_type: ConcreteDataType::timestamp_microsecond_datatype(),
1960 }],
1961 };
1962 assert!(
1963 meta.builder_with_alter_kind("my_table", &alter_kind)
1964 .is_ok()
1965 );
1966 }
1967
1968 #[test]
1969 fn test_add_columns() {
1970 let schema = Arc::new(new_test_schema());
1971 let meta = TableMetaBuilder::empty()
1972 .schema(schema)
1973 .primary_key_indices(vec![0])
1974 .engine("engine")
1975 .next_column_id(3)
1976 .build()
1977 .unwrap();
1978
1979 let new_meta = add_columns_to_meta(&meta);
1980 let names: Vec<String> = new_meta
1981 .schema
1982 .column_schemas()
1983 .iter()
1984 .map(|column_schema| column_schema.name.clone())
1985 .collect();
1986 assert_eq!(&["col1", "ts", "col2", "my_tag", "my_field"], &names[..]);
1987 assert_eq!(&[0, 3], &new_meta.primary_key_indices[..]);
1988 assert_eq!(&[1, 2, 4], &new_meta.value_indices[..]);
1989 }
1990
1991 #[test]
1992 fn test_set_append_mode_true_clears_merge_mode_option() {
1993 let schema = Arc::new(new_test_schema());
1994 let mut table_options = TableOptions::default();
1995 table_options
1996 .extra_options
1997 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1998 let meta = TableMetaBuilder::empty()
1999 .schema(schema)
2000 .primary_key_indices(vec![0])
2001 .engine("engine")
2002 .next_column_id(3)
2003 .options(table_options)
2004 .build()
2005 .unwrap();
2006
2007 let alter_kind = AlterKind::SetTableOptions {
2008 options: vec![SetRegionOption::AppendMode(true)],
2009 };
2010 let new_meta = meta
2011 .builder_with_alter_kind("my_table", &alter_kind)
2012 .unwrap()
2013 .build()
2014 .unwrap();
2015
2016 assert_eq!(
2017 Some("true"),
2018 new_meta
2019 .options
2020 .extra_options
2021 .get(APPEND_MODE_KEY)
2022 .map(String::as_str)
2023 );
2024 assert!(!new_meta.options.extra_options.contains_key(MERGE_MODE_KEY));
2025 }
2026
2027 #[test]
2028 fn test_set_append_mode_false_keeps_merge_mode_option() {
2029 let schema = Arc::new(new_test_schema());
2030 let mut table_options = TableOptions::default();
2031 table_options
2032 .extra_options
2033 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
2034 let meta = TableMetaBuilder::empty()
2035 .schema(schema)
2036 .primary_key_indices(vec![0])
2037 .engine("engine")
2038 .next_column_id(3)
2039 .options(table_options)
2040 .build()
2041 .unwrap();
2042
2043 let alter_kind = AlterKind::SetTableOptions {
2044 options: vec![SetRegionOption::AppendMode(false)],
2045 };
2046 let new_meta = meta
2047 .builder_with_alter_kind("my_table", &alter_kind)
2048 .unwrap()
2049 .build()
2050 .unwrap();
2051
2052 assert_eq!(
2053 Some("false"),
2054 new_meta
2055 .options
2056 .extra_options
2057 .get(APPEND_MODE_KEY)
2058 .map(String::as_str)
2059 );
2060 assert_eq!(
2061 Some("last_non_null"),
2062 new_meta
2063 .options
2064 .extra_options
2065 .get(MERGE_MODE_KEY)
2066 .map(String::as_str)
2067 );
2068 }
2069
2070 #[test]
2071 fn test_set_preserve_row_sequence_option() {
2072 let schema = Arc::new(new_test_schema());
2073 let meta = TableMetaBuilder::empty()
2074 .schema(schema)
2075 .primary_key_indices(vec![0])
2076 .engine("engine")
2077 .next_column_id(3)
2078 .build()
2079 .unwrap();
2080
2081 let apply = |meta: &TableMeta, kind: &AlterKind| {
2082 meta.builder_with_alter_kind("my_table", kind)
2083 .unwrap()
2084 .build()
2085 .unwrap()
2086 };
2087
2088 let with_true = apply(
2089 &meta,
2090 &AlterKind::SetTableOptions {
2091 options: vec![SetRegionOption::PreserveRowSequence(true)],
2092 },
2093 );
2094 assert_eq!(
2095 Some("true"),
2096 with_true
2097 .options
2098 .extra_options
2099 .get(PRESERVE_ROW_SEQUENCE)
2100 .map(String::as_str)
2101 );
2102
2103 let set_false = apply(
2104 &with_true,
2105 &AlterKind::SetTableOptions {
2106 options: vec![SetRegionOption::PreserveRowSequence(false)],
2107 },
2108 );
2109 assert!(
2110 !set_false
2111 .options
2112 .extra_options
2113 .contains_key(PRESERVE_ROW_SEQUENCE)
2114 );
2115
2116 let unset = apply(
2117 &with_true,
2118 &AlterKind::UnsetTableOptions {
2119 keys: vec![UnsetRegionOption::PreserveRowSequence],
2120 },
2121 );
2122 assert!(
2123 !unset
2124 .options
2125 .extra_options
2126 .contains_key(PRESERVE_ROW_SEQUENCE)
2127 );
2128 }
2129
2130 #[test]
2131 fn test_set_skip_wal_updates_typed_and_extra_options() {
2132 let mut meta = TableMetaBuilder::empty()
2133 .schema(Arc::new(new_test_schema()))
2134 .primary_key_indices(vec![0])
2135 .engine("engine")
2136 .next_column_id(3)
2137 .build()
2138 .unwrap();
2139 meta.options
2140 .extra_options
2141 .insert(SKIP_WAL_KEY.to_string(), false.to_string());
2142
2143 let alter_kind = AlterKind::SetTableOptions {
2144 options: vec![SetRegionOption::SkipWal(true)],
2145 };
2146 let new_meta = meta
2147 .builder_with_alter_kind("my_table", &alter_kind)
2148 .unwrap()
2149 .build()
2150 .unwrap();
2151
2152 assert!(new_meta.options.skip_wal);
2153 assert_eq!(
2154 Some("true"),
2155 new_meta
2156 .options
2157 .extra_options
2158 .get(SKIP_WAL_KEY)
2159 .map(String::as_str)
2160 );
2161
2162 let alter_kind = AlterKind::SetTableOptions {
2163 options: vec![SetRegionOption::SkipWal(false)],
2164 };
2165 let new_meta = new_meta
2166 .builder_with_alter_kind("my_table", &alter_kind)
2167 .unwrap()
2168 .build()
2169 .unwrap();
2170
2171 assert!(!new_meta.options.skip_wal);
2172 assert_eq!(
2173 Some("false"),
2174 new_meta
2175 .options
2176 .extra_options
2177 .get(SKIP_WAL_KEY)
2178 .map(String::as_str)
2179 );
2180 }
2181
2182 #[test]
2183 fn test_set_unset_repartition_hints_together() {
2184 let meta = TableMetaBuilder::empty()
2185 .schema(Arc::new(new_test_schema()))
2186 .primary_key_indices(vec![0])
2187 .engine("engine")
2188 .next_column_id(3)
2189 .build()
2190 .unwrap();
2191 let options = vec![
2192 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string()),
2193 (
2194 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2195 "10".to_string(),
2196 ),
2197 ];
2198 for reverse in [false, true] {
2199 let mut options = options.clone();
2200 if reverse {
2201 options.reverse();
2202 }
2203 let updated = meta
2204 .builder_with_alter_kind(
2205 "t",
2206 &AlterKind::SetAnnotations {
2207 family: AnnotationFamily::RepartitionHint,
2208 options: options.clone(),
2209 },
2210 )
2211 .unwrap()
2212 .build()
2213 .unwrap();
2214 assert_eq!(
2215 updated.options.extra_options,
2216 options.iter().cloned().collect()
2217 );
2218 for (key, value) in [
2219 (REPARTITION_COLUMN_HINT_KEY, "missing"),
2220 (REPARTITION_PARTITION_NUM_HINT_KEY, "0"),
2221 ] {
2222 let invalid = options
2223 .iter()
2224 .map(|(k, v)| {
2225 (
2226 k.clone(),
2227 if k == key {
2228 value.to_string()
2229 } else {
2230 v.clone()
2231 },
2232 )
2233 })
2234 .collect();
2235 assert!(
2236 updated
2237 .builder_with_alter_kind(
2238 "t",
2239 &AlterKind::SetAnnotations {
2240 family: AnnotationFamily::RepartitionHint,
2241 options: invalid,
2242 }
2243 )
2244 .is_err()
2245 );
2246 assert_eq!(
2247 updated.options.extra_options,
2248 options.iter().cloned().collect()
2249 );
2250 }
2251 let cleared = updated
2252 .builder_with_alter_kind(
2253 "t",
2254 &AlterKind::UnsetAnnotations {
2255 family: AnnotationFamily::RepartitionHint,
2256 keys: options.into_iter().map(|(key, _)| key).collect(),
2257 },
2258 )
2259 .unwrap()
2260 .build()
2261 .unwrap();
2262 assert!(cleared.options.extra_options.is_empty());
2263 }
2264 }
2265
2266 #[test]
2267 fn test_repartition_partition_num_hint() {
2268 for partition_key_indices in [vec![], vec![0]] {
2269 let mut meta = TableMetaBuilder::empty()
2270 .schema(Arc::new(new_test_schema()))
2271 .primary_key_indices(vec![0])
2272 .partition_key_indices(partition_key_indices)
2273 .engine("engine")
2274 .next_column_id(3)
2275 .build()
2276 .unwrap();
2277 for value in ["", " ", "0", "-1", "1.5", "abc", "4294967296"] {
2278 let err = meta
2279 .builder_with_alter_kind(
2280 "t",
2281 &AlterKind::SetAnnotations {
2282 family: AnnotationFamily::RepartitionHint,
2283 options: vec![(
2284 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2285 value.to_string(),
2286 )],
2287 },
2288 )
2289 .err()
2290 .unwrap();
2291 assert!(
2292 err.to_string().contains("expects a positive integer"),
2293 "{err}"
2294 );
2295 }
2296 for (value, expected) in [(" 8 ", "8"), ("1", "1"), ("4294967295", "4294967295")] {
2297 meta = meta
2298 .builder_with_alter_kind(
2299 "t",
2300 &AlterKind::SetAnnotations {
2301 family: AnnotationFamily::RepartitionHint,
2302 options: vec![(
2303 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2304 value.to_string(),
2305 )],
2306 },
2307 )
2308 .unwrap()
2309 .build()
2310 .unwrap();
2311 assert_eq!(
2312 meta.options.extra_options,
2313 HashMap::from([(
2314 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2315 expected.to_string()
2316 )])
2317 );
2318 }
2319 for _ in 0..2 {
2320 meta = meta
2321 .builder_with_alter_kind(
2322 "t",
2323 &AlterKind::UnsetAnnotations {
2324 family: AnnotationFamily::RepartitionHint,
2325 keys: vec![REPARTITION_PARTITION_NUM_HINT_KEY.to_string()],
2326 },
2327 )
2328 .unwrap()
2329 .build()
2330 .unwrap();
2331 assert!(meta.options.extra_options.is_empty());
2332 }
2333 }
2334 }
2335
2336 #[test]
2337 fn test_set_repartition_column_hint() {
2338 let meta = TableMetaBuilder::empty()
2339 .schema(Arc::new(new_test_schema()))
2340 .primary_key_indices(vec![0])
2341 .engine("engine")
2342 .next_column_id(3)
2343 .build()
2344 .unwrap();
2345
2346 let alter_kind = AlterKind::SetAnnotations {
2347 family: AnnotationFamily::RepartitionHint,
2348 options: vec![(
2349 REPARTITION_COLUMN_HINT_KEY.to_string(),
2350 " col1 ".to_string(),
2351 )],
2352 };
2353 let new_meta = meta
2354 .builder_with_alter_kind("my_table", &alter_kind)
2355 .unwrap()
2356 .build()
2357 .unwrap();
2358
2359 assert_eq!(
2360 Some("col1"),
2361 new_meta
2362 .options
2363 .extra_options
2364 .get(REPARTITION_COLUMN_HINT_KEY)
2365 .map(String::as_str)
2366 );
2367 }
2368
2369 #[test]
2370 fn test_set_repartition_column_hint_rejects_empty_column() {
2371 let meta = TableMetaBuilder::empty()
2372 .schema(Arc::new(new_test_schema()))
2373 .primary_key_indices(vec![0])
2374 .engine("engine")
2375 .next_column_id(3)
2376 .build()
2377 .unwrap();
2378
2379 let alter_kind = AlterKind::SetAnnotations {
2380 family: AnnotationFamily::RepartitionHint,
2381 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), " ".to_string())],
2382 };
2383 let err = meta
2384 .builder_with_alter_kind("my_table", &alter_kind)
2385 .err()
2386 .unwrap();
2387
2388 assert!(
2389 err.to_string()
2390 .contains("repartition.column.hint expects exactly one column name")
2391 );
2392 }
2393
2394 #[test]
2395 fn test_set_repartition_column_hint_rejects_multiple_columns() {
2396 let meta = TableMetaBuilder::empty()
2397 .schema(Arc::new(new_test_schema()))
2398 .primary_key_indices(vec![0])
2399 .engine("engine")
2400 .next_column_id(3)
2401 .build()
2402 .unwrap();
2403
2404 let alter_kind = AlterKind::SetAnnotations {
2405 family: AnnotationFamily::RepartitionHint,
2406 options: vec![(
2407 REPARTITION_COLUMN_HINT_KEY.to_string(),
2408 "col1,col2".to_string(),
2409 )],
2410 };
2411 let err = meta
2412 .builder_with_alter_kind("my_table", &alter_kind)
2413 .err()
2414 .unwrap();
2415
2416 assert!(
2417 err.to_string()
2418 .contains("repartition.column.hint expects exactly one column name")
2419 );
2420 }
2421
2422 #[test]
2423 fn test_set_repartition_column_hint_rejects_missing_column() {
2424 let meta = TableMetaBuilder::empty()
2425 .schema(Arc::new(new_test_schema()))
2426 .primary_key_indices(vec![0])
2427 .engine("engine")
2428 .next_column_id(3)
2429 .build()
2430 .unwrap();
2431
2432 let alter_kind = AlterKind::SetAnnotations {
2433 family: AnnotationFamily::RepartitionHint,
2434 options: vec![(
2435 REPARTITION_COLUMN_HINT_KEY.to_string(),
2436 "missing".to_string(),
2437 )],
2438 };
2439 let err = meta
2440 .builder_with_alter_kind("my_table", &alter_kind)
2441 .err()
2442 .unwrap();
2443
2444 assert!(err.to_string().contains("Column missing not exists"));
2445 }
2446
2447 #[test]
2448 fn test_set_repartition_column_hint_rejects_time_index_column() {
2449 let meta = TableMetaBuilder::empty()
2450 .schema(Arc::new(new_test_schema()))
2451 .primary_key_indices(vec![0])
2452 .engine("engine")
2453 .next_column_id(3)
2454 .build()
2455 .unwrap();
2456
2457 let alter_kind = AlterKind::SetAnnotations {
2458 family: AnnotationFamily::RepartitionHint,
2459 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), "ts".to_string())],
2460 };
2461 let err = meta
2462 .builder_with_alter_kind("my_table", &alter_kind)
2463 .err()
2464 .unwrap();
2465
2466 assert!(
2467 err.to_string()
2468 .contains("cannot set repartition.column.hint to the time index column")
2469 );
2470 }
2471
2472 #[test]
2473 fn test_set_repartition_column_hint_rejects_partitioned_table() {
2474 let meta = TableMetaBuilder::empty()
2475 .schema(Arc::new(new_test_schema()))
2476 .primary_key_indices(vec![0])
2477 .engine("engine")
2478 .next_column_id(3)
2479 .partition_key_indices(vec![0])
2480 .build()
2481 .unwrap();
2482
2483 let alter_kind = AlterKind::SetAnnotations {
2484 family: AnnotationFamily::RepartitionHint,
2485 options: vec![(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string())],
2486 };
2487 let err = meta
2488 .builder_with_alter_kind("my_table", &alter_kind)
2489 .err()
2490 .unwrap();
2491
2492 assert!(
2493 err.to_string()
2494 .contains("cannot set repartition.column.hint on a table with partition metadata")
2495 );
2496 }
2497
2498 #[test]
2499 fn test_unset_repartition_column_hint() {
2500 let mut table_options = TableOptions::default();
2501 table_options
2502 .extra_options
2503 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
2504 let meta = TableMetaBuilder::empty()
2505 .schema(Arc::new(new_test_schema()))
2506 .primary_key_indices(vec![0])
2507 .engine("engine")
2508 .next_column_id(3)
2509 .options(table_options)
2510 .build()
2511 .unwrap();
2512
2513 let new_meta = meta
2514 .builder_with_alter_kind(
2515 "my_table",
2516 &AlterKind::UnsetAnnotations {
2517 family: AnnotationFamily::RepartitionHint,
2518 keys: vec![REPARTITION_COLUMN_HINT_KEY.to_string()],
2519 },
2520 )
2521 .unwrap()
2522 .build()
2523 .unwrap();
2524
2525 assert!(
2526 !new_meta
2527 .options
2528 .extra_options
2529 .contains_key(REPARTITION_COLUMN_HINT_KEY)
2530 );
2531 }
2532
2533 #[test]
2534 fn test_repartition_hints_are_not_region_options() {
2535 let mut table_options = TableOptions::default();
2536 table_options
2537 .extra_options
2538 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
2539 table_options.extra_options.insert(
2540 REPARTITION_PARTITION_NUM_HINT_KEY.to_string(),
2541 "8".to_string(),
2542 );
2543 let table_info = TableInfoBuilder::default()
2544 .table_id(1)
2545 .table_version(0)
2546 .name("my_table")
2547 .catalog_name(DEFAULT_CATALOG_NAME)
2548 .schema_name(DEFAULT_SCHEMA_NAME)
2549 .meta(
2550 TableMetaBuilder::empty()
2551 .schema(Arc::new(new_test_schema()))
2552 .primary_key_indices(vec![0])
2553 .engine("engine")
2554 .next_column_id(3)
2555 .options(table_options)
2556 .build()
2557 .unwrap(),
2558 )
2559 .build()
2560 .unwrap();
2561
2562 assert!(
2563 !table_info
2564 .to_region_options()
2565 .contains_key(REPARTITION_PARTITION_NUM_HINT_KEY)
2566 );
2567 assert!(
2568 !table_info
2569 .to_region_options()
2570 .contains_key(REPARTITION_COLUMN_HINT_KEY)
2571 );
2572 }
2573
2574 #[test]
2575 fn test_set_auto_flush_interval() {
2576 let schema = Arc::new(new_test_schema());
2577 let table_options = TableOptions::default();
2578 let meta = TableMetaBuilder::empty()
2579 .schema(schema)
2580 .primary_key_indices(vec![0])
2581 .engine("engine")
2582 .next_column_id(3)
2583 .options(table_options)
2584 .build()
2585 .unwrap();
2586
2587 let alter_kind = AlterKind::SetTableOptions {
2588 options: vec![SetRegionOption::AutoFlushInterval(Some(
2589 std::time::Duration::from_secs(300),
2590 ))],
2591 };
2592 let new_meta = meta
2593 .builder_with_alter_kind("my_table", &alter_kind)
2594 .unwrap()
2595 .build()
2596 .unwrap();
2597
2598 assert_eq!(
2599 Some("5m"),
2600 new_meta
2601 .options
2602 .extra_options
2603 .get(AUTO_FLUSH_INTERVAL_KEY)
2604 .map(String::as_str)
2605 );
2606 }
2607
2608 #[test]
2609 fn test_set_auto_flush_interval_none_removes_existing() {
2610 let schema = Arc::new(new_test_schema());
2611 let mut table_options = TableOptions::default();
2612 table_options
2613 .extra_options
2614 .insert(AUTO_FLUSH_INTERVAL_KEY.to_string(), "5m".to_string());
2615 let meta = TableMetaBuilder::empty()
2616 .schema(schema)
2617 .primary_key_indices(vec![0])
2618 .engine("engine")
2619 .next_column_id(3)
2620 .options(table_options)
2621 .build()
2622 .unwrap();
2623
2624 let alter_kind = AlterKind::SetTableOptions {
2625 options: vec![SetRegionOption::AutoFlushInterval(None)],
2626 };
2627 let new_meta = meta
2628 .builder_with_alter_kind("my_table", &alter_kind)
2629 .unwrap()
2630 .build()
2631 .unwrap();
2632
2633 assert!(
2634 !new_meta
2635 .options
2636 .extra_options
2637 .contains_key(AUTO_FLUSH_INTERVAL_KEY)
2638 );
2639 }
2640
2641 #[test]
2642 fn test_set_twcs_trigger_persists_legacy_key() {
2643 for key in [TWCS_TRIGGER_FILE_NUM, TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM] {
2644 let mut table_options = TableOptions::default();
2645 table_options.extra_options.insert(
2646 TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM.to_string(),
2647 "4".to_string(),
2648 );
2649 let meta = TableMetaBuilder::empty()
2650 .schema(Arc::new(new_test_schema()))
2651 .primary_key_indices(vec![0])
2652 .engine("engine")
2653 .next_column_id(3)
2654 .options(table_options)
2655 .build()
2656 .unwrap();
2657 let alter_kind = AlterKind::SetTableOptions {
2658 options: vec![SetRegionOption::Twsc(key.to_string(), "8".to_string())],
2659 };
2660
2661 let new_meta = meta
2662 .builder_with_alter_kind("my_table", &alter_kind)
2663 .unwrap()
2664 .build()
2665 .unwrap();
2666
2667 assert_eq!(
2668 Some("8"),
2669 new_meta
2670 .options
2671 .extra_options
2672 .get(TWCS_TRIGGER_FILE_NUM)
2673 .map(String::as_str)
2674 );
2675 assert!(
2676 !new_meta
2677 .options
2678 .extra_options
2679 .contains_key(TWCS_ACTIVE_WINDOW_TRIGGER_FILE_NUM)
2680 );
2681 }
2682 }
2683
2684 #[test]
2685 fn test_set_and_unset_max_row_group_row_count() {
2686 let meta = TableMetaBuilder::empty()
2687 .schema(Arc::new(new_test_schema()))
2688 .primary_key_indices(vec![0])
2689 .engine("engine")
2690 .next_column_id(3)
2691 .options(TableOptions::default())
2692 .build()
2693 .unwrap();
2694
2695 let alter_kind = AlterKind::SetTableOptions {
2696 options: vec![SetRegionOption::MaxRowGroupRowCount(Some(512))],
2697 };
2698 let new_meta = meta
2699 .builder_with_alter_kind("my_table", &alter_kind)
2700 .unwrap()
2701 .build()
2702 .unwrap();
2703 assert_eq!(
2704 Some("512"),
2705 new_meta
2706 .options
2707 .extra_options
2708 .get(MAX_ROW_GROUP_ROW_COUNT)
2709 .map(String::as_str)
2710 );
2711
2712 let alter_kind = AlterKind::UnsetTableOptions {
2713 keys: vec![UnsetRegionOption::MaxRowGroupRowCount],
2714 };
2715 let new_meta = new_meta
2716 .builder_with_alter_kind("my_table", &alter_kind)
2717 .unwrap()
2718 .build()
2719 .unwrap();
2720 assert!(
2721 !new_meta
2722 .options
2723 .extra_options
2724 .contains_key(MAX_ROW_GROUP_ROW_COUNT)
2725 );
2726 }
2727
2728 #[test]
2729 fn test_add_columns_multiple_times() {
2730 let schema = Arc::new(new_test_schema());
2731 let meta = TableMetaBuilder::empty()
2732 .schema(schema)
2733 .primary_key_indices(vec![0])
2734 .engine("engine")
2735 .next_column_id(3)
2736 .build()
2737 .unwrap();
2738
2739 let alter_kind = AlterKind::AddColumns {
2740 columns: vec![
2741 AddColumnRequest {
2742 column_schema: ColumnSchema::new(
2743 "col3",
2744 ConcreteDataType::int32_datatype(),
2745 true,
2746 ),
2747 is_key: true,
2748 location: None,
2749 add_if_not_exists: true,
2750 },
2751 AddColumnRequest {
2752 column_schema: ColumnSchema::new(
2753 "col3",
2754 ConcreteDataType::int32_datatype(),
2755 true,
2756 ),
2757 is_key: true,
2758 location: None,
2759 add_if_not_exists: true,
2760 },
2761 ],
2762 };
2763 let err = meta
2764 .builder_with_alter_kind("my_table", &alter_kind)
2765 .err()
2766 .unwrap();
2767 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2768 }
2769
2770 #[test]
2771 fn test_remove_columns() {
2772 let schema = Arc::new(new_test_schema());
2773 let meta = TableMetaBuilder::empty()
2774 .schema(schema.clone())
2775 .primary_key_indices(vec![0])
2776 .engine("engine")
2777 .next_column_id(3)
2778 .build()
2779 .unwrap();
2780 let meta = add_columns_to_meta(&meta);
2782
2783 let alter_kind = AlterKind::DropColumns {
2784 names: vec![String::from("col2"), String::from("my_field")],
2785 };
2786 let new_meta = meta
2787 .builder_with_alter_kind("my_table", &alter_kind)
2788 .unwrap()
2789 .build()
2790 .unwrap();
2791
2792 let names: Vec<String> = new_meta
2793 .schema
2794 .column_schemas()
2795 .iter()
2796 .map(|column_schema| column_schema.name.clone())
2797 .collect();
2798 assert_eq!(&["col1", "ts", "my_tag"], &names[..]);
2799 assert_eq!(&[0, 2], &new_meta.primary_key_indices[..]);
2800 assert_eq!(&[1], &new_meta.value_indices[..]);
2801 assert_eq!(
2802 schema.timestamp_column(),
2803 new_meta.schema.timestamp_column()
2804 );
2805 }
2806
2807 #[test]
2808 fn test_remove_multiple_columns_before_timestamp() {
2809 let column_schemas = vec![
2810 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2811 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
2812 ColumnSchema::new("col3", ConcreteDataType::int32_datatype(), true),
2813 ColumnSchema::new(
2814 "ts",
2815 ConcreteDataType::timestamp_millisecond_datatype(),
2816 false,
2817 )
2818 .with_time_index(true),
2819 ];
2820 let schema = Arc::new(
2821 SchemaBuilder::try_from(column_schemas)
2822 .unwrap()
2823 .version(123)
2824 .build()
2825 .unwrap(),
2826 );
2827 let meta = TableMetaBuilder::empty()
2828 .schema(schema.clone())
2829 .primary_key_indices(vec![1])
2830 .engine("engine")
2831 .next_column_id(4)
2832 .build()
2833 .unwrap();
2834
2835 let alter_kind = AlterKind::DropColumns {
2837 names: vec![String::from("col3"), String::from("col1")],
2838 };
2839 let new_meta = meta
2840 .builder_with_alter_kind("my_table", &alter_kind)
2841 .unwrap()
2842 .build()
2843 .unwrap();
2844
2845 let names: Vec<String> = new_meta
2846 .schema
2847 .column_schemas()
2848 .iter()
2849 .map(|column_schema| column_schema.name.clone())
2850 .collect();
2851 assert_eq!(&["col2", "ts"], &names[..]);
2852 assert_eq!(&[0], &new_meta.primary_key_indices[..]);
2853 assert_eq!(&[1], &new_meta.value_indices[..]);
2854 assert_eq!(
2855 schema.timestamp_column(),
2856 new_meta.schema.timestamp_column()
2857 );
2858 }
2859
2860 #[test]
2861 fn test_add_existing_column() {
2862 let schema = Arc::new(new_test_schema());
2863 let meta = TableMetaBuilder::empty()
2864 .schema(schema)
2865 .primary_key_indices(vec![0])
2866 .engine("engine")
2867 .next_column_id(3)
2868 .build()
2869 .unwrap();
2870
2871 let alter_kind = AlterKind::AddColumns {
2872 columns: vec![AddColumnRequest {
2873 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2874 is_key: false,
2875 location: None,
2876 add_if_not_exists: false,
2877 }],
2878 };
2879
2880 let err = meta
2881 .builder_with_alter_kind("my_table", &alter_kind)
2882 .err()
2883 .unwrap();
2884 assert_eq!(StatusCode::TableColumnExists, err.status_code());
2885
2886 let alter_kind = AlterKind::AddColumns {
2888 columns: vec![AddColumnRequest {
2889 column_schema: ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2890 is_key: true,
2891 location: None,
2892 add_if_not_exists: true,
2893 }],
2894 };
2895 let new_meta = meta
2896 .builder_with_alter_kind("my_table", &alter_kind)
2897 .unwrap()
2898 .build()
2899 .unwrap();
2900 assert_eq!(
2901 meta.schema.column_schemas(),
2902 new_meta.schema.column_schemas()
2903 );
2904 assert_eq!(meta.schema.version() + 1, new_meta.schema.version());
2905 }
2906
2907 #[test]
2908 fn test_add_different_type_column() {
2909 let schema = Arc::new(new_test_schema());
2910 let meta = TableMetaBuilder::empty()
2911 .schema(schema)
2912 .primary_key_indices(vec![0])
2913 .engine("engine")
2914 .next_column_id(3)
2915 .build()
2916 .unwrap();
2917
2918 let alter_kind = AlterKind::AddColumns {
2920 columns: vec![AddColumnRequest {
2921 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2922 is_key: false,
2923 location: None,
2924 add_if_not_exists: true,
2925 }],
2926 };
2927 let err = meta
2928 .builder_with_alter_kind("my_table", &alter_kind)
2929 .err()
2930 .unwrap();
2931 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2932 }
2933
2934 #[test]
2935 fn test_add_invalid_column() {
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::AddColumns {
2947 columns: vec![AddColumnRequest {
2948 column_schema: ColumnSchema::new(
2949 "weny",
2950 ConcreteDataType::string_datatype(),
2951 false,
2952 ),
2953 is_key: false,
2954 location: None,
2955 add_if_not_exists: false,
2956 }],
2957 };
2958
2959 let err = meta
2960 .builder_with_alter_kind("my_table", &alter_kind)
2961 .err()
2962 .unwrap();
2963 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2964 }
2965
2966 #[test]
2967 fn test_remove_unknown_column() {
2968 let schema = Arc::new(new_test_schema());
2969 let meta = TableMetaBuilder::empty()
2970 .schema(schema)
2971 .primary_key_indices(vec![0])
2972 .engine("engine")
2973 .next_column_id(3)
2974 .build()
2975 .unwrap();
2976
2977 let alter_kind = AlterKind::DropColumns {
2978 names: vec![String::from("unknown")],
2979 };
2980
2981 let err = meta
2982 .builder_with_alter_kind("my_table", &alter_kind)
2983 .err()
2984 .unwrap();
2985 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2986 }
2987
2988 #[test]
2989 fn test_change_unknown_column_data_type() {
2990 let schema = Arc::new(new_test_schema());
2991 let meta = TableMetaBuilder::empty()
2992 .schema(schema)
2993 .primary_key_indices(vec![0])
2994 .engine("engine")
2995 .next_column_id(3)
2996 .build()
2997 .unwrap();
2998
2999 let alter_kind = AlterKind::ModifyColumnTypes {
3000 columns: vec![ModifyColumnTypeRequest {
3001 column_name: "unknown".to_string(),
3002 target_type: ConcreteDataType::string_datatype(),
3003 }],
3004 };
3005
3006 let err = meta
3007 .builder_with_alter_kind("my_table", &alter_kind)
3008 .err()
3009 .unwrap();
3010 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
3011 }
3012
3013 #[test]
3014 fn test_remove_key_column() {
3015 let schema = Arc::new(new_test_schema());
3016 let meta = TableMetaBuilder::empty()
3017 .schema(schema)
3018 .primary_key_indices(vec![0])
3019 .engine("engine")
3020 .next_column_id(3)
3021 .build()
3022 .unwrap();
3023
3024 let alter_kind = AlterKind::DropColumns {
3026 names: vec![String::from("col1")],
3027 };
3028
3029 let err = meta
3030 .builder_with_alter_kind("my_table", &alter_kind)
3031 .err()
3032 .unwrap();
3033 assert_eq!(StatusCode::InvalidArguments, err.status_code());
3034
3035 let alter_kind = AlterKind::DropColumns {
3037 names: vec![String::from("ts")],
3038 };
3039
3040 let err = meta
3041 .builder_with_alter_kind("my_table", &alter_kind)
3042 .err()
3043 .unwrap();
3044 assert_eq!(StatusCode::InvalidArguments, err.status_code());
3045 }
3046
3047 #[test]
3048 fn test_remove_partition_column() {
3049 let schema = Arc::new(new_test_schema());
3050 let meta = TableMetaBuilder::empty()
3051 .schema(schema)
3052 .primary_key_indices(vec![])
3053 .partition_key_indices(vec![0])
3054 .engine("engine")
3055 .next_column_id(3)
3056 .build()
3057 .unwrap();
3058 let alter_kind = AlterKind::DropColumns {
3060 names: vec![String::from("col1")],
3061 };
3062
3063 let err = meta
3064 .builder_with_alter_kind("my_table", &alter_kind)
3065 .err()
3066 .unwrap();
3067 assert_matches!(err, Error::RemovePartitionColumn { .. });
3068 }
3069
3070 #[test]
3071 fn test_change_key_column_data_type() {
3072 let schema = Arc::new(new_test_schema());
3073 let meta = TableMetaBuilder::empty()
3074 .schema(schema)
3075 .primary_key_indices(vec![0])
3076 .engine("engine")
3077 .next_column_id(3)
3078 .build()
3079 .unwrap();
3080
3081 let alter_kind = AlterKind::ModifyColumnTypes {
3083 columns: vec![ModifyColumnTypeRequest {
3084 column_name: "col1".to_string(),
3085 target_type: ConcreteDataType::string_datatype(),
3086 }],
3087 };
3088
3089 let err = meta
3090 .builder_with_alter_kind("my_table", &alter_kind)
3091 .err()
3092 .unwrap();
3093 assert_eq!(StatusCode::InvalidArguments, err.status_code());
3094
3095 let alter_kind = AlterKind::ModifyColumnTypes {
3097 columns: vec![ModifyColumnTypeRequest {
3098 column_name: "ts".to_string(),
3099 target_type: ConcreteDataType::string_datatype(),
3100 }],
3101 };
3102
3103 let err = meta
3104 .builder_with_alter_kind("my_table", &alter_kind)
3105 .err()
3106 .unwrap();
3107 assert_eq!(StatusCode::InvalidArguments, err.status_code());
3108 }
3109
3110 #[test]
3111 fn test_alloc_new_column() {
3112 let schema = Arc::new(new_test_schema());
3113 let mut meta = TableMetaBuilder::empty()
3114 .schema(schema)
3115 .primary_key_indices(vec![0])
3116 .engine("engine")
3117 .next_column_id(3)
3118 .build()
3119 .unwrap();
3120 assert_eq!(3, meta.next_column_id);
3121
3122 let column_schema = ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true);
3123 let desc = meta.alloc_new_column("test_table", &column_schema).unwrap();
3124
3125 assert_eq!(4, meta.next_column_id);
3126 assert_eq!(column_schema.name, desc.name);
3127 }
3128
3129 #[test]
3130 fn test_add_columns_with_location() {
3131 let schema = Arc::new(new_test_schema());
3132 let meta = TableMetaBuilder::empty()
3133 .schema(schema)
3134 .primary_key_indices(vec![0])
3135 .partition_key_indices(vec![0, 2])
3137 .engine("engine")
3138 .next_column_id(3)
3139 .build()
3140 .unwrap();
3141
3142 let new_meta = add_columns_to_meta_with_location(&meta);
3143 let names: Vec<String> = new_meta
3144 .schema
3145 .column_schemas()
3146 .iter()
3147 .map(|column_schema| column_schema.name.clone())
3148 .collect();
3149 assert_eq!(
3150 &[
3151 "my_tag_first", "col1", "ts", "yet_another_field_after_ts", "my_field_after_ts", "col2", ],
3158 &names[..]
3159 );
3160 assert_eq!(&[0, 1, 3], &new_meta.primary_key_indices[..]);
3161 assert_eq!(&[2, 4, 5], &new_meta.value_indices[..]);
3162 assert_eq!(&[1, 5], &new_meta.partition_key_indices[..]);
3163 }
3164
3165 #[test]
3166 fn test_modify_column_fulltext_options() {
3167 let schema = Arc::new(new_test_schema());
3168 let meta = TableMetaBuilder::empty()
3169 .schema(schema)
3170 .primary_key_indices(vec![0])
3171 .engine("engine")
3172 .next_column_id(3)
3173 .build()
3174 .unwrap();
3175
3176 let alter_kind = AlterKind::SetIndexes {
3177 options: vec![SetIndexOption::Fulltext {
3178 column_name: "col1".to_string(),
3179 options: FulltextOptions::default(),
3180 }],
3181 };
3182 let err = meta
3183 .builder_with_alter_kind("my_table", &alter_kind)
3184 .err()
3185 .unwrap();
3186 assert_eq!(
3187 "Invalid column option, column name: col1, error: FULLTEXT index only supports string type",
3188 err.to_string()
3189 );
3190
3191 let new_meta = add_columns_to_meta_with_location(&meta);
3193 let alter_kind = AlterKind::SetIndexes {
3194 options: vec![SetIndexOption::Fulltext {
3195 column_name: "my_tag_first".to_string(),
3196 options: FulltextOptions::new_unchecked(
3197 true,
3198 FulltextAnalyzer::Chinese,
3199 true,
3200 FulltextBackend::Bloom,
3201 1000,
3202 0.01,
3203 ),
3204 }],
3205 };
3206 let new_meta = new_meta
3207 .builder_with_alter_kind("my_table", &alter_kind)
3208 .unwrap()
3209 .build()
3210 .unwrap();
3211 let column_schema = new_meta
3212 .schema
3213 .column_schema_by_name("my_tag_first")
3214 .unwrap();
3215 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
3216 assert!(fulltext_options.enable);
3217 assert_eq!(
3218 datatypes::schema::FulltextAnalyzer::Chinese,
3219 fulltext_options.analyzer
3220 );
3221 assert!(fulltext_options.case_sensitive);
3222
3223 let alter_kind = AlterKind::UnsetIndexes {
3224 options: vec![UnsetIndexOption::Fulltext {
3225 column_name: "my_tag_first".to_string(),
3226 }],
3227 };
3228 let new_meta = new_meta
3229 .builder_with_alter_kind("my_table", &alter_kind)
3230 .unwrap()
3231 .build()
3232 .unwrap();
3233 let column_schema = new_meta
3234 .schema
3235 .column_schema_by_name("my_tag_first")
3236 .unwrap();
3237 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
3238 assert!(!fulltext_options.enable);
3239 }
3240
3241 #[test]
3242 fn test_table_info_serde_compatibility() {
3243 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"}"#;
3276
3277 let actual: TableInfo = serde_json::from_str(serialized).unwrap();
3278 let expected = TableInfo {
3279 ident: TableIdent {
3280 table_id: 1024,
3281 version: 1,
3282 },
3283 name: "foo".to_string(),
3284 desc: Some("my table".to_string()),
3285 catalog_name: "greptime".to_string(),
3286 schema_name: "public".to_string(),
3287 meta: TableMeta {
3288 schema: Arc::new(new_test_schema()),
3289 primary_key_indices: vec![0],
3290 value_indices: vec![1, 2],
3291 engine: "mito".to_string(),
3292 next_column_id: 3,
3293 options: TableOptions {
3294 ttl: Some(common_time::TimeToLive::Duration(
3295 std::time::Duration::from_secs(3600),
3296 )),
3297 ..Default::default()
3298 },
3299 created_on: DateTime::<Utc>::MIN_UTC,
3300 updated_on: DateTime::<Utc>::MAX_UTC,
3301 partition_key_indices: vec![2],
3302 column_ids: vec![0, 1, 2],
3303 },
3304 table_type: TableType::Base,
3305 };
3306 assert_eq!(actual, expected);
3307 }
3308
3309 use crate::requests::{SEMANTIC_METRIC_UNIT, SEMANTIC_SIGNAL_TYPE};
3310
3311 fn semantic_test_meta() -> TableMeta {
3314 let column_schemas = vec![
3315 ColumnSchema::new("host", ConcreteDataType::string_datatype(), true),
3316 ColumnSchema::new(
3317 "ts",
3318 ConcreteDataType::timestamp_millisecond_datatype(),
3319 false,
3320 )
3321 .with_time_index(true),
3322 ColumnSchema::new("payload", ConcreteDataType::binary_datatype(), true),
3323 ColumnSchema::new("service", ConcreteDataType::string_datatype(), true),
3324 ColumnSchema::new("note", ConcreteDataType::string_datatype(), true),
3325 ];
3326 let schema = Arc::new(
3327 SchemaBuilder::try_from(column_schemas)
3328 .unwrap()
3329 .build()
3330 .unwrap(),
3331 );
3332 TableMetaBuilder::empty()
3333 .schema(schema)
3334 .primary_key_indices(vec![0])
3335 .engine("engine")
3336 .next_column_id(5)
3337 .build()
3338 .unwrap()
3339 }
3340
3341 #[test]
3342 fn test_set_semantic_annotations() {
3343 let meta = semantic_test_meta();
3344 let alter_kind = AlterKind::SetAnnotations {
3346 family: AnnotationFamily::Semantic,
3347 options: vec![
3348 (SEMANTIC_SIGNAL_TYPE.to_string(), "trace".to_string()),
3349 (
3350 "greptime.semantic.entity.service.id".to_string(),
3351 "service".to_string(),
3352 ),
3353 ],
3354 };
3355 let new_meta = meta
3356 .builder_with_alter_kind("my_table", &alter_kind)
3357 .unwrap()
3358 .build()
3359 .unwrap();
3360 assert_eq!(
3361 new_meta.options.extra_options.get(SEMANTIC_SIGNAL_TYPE),
3362 Some(&"trace".to_string())
3363 );
3364 assert_eq!(
3365 new_meta
3366 .options
3367 .extra_options
3368 .get("greptime.semantic.entity.service.id"),
3369 Some(&"service".to_string())
3370 );
3371 }
3372
3373 #[test]
3374 fn test_repartition_hint_batch_rejects_duplicate_keys() {
3375 let meta = TableMetaBuilder::empty()
3376 .schema(Arc::new(new_test_schema()))
3377 .primary_key_indices(vec![0])
3378 .engine("engine")
3379 .next_column_id(3)
3380 .build()
3381 .unwrap();
3382
3383 let dup_set = AlterKind::SetAnnotations {
3386 family: AnnotationFamily::RepartitionHint,
3387 options: vec![
3388 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string()),
3389 (REPARTITION_COLUMN_HINT_KEY.to_string(), "col2".to_string()),
3390 ],
3391 };
3392 let err = meta
3393 .builder_with_alter_kind("my_table", &dup_set)
3394 .err()
3395 .unwrap();
3396 assert!(
3397 err.to_string().contains("duplicate repartition hint keys"),
3398 "{err}"
3399 );
3400
3401 let dup_unset = AlterKind::UnsetAnnotations {
3402 family: AnnotationFamily::RepartitionHint,
3403 keys: vec![
3404 REPARTITION_COLUMN_HINT_KEY.to_string(),
3405 REPARTITION_COLUMN_HINT_KEY.to_string(),
3406 ],
3407 };
3408 let err = meta
3409 .builder_with_alter_kind("my_table", &dup_unset)
3410 .err()
3411 .unwrap();
3412 assert!(
3413 err.to_string().contains("duplicate repartition hint keys"),
3414 "{err}"
3415 );
3416 }
3417
3418 #[test]
3419 fn test_set_semantic_annotations_rejects_invalid() {
3420 let meta = semantic_test_meta();
3421 let cases = [
3422 (
3423 "greptime.semantic.unknown_key",
3424 "x",
3425 "unknown semantic option",
3426 ),
3427 (SEMANTIC_SIGNAL_TYPE, "garbage", "invalid value"),
3428 (
3429 "greptime.semantic.entity.host.id",
3430 "no_such_column",
3431 "no_such_column",
3432 ),
3433 (
3434 "greptime.semantic.entity.host.id",
3435 "payload",
3436 "cannot render as a string",
3437 ),
3438 ];
3439 for (key, value, needle) in cases {
3440 let alter_kind = AlterKind::SetAnnotations {
3441 family: AnnotationFamily::Semantic,
3442 options: vec![(key.to_string(), value.to_string())],
3443 };
3444 let err = meta
3445 .builder_with_alter_kind("my_table", &alter_kind)
3446 .err()
3447 .unwrap();
3448 assert!(
3449 err.to_string().contains(needle),
3450 "key `{key}`: unexpected error `{err}`"
3451 );
3452 }
3453
3454 let missing = meta
3457 .builder_with_alter_kind(
3458 "my_table",
3459 &AlterKind::SetAnnotations {
3460 family: AnnotationFamily::Semantic,
3461 options: vec![(
3462 "greptime.semantic.entity.host.id".to_string(),
3463 "no_such_column".to_string(),
3464 )],
3465 },
3466 )
3467 .err()
3468 .unwrap();
3469 assert_eq!(
3470 common_error::status_code::StatusCode::TableColumnNotFound,
3471 common_error::ext::ErrorExt::status_code(&missing)
3472 );
3473 }
3474
3475 #[test]
3476 fn test_unset_semantic_annotations() {
3477 let mut meta = semantic_test_meta();
3478 meta.options
3479 .extra_options
3480 .insert(SEMANTIC_SIGNAL_TYPE.to_string(), "trace".to_string());
3481 meta.options
3483 .extra_options
3484 .insert("greptime.semantic.future_key".to_string(), "x".to_string());
3485
3486 let alter_kind = AlterKind::UnsetAnnotations {
3487 family: AnnotationFamily::Semantic,
3488 keys: vec![
3489 SEMANTIC_SIGNAL_TYPE.to_string(),
3490 "greptime.semantic.future_key".to_string(),
3491 SEMANTIC_METRIC_UNIT.to_string(),
3493 ],
3494 };
3495 let new_meta = meta
3496 .builder_with_alter_kind("my_table", &alter_kind)
3497 .unwrap()
3498 .build()
3499 .unwrap();
3500 assert!(
3501 !new_meta
3502 .options
3503 .extra_options
3504 .contains_key(SEMANTIC_SIGNAL_TYPE)
3505 );
3506 assert!(
3507 !new_meta
3508 .options
3509 .extra_options
3510 .contains_key("greptime.semantic.future_key")
3511 );
3512
3513 let outside = AlterKind::UnsetAnnotations {
3514 family: AnnotationFamily::Semantic,
3515 keys: vec!["ttl".to_string()],
3516 };
3517 let err = meta
3518 .builder_with_alter_kind("my_table", &outside)
3519 .err()
3520 .unwrap();
3521 assert!(err.to_string().contains("annotation namespace"), "{err}");
3522 }
3523
3524 #[test]
3525 fn test_modify_entity_column_type_keeps_string_form() {
3526 let mut meta = semantic_test_meta();
3527 meta.options.extra_options.insert(
3528 "greptime.semantic.entity.service.id".to_string(),
3529 "service".to_string(),
3530 );
3531
3532 let alter_kind = AlterKind::ModifyColumnTypes {
3533 columns: vec![ModifyColumnTypeRequest {
3534 column_name: "service".to_string(),
3535 target_type: ConcreteDataType::binary_datatype(),
3536 }],
3537 };
3538 let err = meta
3539 .builder_with_alter_kind("my_table", &alter_kind)
3540 .err()
3541 .unwrap();
3542 assert!(
3543 err.to_string()
3544 .contains("must keep a type that renders as a string"),
3545 "{err}"
3546 );
3547
3548 let alter_kind = AlterKind::ModifyColumnTypes {
3550 columns: vec![ModifyColumnTypeRequest {
3551 column_name: "note".to_string(),
3552 target_type: ConcreteDataType::binary_datatype(),
3553 }],
3554 };
3555 meta.builder_with_alter_kind("my_table", &alter_kind)
3556 .unwrap();
3557 }
3558
3559 #[test]
3560 fn test_stale_entity_declaration_guards_readd_and_reports_missing_column() {
3561 let mut meta = semantic_test_meta();
3563 meta.options.extra_options.insert(
3564 "greptime.semantic.entity.service.id".to_string(),
3565 "gone".to_string(),
3566 );
3567
3568 let modify = AlterKind::ModifyColumnTypes {
3571 columns: vec![ModifyColumnTypeRequest {
3572 column_name: "gone".to_string(),
3573 target_type: ConcreteDataType::binary_datatype(),
3574 }],
3575 };
3576 let err = meta
3577 .builder_with_alter_kind("my_table", &modify)
3578 .err()
3579 .unwrap();
3580 assert_eq!(
3581 common_error::status_code::StatusCode::TableColumnNotFound,
3582 common_error::ext::ErrorExt::status_code(&err)
3583 );
3584
3585 let add = |ty: ConcreteDataType| AlterKind::AddColumns {
3588 columns: vec![AddColumnRequest {
3589 column_schema: ColumnSchema::new("gone", ty, true),
3590 is_key: false,
3591 location: None,
3592 add_if_not_exists: false,
3593 }],
3594 };
3595 let err = meta
3596 .builder_with_alter_kind("my_table", &add(ConcreteDataType::binary_datatype()))
3597 .err()
3598 .unwrap();
3599 assert!(
3600 err.to_string()
3601 .contains("must keep a type that renders as a string"),
3602 "{err}"
3603 );
3604
3605 meta.builder_with_alter_kind("my_table", &add(ConcreteDataType::string_datatype()))
3607 .unwrap();
3608 }
3609}