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;
23pub use datatypes::error::{Error as ConvertError, Result as ConvertResult};
24use datatypes::schema::{
25 ColumnSchema, FulltextOptions, Schema, SchemaBuilder, SchemaRef, SkippingIndexOptions,
26};
27use derive_builder::Builder;
28use serde::{Deserialize, Deserializer, Serialize};
29use snafu::{OptionExt, ResultExt, ensure};
30use store_api::metric_engine_consts::PHYSICAL_TABLE_METADATA_KEY;
31use store_api::mito_engine_options::{
32 APPEND_MODE_KEY, AUTO_FLUSH_INTERVAL_KEY, COMPACTION_TYPE, COMPACTION_TYPE_TWCS,
33 MAX_ROW_GROUP_ROW_COUNT, MERGE_MODE_KEY, SKIP_WAL_KEY, SST_FORMAT_KEY,
34};
35use store_api::region_request::{SetRegionOption, UnsetRegionOption};
36use store_api::storage::{ColumnDescriptor, ColumnDescriptorBuilder, ColumnId};
37
38use crate::error::{self, Result};
39use crate::requests::{
40 AddColumnRequest, AlterKind, ModifyColumnTypeRequest, REPARTITION_COLUMN_HINT_KEY,
41 SetDefaultRequest, SetIndexOption, TableOptions, UnsetIndexOption,
42};
43use crate::table_reference::TableReference;
44
45pub type TableId = u32;
46pub type TableVersion = u64;
47
48#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
51pub enum FilterPushDownType {
52 Unsupported,
54 Inexact,
59 Exact,
63}
64
65impl From<TableProviderFilterPushDown> for FilterPushDownType {
66 fn from(value: TableProviderFilterPushDown) -> Self {
67 match value {
68 TableProviderFilterPushDown::Unsupported => FilterPushDownType::Unsupported,
69 TableProviderFilterPushDown::Inexact => FilterPushDownType::Inexact,
70 TableProviderFilterPushDown::Exact => FilterPushDownType::Exact,
71 }
72 }
73}
74
75impl From<FilterPushDownType> for TableProviderFilterPushDown {
76 fn from(value: FilterPushDownType) -> Self {
77 match value {
78 FilterPushDownType::Unsupported => TableProviderFilterPushDown::Unsupported,
79 FilterPushDownType::Inexact => TableProviderFilterPushDown::Inexact,
80 FilterPushDownType::Exact => TableProviderFilterPushDown::Exact,
81 }
82 }
83}
84
85#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TableType {
88 Base,
90 View,
92 Temporary,
94}
95
96impl std::fmt::Display for TableType {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self {
99 TableType::Base => f.write_str("BASE TABLE"),
100 TableType::Temporary => f.write_str("TEMPORARY"),
101 TableType::View => f.write_str("VIEW"),
102 }
103 }
104}
105
106impl From<TableType> for datafusion::datasource::TableType {
107 fn from(t: TableType) -> datafusion::datasource::TableType {
108 match t {
109 TableType::Base => datafusion::datasource::TableType::Base,
110 TableType::View => datafusion::datasource::TableType::View,
111 TableType::Temporary => datafusion::datasource::TableType::Temporary,
112 }
113 }
114}
115
116#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Default)]
118pub struct TableIdent {
119 pub table_id: TableId,
121 pub version: TableVersion,
124}
125
126#[derive(Clone, Debug, Builder, PartialEq, Eq, ToMetaBuilder, Serialize)]
130#[builder(pattern = "mutable", custom_constructor)]
131pub struct TableMeta {
132 pub schema: SchemaRef,
133 pub primary_key_indices: Vec<usize>,
136 #[builder(default = "self.default_value_indices()?")]
137 pub value_indices: Vec<usize>,
138 #[builder(default, setter(into))]
139 pub engine: String,
140 pub next_column_id: ColumnId,
141 #[builder(default)]
143 pub options: TableOptions,
144 #[builder(default = "Utc::now()")]
145 pub created_on: DateTime<Utc>,
146 #[builder(default = "self.default_updated_on()")]
147 pub updated_on: DateTime<Utc>,
148 #[builder(default = "Vec::new()")]
149 pub partition_key_indices: Vec<usize>,
150 #[builder(default = "Vec::new()")]
151 pub column_ids: Vec<ColumnId>,
152}
153
154impl TableMeta {
155 pub fn empty() -> Self {
156 Self {
157 schema: Arc::new(Schema::new(vec![])),
158 primary_key_indices: vec![],
159 value_indices: vec![],
160 engine: "".to_string(),
161 next_column_id: 0,
162 options: TableOptions::default(),
163 created_on: Utc::now(),
164 updated_on: Utc::now(),
165 partition_key_indices: vec![],
166 column_ids: vec![],
167 }
168 }
169}
170
171impl<'de> Deserialize<'de> for TableMeta {
172 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
173 where
174 D: Deserializer<'de>,
175 {
176 #[derive(Deserialize)]
177 struct RawTableMeta {
178 schema: SchemaRef,
179 primary_key_indices: Vec<usize>,
180 value_indices: Vec<usize>,
181 engine: String,
182 next_column_id: ColumnId,
183 options: TableOptions,
184 created_on: DateTime<Utc>,
185 updated_on: Option<DateTime<Utc>>,
186 #[serde(default)]
187 partition_key_indices: Vec<usize>,
188 #[serde(default)]
189 column_ids: Vec<ColumnId>,
190 }
191
192 let RawTableMeta {
193 schema,
194 primary_key_indices,
195 value_indices,
196 engine,
197 next_column_id,
198 options,
199 created_on,
200 updated_on,
201 partition_key_indices,
202 column_ids,
203 } = RawTableMeta::deserialize(deserializer)?;
204
205 Ok(Self {
206 schema,
207 primary_key_indices,
208 value_indices,
209 engine,
210 next_column_id,
211 options,
212 created_on,
213 updated_on: updated_on.unwrap_or(created_on),
214 partition_key_indices,
215 column_ids,
216 })
217 }
218}
219
220impl TableMetaBuilder {
221 #[cfg(any(test, feature = "testing"))]
223 pub fn empty() -> Self {
224 Self {
225 schema: None,
226 primary_key_indices: None,
227 value_indices: None,
228 engine: None,
229 next_column_id: None,
230 options: None,
231 created_on: None,
232 updated_on: None,
233 partition_key_indices: None,
234 column_ids: None,
235 }
236 }
237}
238
239impl TableMetaBuilder {
240 fn default_value_indices(&self) -> std::result::Result<Vec<usize>, String> {
241 match (&self.primary_key_indices, &self.schema) {
242 (Some(v), Some(schema)) => {
243 let column_schemas = schema.column_schemas();
244 Ok((0..column_schemas.len())
245 .filter(|idx| !v.contains(idx))
246 .collect())
247 }
248 _ => Err("Missing primary_key_indices or schema to create value_indices".to_string()),
249 }
250 }
251
252 fn default_updated_on(&self) -> DateTime<Utc> {
253 self.created_on.unwrap_or_default()
254 }
255
256 pub fn new_external_table() -> Self {
257 Self {
258 schema: None,
259 primary_key_indices: Some(Vec::new()),
260 value_indices: Some(Vec::new()),
261 engine: None,
262 next_column_id: Some(0),
263 options: None,
264 created_on: None,
265 updated_on: None,
266 partition_key_indices: None,
267 column_ids: None,
268 }
269 }
270}
271
272struct SplitResult<'a> {
274 columns_at_first: Vec<&'a AddColumnRequest>,
276 columns_at_after: HashMap<String, Vec<&'a AddColumnRequest>>,
278 columns_at_last: Vec<&'a AddColumnRequest>,
280 column_names: Vec<String>,
282}
283
284impl TableMeta {
285 pub fn row_key_column_names(&self) -> impl Iterator<Item = &String> {
286 let columns_schemas = &self.schema.column_schemas();
287 self.primary_key_indices
288 .iter()
289 .map(|idx| &columns_schemas[*idx].name)
290 }
291
292 pub fn field_column_names(&self) -> impl Iterator<Item = &String> {
293 let columns_schemas = self.schema.column_schemas();
295 let primary_key_indices = &self.primary_key_indices;
296 columns_schemas
297 .iter()
298 .enumerate()
299 .filter(|(i, cs)| !primary_key_indices.contains(i) && !cs.is_time_index())
300 .map(|(_, cs)| &cs.name)
301 }
302
303 pub fn partition_column_names(&self) -> impl Iterator<Item = &String> {
304 let columns_schemas = &self.schema.column_schemas();
305 self.partition_key_indices
306 .iter()
307 .map(|idx| &columns_schemas[*idx].name)
308 }
309
310 pub fn partition_columns(&self) -> impl Iterator<Item = &ColumnSchema> {
311 self.partition_key_indices
312 .iter()
313 .map(|idx| &self.schema.column_schemas()[*idx])
314 }
315
316 pub fn builder_with_alter_kind(
320 &self,
321 table_name: &str,
322 alter_kind: &AlterKind,
323 ) -> Result<TableMetaBuilder> {
324 let mut builder = match alter_kind {
325 AlterKind::AddColumns { columns } => self.add_columns(table_name, columns),
326 AlterKind::DropColumns { names } => self.remove_columns(table_name, names),
327 AlterKind::ModifyColumnTypes { columns } => {
328 self.modify_column_types(table_name, columns)
329 }
330 AlterKind::RenameTable { .. } => Ok(self.new_meta_builder()),
332 AlterKind::SetTableOptions { options } => self.set_table_options(options),
333 AlterKind::UnsetTableOptions { keys } => self.unset_table_options(keys),
334 AlterKind::SetRepartitionColumnHint { column_name } => {
335 self.set_repartition_column_hint(table_name, column_name)
336 }
337 AlterKind::UnsetRepartitionColumnHint => self.unset_repartition_column_hint(),
338 AlterKind::SetIndexes { options } => self.set_indexes(table_name, options),
339 AlterKind::UnsetIndexes { options } => self.unset_indexes(table_name, options),
340 AlterKind::DropDefaults { names } => self.drop_defaults(table_name, names),
341 AlterKind::SetDefaults { defaults } => self.set_defaults(table_name, defaults),
342 }?;
343 let _ = builder.updated_on(Utc::now());
344 Ok(builder)
345 }
346
347 fn set_table_options(&self, requests: &[SetRegionOption]) -> Result<TableMetaBuilder> {
349 let mut new_options = self.options.clone();
350
351 for request in requests {
352 match request {
353 SetRegionOption::WriteBufferSize(new_write_buffer_size) => {
354 new_options.write_buffer_size = *new_write_buffer_size;
355 }
356 SetRegionOption::Ttl(new_ttl) => {
357 new_options.ttl = *new_ttl;
358 }
359 SetRegionOption::Twsc(key, value) => {
360 if !value.is_empty() {
361 new_options.extra_options.insert(key.clone(), value.clone());
362 new_options.extra_options.insert(
364 COMPACTION_TYPE.to_string(),
365 COMPACTION_TYPE_TWCS.to_string(),
366 );
367 } else {
368 new_options.extra_options.remove(key.as_str());
370 }
371 }
372 SetRegionOption::Format(value) => {
373 new_options
374 .extra_options
375 .insert(SST_FORMAT_KEY.to_string(), value.clone());
376 }
377 SetRegionOption::AppendMode(value) => {
378 new_options
379 .extra_options
380 .insert(APPEND_MODE_KEY.to_string(), value.to_string());
381 if *value {
382 new_options.extra_options.remove(MERGE_MODE_KEY);
383 }
384 }
385 SetRegionOption::AutoFlushInterval(new_interval) => {
386 if let Some(interval) = new_interval {
387 new_options.extra_options.insert(
388 AUTO_FLUSH_INTERVAL_KEY.to_string(),
389 humantime::format_duration(*interval).to_string(),
390 );
391 } else {
392 new_options.extra_options.remove(AUTO_FLUSH_INTERVAL_KEY);
393 }
394 }
395 SetRegionOption::MaxRowGroupRowCount(row_count) => {
396 if let Some(row_count) = row_count {
397 new_options
398 .extra_options
399 .insert(MAX_ROW_GROUP_ROW_COUNT.to_string(), row_count.to_string());
400 } else {
401 new_options.extra_options.remove(MAX_ROW_GROUP_ROW_COUNT);
402 }
403 }
404 SetRegionOption::SkipWal => {
405 new_options.skip_wal = true;
406 new_options
409 .extra_options
410 .insert(SKIP_WAL_KEY.to_string(), true.to_string());
411 }
412 }
413 }
414 let mut builder = self.new_meta_builder();
415 builder.options(new_options);
416
417 Ok(builder)
418 }
419
420 fn unset_table_options(&self, requests: &[UnsetRegionOption]) -> Result<TableMetaBuilder> {
421 let requests = requests.iter().map(Into::into).collect::<Vec<_>>();
422 self.set_table_options(&requests)
423 }
424
425 fn set_repartition_column_hint(
426 &self,
427 table_name: &str,
428 column_name: &str,
429 ) -> Result<TableMetaBuilder> {
430 let column_name = column_name.trim();
431 ensure!(
432 !column_name.is_empty() && !column_name.contains(','),
433 error::InvalidAlterRequestSnafu {
434 table: table_name,
435 err: format!("{REPARTITION_COLUMN_HINT_KEY} expects exactly one column name"),
436 }
437 );
438
439 ensure!(
440 self.partition_key_indices.is_empty(),
441 error::InvalidAlterRequestSnafu {
442 table: table_name,
443 err: format!(
444 "cannot set {REPARTITION_COLUMN_HINT_KEY} on a table with partition metadata"
445 ),
446 }
447 );
448
449 let column_index = self
450 .schema
451 .column_index_by_name(column_name)
452 .with_context(|| error::ColumnNotExistsSnafu {
453 column_name,
454 table_name,
455 })?;
456
457 if let Some(time_index) = self.schema.timestamp_index() {
458 ensure!(
459 column_index != time_index,
460 error::InvalidAlterRequestSnafu {
461 table: table_name,
462 err: format!(
463 "cannot set {REPARTITION_COLUMN_HINT_KEY} to the time index column"
464 ),
465 }
466 );
467 }
468
469 let mut new_options = self.options.clone();
470 new_options.extra_options.insert(
471 REPARTITION_COLUMN_HINT_KEY.to_string(),
472 column_name.to_string(),
473 );
474
475 let mut builder = self.new_meta_builder();
476 builder.options(new_options);
477 Ok(builder)
478 }
479
480 fn unset_repartition_column_hint(&self) -> Result<TableMetaBuilder> {
481 let mut new_options = self.options.clone();
482 new_options
483 .extra_options
484 .remove(REPARTITION_COLUMN_HINT_KEY);
485
486 let mut builder = self.new_meta_builder();
487 builder.options(new_options);
488 Ok(builder)
489 }
490
491 fn set_indexes(
492 &self,
493 table_name: &str,
494 requests: &[SetIndexOption],
495 ) -> Result<TableMetaBuilder> {
496 let table_schema = &self.schema;
497 let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
498 for request in requests {
499 let column_name = request.column_name();
500 table_schema
501 .column_index_by_name(column_name)
502 .with_context(|| error::ColumnNotExistsSnafu {
503 column_name,
504 table_name,
505 })?;
506 set_index_options
507 .entry(column_name)
508 .or_default()
509 .push(request);
510 }
511
512 let mut meta_builder = self.new_meta_builder();
513 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
514 for mut column in table_schema.column_schemas().iter().cloned() {
515 if let Some(request) = set_index_options.get(column.name.as_str()) {
516 for request in request {
517 self.set_index(&mut column, request)?;
518 }
519 }
520 columns.push(column);
521 }
522
523 let mut builder = SchemaBuilder::try_from_columns(columns)
524 .with_context(|_| error::SchemaBuildSnafu {
525 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
526 })?
527 .version(table_schema.version() + 1);
528
529 for (k, v) in table_schema.metadata().iter() {
530 builder = builder.add_metadata(k, v);
531 }
532
533 let new_schema = builder.build().with_context(|_| {
534 let column_names = requests
535 .iter()
536 .map(|request| request.column_name())
537 .collect::<Vec<_>>();
538 error::SchemaBuildSnafu {
539 msg: format!(
540 "Table {table_name} cannot set index options with columns {column_names:?}",
541 ),
542 }
543 })?;
544 let _ = meta_builder
545 .schema(Arc::new(new_schema))
546 .primary_key_indices(self.primary_key_indices.clone());
547
548 Ok(meta_builder)
549 }
550
551 fn unset_indexes(
552 &self,
553 table_name: &str,
554 requests: &[UnsetIndexOption],
555 ) -> Result<TableMetaBuilder> {
556 let table_schema = &self.schema;
557 let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
558 for request in requests {
559 let column_name = request.column_name();
560 table_schema
561 .column_index_by_name(column_name)
562 .with_context(|| error::ColumnNotExistsSnafu {
563 column_name,
564 table_name,
565 })?;
566 set_index_options
567 .entry(column_name)
568 .or_default()
569 .push(request);
570 }
571
572 let mut meta_builder = self.new_meta_builder();
573 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
574 for mut column in table_schema.column_schemas().iter().cloned() {
575 if let Some(request) = set_index_options.get(column.name.as_str()) {
576 for request in request {
577 self.unset_index(&mut column, request)?;
578 }
579 }
580 columns.push(column);
581 }
582
583 let mut builder = SchemaBuilder::try_from_columns(columns)
584 .with_context(|_| error::SchemaBuildSnafu {
585 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
586 })?
587 .version(table_schema.version() + 1);
588
589 for (k, v) in table_schema.metadata().iter() {
590 builder = builder.add_metadata(k, v);
591 }
592
593 let new_schema = builder.build().with_context(|_| {
594 let column_names = requests
595 .iter()
596 .map(|request| request.column_name())
597 .collect::<Vec<_>>();
598 error::SchemaBuildSnafu {
599 msg: format!(
600 "Table {table_name} cannot set index options with columns {column_names:?}",
601 ),
602 }
603 })?;
604 let _ = meta_builder
605 .schema(Arc::new(new_schema))
606 .primary_key_indices(self.primary_key_indices.clone());
607
608 Ok(meta_builder)
609 }
610
611 fn set_index(&self, column_schema: &mut ColumnSchema, request: &SetIndexOption) -> Result<()> {
612 match request {
613 SetIndexOption::Fulltext {
614 column_name,
615 options,
616 } => {
617 ensure!(
618 column_schema.data_type.is_string(),
619 error::InvalidColumnOptionSnafu {
620 column_name,
621 msg: "FULLTEXT index only supports string type",
622 }
623 );
624
625 let current_fulltext_options = column_schema
626 .fulltext_options()
627 .context(error::SetFulltextOptionsSnafu { column_name })?;
628 set_column_fulltext_options(
629 column_schema,
630 column_name,
631 options,
632 current_fulltext_options,
633 )?;
634 }
635 SetIndexOption::Inverted { column_name } => {
636 debug_assert_eq!(column_schema.name, *column_name);
637 column_schema.set_inverted_index(true);
638 }
639 SetIndexOption::Skipping {
640 column_name,
641 options,
642 } => {
643 set_column_skipping_index_options(column_schema, column_name, options)?;
644 }
645 }
646
647 Ok(())
648 }
649
650 fn unset_index(
651 &self,
652 column_schema: &mut ColumnSchema,
653 request: &UnsetIndexOption,
654 ) -> Result<()> {
655 match request {
656 UnsetIndexOption::Fulltext { column_name } => {
657 let current_fulltext_options = column_schema
658 .fulltext_options()
659 .context(error::SetFulltextOptionsSnafu { column_name })?;
660 unset_column_fulltext_options(
661 column_schema,
662 column_name,
663 current_fulltext_options.clone(),
664 )?
665 }
666 UnsetIndexOption::Inverted { .. } => {
667 column_schema.set_inverted_index(false);
668 }
669 UnsetIndexOption::Skipping { column_name } => {
670 unset_column_skipping_index_options(column_schema, column_name)?;
671 }
672 }
673
674 Ok(())
675 }
676
677 pub fn alloc_new_column(
682 &mut self,
683 table_name: &str,
684 new_column: &ColumnSchema,
685 ) -> Result<ColumnDescriptor> {
686 let desc = ColumnDescriptorBuilder::new(
687 self.next_column_id as ColumnId,
688 &new_column.name,
689 new_column.data_type.clone(),
690 )
691 .is_nullable(new_column.is_nullable())
692 .default_constraint(new_column.default_constraint().cloned())
693 .build()
694 .context(error::BuildColumnDescriptorSnafu {
695 table_name,
696 column_name: &new_column.name,
697 })?;
698
699 self.next_column_id += 1;
701
702 Ok(desc)
703 }
704
705 fn new_meta_builder(&self) -> TableMetaBuilder {
707 let mut builder = TableMetaBuilder::from(self);
708 builder.value_indices = None;
710 builder
711 }
712
713 fn add_columns(
715 &self,
716 table_name: &str,
717 requests: &[AddColumnRequest],
718 ) -> Result<TableMetaBuilder> {
719 let table_schema = &self.schema;
720 let mut meta_builder = self.new_meta_builder();
721 let original_primary_key_indices: HashSet<&usize> =
722 self.primary_key_indices.iter().collect();
723
724 let mut names = HashSet::with_capacity(requests.len());
725 let mut new_columns = Vec::with_capacity(requests.len());
726 for col_to_add in requests {
727 if let Some(column_schema) =
728 table_schema.column_schema_by_name(&col_to_add.column_schema.name)
729 {
730 ensure!(
732 col_to_add.add_if_not_exists,
733 error::ColumnExistsSnafu {
734 table_name,
735 column_name: &col_to_add.column_schema.name
736 },
737 );
738
739 ensure!(
741 column_schema.data_type == col_to_add.column_schema.data_type,
742 error::InvalidAlterRequestSnafu {
743 table: table_name,
744 err: format!(
745 "column {} already exists with different type {:?}",
746 col_to_add.column_schema.name, column_schema.data_type,
747 ),
748 }
749 );
750 } else {
751 ensure!(
754 names.insert(&col_to_add.column_schema.name),
755 error::InvalidAlterRequestSnafu {
756 table: table_name,
757 err: format!(
758 "add column {} more than once",
759 col_to_add.column_schema.name
760 ),
761 }
762 );
763
764 ensure!(
765 col_to_add.column_schema.is_nullable()
766 || col_to_add.column_schema.default_constraint().is_some(),
767 error::InvalidAlterRequestSnafu {
768 table: table_name,
769 err: format!(
770 "no default value for column {}",
771 col_to_add.column_schema.name
772 ),
773 },
774 );
775
776 new_columns.push(col_to_add.clone());
777 }
778 }
779 let requests = &new_columns[..];
780
781 let SplitResult {
782 columns_at_first,
783 columns_at_after,
784 columns_at_last,
785 column_names,
786 } = self.split_requests_by_column_location(table_name, requests)?;
787 let mut primary_key_indices = Vec::with_capacity(self.primary_key_indices.len());
788 let mut columns = Vec::with_capacity(table_schema.num_columns() + requests.len());
789 columns_at_first.iter().rev().for_each(|request| {
791 if request.is_key {
792 primary_key_indices.push(columns.len());
794 }
795 columns.push(request.column_schema.clone());
796 });
797 for (index, column_schema) in table_schema.column_schemas().iter().enumerate() {
799 if original_primary_key_indices.contains(&index) {
800 primary_key_indices.push(columns.len());
801 }
802 columns.push(column_schema.clone());
803 if let Some(requests) = columns_at_after.get(&column_schema.name) {
804 requests.iter().rev().for_each(|request| {
805 if request.is_key {
806 primary_key_indices.push(columns.len());
808 }
809 columns.push(request.column_schema.clone());
810 });
811 }
812 }
813 columns_at_last.iter().for_each(|request| {
815 if request.is_key {
816 primary_key_indices.push(columns.len());
818 }
819 columns.push(request.column_schema.clone());
820 });
821
822 let mut builder = SchemaBuilder::try_from(columns)
823 .with_context(|_| error::SchemaBuildSnafu {
824 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
825 })?
826 .version(table_schema.version() + 1);
828 for (k, v) in table_schema.metadata().iter() {
829 builder = builder.add_metadata(k, v);
830 }
831 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
832 msg: format!("Table {table_name} cannot add new columns {column_names:?}"),
833 })?;
834
835 let partition_key_indices = self
836 .partition_key_indices
837 .iter()
838 .map(|idx| table_schema.column_name_by_index(*idx))
839 .map(|name| new_schema.column_index_by_name(name).unwrap())
841 .collect();
842
843 let _ = meta_builder
845 .schema(Arc::new(new_schema))
846 .primary_key_indices(primary_key_indices)
847 .partition_key_indices(partition_key_indices);
848
849 Ok(meta_builder)
850 }
851
852 fn remove_columns(
853 &self,
854 table_name: &str,
855 column_names: &[String],
856 ) -> Result<TableMetaBuilder> {
857 let table_schema = &self.schema;
858 let column_names: HashSet<_> = column_names.iter().collect();
859 let mut meta_builder = self.new_meta_builder();
860
861 let timestamp_index = table_schema.timestamp_index();
862 for column_name in &column_names {
864 if let Some(index) = table_schema.column_index_by_name(column_name) {
865 ensure!(
868 !self.primary_key_indices.contains(&index),
869 error::RemoveColumnInIndexSnafu {
870 column_name: *column_name,
871 table_name,
872 }
873 );
874
875 ensure!(
876 !self.partition_key_indices.contains(&index),
877 error::RemovePartitionColumnSnafu {
878 column_name: *column_name,
879 table_name,
880 }
881 );
882
883 if let Some(ts_index) = timestamp_index {
884 ensure!(
886 index != ts_index,
887 error::RemoveColumnInIndexSnafu {
888 column_name: table_schema.column_name_by_index(ts_index),
889 table_name,
890 }
891 );
892 }
893 } else {
894 return error::ColumnNotExistsSnafu {
895 column_name: *column_name,
896 table_name,
897 }
898 .fail()?;
899 }
900 }
901
902 let columns: Vec<_> = table_schema
904 .column_schemas()
905 .iter()
906 .filter(|column_schema| !column_names.contains(&column_schema.name))
907 .cloned()
908 .collect();
909
910 let mut builder = SchemaBuilder::try_from_columns(columns)
911 .with_context(|_| error::SchemaBuildSnafu {
912 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
913 })?
914 .version(table_schema.version() + 1);
916 for (k, v) in table_schema.metadata().iter() {
917 builder = builder.add_metadata(k, v);
918 }
919 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
920 msg: format!("Table {table_name} cannot add remove columns {column_names:?}"),
921 })?;
922
923 let primary_key_indices = self
925 .primary_key_indices
926 .iter()
927 .map(|idx| table_schema.column_name_by_index(*idx))
928 .map(|name| new_schema.column_index_by_name(name).unwrap())
930 .collect();
931
932 let partition_key_indices = self
933 .partition_key_indices
934 .iter()
935 .map(|idx| table_schema.column_name_by_index(*idx))
936 .map(|name| new_schema.column_index_by_name(name).unwrap())
938 .collect();
939
940 let _ = meta_builder
941 .schema(Arc::new(new_schema))
942 .primary_key_indices(primary_key_indices)
943 .partition_key_indices(partition_key_indices);
944
945 Ok(meta_builder)
946 }
947
948 fn modify_column_types(
949 &self,
950 table_name: &str,
951 requests: &[ModifyColumnTypeRequest],
952 ) -> Result<TableMetaBuilder> {
953 let table_schema = &self.schema;
954 let mut meta_builder = self.new_meta_builder();
955
956 let mut modify_column_types = HashMap::with_capacity(requests.len());
957 let timestamp_index = table_schema.timestamp_index();
958
959 for col_to_change in requests {
960 let change_column_name = &col_to_change.column_name;
961
962 let index = table_schema
963 .column_index_by_name(change_column_name)
964 .with_context(|| error::ColumnNotExistsSnafu {
965 column_name: change_column_name,
966 table_name,
967 })?;
968
969 let column = &table_schema.column_schemas()[index];
970
971 ensure!(
972 !self.primary_key_indices.contains(&index),
973 error::InvalidAlterRequestSnafu {
974 table: table_name,
975 err: format!(
976 "Not allowed to change primary key index column '{}'",
977 column.name
978 )
979 }
980 );
981
982 if let Some(ts_index) = timestamp_index {
983 ensure!(
985 index != ts_index,
986 error::InvalidAlterRequestSnafu {
987 table: table_name,
988 err: format!(
989 "Not allowed to change timestamp index column '{}' datatype",
990 column.name
991 )
992 }
993 );
994 }
995
996 ensure!(
997 modify_column_types
998 .insert(&col_to_change.column_name, col_to_change)
999 .is_none(),
1000 error::InvalidAlterRequestSnafu {
1001 table: table_name,
1002 err: format!(
1003 "change column datatype {} more than once",
1004 col_to_change.column_name
1005 ),
1006 }
1007 );
1008
1009 ensure!(
1010 column
1011 .data_type
1012 .can_arrow_type_cast_to(&col_to_change.target_type),
1013 error::InvalidAlterRequestSnafu {
1014 table: table_name,
1015 err: format!(
1016 "column '{}' cannot be cast automatically to type '{}'",
1017 col_to_change.column_name, col_to_change.target_type,
1018 ),
1019 }
1020 );
1021
1022 ensure!(
1023 column.is_nullable(),
1024 error::InvalidAlterRequestSnafu {
1025 table: table_name,
1026 err: format!(
1027 "column '{}' must be nullable to ensure safe conversion.",
1028 col_to_change.column_name,
1029 ),
1030 }
1031 );
1032 }
1033 let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
1036 for mut column in table_schema.column_schemas().iter().cloned() {
1037 if let Some(change_column) = modify_column_types.get(&column.name) {
1038 column.data_type = change_column.target_type.clone();
1039 let new_default = if let Some(default_value) = column.default_constraint() {
1040 Some(
1041 default_value
1042 .cast_to_datatype(&change_column.target_type)
1043 .with_context(|_| error::CastDefaultValueSnafu {
1044 reason: format!(
1045 "Failed to cast default value from {:?} to type {:?}",
1046 default_value, &change_column.target_type
1047 ),
1048 })?,
1049 )
1050 } else {
1051 None
1052 };
1053 column = column
1054 .clone()
1055 .with_default_constraint(new_default.clone())
1056 .with_context(|_| error::CastDefaultValueSnafu {
1057 reason: format!("Failed to set new default: {:?}", new_default),
1058 })?;
1059 }
1060 columns.push(column)
1061 }
1062
1063 let mut builder = SchemaBuilder::try_from_columns(columns)
1064 .with_context(|_| error::SchemaBuildSnafu {
1065 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1066 })?
1067 .version(table_schema.version() + 1);
1069 for (k, v) in table_schema.metadata().iter() {
1070 builder = builder.add_metadata(k, v);
1071 }
1072 let new_schema = builder.build().with_context(|_| {
1073 let column_names: Vec<_> = requests
1074 .iter()
1075 .map(|request| &request.column_name)
1076 .collect();
1077
1078 error::SchemaBuildSnafu {
1079 msg: format!(
1080 "Table {table_name} cannot change datatype with columns {column_names:?}"
1081 ),
1082 }
1083 })?;
1084
1085 let _ = meta_builder
1086 .schema(Arc::new(new_schema))
1087 .primary_key_indices(self.primary_key_indices.clone());
1088
1089 Ok(meta_builder)
1090 }
1091
1092 fn split_requests_by_column_location<'a>(
1094 &self,
1095 table_name: &str,
1096 requests: &'a [AddColumnRequest],
1097 ) -> Result<SplitResult<'a>> {
1098 let table_schema = &self.schema;
1099 let mut columns_at_first = Vec::new();
1100 let mut columns_at_after = HashMap::new();
1101 let mut columns_at_last = Vec::new();
1102 let mut column_names = Vec::with_capacity(requests.len());
1103 for request in requests {
1104 let column_name = &request.column_schema.name;
1106 column_names.push(column_name.clone());
1107 ensure!(
1108 table_schema.column_schema_by_name(column_name).is_none(),
1109 error::ColumnExistsSnafu {
1110 column_name,
1111 table_name,
1112 }
1113 );
1114 match request.location.as_ref() {
1115 Some(AddColumnLocation::First) => {
1116 columns_at_first.push(request);
1117 }
1118 Some(AddColumnLocation::After { column_name }) => {
1119 ensure!(
1120 table_schema.column_schema_by_name(column_name).is_some(),
1121 error::ColumnNotExistsSnafu {
1122 column_name,
1123 table_name,
1124 }
1125 );
1126 columns_at_after
1127 .entry(column_name.clone())
1128 .or_insert(Vec::new())
1129 .push(request);
1130 }
1131 None => {
1132 columns_at_last.push(request);
1133 }
1134 }
1135 }
1136 Ok(SplitResult {
1137 columns_at_first,
1138 columns_at_after,
1139 columns_at_last,
1140 column_names,
1141 })
1142 }
1143
1144 fn drop_defaults(&self, table_name: &str, column_names: &[String]) -> Result<TableMetaBuilder> {
1145 let table_schema = &self.schema;
1146 let mut meta_builder = self.new_meta_builder();
1147 let mut columns = Vec::with_capacity(table_schema.num_columns());
1148 for column_schema in table_schema.column_schemas() {
1149 if let Some(name) = column_names.iter().find(|s| **s == column_schema.name) {
1150 ensure!(
1152 column_schema.default_constraint().is_some(),
1153 error::InvalidAlterRequestSnafu {
1154 table: table_name,
1155 err: format!("column {name} does not have a default value"),
1156 }
1157 );
1158 if !column_schema.is_nullable() {
1159 return error::InvalidAlterRequestSnafu {
1160 table: table_name,
1161 err: format!(
1162 "column {name} is not nullable and `default` cannot be dropped",
1163 ),
1164 }
1165 .fail();
1166 }
1167 let new_column_schema = column_schema.clone();
1168 let new_column_schema = new_column_schema
1169 .with_default_constraint(None)
1170 .with_context(|_| error::SchemaBuildSnafu {
1171 msg: format!("Table {table_name} cannot drop default values"),
1172 })?;
1173 columns.push(new_column_schema);
1174 } else {
1175 columns.push(column_schema.clone());
1176 }
1177 }
1178
1179 let mut builder = SchemaBuilder::try_from_columns(columns)
1180 .with_context(|_| error::SchemaBuildSnafu {
1181 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1182 })?
1183 .version(table_schema.version() + 1);
1185 for (k, v) in table_schema.metadata().iter() {
1186 builder = builder.add_metadata(k, v);
1187 }
1188 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1189 msg: format!("Table {table_name} cannot drop default values"),
1190 })?;
1191
1192 let _ = meta_builder.schema(Arc::new(new_schema));
1193
1194 Ok(meta_builder)
1195 }
1196
1197 fn set_defaults(
1198 &self,
1199 table_name: &str,
1200 set_defaults: &[SetDefaultRequest],
1201 ) -> Result<TableMetaBuilder> {
1202 let table_schema = &self.schema;
1203 let mut meta_builder = self.new_meta_builder();
1204 let mut columns = Vec::with_capacity(table_schema.num_columns());
1205 for column_schema in table_schema.column_schemas() {
1206 if let Some(set_default) = set_defaults
1207 .iter()
1208 .find(|s| s.column_name == column_schema.name)
1209 {
1210 let new_column_schema = column_schema.clone();
1211 let new_column_schema = new_column_schema
1212 .with_default_constraint(set_default.default_constraint.clone())
1213 .with_context(|_| error::SchemaBuildSnafu {
1214 msg: format!("Table {table_name} cannot set default values"),
1215 })?;
1216 columns.push(new_column_schema);
1217 } else {
1218 columns.push(column_schema.clone());
1219 }
1220 }
1221
1222 let mut builder = SchemaBuilder::try_from_columns(columns)
1223 .with_context(|_| error::SchemaBuildSnafu {
1224 msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1225 })?
1226 .version(table_schema.version() + 1);
1228 for (k, v) in table_schema.metadata().iter() {
1229 builder = builder.add_metadata(k, v);
1230 }
1231 let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1232 msg: format!("Table {table_name} cannot set default values"),
1233 })?;
1234
1235 let _ = meta_builder.schema(Arc::new(new_schema));
1236
1237 Ok(meta_builder)
1238 }
1239}
1240
1241#[derive(Clone, Debug, PartialEq, Eq, Builder, Serialize, Deserialize)]
1242#[builder(pattern = "owned")]
1243pub struct TableInfo {
1244 #[builder(default, setter(into))]
1246 pub ident: TableIdent,
1247 #[builder(setter(into))]
1249 pub name: String,
1250 #[builder(default, setter(into))]
1252 pub desc: Option<String>,
1253 #[builder(default = "DEFAULT_CATALOG_NAME.to_string()", setter(into))]
1254 pub catalog_name: String,
1255 #[builder(default = "DEFAULT_SCHEMA_NAME.to_string()", setter(into))]
1256 pub schema_name: String,
1257 pub meta: TableMeta,
1258 #[builder(default = "TableType::Base")]
1259 pub table_type: TableType,
1260}
1261
1262pub type TableInfoRef = Arc<TableInfo>;
1263
1264impl TableInfo {
1265 pub fn table_id(&self) -> TableId {
1266 self.ident.table_id
1267 }
1268
1269 pub fn full_table_name(&self) -> String {
1271 common_catalog::format_full_table_name(&self.catalog_name, &self.schema_name, &self.name)
1272 }
1273
1274 pub fn get_db_string(&self) -> String {
1275 common_catalog::build_db_string(&self.catalog_name, &self.schema_name)
1276 }
1277
1278 pub fn is_physical_table(&self) -> bool {
1280 self.meta
1281 .options
1282 .extra_options
1283 .contains_key(PHYSICAL_TABLE_METADATA_KEY)
1284 }
1285
1286 pub fn is_ttl_instant_table(&self) -> bool {
1288 self.meta
1289 .options
1290 .ttl
1291 .map(|t| t.is_instant())
1292 .unwrap_or(false)
1293 }
1294}
1295
1296impl TableInfoBuilder {
1297 pub fn new<S: Into<String>>(name: S, meta: TableMeta) -> Self {
1298 Self {
1299 name: Some(name.into()),
1300 meta: Some(meta),
1301 ..Default::default()
1302 }
1303 }
1304
1305 pub fn table_id(mut self, id: TableId) -> Self {
1306 let ident = self.ident.get_or_insert_with(TableIdent::default);
1307 ident.table_id = id;
1308 self
1309 }
1310
1311 pub fn table_version(mut self, version: TableVersion) -> Self {
1312 let ident = self.ident.get_or_insert_with(TableIdent::default);
1313 ident.version = version;
1314 self
1315 }
1316}
1317
1318impl TableIdent {
1319 pub fn new(table_id: TableId) -> Self {
1320 Self {
1321 table_id,
1322 version: 0,
1323 }
1324 }
1325}
1326
1327impl From<TableId> for TableIdent {
1328 fn from(table_id: TableId) -> Self {
1329 Self::new(table_id)
1330 }
1331}
1332
1333impl TableInfo {
1334 pub fn name_to_ids(&self) -> Option<HashMap<String, ColumnId>> {
1338 let column_schemas = self.meta.schema.column_schemas();
1339 if self.meta.column_ids.len() != column_schemas.len() {
1340 None
1341 } else {
1342 Some(
1343 self.meta
1344 .column_ids
1345 .iter()
1346 .enumerate()
1347 .map(|(index, id)| (column_schemas[index].name.clone(), *id))
1348 .collect(),
1349 )
1350 }
1351 }
1352
1353 pub fn sort_columns(&mut self) {
1355 let column_schemas = self.meta.schema.column_schemas();
1356 let primary_keys = self
1357 .meta
1358 .primary_key_indices
1359 .iter()
1360 .map(|index| column_schemas[*index].name.clone())
1361 .collect::<HashSet<_>>();
1362
1363 let name_to_ids = self.name_to_ids().unwrap_or_default();
1364 let mut column_schemas = column_schemas.to_vec();
1365 column_schemas.sort_unstable_by(|a, b| a.name.cmp(&b.name));
1366
1367 let mut primary_key_indices = Vec::with_capacity(primary_keys.len());
1369 let mut value_indices = Vec::with_capacity(column_schemas.len() - primary_keys.len());
1370 let mut column_ids = Vec::with_capacity(column_schemas.len());
1371 for (index, column_schema) in column_schemas.iter().enumerate() {
1372 if primary_keys.contains(&column_schema.name) {
1373 primary_key_indices.push(index);
1374 } else {
1375 value_indices.push(index);
1376 }
1377 if let Some(id) = name_to_ids.get(&column_schema.name) {
1378 column_ids.push(*id);
1379 }
1380 }
1381
1382 self.meta.schema = Arc::new(Schema::new_with_version(
1384 column_schemas,
1385 self.meta.schema.version(),
1386 ));
1387 self.meta.primary_key_indices = primary_key_indices;
1388 self.meta.value_indices = value_indices;
1389 self.meta.column_ids = column_ids;
1390 }
1391
1392 pub fn to_region_options(&self) -> HashMap<String, String> {
1396 let mut options = HashMap::from(&self.meta.options);
1397 options.remove(REPARTITION_COLUMN_HINT_KEY);
1398 options
1399 }
1400
1401 pub fn table_ref(&self) -> TableReference<'_> {
1403 TableReference::full(
1404 self.catalog_name.as_str(),
1405 self.schema_name.as_str(),
1406 self.name.as_str(),
1407 )
1408 }
1409}
1410
1411fn set_column_fulltext_options(
1420 column_schema: &mut ColumnSchema,
1421 column_name: &str,
1422 options: &FulltextOptions,
1423 current_options: Option<FulltextOptions>,
1424) -> Result<()> {
1425 if let Some(current_options) = current_options {
1426 ensure!(
1427 current_options.analyzer == options.analyzer
1428 && current_options.case_sensitive == options.case_sensitive,
1429 error::InvalidColumnOptionSnafu {
1430 column_name,
1431 msg: format!(
1432 "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1433 current_options.analyzer, current_options.case_sensitive
1434 ),
1435 }
1436 );
1437 }
1438
1439 column_schema
1440 .set_fulltext_options(options)
1441 .context(error::SetFulltextOptionsSnafu { column_name })?;
1442
1443 Ok(())
1444}
1445
1446fn unset_column_fulltext_options(
1447 column_schema: &mut ColumnSchema,
1448 column_name: &str,
1449 current_options: Option<FulltextOptions>,
1450) -> Result<()> {
1451 ensure!(
1452 current_options
1453 .as_ref()
1454 .is_some_and(|options| options.enable),
1455 error::InvalidColumnOptionSnafu {
1456 column_name,
1457 msg: "FULLTEXT index already disabled".to_string(),
1458 }
1459 );
1460
1461 let mut options = current_options.unwrap();
1462 options.enable = false;
1463 column_schema
1464 .set_fulltext_options(&options)
1465 .context(error::SetFulltextOptionsSnafu { column_name })?;
1466
1467 Ok(())
1468}
1469
1470fn set_column_skipping_index_options(
1471 column_schema: &mut ColumnSchema,
1472 column_name: &str,
1473 options: &SkippingIndexOptions,
1474) -> Result<()> {
1475 column_schema
1476 .set_skipping_options(options)
1477 .context(error::SetSkippingOptionsSnafu { column_name })?;
1478
1479 Ok(())
1480}
1481
1482fn unset_column_skipping_index_options(
1483 column_schema: &mut ColumnSchema,
1484 column_name: &str,
1485) -> Result<()> {
1486 column_schema
1487 .unset_skipping_options()
1488 .context(error::UnsetSkippingOptionsSnafu { column_name })?;
1489 Ok(())
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494 use std::assert_matches;
1495
1496 use common_error::ext::ErrorExt;
1497 use common_error::status_code::StatusCode;
1498 use datatypes::data_type::ConcreteDataType;
1499 use datatypes::schema::{
1500 ColumnSchema, FulltextAnalyzer, FulltextBackend, Schema, SchemaBuilder,
1501 };
1502
1503 use super::*;
1504 use crate::Error;
1505
1506 fn new_test_schema() -> Schema {
1508 let column_schemas = vec![
1509 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
1510 ColumnSchema::new(
1511 "ts",
1512 ConcreteDataType::timestamp_millisecond_datatype(),
1513 false,
1514 )
1515 .with_time_index(true),
1516 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
1517 ];
1518 SchemaBuilder::try_from(column_schemas)
1519 .unwrap()
1520 .version(123)
1521 .build()
1522 .unwrap()
1523 }
1524
1525 fn add_columns_to_meta(meta: &TableMeta) -> TableMeta {
1526 let new_tag = ColumnSchema::new("my_tag", ConcreteDataType::string_datatype(), true);
1527 let new_field = ColumnSchema::new("my_field", ConcreteDataType::string_datatype(), true);
1528 let alter_kind = AlterKind::AddColumns {
1529 columns: vec![
1530 AddColumnRequest {
1531 column_schema: new_tag,
1532 is_key: true,
1533 location: None,
1534 add_if_not_exists: false,
1535 },
1536 AddColumnRequest {
1537 column_schema: new_field,
1538 is_key: false,
1539 location: None,
1540 add_if_not_exists: false,
1541 },
1542 ],
1543 };
1544
1545 let builder = meta
1546 .builder_with_alter_kind("my_table", &alter_kind)
1547 .unwrap();
1548 builder.build().unwrap()
1549 }
1550
1551 fn add_columns_to_meta_with_location(meta: &TableMeta) -> TableMeta {
1552 let new_tag = ColumnSchema::new("my_tag_first", ConcreteDataType::string_datatype(), true);
1553 let new_field = ColumnSchema::new(
1554 "my_field_after_ts",
1555 ConcreteDataType::string_datatype(),
1556 true,
1557 );
1558 let yet_another_field = ColumnSchema::new(
1559 "yet_another_field_after_ts",
1560 ConcreteDataType::int64_datatype(),
1561 true,
1562 );
1563 let alter_kind = AlterKind::AddColumns {
1564 columns: vec![
1565 AddColumnRequest {
1566 column_schema: new_tag,
1567 is_key: true,
1568 location: Some(AddColumnLocation::First),
1569 add_if_not_exists: false,
1570 },
1571 AddColumnRequest {
1572 column_schema: new_field,
1573 is_key: false,
1574 location: Some(AddColumnLocation::After {
1575 column_name: "ts".to_string(),
1576 }),
1577 add_if_not_exists: false,
1578 },
1579 AddColumnRequest {
1580 column_schema: yet_another_field,
1581 is_key: true,
1582 location: Some(AddColumnLocation::After {
1583 column_name: "ts".to_string(),
1584 }),
1585 add_if_not_exists: false,
1586 },
1587 ],
1588 };
1589
1590 let builder = meta
1591 .builder_with_alter_kind("my_table", &alter_kind)
1592 .unwrap();
1593 builder.build().unwrap()
1594 }
1595
1596 #[test]
1597 fn test_add_columns() {
1598 let schema = Arc::new(new_test_schema());
1599 let meta = TableMetaBuilder::empty()
1600 .schema(schema)
1601 .primary_key_indices(vec![0])
1602 .engine("engine")
1603 .next_column_id(3)
1604 .build()
1605 .unwrap();
1606
1607 let new_meta = add_columns_to_meta(&meta);
1608 let names: Vec<String> = new_meta
1609 .schema
1610 .column_schemas()
1611 .iter()
1612 .map(|column_schema| column_schema.name.clone())
1613 .collect();
1614 assert_eq!(&["col1", "ts", "col2", "my_tag", "my_field"], &names[..]);
1615 assert_eq!(&[0, 3], &new_meta.primary_key_indices[..]);
1616 assert_eq!(&[1, 2, 4], &new_meta.value_indices[..]);
1617 }
1618
1619 #[test]
1620 fn test_set_append_mode_true_clears_merge_mode_option() {
1621 let schema = Arc::new(new_test_schema());
1622 let mut table_options = TableOptions::default();
1623 table_options
1624 .extra_options
1625 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1626 let meta = TableMetaBuilder::empty()
1627 .schema(schema)
1628 .primary_key_indices(vec![0])
1629 .engine("engine")
1630 .next_column_id(3)
1631 .options(table_options)
1632 .build()
1633 .unwrap();
1634
1635 let alter_kind = AlterKind::SetTableOptions {
1636 options: vec![SetRegionOption::AppendMode(true)],
1637 };
1638 let new_meta = meta
1639 .builder_with_alter_kind("my_table", &alter_kind)
1640 .unwrap()
1641 .build()
1642 .unwrap();
1643
1644 assert_eq!(
1645 Some("true"),
1646 new_meta
1647 .options
1648 .extra_options
1649 .get(APPEND_MODE_KEY)
1650 .map(String::as_str)
1651 );
1652 assert!(!new_meta.options.extra_options.contains_key(MERGE_MODE_KEY));
1653 }
1654
1655 #[test]
1656 fn test_set_append_mode_false_keeps_merge_mode_option() {
1657 let schema = Arc::new(new_test_schema());
1658 let mut table_options = TableOptions::default();
1659 table_options
1660 .extra_options
1661 .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1662 let meta = TableMetaBuilder::empty()
1663 .schema(schema)
1664 .primary_key_indices(vec![0])
1665 .engine("engine")
1666 .next_column_id(3)
1667 .options(table_options)
1668 .build()
1669 .unwrap();
1670
1671 let alter_kind = AlterKind::SetTableOptions {
1672 options: vec![SetRegionOption::AppendMode(false)],
1673 };
1674 let new_meta = meta
1675 .builder_with_alter_kind("my_table", &alter_kind)
1676 .unwrap()
1677 .build()
1678 .unwrap();
1679
1680 assert_eq!(
1681 Some("false"),
1682 new_meta
1683 .options
1684 .extra_options
1685 .get(APPEND_MODE_KEY)
1686 .map(String::as_str)
1687 );
1688 assert_eq!(
1689 Some("last_non_null"),
1690 new_meta
1691 .options
1692 .extra_options
1693 .get(MERGE_MODE_KEY)
1694 .map(String::as_str)
1695 );
1696 }
1697
1698 #[test]
1699 fn test_set_skip_wal_updates_typed_and_extra_options() {
1700 let mut meta = TableMetaBuilder::empty()
1701 .schema(Arc::new(new_test_schema()))
1702 .primary_key_indices(vec![0])
1703 .engine("engine")
1704 .next_column_id(3)
1705 .build()
1706 .unwrap();
1707 meta.options
1708 .extra_options
1709 .insert(SKIP_WAL_KEY.to_string(), false.to_string());
1710
1711 let alter_kind = AlterKind::SetTableOptions {
1712 options: vec![SetRegionOption::SkipWal],
1713 };
1714 let new_meta = meta
1715 .builder_with_alter_kind("my_table", &alter_kind)
1716 .unwrap()
1717 .build()
1718 .unwrap();
1719
1720 assert!(new_meta.options.skip_wal);
1721 assert_eq!(
1722 Some("true"),
1723 new_meta
1724 .options
1725 .extra_options
1726 .get(SKIP_WAL_KEY)
1727 .map(String::as_str)
1728 );
1729 }
1730
1731 #[test]
1732 fn test_set_repartition_column_hint() {
1733 let meta = TableMetaBuilder::empty()
1734 .schema(Arc::new(new_test_schema()))
1735 .primary_key_indices(vec![0])
1736 .engine("engine")
1737 .next_column_id(3)
1738 .build()
1739 .unwrap();
1740
1741 let alter_kind = AlterKind::SetRepartitionColumnHint {
1742 column_name: " col1 ".to_string(),
1743 };
1744 let new_meta = meta
1745 .builder_with_alter_kind("my_table", &alter_kind)
1746 .unwrap()
1747 .build()
1748 .unwrap();
1749
1750 assert_eq!(
1751 Some("col1"),
1752 new_meta
1753 .options
1754 .extra_options
1755 .get(REPARTITION_COLUMN_HINT_KEY)
1756 .map(String::as_str)
1757 );
1758 }
1759
1760 #[test]
1761 fn test_set_repartition_column_hint_rejects_empty_column() {
1762 let meta = TableMetaBuilder::empty()
1763 .schema(Arc::new(new_test_schema()))
1764 .primary_key_indices(vec![0])
1765 .engine("engine")
1766 .next_column_id(3)
1767 .build()
1768 .unwrap();
1769
1770 let alter_kind = AlterKind::SetRepartitionColumnHint {
1771 column_name: " ".to_string(),
1772 };
1773 let err = meta
1774 .builder_with_alter_kind("my_table", &alter_kind)
1775 .err()
1776 .unwrap();
1777
1778 assert!(
1779 err.to_string()
1780 .contains("repartition.column.hint expects exactly one column name")
1781 );
1782 }
1783
1784 #[test]
1785 fn test_set_repartition_column_hint_rejects_multiple_columns() {
1786 let meta = TableMetaBuilder::empty()
1787 .schema(Arc::new(new_test_schema()))
1788 .primary_key_indices(vec![0])
1789 .engine("engine")
1790 .next_column_id(3)
1791 .build()
1792 .unwrap();
1793
1794 let alter_kind = AlterKind::SetRepartitionColumnHint {
1795 column_name: "col1,col2".to_string(),
1796 };
1797 let err = meta
1798 .builder_with_alter_kind("my_table", &alter_kind)
1799 .err()
1800 .unwrap();
1801
1802 assert!(
1803 err.to_string()
1804 .contains("repartition.column.hint expects exactly one column name")
1805 );
1806 }
1807
1808 #[test]
1809 fn test_set_repartition_column_hint_rejects_missing_column() {
1810 let meta = TableMetaBuilder::empty()
1811 .schema(Arc::new(new_test_schema()))
1812 .primary_key_indices(vec![0])
1813 .engine("engine")
1814 .next_column_id(3)
1815 .build()
1816 .unwrap();
1817
1818 let alter_kind = AlterKind::SetRepartitionColumnHint {
1819 column_name: "missing".to_string(),
1820 };
1821 let err = meta
1822 .builder_with_alter_kind("my_table", &alter_kind)
1823 .err()
1824 .unwrap();
1825
1826 assert!(err.to_string().contains("Column missing not exists"));
1827 }
1828
1829 #[test]
1830 fn test_set_repartition_column_hint_rejects_time_index_column() {
1831 let meta = TableMetaBuilder::empty()
1832 .schema(Arc::new(new_test_schema()))
1833 .primary_key_indices(vec![0])
1834 .engine("engine")
1835 .next_column_id(3)
1836 .build()
1837 .unwrap();
1838
1839 let alter_kind = AlterKind::SetRepartitionColumnHint {
1840 column_name: "ts".to_string(),
1841 };
1842 let err = meta
1843 .builder_with_alter_kind("my_table", &alter_kind)
1844 .err()
1845 .unwrap();
1846
1847 assert!(
1848 err.to_string()
1849 .contains("cannot set repartition.column.hint to the time index column")
1850 );
1851 }
1852
1853 #[test]
1854 fn test_set_repartition_column_hint_rejects_partitioned_table() {
1855 let meta = TableMetaBuilder::empty()
1856 .schema(Arc::new(new_test_schema()))
1857 .primary_key_indices(vec![0])
1858 .engine("engine")
1859 .next_column_id(3)
1860 .partition_key_indices(vec![0])
1861 .build()
1862 .unwrap();
1863
1864 let alter_kind = AlterKind::SetRepartitionColumnHint {
1865 column_name: "col1".to_string(),
1866 };
1867 let err = meta
1868 .builder_with_alter_kind("my_table", &alter_kind)
1869 .err()
1870 .unwrap();
1871
1872 assert!(
1873 err.to_string()
1874 .contains("cannot set repartition.column.hint on a table with partition metadata")
1875 );
1876 }
1877
1878 #[test]
1879 fn test_unset_repartition_column_hint() {
1880 let mut table_options = TableOptions::default();
1881 table_options
1882 .extra_options
1883 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
1884 let meta = TableMetaBuilder::empty()
1885 .schema(Arc::new(new_test_schema()))
1886 .primary_key_indices(vec![0])
1887 .engine("engine")
1888 .next_column_id(3)
1889 .options(table_options)
1890 .build()
1891 .unwrap();
1892
1893 let new_meta = meta
1894 .builder_with_alter_kind("my_table", &AlterKind::UnsetRepartitionColumnHint)
1895 .unwrap()
1896 .build()
1897 .unwrap();
1898
1899 assert!(
1900 !new_meta
1901 .options
1902 .extra_options
1903 .contains_key(REPARTITION_COLUMN_HINT_KEY)
1904 );
1905 }
1906
1907 #[test]
1908 fn test_repartition_column_hint_is_not_region_option() {
1909 let mut table_options = TableOptions::default();
1910 table_options
1911 .extra_options
1912 .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
1913 let table_info = TableInfoBuilder::default()
1914 .table_id(1)
1915 .table_version(0)
1916 .name("my_table")
1917 .catalog_name(DEFAULT_CATALOG_NAME)
1918 .schema_name(DEFAULT_SCHEMA_NAME)
1919 .meta(
1920 TableMetaBuilder::empty()
1921 .schema(Arc::new(new_test_schema()))
1922 .primary_key_indices(vec![0])
1923 .engine("engine")
1924 .next_column_id(3)
1925 .options(table_options)
1926 .build()
1927 .unwrap(),
1928 )
1929 .build()
1930 .unwrap();
1931
1932 assert!(
1933 !table_info
1934 .to_region_options()
1935 .contains_key(REPARTITION_COLUMN_HINT_KEY)
1936 );
1937 }
1938
1939 #[test]
1940 fn test_set_auto_flush_interval() {
1941 let schema = Arc::new(new_test_schema());
1942 let table_options = TableOptions::default();
1943 let meta = TableMetaBuilder::empty()
1944 .schema(schema)
1945 .primary_key_indices(vec![0])
1946 .engine("engine")
1947 .next_column_id(3)
1948 .options(table_options)
1949 .build()
1950 .unwrap();
1951
1952 let alter_kind = AlterKind::SetTableOptions {
1953 options: vec![SetRegionOption::AutoFlushInterval(Some(
1954 std::time::Duration::from_secs(300),
1955 ))],
1956 };
1957 let new_meta = meta
1958 .builder_with_alter_kind("my_table", &alter_kind)
1959 .unwrap()
1960 .build()
1961 .unwrap();
1962
1963 assert_eq!(
1964 Some("5m"),
1965 new_meta
1966 .options
1967 .extra_options
1968 .get(AUTO_FLUSH_INTERVAL_KEY)
1969 .map(String::as_str)
1970 );
1971 }
1972
1973 #[test]
1974 fn test_set_auto_flush_interval_none_removes_existing() {
1975 let schema = Arc::new(new_test_schema());
1976 let mut table_options = TableOptions::default();
1977 table_options
1978 .extra_options
1979 .insert(AUTO_FLUSH_INTERVAL_KEY.to_string(), "5m".to_string());
1980 let meta = TableMetaBuilder::empty()
1981 .schema(schema)
1982 .primary_key_indices(vec![0])
1983 .engine("engine")
1984 .next_column_id(3)
1985 .options(table_options)
1986 .build()
1987 .unwrap();
1988
1989 let alter_kind = AlterKind::SetTableOptions {
1990 options: vec![SetRegionOption::AutoFlushInterval(None)],
1991 };
1992 let new_meta = meta
1993 .builder_with_alter_kind("my_table", &alter_kind)
1994 .unwrap()
1995 .build()
1996 .unwrap();
1997
1998 assert!(
1999 !new_meta
2000 .options
2001 .extra_options
2002 .contains_key(AUTO_FLUSH_INTERVAL_KEY)
2003 );
2004 }
2005
2006 #[test]
2007 fn test_set_and_unset_max_row_group_row_count() {
2008 let meta = TableMetaBuilder::empty()
2009 .schema(Arc::new(new_test_schema()))
2010 .primary_key_indices(vec![0])
2011 .engine("engine")
2012 .next_column_id(3)
2013 .options(TableOptions::default())
2014 .build()
2015 .unwrap();
2016
2017 let alter_kind = AlterKind::SetTableOptions {
2018 options: vec![SetRegionOption::MaxRowGroupRowCount(Some(512))],
2019 };
2020 let new_meta = meta
2021 .builder_with_alter_kind("my_table", &alter_kind)
2022 .unwrap()
2023 .build()
2024 .unwrap();
2025 assert_eq!(
2026 Some("512"),
2027 new_meta
2028 .options
2029 .extra_options
2030 .get(MAX_ROW_GROUP_ROW_COUNT)
2031 .map(String::as_str)
2032 );
2033
2034 let alter_kind = AlterKind::UnsetTableOptions {
2035 keys: vec![UnsetRegionOption::MaxRowGroupRowCount],
2036 };
2037 let new_meta = new_meta
2038 .builder_with_alter_kind("my_table", &alter_kind)
2039 .unwrap()
2040 .build()
2041 .unwrap();
2042 assert!(
2043 !new_meta
2044 .options
2045 .extra_options
2046 .contains_key(MAX_ROW_GROUP_ROW_COUNT)
2047 );
2048 }
2049
2050 #[test]
2051 fn test_add_columns_multiple_times() {
2052 let schema = Arc::new(new_test_schema());
2053 let meta = TableMetaBuilder::empty()
2054 .schema(schema)
2055 .primary_key_indices(vec![0])
2056 .engine("engine")
2057 .next_column_id(3)
2058 .build()
2059 .unwrap();
2060
2061 let alter_kind = AlterKind::AddColumns {
2062 columns: vec![
2063 AddColumnRequest {
2064 column_schema: ColumnSchema::new(
2065 "col3",
2066 ConcreteDataType::int32_datatype(),
2067 true,
2068 ),
2069 is_key: true,
2070 location: None,
2071 add_if_not_exists: true,
2072 },
2073 AddColumnRequest {
2074 column_schema: ColumnSchema::new(
2075 "col3",
2076 ConcreteDataType::int32_datatype(),
2077 true,
2078 ),
2079 is_key: true,
2080 location: None,
2081 add_if_not_exists: true,
2082 },
2083 ],
2084 };
2085 let err = meta
2086 .builder_with_alter_kind("my_table", &alter_kind)
2087 .err()
2088 .unwrap();
2089 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2090 }
2091
2092 #[test]
2093 fn test_remove_columns() {
2094 let schema = Arc::new(new_test_schema());
2095 let meta = TableMetaBuilder::empty()
2096 .schema(schema.clone())
2097 .primary_key_indices(vec![0])
2098 .engine("engine")
2099 .next_column_id(3)
2100 .build()
2101 .unwrap();
2102 let meta = add_columns_to_meta(&meta);
2104
2105 let alter_kind = AlterKind::DropColumns {
2106 names: vec![String::from("col2"), String::from("my_field")],
2107 };
2108 let new_meta = meta
2109 .builder_with_alter_kind("my_table", &alter_kind)
2110 .unwrap()
2111 .build()
2112 .unwrap();
2113
2114 let names: Vec<String> = new_meta
2115 .schema
2116 .column_schemas()
2117 .iter()
2118 .map(|column_schema| column_schema.name.clone())
2119 .collect();
2120 assert_eq!(&["col1", "ts", "my_tag"], &names[..]);
2121 assert_eq!(&[0, 2], &new_meta.primary_key_indices[..]);
2122 assert_eq!(&[1], &new_meta.value_indices[..]);
2123 assert_eq!(
2124 schema.timestamp_column(),
2125 new_meta.schema.timestamp_column()
2126 );
2127 }
2128
2129 #[test]
2130 fn test_remove_multiple_columns_before_timestamp() {
2131 let column_schemas = vec![
2132 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2133 ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
2134 ColumnSchema::new("col3", ConcreteDataType::int32_datatype(), true),
2135 ColumnSchema::new(
2136 "ts",
2137 ConcreteDataType::timestamp_millisecond_datatype(),
2138 false,
2139 )
2140 .with_time_index(true),
2141 ];
2142 let schema = Arc::new(
2143 SchemaBuilder::try_from(column_schemas)
2144 .unwrap()
2145 .version(123)
2146 .build()
2147 .unwrap(),
2148 );
2149 let meta = TableMetaBuilder::empty()
2150 .schema(schema.clone())
2151 .primary_key_indices(vec![1])
2152 .engine("engine")
2153 .next_column_id(4)
2154 .build()
2155 .unwrap();
2156
2157 let alter_kind = AlterKind::DropColumns {
2159 names: vec![String::from("col3"), String::from("col1")],
2160 };
2161 let new_meta = meta
2162 .builder_with_alter_kind("my_table", &alter_kind)
2163 .unwrap()
2164 .build()
2165 .unwrap();
2166
2167 let names: Vec<String> = new_meta
2168 .schema
2169 .column_schemas()
2170 .iter()
2171 .map(|column_schema| column_schema.name.clone())
2172 .collect();
2173 assert_eq!(&["col2", "ts"], &names[..]);
2174 assert_eq!(&[0], &new_meta.primary_key_indices[..]);
2175 assert_eq!(&[1], &new_meta.value_indices[..]);
2176 assert_eq!(
2177 schema.timestamp_column(),
2178 new_meta.schema.timestamp_column()
2179 );
2180 }
2181
2182 #[test]
2183 fn test_add_existing_column() {
2184 let schema = Arc::new(new_test_schema());
2185 let meta = TableMetaBuilder::empty()
2186 .schema(schema)
2187 .primary_key_indices(vec![0])
2188 .engine("engine")
2189 .next_column_id(3)
2190 .build()
2191 .unwrap();
2192
2193 let alter_kind = AlterKind::AddColumns {
2194 columns: vec![AddColumnRequest {
2195 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2196 is_key: false,
2197 location: None,
2198 add_if_not_exists: false,
2199 }],
2200 };
2201
2202 let err = meta
2203 .builder_with_alter_kind("my_table", &alter_kind)
2204 .err()
2205 .unwrap();
2206 assert_eq!(StatusCode::TableColumnExists, err.status_code());
2207
2208 let alter_kind = AlterKind::AddColumns {
2210 columns: vec![AddColumnRequest {
2211 column_schema: ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2212 is_key: true,
2213 location: None,
2214 add_if_not_exists: true,
2215 }],
2216 };
2217 let new_meta = meta
2218 .builder_with_alter_kind("my_table", &alter_kind)
2219 .unwrap()
2220 .build()
2221 .unwrap();
2222 assert_eq!(
2223 meta.schema.column_schemas(),
2224 new_meta.schema.column_schemas()
2225 );
2226 assert_eq!(meta.schema.version() + 1, new_meta.schema.version());
2227 }
2228
2229 #[test]
2230 fn test_add_different_type_column() {
2231 let schema = Arc::new(new_test_schema());
2232 let meta = TableMetaBuilder::empty()
2233 .schema(schema)
2234 .primary_key_indices(vec![0])
2235 .engine("engine")
2236 .next_column_id(3)
2237 .build()
2238 .unwrap();
2239
2240 let alter_kind = AlterKind::AddColumns {
2242 columns: vec![AddColumnRequest {
2243 column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2244 is_key: false,
2245 location: None,
2246 add_if_not_exists: true,
2247 }],
2248 };
2249 let err = meta
2250 .builder_with_alter_kind("my_table", &alter_kind)
2251 .err()
2252 .unwrap();
2253 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2254 }
2255
2256 #[test]
2257 fn test_add_invalid_column() {
2258 let schema = Arc::new(new_test_schema());
2259 let meta = TableMetaBuilder::empty()
2260 .schema(schema)
2261 .primary_key_indices(vec![0])
2262 .engine("engine")
2263 .next_column_id(3)
2264 .build()
2265 .unwrap();
2266
2267 let alter_kind = AlterKind::AddColumns {
2269 columns: vec![AddColumnRequest {
2270 column_schema: ColumnSchema::new(
2271 "weny",
2272 ConcreteDataType::string_datatype(),
2273 false,
2274 ),
2275 is_key: false,
2276 location: None,
2277 add_if_not_exists: false,
2278 }],
2279 };
2280
2281 let err = meta
2282 .builder_with_alter_kind("my_table", &alter_kind)
2283 .err()
2284 .unwrap();
2285 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2286 }
2287
2288 #[test]
2289 fn test_remove_unknown_column() {
2290 let schema = Arc::new(new_test_schema());
2291 let meta = TableMetaBuilder::empty()
2292 .schema(schema)
2293 .primary_key_indices(vec![0])
2294 .engine("engine")
2295 .next_column_id(3)
2296 .build()
2297 .unwrap();
2298
2299 let alter_kind = AlterKind::DropColumns {
2300 names: vec![String::from("unknown")],
2301 };
2302
2303 let err = meta
2304 .builder_with_alter_kind("my_table", &alter_kind)
2305 .err()
2306 .unwrap();
2307 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2308 }
2309
2310 #[test]
2311 fn test_change_unknown_column_data_type() {
2312 let schema = Arc::new(new_test_schema());
2313 let meta = TableMetaBuilder::empty()
2314 .schema(schema)
2315 .primary_key_indices(vec![0])
2316 .engine("engine")
2317 .next_column_id(3)
2318 .build()
2319 .unwrap();
2320
2321 let alter_kind = AlterKind::ModifyColumnTypes {
2322 columns: vec![ModifyColumnTypeRequest {
2323 column_name: "unknown".to_string(),
2324 target_type: ConcreteDataType::string_datatype(),
2325 }],
2326 };
2327
2328 let err = meta
2329 .builder_with_alter_kind("my_table", &alter_kind)
2330 .err()
2331 .unwrap();
2332 assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2333 }
2334
2335 #[test]
2336 fn test_remove_key_column() {
2337 let schema = Arc::new(new_test_schema());
2338 let meta = TableMetaBuilder::empty()
2339 .schema(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::DropColumns {
2348 names: vec![String::from("col1")],
2349 };
2350
2351 let err = meta
2352 .builder_with_alter_kind("my_table", &alter_kind)
2353 .err()
2354 .unwrap();
2355 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2356
2357 let alter_kind = AlterKind::DropColumns {
2359 names: vec![String::from("ts")],
2360 };
2361
2362 let err = meta
2363 .builder_with_alter_kind("my_table", &alter_kind)
2364 .err()
2365 .unwrap();
2366 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2367 }
2368
2369 #[test]
2370 fn test_remove_partition_column() {
2371 let schema = Arc::new(new_test_schema());
2372 let meta = TableMetaBuilder::empty()
2373 .schema(schema)
2374 .primary_key_indices(vec![])
2375 .partition_key_indices(vec![0])
2376 .engine("engine")
2377 .next_column_id(3)
2378 .build()
2379 .unwrap();
2380 let alter_kind = AlterKind::DropColumns {
2382 names: vec![String::from("col1")],
2383 };
2384
2385 let err = meta
2386 .builder_with_alter_kind("my_table", &alter_kind)
2387 .err()
2388 .unwrap();
2389 assert_matches!(err, Error::RemovePartitionColumn { .. });
2390 }
2391
2392 #[test]
2393 fn test_change_key_column_data_type() {
2394 let schema = Arc::new(new_test_schema());
2395 let meta = TableMetaBuilder::empty()
2396 .schema(schema)
2397 .primary_key_indices(vec![0])
2398 .engine("engine")
2399 .next_column_id(3)
2400 .build()
2401 .unwrap();
2402
2403 let alter_kind = AlterKind::ModifyColumnTypes {
2405 columns: vec![ModifyColumnTypeRequest {
2406 column_name: "col1".to_string(),
2407 target_type: ConcreteDataType::string_datatype(),
2408 }],
2409 };
2410
2411 let err = meta
2412 .builder_with_alter_kind("my_table", &alter_kind)
2413 .err()
2414 .unwrap();
2415 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2416
2417 let alter_kind = AlterKind::ModifyColumnTypes {
2419 columns: vec![ModifyColumnTypeRequest {
2420 column_name: "ts".to_string(),
2421 target_type: ConcreteDataType::string_datatype(),
2422 }],
2423 };
2424
2425 let err = meta
2426 .builder_with_alter_kind("my_table", &alter_kind)
2427 .err()
2428 .unwrap();
2429 assert_eq!(StatusCode::InvalidArguments, err.status_code());
2430 }
2431
2432 #[test]
2433 fn test_alloc_new_column() {
2434 let schema = Arc::new(new_test_schema());
2435 let mut meta = TableMetaBuilder::empty()
2436 .schema(schema)
2437 .primary_key_indices(vec![0])
2438 .engine("engine")
2439 .next_column_id(3)
2440 .build()
2441 .unwrap();
2442 assert_eq!(3, meta.next_column_id);
2443
2444 let column_schema = ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true);
2445 let desc = meta.alloc_new_column("test_table", &column_schema).unwrap();
2446
2447 assert_eq!(4, meta.next_column_id);
2448 assert_eq!(column_schema.name, desc.name);
2449 }
2450
2451 #[test]
2452 fn test_add_columns_with_location() {
2453 let schema = Arc::new(new_test_schema());
2454 let meta = TableMetaBuilder::empty()
2455 .schema(schema)
2456 .primary_key_indices(vec![0])
2457 .partition_key_indices(vec![0, 2])
2459 .engine("engine")
2460 .next_column_id(3)
2461 .build()
2462 .unwrap();
2463
2464 let new_meta = add_columns_to_meta_with_location(&meta);
2465 let names: Vec<String> = new_meta
2466 .schema
2467 .column_schemas()
2468 .iter()
2469 .map(|column_schema| column_schema.name.clone())
2470 .collect();
2471 assert_eq!(
2472 &[
2473 "my_tag_first", "col1", "ts", "yet_another_field_after_ts", "my_field_after_ts", "col2", ],
2480 &names[..]
2481 );
2482 assert_eq!(&[0, 1, 3], &new_meta.primary_key_indices[..]);
2483 assert_eq!(&[2, 4, 5], &new_meta.value_indices[..]);
2484 assert_eq!(&[1, 5], &new_meta.partition_key_indices[..]);
2485 }
2486
2487 #[test]
2488 fn test_modify_column_fulltext_options() {
2489 let schema = Arc::new(new_test_schema());
2490 let meta = TableMetaBuilder::empty()
2491 .schema(schema)
2492 .primary_key_indices(vec![0])
2493 .engine("engine")
2494 .next_column_id(3)
2495 .build()
2496 .unwrap();
2497
2498 let alter_kind = AlterKind::SetIndexes {
2499 options: vec![SetIndexOption::Fulltext {
2500 column_name: "col1".to_string(),
2501 options: FulltextOptions::default(),
2502 }],
2503 };
2504 let err = meta
2505 .builder_with_alter_kind("my_table", &alter_kind)
2506 .err()
2507 .unwrap();
2508 assert_eq!(
2509 "Invalid column option, column name: col1, error: FULLTEXT index only supports string type",
2510 err.to_string()
2511 );
2512
2513 let new_meta = add_columns_to_meta_with_location(&meta);
2515 let alter_kind = AlterKind::SetIndexes {
2516 options: vec![SetIndexOption::Fulltext {
2517 column_name: "my_tag_first".to_string(),
2518 options: FulltextOptions::new_unchecked(
2519 true,
2520 FulltextAnalyzer::Chinese,
2521 true,
2522 FulltextBackend::Bloom,
2523 1000,
2524 0.01,
2525 ),
2526 }],
2527 };
2528 let new_meta = new_meta
2529 .builder_with_alter_kind("my_table", &alter_kind)
2530 .unwrap()
2531 .build()
2532 .unwrap();
2533 let column_schema = new_meta
2534 .schema
2535 .column_schema_by_name("my_tag_first")
2536 .unwrap();
2537 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
2538 assert!(fulltext_options.enable);
2539 assert_eq!(
2540 datatypes::schema::FulltextAnalyzer::Chinese,
2541 fulltext_options.analyzer
2542 );
2543 assert!(fulltext_options.case_sensitive);
2544
2545 let alter_kind = AlterKind::UnsetIndexes {
2546 options: vec![UnsetIndexOption::Fulltext {
2547 column_name: "my_tag_first".to_string(),
2548 }],
2549 };
2550 let new_meta = new_meta
2551 .builder_with_alter_kind("my_table", &alter_kind)
2552 .unwrap()
2553 .build()
2554 .unwrap();
2555 let column_schema = new_meta
2556 .schema
2557 .column_schema_by_name("my_tag_first")
2558 .unwrap();
2559 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
2560 assert!(!fulltext_options.enable);
2561 }
2562
2563 #[test]
2564 fn test_table_info_serde_compatibility() {
2565 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"}"#;
2598
2599 let actual: TableInfo = serde_json::from_str(serialized).unwrap();
2600 let expected = TableInfo {
2601 ident: TableIdent {
2602 table_id: 1024,
2603 version: 1,
2604 },
2605 name: "foo".to_string(),
2606 desc: Some("my table".to_string()),
2607 catalog_name: "greptime".to_string(),
2608 schema_name: "public".to_string(),
2609 meta: TableMeta {
2610 schema: Arc::new(new_test_schema()),
2611 primary_key_indices: vec![0],
2612 value_indices: vec![1, 2],
2613 engine: "mito".to_string(),
2614 next_column_id: 3,
2615 options: TableOptions {
2616 ttl: Some(common_time::TimeToLive::Duration(
2617 std::time::Duration::from_secs(3600),
2618 )),
2619 ..Default::default()
2620 },
2621 created_on: DateTime::<Utc>::MIN_UTC,
2622 updated_on: DateTime::<Utc>::MAX_UTC,
2623 partition_key_indices: vec![2],
2624 column_ids: vec![0, 1, 2],
2625 },
2626 table_type: TableType::Base,
2627 };
2628 assert_eq!(actual, expected);
2629 }
2630}