Skip to main content

table/
metadata.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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    MERGE_MODE_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/// Indicates whether and how a filter expression can be handled by a
49/// Table for table scans.
50#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
51pub enum FilterPushDownType {
52    /// The expression cannot be used by the provider.
53    Unsupported,
54    /// The expression can be used to help minimise the data retrieved,
55    /// but the provider cannot guarantee that all returned tuples
56    /// satisfy the filter. The Filter plan node containing this expression
57    /// will be preserved.
58    Inexact,
59    /// The provider guarantees that all returned data satisfies this
60    /// filter expression. The Filter plan node containing this expression
61    /// will be removed.
62    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/// Indicates the type of this table for metadata/catalog purposes.
86#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TableType {
88    /// An ordinary physical table.
89    Base,
90    /// A non-materialised table that itself uses a query internally to provide data.
91    View,
92    /// A transient table.
93    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/// Identifier of the table.
117#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Default)]
118pub struct TableIdent {
119    /// Unique id of this table.
120    pub table_id: TableId,
121    /// Version of the table, bumped when metadata (such as schema) of the table
122    /// being changed.
123    pub version: TableVersion,
124}
125
126/// The table metadata.
127///
128/// Note: if you add new fields to this struct, please ensure 'new_meta_builder' function works.
129#[derive(Clone, Debug, Builder, PartialEq, Eq, ToMetaBuilder, Serialize)]
130#[builder(pattern = "mutable", custom_constructor)]
131pub struct TableMeta {
132    pub schema: SchemaRef,
133    /// The indices of columns in primary key. Note that the index of timestamp column
134    /// is not included in these indices.
135    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    /// Table options.
142    #[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    /// Note: Please always use [new_meta_builder] to create new [TableMetaBuilder].
222    #[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
272/// The result after splitting requests by column location info.
273struct SplitResult<'a> {
274    /// column requests should be added at first place.
275    columns_at_first: Vec<&'a AddColumnRequest>,
276    /// column requests should be added after already exist columns.
277    columns_at_after: HashMap<String, Vec<&'a AddColumnRequest>>,
278    /// column requests should be added at last place.
279    columns_at_last: Vec<&'a AddColumnRequest>,
280    /// all column names should be added.
281    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        // `value_indices` is wrong under distributed mode. Use the logic copied from DESC TABLE
294        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    /// Returns the new [TableMetaBuilder] after applying given `alter_kind`.
317    ///
318    /// The returned builder would derive the next column id of this meta.
319    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            // No need to rebuild table meta when renaming tables.
331            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    /// Creates a [TableMetaBuilder] with modified table options.
348    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                        // Ensure node restart correctly.
363                        new_options.extra_options.insert(
364                            COMPACTION_TYPE.to_string(),
365                            COMPACTION_TYPE_TWCS.to_string(),
366                        );
367                    } else {
368                        // Invalidate the previous change option if an empty value has been set.
369                        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            }
396        }
397        let mut builder = self.new_meta_builder();
398        builder.options(new_options);
399
400        Ok(builder)
401    }
402
403    fn unset_table_options(&self, requests: &[UnsetRegionOption]) -> Result<TableMetaBuilder> {
404        let requests = requests.iter().map(Into::into).collect::<Vec<_>>();
405        self.set_table_options(&requests)
406    }
407
408    fn set_repartition_column_hint(
409        &self,
410        table_name: &str,
411        column_name: &str,
412    ) -> Result<TableMetaBuilder> {
413        let column_name = column_name.trim();
414        ensure!(
415            !column_name.is_empty() && !column_name.contains(','),
416            error::InvalidAlterRequestSnafu {
417                table: table_name,
418                err: format!("{REPARTITION_COLUMN_HINT_KEY} expects exactly one column name"),
419            }
420        );
421
422        ensure!(
423            self.partition_key_indices.is_empty(),
424            error::InvalidAlterRequestSnafu {
425                table: table_name,
426                err: format!(
427                    "cannot set {REPARTITION_COLUMN_HINT_KEY} on a table with partition metadata"
428                ),
429            }
430        );
431
432        let column_index = self
433            .schema
434            .column_index_by_name(column_name)
435            .with_context(|| error::ColumnNotExistsSnafu {
436                column_name,
437                table_name,
438            })?;
439
440        if let Some(time_index) = self.schema.timestamp_index() {
441            ensure!(
442                column_index != time_index,
443                error::InvalidAlterRequestSnafu {
444                    table: table_name,
445                    err: format!(
446                        "cannot set {REPARTITION_COLUMN_HINT_KEY} to the time index column"
447                    ),
448                }
449            );
450        }
451
452        let mut new_options = self.options.clone();
453        new_options.extra_options.insert(
454            REPARTITION_COLUMN_HINT_KEY.to_string(),
455            column_name.to_string(),
456        );
457
458        let mut builder = self.new_meta_builder();
459        builder.options(new_options);
460        Ok(builder)
461    }
462
463    fn unset_repartition_column_hint(&self) -> Result<TableMetaBuilder> {
464        let mut new_options = self.options.clone();
465        new_options
466            .extra_options
467            .remove(REPARTITION_COLUMN_HINT_KEY);
468
469        let mut builder = self.new_meta_builder();
470        builder.options(new_options);
471        Ok(builder)
472    }
473
474    fn set_indexes(
475        &self,
476        table_name: &str,
477        requests: &[SetIndexOption],
478    ) -> Result<TableMetaBuilder> {
479        let table_schema = &self.schema;
480        let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
481        for request in requests {
482            let column_name = request.column_name();
483            table_schema
484                .column_index_by_name(column_name)
485                .with_context(|| error::ColumnNotExistsSnafu {
486                    column_name,
487                    table_name,
488                })?;
489            set_index_options
490                .entry(column_name)
491                .or_default()
492                .push(request);
493        }
494
495        let mut meta_builder = self.new_meta_builder();
496        let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
497        for mut column in table_schema.column_schemas().iter().cloned() {
498            if let Some(request) = set_index_options.get(column.name.as_str()) {
499                for request in request {
500                    self.set_index(&mut column, request)?;
501                }
502            }
503            columns.push(column);
504        }
505
506        let mut builder = SchemaBuilder::try_from_columns(columns)
507            .with_context(|_| error::SchemaBuildSnafu {
508                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
509            })?
510            .version(table_schema.version() + 1);
511
512        for (k, v) in table_schema.metadata().iter() {
513            builder = builder.add_metadata(k, v);
514        }
515
516        let new_schema = builder.build().with_context(|_| {
517            let column_names = requests
518                .iter()
519                .map(|request| request.column_name())
520                .collect::<Vec<_>>();
521            error::SchemaBuildSnafu {
522                msg: format!(
523                    "Table {table_name} cannot set index options with columns {column_names:?}",
524                ),
525            }
526        })?;
527        let _ = meta_builder
528            .schema(Arc::new(new_schema))
529            .primary_key_indices(self.primary_key_indices.clone());
530
531        Ok(meta_builder)
532    }
533
534    fn unset_indexes(
535        &self,
536        table_name: &str,
537        requests: &[UnsetIndexOption],
538    ) -> Result<TableMetaBuilder> {
539        let table_schema = &self.schema;
540        let mut set_index_options: HashMap<&str, Vec<_>> = HashMap::new();
541        for request in requests {
542            let column_name = request.column_name();
543            table_schema
544                .column_index_by_name(column_name)
545                .with_context(|| error::ColumnNotExistsSnafu {
546                    column_name,
547                    table_name,
548                })?;
549            set_index_options
550                .entry(column_name)
551                .or_default()
552                .push(request);
553        }
554
555        let mut meta_builder = self.new_meta_builder();
556        let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
557        for mut column in table_schema.column_schemas().iter().cloned() {
558            if let Some(request) = set_index_options.get(column.name.as_str()) {
559                for request in request {
560                    self.unset_index(&mut column, request)?;
561                }
562            }
563            columns.push(column);
564        }
565
566        let mut builder = SchemaBuilder::try_from_columns(columns)
567            .with_context(|_| error::SchemaBuildSnafu {
568                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
569            })?
570            .version(table_schema.version() + 1);
571
572        for (k, v) in table_schema.metadata().iter() {
573            builder = builder.add_metadata(k, v);
574        }
575
576        let new_schema = builder.build().with_context(|_| {
577            let column_names = requests
578                .iter()
579                .map(|request| request.column_name())
580                .collect::<Vec<_>>();
581            error::SchemaBuildSnafu {
582                msg: format!(
583                    "Table {table_name} cannot set index options with columns {column_names:?}",
584                ),
585            }
586        })?;
587        let _ = meta_builder
588            .schema(Arc::new(new_schema))
589            .primary_key_indices(self.primary_key_indices.clone());
590
591        Ok(meta_builder)
592    }
593
594    fn set_index(&self, column_schema: &mut ColumnSchema, request: &SetIndexOption) -> Result<()> {
595        match request {
596            SetIndexOption::Fulltext {
597                column_name,
598                options,
599            } => {
600                ensure!(
601                    column_schema.data_type.is_string(),
602                    error::InvalidColumnOptionSnafu {
603                        column_name,
604                        msg: "FULLTEXT index only supports string type",
605                    }
606                );
607
608                let current_fulltext_options = column_schema
609                    .fulltext_options()
610                    .context(error::SetFulltextOptionsSnafu { column_name })?;
611                set_column_fulltext_options(
612                    column_schema,
613                    column_name,
614                    options,
615                    current_fulltext_options,
616                )?;
617            }
618            SetIndexOption::Inverted { column_name } => {
619                debug_assert_eq!(column_schema.name, *column_name);
620                column_schema.set_inverted_index(true);
621            }
622            SetIndexOption::Skipping {
623                column_name,
624                options,
625            } => {
626                set_column_skipping_index_options(column_schema, column_name, options)?;
627            }
628        }
629
630        Ok(())
631    }
632
633    fn unset_index(
634        &self,
635        column_schema: &mut ColumnSchema,
636        request: &UnsetIndexOption,
637    ) -> Result<()> {
638        match request {
639            UnsetIndexOption::Fulltext { column_name } => {
640                let current_fulltext_options = column_schema
641                    .fulltext_options()
642                    .context(error::SetFulltextOptionsSnafu { column_name })?;
643                unset_column_fulltext_options(
644                    column_schema,
645                    column_name,
646                    current_fulltext_options.clone(),
647                )?
648            }
649            UnsetIndexOption::Inverted { .. } => {
650                column_schema.set_inverted_index(false);
651            }
652            UnsetIndexOption::Skipping { column_name } => {
653                unset_column_skipping_index_options(column_schema, column_name)?;
654            }
655        }
656
657        Ok(())
658    }
659
660    // TODO(yingwen): Remove this.
661    /// Allocate a new column for the table.
662    ///
663    /// This method would bump the `next_column_id` of the meta.
664    pub fn alloc_new_column(
665        &mut self,
666        table_name: &str,
667        new_column: &ColumnSchema,
668    ) -> Result<ColumnDescriptor> {
669        let desc = ColumnDescriptorBuilder::new(
670            self.next_column_id as ColumnId,
671            &new_column.name,
672            new_column.data_type.clone(),
673        )
674        .is_nullable(new_column.is_nullable())
675        .default_constraint(new_column.default_constraint().cloned())
676        .build()
677        .context(error::BuildColumnDescriptorSnafu {
678            table_name,
679            column_name: &new_column.name,
680        })?;
681
682        // Bump next column id.
683        self.next_column_id += 1;
684
685        Ok(desc)
686    }
687
688    /// Create a [`TableMetaBuilder`] from the current TableMeta.
689    fn new_meta_builder(&self) -> TableMetaBuilder {
690        let mut builder = TableMetaBuilder::from(self);
691        // Manually remove value_indices.
692        builder.value_indices = None;
693        builder
694    }
695
696    // TODO(yingwen): Tests add if not exists.
697    fn add_columns(
698        &self,
699        table_name: &str,
700        requests: &[AddColumnRequest],
701    ) -> Result<TableMetaBuilder> {
702        let table_schema = &self.schema;
703        let mut meta_builder = self.new_meta_builder();
704        let original_primary_key_indices: HashSet<&usize> =
705            self.primary_key_indices.iter().collect();
706
707        let mut names = HashSet::with_capacity(requests.len());
708        let mut new_columns = Vec::with_capacity(requests.len());
709        for col_to_add in requests {
710            if let Some(column_schema) =
711                table_schema.column_schema_by_name(&col_to_add.column_schema.name)
712            {
713                // If the column already exists.
714                ensure!(
715                    col_to_add.add_if_not_exists,
716                    error::ColumnExistsSnafu {
717                        table_name,
718                        column_name: &col_to_add.column_schema.name
719                    },
720                );
721
722                // Checks if the type is the same
723                ensure!(
724                    column_schema.data_type == col_to_add.column_schema.data_type,
725                    error::InvalidAlterRequestSnafu {
726                        table: table_name,
727                        err: format!(
728                            "column {} already exists with different type {:?}",
729                            col_to_add.column_schema.name, column_schema.data_type,
730                        ),
731                    }
732                );
733            } else {
734                // A new column.
735                // Ensures we only add a column once.
736                ensure!(
737                    names.insert(&col_to_add.column_schema.name),
738                    error::InvalidAlterRequestSnafu {
739                        table: table_name,
740                        err: format!(
741                            "add column {} more than once",
742                            col_to_add.column_schema.name
743                        ),
744                    }
745                );
746
747                ensure!(
748                    col_to_add.column_schema.is_nullable()
749                        || col_to_add.column_schema.default_constraint().is_some(),
750                    error::InvalidAlterRequestSnafu {
751                        table: table_name,
752                        err: format!(
753                            "no default value for column {}",
754                            col_to_add.column_schema.name
755                        ),
756                    },
757                );
758
759                new_columns.push(col_to_add.clone());
760            }
761        }
762        let requests = &new_columns[..];
763
764        let SplitResult {
765            columns_at_first,
766            columns_at_after,
767            columns_at_last,
768            column_names,
769        } = self.split_requests_by_column_location(table_name, requests)?;
770        let mut primary_key_indices = Vec::with_capacity(self.primary_key_indices.len());
771        let mut columns = Vec::with_capacity(table_schema.num_columns() + requests.len());
772        // add new columns with FIRST, and in reverse order of requests.
773        columns_at_first.iter().rev().for_each(|request| {
774            if request.is_key {
775                // If a key column is added, we also need to store its index in primary_key_indices.
776                primary_key_indices.push(columns.len());
777            }
778            columns.push(request.column_schema.clone());
779        });
780        // add existed columns in original order and handle new columns with AFTER.
781        for (index, column_schema) in table_schema.column_schemas().iter().enumerate() {
782            if original_primary_key_indices.contains(&index) {
783                primary_key_indices.push(columns.len());
784            }
785            columns.push(column_schema.clone());
786            if let Some(requests) = columns_at_after.get(&column_schema.name) {
787                requests.iter().rev().for_each(|request| {
788                    if request.is_key {
789                        // If a key column is added, we also need to store its index in primary_key_indices.
790                        primary_key_indices.push(columns.len());
791                    }
792                    columns.push(request.column_schema.clone());
793                });
794            }
795        }
796        // add new columns without location info to last.
797        columns_at_last.iter().for_each(|request| {
798            if request.is_key {
799                // If a key column is added, we also need to store its index in primary_key_indices.
800                primary_key_indices.push(columns.len());
801            }
802            columns.push(request.column_schema.clone());
803        });
804
805        let mut builder = SchemaBuilder::try_from(columns)
806            .with_context(|_| error::SchemaBuildSnafu {
807                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
808            })?
809            // Also bump the schema version.
810            .version(table_schema.version() + 1);
811        for (k, v) in table_schema.metadata().iter() {
812            builder = builder.add_metadata(k, v);
813        }
814        let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
815            msg: format!("Table {table_name} cannot add new columns {column_names:?}"),
816        })?;
817
818        let partition_key_indices = self
819            .partition_key_indices
820            .iter()
821            .map(|idx| table_schema.column_name_by_index(*idx))
822            // This unwrap is safe since we only add new columns.
823            .map(|name| new_schema.column_index_by_name(name).unwrap())
824            .collect();
825
826        // value_indices would be generated automatically.
827        let _ = meta_builder
828            .schema(Arc::new(new_schema))
829            .primary_key_indices(primary_key_indices)
830            .partition_key_indices(partition_key_indices);
831
832        Ok(meta_builder)
833    }
834
835    fn remove_columns(
836        &self,
837        table_name: &str,
838        column_names: &[String],
839    ) -> Result<TableMetaBuilder> {
840        let table_schema = &self.schema;
841        let column_names: HashSet<_> = column_names.iter().collect();
842        let mut meta_builder = self.new_meta_builder();
843
844        let timestamp_index = table_schema.timestamp_index();
845        // Check whether columns are existing and not in primary key index.
846        for column_name in &column_names {
847            if let Some(index) = table_schema.column_index_by_name(column_name) {
848                // This is a linear search, but since there won't be too much columns, the performance should
849                // be acceptable.
850                ensure!(
851                    !self.primary_key_indices.contains(&index),
852                    error::RemoveColumnInIndexSnafu {
853                        column_name: *column_name,
854                        table_name,
855                    }
856                );
857
858                ensure!(
859                    !self.partition_key_indices.contains(&index),
860                    error::RemovePartitionColumnSnafu {
861                        column_name: *column_name,
862                        table_name,
863                    }
864                );
865
866                if let Some(ts_index) = timestamp_index {
867                    // Not allowed to remove column in timestamp index.
868                    ensure!(
869                        index != ts_index,
870                        error::RemoveColumnInIndexSnafu {
871                            column_name: table_schema.column_name_by_index(ts_index),
872                            table_name,
873                        }
874                    );
875                }
876            } else {
877                return error::ColumnNotExistsSnafu {
878                    column_name: *column_name,
879                    table_name,
880                }
881                .fail()?;
882            }
883        }
884
885        // Collect columns after removal.
886        let columns: Vec<_> = table_schema
887            .column_schemas()
888            .iter()
889            .filter(|column_schema| !column_names.contains(&column_schema.name))
890            .cloned()
891            .collect();
892
893        let mut builder = SchemaBuilder::try_from_columns(columns)
894            .with_context(|_| error::SchemaBuildSnafu {
895                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
896            })?
897            // Also bump the schema version.
898            .version(table_schema.version() + 1);
899        for (k, v) in table_schema.metadata().iter() {
900            builder = builder.add_metadata(k, v);
901        }
902        let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
903            msg: format!("Table {table_name} cannot add remove columns {column_names:?}"),
904        })?;
905
906        // Rebuild the indices of primary key columns.
907        let primary_key_indices = self
908            .primary_key_indices
909            .iter()
910            .map(|idx| table_schema.column_name_by_index(*idx))
911            // This unwrap is safe since we don't allow removing a primary key column.
912            .map(|name| new_schema.column_index_by_name(name).unwrap())
913            .collect();
914
915        let partition_key_indices = self
916            .partition_key_indices
917            .iter()
918            .map(|idx| table_schema.column_name_by_index(*idx))
919            // This unwrap is safe since we don't allow removing a partition key column.
920            .map(|name| new_schema.column_index_by_name(name).unwrap())
921            .collect();
922
923        let _ = meta_builder
924            .schema(Arc::new(new_schema))
925            .primary_key_indices(primary_key_indices)
926            .partition_key_indices(partition_key_indices);
927
928        Ok(meta_builder)
929    }
930
931    fn modify_column_types(
932        &self,
933        table_name: &str,
934        requests: &[ModifyColumnTypeRequest],
935    ) -> Result<TableMetaBuilder> {
936        let table_schema = &self.schema;
937        let mut meta_builder = self.new_meta_builder();
938
939        let mut modify_column_types = HashMap::with_capacity(requests.len());
940        let timestamp_index = table_schema.timestamp_index();
941
942        for col_to_change in requests {
943            let change_column_name = &col_to_change.column_name;
944
945            let index = table_schema
946                .column_index_by_name(change_column_name)
947                .with_context(|| error::ColumnNotExistsSnafu {
948                    column_name: change_column_name,
949                    table_name,
950                })?;
951
952            let column = &table_schema.column_schemas()[index];
953
954            ensure!(
955                !self.primary_key_indices.contains(&index),
956                error::InvalidAlterRequestSnafu {
957                    table: table_name,
958                    err: format!(
959                        "Not allowed to change primary key index column '{}'",
960                        column.name
961                    )
962                }
963            );
964
965            if let Some(ts_index) = timestamp_index {
966                // Not allowed to change column datatype in timestamp index.
967                ensure!(
968                    index != ts_index,
969                    error::InvalidAlterRequestSnafu {
970                        table: table_name,
971                        err: format!(
972                            "Not allowed to change timestamp index column '{}' datatype",
973                            column.name
974                        )
975                    }
976                );
977            }
978
979            ensure!(
980                modify_column_types
981                    .insert(&col_to_change.column_name, col_to_change)
982                    .is_none(),
983                error::InvalidAlterRequestSnafu {
984                    table: table_name,
985                    err: format!(
986                        "change column datatype {} more than once",
987                        col_to_change.column_name
988                    ),
989                }
990            );
991
992            ensure!(
993                column
994                    .data_type
995                    .can_arrow_type_cast_to(&col_to_change.target_type),
996                error::InvalidAlterRequestSnafu {
997                    table: table_name,
998                    err: format!(
999                        "column '{}' cannot be cast automatically to type '{}'",
1000                        col_to_change.column_name, col_to_change.target_type,
1001                    ),
1002                }
1003            );
1004
1005            ensure!(
1006                column.is_nullable(),
1007                error::InvalidAlterRequestSnafu {
1008                    table: table_name,
1009                    err: format!(
1010                        "column '{}' must be nullable to ensure safe conversion.",
1011                        col_to_change.column_name,
1012                    ),
1013                }
1014            );
1015        }
1016        // Collect columns after changed.
1017
1018        let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
1019        for mut column in table_schema.column_schemas().iter().cloned() {
1020            if let Some(change_column) = modify_column_types.get(&column.name) {
1021                column.data_type = change_column.target_type.clone();
1022                let new_default = if let Some(default_value) = column.default_constraint() {
1023                    Some(
1024                        default_value
1025                            .cast_to_datatype(&change_column.target_type)
1026                            .with_context(|_| error::CastDefaultValueSnafu {
1027                                reason: format!(
1028                                    "Failed to cast default value from {:?} to type {:?}",
1029                                    default_value, &change_column.target_type
1030                                ),
1031                            })?,
1032                    )
1033                } else {
1034                    None
1035                };
1036                column = column
1037                    .clone()
1038                    .with_default_constraint(new_default.clone())
1039                    .with_context(|_| error::CastDefaultValueSnafu {
1040                        reason: format!("Failed to set new default: {:?}", new_default),
1041                    })?;
1042            }
1043            columns.push(column)
1044        }
1045
1046        let mut builder = SchemaBuilder::try_from_columns(columns)
1047            .with_context(|_| error::SchemaBuildSnafu {
1048                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1049            })?
1050            // Also bump the schema version.
1051            .version(table_schema.version() + 1);
1052        for (k, v) in table_schema.metadata().iter() {
1053            builder = builder.add_metadata(k, v);
1054        }
1055        let new_schema = builder.build().with_context(|_| {
1056            let column_names: Vec<_> = requests
1057                .iter()
1058                .map(|request| &request.column_name)
1059                .collect();
1060
1061            error::SchemaBuildSnafu {
1062                msg: format!(
1063                    "Table {table_name} cannot change datatype with columns {column_names:?}"
1064                ),
1065            }
1066        })?;
1067
1068        let _ = meta_builder
1069            .schema(Arc::new(new_schema))
1070            .primary_key_indices(self.primary_key_indices.clone());
1071
1072        Ok(meta_builder)
1073    }
1074
1075    /// Split requests into different groups using column location info.
1076    fn split_requests_by_column_location<'a>(
1077        &self,
1078        table_name: &str,
1079        requests: &'a [AddColumnRequest],
1080    ) -> Result<SplitResult<'a>> {
1081        let table_schema = &self.schema;
1082        let mut columns_at_first = Vec::new();
1083        let mut columns_at_after = HashMap::new();
1084        let mut columns_at_last = Vec::new();
1085        let mut column_names = Vec::with_capacity(requests.len());
1086        for request in requests {
1087            // Check whether columns to add are already existing.
1088            let column_name = &request.column_schema.name;
1089            column_names.push(column_name.clone());
1090            ensure!(
1091                table_schema.column_schema_by_name(column_name).is_none(),
1092                error::ColumnExistsSnafu {
1093                    column_name,
1094                    table_name,
1095                }
1096            );
1097            match request.location.as_ref() {
1098                Some(AddColumnLocation::First) => {
1099                    columns_at_first.push(request);
1100                }
1101                Some(AddColumnLocation::After { column_name }) => {
1102                    ensure!(
1103                        table_schema.column_schema_by_name(column_name).is_some(),
1104                        error::ColumnNotExistsSnafu {
1105                            column_name,
1106                            table_name,
1107                        }
1108                    );
1109                    columns_at_after
1110                        .entry(column_name.clone())
1111                        .or_insert(Vec::new())
1112                        .push(request);
1113                }
1114                None => {
1115                    columns_at_last.push(request);
1116                }
1117            }
1118        }
1119        Ok(SplitResult {
1120            columns_at_first,
1121            columns_at_after,
1122            columns_at_last,
1123            column_names,
1124        })
1125    }
1126
1127    fn drop_defaults(&self, table_name: &str, column_names: &[String]) -> Result<TableMetaBuilder> {
1128        let table_schema = &self.schema;
1129        let mut meta_builder = self.new_meta_builder();
1130        let mut columns = Vec::with_capacity(table_schema.num_columns());
1131        for column_schema in table_schema.column_schemas() {
1132            if let Some(name) = column_names.iter().find(|s| **s == column_schema.name) {
1133                // Drop default constraint.
1134                ensure!(
1135                    column_schema.default_constraint().is_some(),
1136                    error::InvalidAlterRequestSnafu {
1137                        table: table_name,
1138                        err: format!("column {name} does not have a default value"),
1139                    }
1140                );
1141                if !column_schema.is_nullable() {
1142                    return error::InvalidAlterRequestSnafu {
1143                        table: table_name,
1144                        err: format!(
1145                            "column {name} is not nullable and `default` cannot be dropped",
1146                        ),
1147                    }
1148                    .fail();
1149                }
1150                let new_column_schema = column_schema.clone();
1151                let new_column_schema = new_column_schema
1152                    .with_default_constraint(None)
1153                    .with_context(|_| error::SchemaBuildSnafu {
1154                        msg: format!("Table {table_name} cannot drop default values"),
1155                    })?;
1156                columns.push(new_column_schema);
1157            } else {
1158                columns.push(column_schema.clone());
1159            }
1160        }
1161
1162        let mut builder = SchemaBuilder::try_from_columns(columns)
1163            .with_context(|_| error::SchemaBuildSnafu {
1164                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1165            })?
1166            // Also bump the schema version.
1167            .version(table_schema.version() + 1);
1168        for (k, v) in table_schema.metadata().iter() {
1169            builder = builder.add_metadata(k, v);
1170        }
1171        let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1172            msg: format!("Table {table_name} cannot drop default values"),
1173        })?;
1174
1175        let _ = meta_builder.schema(Arc::new(new_schema));
1176
1177        Ok(meta_builder)
1178    }
1179
1180    fn set_defaults(
1181        &self,
1182        table_name: &str,
1183        set_defaults: &[SetDefaultRequest],
1184    ) -> Result<TableMetaBuilder> {
1185        let table_schema = &self.schema;
1186        let mut meta_builder = self.new_meta_builder();
1187        let mut columns = Vec::with_capacity(table_schema.num_columns());
1188        for column_schema in table_schema.column_schemas() {
1189            if let Some(set_default) = set_defaults
1190                .iter()
1191                .find(|s| s.column_name == column_schema.name)
1192            {
1193                let new_column_schema = column_schema.clone();
1194                let new_column_schema = new_column_schema
1195                    .with_default_constraint(set_default.default_constraint.clone())
1196                    .with_context(|_| error::SchemaBuildSnafu {
1197                        msg: format!("Table {table_name} cannot set default values"),
1198                    })?;
1199                columns.push(new_column_schema);
1200            } else {
1201                columns.push(column_schema.clone());
1202            }
1203        }
1204
1205        let mut builder = SchemaBuilder::try_from_columns(columns)
1206            .with_context(|_| error::SchemaBuildSnafu {
1207                msg: format!("Failed to convert column schemas into schema for table {table_name}"),
1208            })?
1209            // Also bump the schema version.
1210            .version(table_schema.version() + 1);
1211        for (k, v) in table_schema.metadata().iter() {
1212            builder = builder.add_metadata(k, v);
1213        }
1214        let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
1215            msg: format!("Table {table_name} cannot set default values"),
1216        })?;
1217
1218        let _ = meta_builder.schema(Arc::new(new_schema));
1219
1220        Ok(meta_builder)
1221    }
1222}
1223
1224#[derive(Clone, Debug, PartialEq, Eq, Builder, Serialize, Deserialize)]
1225#[builder(pattern = "owned")]
1226pub struct TableInfo {
1227    /// Id and version of the table.
1228    #[builder(default, setter(into))]
1229    pub ident: TableIdent,
1230    /// Name of the table.
1231    #[builder(setter(into))]
1232    pub name: String,
1233    /// Comment of the table.
1234    #[builder(default, setter(into))]
1235    pub desc: Option<String>,
1236    #[builder(default = "DEFAULT_CATALOG_NAME.to_string()", setter(into))]
1237    pub catalog_name: String,
1238    #[builder(default = "DEFAULT_SCHEMA_NAME.to_string()", setter(into))]
1239    pub schema_name: String,
1240    pub meta: TableMeta,
1241    #[builder(default = "TableType::Base")]
1242    pub table_type: TableType,
1243}
1244
1245pub type TableInfoRef = Arc<TableInfo>;
1246
1247impl TableInfo {
1248    pub fn table_id(&self) -> TableId {
1249        self.ident.table_id
1250    }
1251
1252    /// Returns the full table name in the form of `{catalog}.{schema}.{table}`.
1253    pub fn full_table_name(&self) -> String {
1254        common_catalog::format_full_table_name(&self.catalog_name, &self.schema_name, &self.name)
1255    }
1256
1257    pub fn get_db_string(&self) -> String {
1258        common_catalog::build_db_string(&self.catalog_name, &self.schema_name)
1259    }
1260
1261    /// Returns true when the table is the metric engine's physical table.
1262    pub fn is_physical_table(&self) -> bool {
1263        self.meta
1264            .options
1265            .extra_options
1266            .contains_key(PHYSICAL_TABLE_METADATA_KEY)
1267    }
1268
1269    /// Return true if the table's TTL is `instant`.
1270    pub fn is_ttl_instant_table(&self) -> bool {
1271        self.meta
1272            .options
1273            .ttl
1274            .map(|t| t.is_instant())
1275            .unwrap_or(false)
1276    }
1277}
1278
1279impl TableInfoBuilder {
1280    pub fn new<S: Into<String>>(name: S, meta: TableMeta) -> Self {
1281        Self {
1282            name: Some(name.into()),
1283            meta: Some(meta),
1284            ..Default::default()
1285        }
1286    }
1287
1288    pub fn table_id(mut self, id: TableId) -> Self {
1289        let ident = self.ident.get_or_insert_with(TableIdent::default);
1290        ident.table_id = id;
1291        self
1292    }
1293
1294    pub fn table_version(mut self, version: TableVersion) -> Self {
1295        let ident = self.ident.get_or_insert_with(TableIdent::default);
1296        ident.version = version;
1297        self
1298    }
1299}
1300
1301impl TableIdent {
1302    pub fn new(table_id: TableId) -> Self {
1303        Self {
1304            table_id,
1305            version: 0,
1306        }
1307    }
1308}
1309
1310impl From<TableId> for TableIdent {
1311    fn from(table_id: TableId) -> Self {
1312        Self::new(table_id)
1313    }
1314}
1315
1316impl TableInfo {
1317    /// Returns the map of column name to column id.
1318    ///
1319    /// Note: This method may return an empty map for older versions that did not include this field.
1320    pub fn name_to_ids(&self) -> Option<HashMap<String, ColumnId>> {
1321        let column_schemas = self.meta.schema.column_schemas();
1322        if self.meta.column_ids.len() != column_schemas.len() {
1323            None
1324        } else {
1325            Some(
1326                self.meta
1327                    .column_ids
1328                    .iter()
1329                    .enumerate()
1330                    .map(|(index, id)| (column_schemas[index].name.clone(), *id))
1331                    .collect(),
1332            )
1333        }
1334    }
1335
1336    /// Sort the columns in [TableInfo], logical tables require it.
1337    pub fn sort_columns(&mut self) {
1338        let column_schemas = self.meta.schema.column_schemas();
1339        let primary_keys = self
1340            .meta
1341            .primary_key_indices
1342            .iter()
1343            .map(|index| column_schemas[*index].name.clone())
1344            .collect::<HashSet<_>>();
1345
1346        let name_to_ids = self.name_to_ids().unwrap_or_default();
1347        let mut column_schemas = column_schemas.to_vec();
1348        column_schemas.sort_unstable_by(|a, b| a.name.cmp(&b.name));
1349
1350        // Compute new indices of sorted columns
1351        let mut primary_key_indices = Vec::with_capacity(primary_keys.len());
1352        let mut value_indices = Vec::with_capacity(column_schemas.len() - primary_keys.len());
1353        let mut column_ids = Vec::with_capacity(column_schemas.len());
1354        for (index, column_schema) in column_schemas.iter().enumerate() {
1355            if primary_keys.contains(&column_schema.name) {
1356                primary_key_indices.push(index);
1357            } else {
1358                value_indices.push(index);
1359            }
1360            if let Some(id) = name_to_ids.get(&column_schema.name) {
1361                column_ids.push(*id);
1362            }
1363        }
1364
1365        // Overwrite table meta
1366        self.meta.schema = Arc::new(Schema::new_with_version(
1367            column_schemas,
1368            self.meta.schema.version(),
1369        ));
1370        self.meta.primary_key_indices = primary_key_indices;
1371        self.meta.value_indices = value_indices;
1372        self.meta.column_ids = column_ids;
1373    }
1374
1375    /// Extracts region options from table info.
1376    ///
1377    /// All "region options" are actually a copy of table options for redundancy.
1378    pub fn to_region_options(&self) -> HashMap<String, String> {
1379        let mut options = HashMap::from(&self.meta.options);
1380        options.remove(REPARTITION_COLUMN_HINT_KEY);
1381        options
1382    }
1383
1384    /// Returns the table reference.
1385    pub fn table_ref(&self) -> TableReference<'_> {
1386        TableReference::full(
1387            self.catalog_name.as_str(),
1388            self.schema_name.as_str(),
1389            self.name.as_str(),
1390        )
1391    }
1392}
1393
1394/// Set column fulltext options if it passed the validation.
1395///
1396/// Options allowed to modify:
1397/// * backend
1398///
1399/// Options not allowed to modify:
1400/// * analyzer
1401/// * case_sensitive
1402fn set_column_fulltext_options(
1403    column_schema: &mut ColumnSchema,
1404    column_name: &str,
1405    options: &FulltextOptions,
1406    current_options: Option<FulltextOptions>,
1407) -> Result<()> {
1408    if let Some(current_options) = current_options {
1409        ensure!(
1410            current_options.analyzer == options.analyzer
1411                && current_options.case_sensitive == options.case_sensitive,
1412            error::InvalidColumnOptionSnafu {
1413                column_name,
1414                msg: format!(
1415                    "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1416                    current_options.analyzer, current_options.case_sensitive
1417                ),
1418            }
1419        );
1420    }
1421
1422    column_schema
1423        .set_fulltext_options(options)
1424        .context(error::SetFulltextOptionsSnafu { column_name })?;
1425
1426    Ok(())
1427}
1428
1429fn unset_column_fulltext_options(
1430    column_schema: &mut ColumnSchema,
1431    column_name: &str,
1432    current_options: Option<FulltextOptions>,
1433) -> Result<()> {
1434    ensure!(
1435        current_options
1436            .as_ref()
1437            .is_some_and(|options| options.enable),
1438        error::InvalidColumnOptionSnafu {
1439            column_name,
1440            msg: "FULLTEXT index already disabled".to_string(),
1441        }
1442    );
1443
1444    let mut options = current_options.unwrap();
1445    options.enable = false;
1446    column_schema
1447        .set_fulltext_options(&options)
1448        .context(error::SetFulltextOptionsSnafu { column_name })?;
1449
1450    Ok(())
1451}
1452
1453fn set_column_skipping_index_options(
1454    column_schema: &mut ColumnSchema,
1455    column_name: &str,
1456    options: &SkippingIndexOptions,
1457) -> Result<()> {
1458    column_schema
1459        .set_skipping_options(options)
1460        .context(error::SetSkippingOptionsSnafu { column_name })?;
1461
1462    Ok(())
1463}
1464
1465fn unset_column_skipping_index_options(
1466    column_schema: &mut ColumnSchema,
1467    column_name: &str,
1468) -> Result<()> {
1469    column_schema
1470        .unset_skipping_options()
1471        .context(error::UnsetSkippingOptionsSnafu { column_name })?;
1472    Ok(())
1473}
1474
1475#[cfg(test)]
1476mod tests {
1477    use std::assert_matches;
1478
1479    use common_error::ext::ErrorExt;
1480    use common_error::status_code::StatusCode;
1481    use datatypes::data_type::ConcreteDataType;
1482    use datatypes::schema::{
1483        ColumnSchema, FulltextAnalyzer, FulltextBackend, Schema, SchemaBuilder,
1484    };
1485
1486    use super::*;
1487    use crate::Error;
1488
1489    /// Create a test schema with 3 columns: `[col1 int32, ts timestampmills, col2 int32]`.
1490    fn new_test_schema() -> Schema {
1491        let column_schemas = vec![
1492            ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
1493            ColumnSchema::new(
1494                "ts",
1495                ConcreteDataType::timestamp_millisecond_datatype(),
1496                false,
1497            )
1498            .with_time_index(true),
1499            ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
1500        ];
1501        SchemaBuilder::try_from(column_schemas)
1502            .unwrap()
1503            .version(123)
1504            .build()
1505            .unwrap()
1506    }
1507
1508    fn add_columns_to_meta(meta: &TableMeta) -> TableMeta {
1509        let new_tag = ColumnSchema::new("my_tag", ConcreteDataType::string_datatype(), true);
1510        let new_field = ColumnSchema::new("my_field", ConcreteDataType::string_datatype(), true);
1511        let alter_kind = AlterKind::AddColumns {
1512            columns: vec![
1513                AddColumnRequest {
1514                    column_schema: new_tag,
1515                    is_key: true,
1516                    location: None,
1517                    add_if_not_exists: false,
1518                },
1519                AddColumnRequest {
1520                    column_schema: new_field,
1521                    is_key: false,
1522                    location: None,
1523                    add_if_not_exists: false,
1524                },
1525            ],
1526        };
1527
1528        let builder = meta
1529            .builder_with_alter_kind("my_table", &alter_kind)
1530            .unwrap();
1531        builder.build().unwrap()
1532    }
1533
1534    fn add_columns_to_meta_with_location(meta: &TableMeta) -> TableMeta {
1535        let new_tag = ColumnSchema::new("my_tag_first", ConcreteDataType::string_datatype(), true);
1536        let new_field = ColumnSchema::new(
1537            "my_field_after_ts",
1538            ConcreteDataType::string_datatype(),
1539            true,
1540        );
1541        let yet_another_field = ColumnSchema::new(
1542            "yet_another_field_after_ts",
1543            ConcreteDataType::int64_datatype(),
1544            true,
1545        );
1546        let alter_kind = AlterKind::AddColumns {
1547            columns: vec![
1548                AddColumnRequest {
1549                    column_schema: new_tag,
1550                    is_key: true,
1551                    location: Some(AddColumnLocation::First),
1552                    add_if_not_exists: false,
1553                },
1554                AddColumnRequest {
1555                    column_schema: new_field,
1556                    is_key: false,
1557                    location: Some(AddColumnLocation::After {
1558                        column_name: "ts".to_string(),
1559                    }),
1560                    add_if_not_exists: false,
1561                },
1562                AddColumnRequest {
1563                    column_schema: yet_another_field,
1564                    is_key: true,
1565                    location: Some(AddColumnLocation::After {
1566                        column_name: "ts".to_string(),
1567                    }),
1568                    add_if_not_exists: false,
1569                },
1570            ],
1571        };
1572
1573        let builder = meta
1574            .builder_with_alter_kind("my_table", &alter_kind)
1575            .unwrap();
1576        builder.build().unwrap()
1577    }
1578
1579    #[test]
1580    fn test_add_columns() {
1581        let schema = Arc::new(new_test_schema());
1582        let meta = TableMetaBuilder::empty()
1583            .schema(schema)
1584            .primary_key_indices(vec![0])
1585            .engine("engine")
1586            .next_column_id(3)
1587            .build()
1588            .unwrap();
1589
1590        let new_meta = add_columns_to_meta(&meta);
1591        let names: Vec<String> = new_meta
1592            .schema
1593            .column_schemas()
1594            .iter()
1595            .map(|column_schema| column_schema.name.clone())
1596            .collect();
1597        assert_eq!(&["col1", "ts", "col2", "my_tag", "my_field"], &names[..]);
1598        assert_eq!(&[0, 3], &new_meta.primary_key_indices[..]);
1599        assert_eq!(&[1, 2, 4], &new_meta.value_indices[..]);
1600    }
1601
1602    #[test]
1603    fn test_set_append_mode_true_clears_merge_mode_option() {
1604        let schema = Arc::new(new_test_schema());
1605        let mut table_options = TableOptions::default();
1606        table_options
1607            .extra_options
1608            .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1609        let meta = TableMetaBuilder::empty()
1610            .schema(schema)
1611            .primary_key_indices(vec![0])
1612            .engine("engine")
1613            .next_column_id(3)
1614            .options(table_options)
1615            .build()
1616            .unwrap();
1617
1618        let alter_kind = AlterKind::SetTableOptions {
1619            options: vec![SetRegionOption::AppendMode(true)],
1620        };
1621        let new_meta = meta
1622            .builder_with_alter_kind("my_table", &alter_kind)
1623            .unwrap()
1624            .build()
1625            .unwrap();
1626
1627        assert_eq!(
1628            Some("true"),
1629            new_meta
1630                .options
1631                .extra_options
1632                .get(APPEND_MODE_KEY)
1633                .map(String::as_str)
1634        );
1635        assert!(!new_meta.options.extra_options.contains_key(MERGE_MODE_KEY));
1636    }
1637
1638    #[test]
1639    fn test_set_append_mode_false_keeps_merge_mode_option() {
1640        let schema = Arc::new(new_test_schema());
1641        let mut table_options = TableOptions::default();
1642        table_options
1643            .extra_options
1644            .insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1645        let meta = TableMetaBuilder::empty()
1646            .schema(schema)
1647            .primary_key_indices(vec![0])
1648            .engine("engine")
1649            .next_column_id(3)
1650            .options(table_options)
1651            .build()
1652            .unwrap();
1653
1654        let alter_kind = AlterKind::SetTableOptions {
1655            options: vec![SetRegionOption::AppendMode(false)],
1656        };
1657        let new_meta = meta
1658            .builder_with_alter_kind("my_table", &alter_kind)
1659            .unwrap()
1660            .build()
1661            .unwrap();
1662
1663        assert_eq!(
1664            Some("false"),
1665            new_meta
1666                .options
1667                .extra_options
1668                .get(APPEND_MODE_KEY)
1669                .map(String::as_str)
1670        );
1671        assert_eq!(
1672            Some("last_non_null"),
1673            new_meta
1674                .options
1675                .extra_options
1676                .get(MERGE_MODE_KEY)
1677                .map(String::as_str)
1678        );
1679    }
1680
1681    #[test]
1682    fn test_set_repartition_column_hint() {
1683        let meta = TableMetaBuilder::empty()
1684            .schema(Arc::new(new_test_schema()))
1685            .primary_key_indices(vec![0])
1686            .engine("engine")
1687            .next_column_id(3)
1688            .build()
1689            .unwrap();
1690
1691        let alter_kind = AlterKind::SetRepartitionColumnHint {
1692            column_name: " col1 ".to_string(),
1693        };
1694        let new_meta = meta
1695            .builder_with_alter_kind("my_table", &alter_kind)
1696            .unwrap()
1697            .build()
1698            .unwrap();
1699
1700        assert_eq!(
1701            Some("col1"),
1702            new_meta
1703                .options
1704                .extra_options
1705                .get(REPARTITION_COLUMN_HINT_KEY)
1706                .map(String::as_str)
1707        );
1708    }
1709
1710    #[test]
1711    fn test_set_repartition_column_hint_rejects_empty_column() {
1712        let meta = TableMetaBuilder::empty()
1713            .schema(Arc::new(new_test_schema()))
1714            .primary_key_indices(vec![0])
1715            .engine("engine")
1716            .next_column_id(3)
1717            .build()
1718            .unwrap();
1719
1720        let alter_kind = AlterKind::SetRepartitionColumnHint {
1721            column_name: " ".to_string(),
1722        };
1723        let err = meta
1724            .builder_with_alter_kind("my_table", &alter_kind)
1725            .err()
1726            .unwrap();
1727
1728        assert!(
1729            err.to_string()
1730                .contains("repartition.column.hint expects exactly one column name")
1731        );
1732    }
1733
1734    #[test]
1735    fn test_set_repartition_column_hint_rejects_multiple_columns() {
1736        let meta = TableMetaBuilder::empty()
1737            .schema(Arc::new(new_test_schema()))
1738            .primary_key_indices(vec![0])
1739            .engine("engine")
1740            .next_column_id(3)
1741            .build()
1742            .unwrap();
1743
1744        let alter_kind = AlterKind::SetRepartitionColumnHint {
1745            column_name: "col1,col2".to_string(),
1746        };
1747        let err = meta
1748            .builder_with_alter_kind("my_table", &alter_kind)
1749            .err()
1750            .unwrap();
1751
1752        assert!(
1753            err.to_string()
1754                .contains("repartition.column.hint expects exactly one column name")
1755        );
1756    }
1757
1758    #[test]
1759    fn test_set_repartition_column_hint_rejects_missing_column() {
1760        let meta = TableMetaBuilder::empty()
1761            .schema(Arc::new(new_test_schema()))
1762            .primary_key_indices(vec![0])
1763            .engine("engine")
1764            .next_column_id(3)
1765            .build()
1766            .unwrap();
1767
1768        let alter_kind = AlterKind::SetRepartitionColumnHint {
1769            column_name: "missing".to_string(),
1770        };
1771        let err = meta
1772            .builder_with_alter_kind("my_table", &alter_kind)
1773            .err()
1774            .unwrap();
1775
1776        assert!(err.to_string().contains("Column missing not exists"));
1777    }
1778
1779    #[test]
1780    fn test_set_repartition_column_hint_rejects_time_index_column() {
1781        let meta = TableMetaBuilder::empty()
1782            .schema(Arc::new(new_test_schema()))
1783            .primary_key_indices(vec![0])
1784            .engine("engine")
1785            .next_column_id(3)
1786            .build()
1787            .unwrap();
1788
1789        let alter_kind = AlterKind::SetRepartitionColumnHint {
1790            column_name: "ts".to_string(),
1791        };
1792        let err = meta
1793            .builder_with_alter_kind("my_table", &alter_kind)
1794            .err()
1795            .unwrap();
1796
1797        assert!(
1798            err.to_string()
1799                .contains("cannot set repartition.column.hint to the time index column")
1800        );
1801    }
1802
1803    #[test]
1804    fn test_set_repartition_column_hint_rejects_partitioned_table() {
1805        let meta = TableMetaBuilder::empty()
1806            .schema(Arc::new(new_test_schema()))
1807            .primary_key_indices(vec![0])
1808            .engine("engine")
1809            .next_column_id(3)
1810            .partition_key_indices(vec![0])
1811            .build()
1812            .unwrap();
1813
1814        let alter_kind = AlterKind::SetRepartitionColumnHint {
1815            column_name: "col1".to_string(),
1816        };
1817        let err = meta
1818            .builder_with_alter_kind("my_table", &alter_kind)
1819            .err()
1820            .unwrap();
1821
1822        assert!(
1823            err.to_string()
1824                .contains("cannot set repartition.column.hint on a table with partition metadata")
1825        );
1826    }
1827
1828    #[test]
1829    fn test_unset_repartition_column_hint() {
1830        let mut table_options = TableOptions::default();
1831        table_options
1832            .extra_options
1833            .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
1834        let meta = TableMetaBuilder::empty()
1835            .schema(Arc::new(new_test_schema()))
1836            .primary_key_indices(vec![0])
1837            .engine("engine")
1838            .next_column_id(3)
1839            .options(table_options)
1840            .build()
1841            .unwrap();
1842
1843        let new_meta = meta
1844            .builder_with_alter_kind("my_table", &AlterKind::UnsetRepartitionColumnHint)
1845            .unwrap()
1846            .build()
1847            .unwrap();
1848
1849        assert!(
1850            !new_meta
1851                .options
1852                .extra_options
1853                .contains_key(REPARTITION_COLUMN_HINT_KEY)
1854        );
1855    }
1856
1857    #[test]
1858    fn test_repartition_column_hint_is_not_region_option() {
1859        let mut table_options = TableOptions::default();
1860        table_options
1861            .extra_options
1862            .insert(REPARTITION_COLUMN_HINT_KEY.to_string(), "col1".to_string());
1863        let table_info = TableInfoBuilder::default()
1864            .table_id(1)
1865            .table_version(0)
1866            .name("my_table")
1867            .catalog_name(DEFAULT_CATALOG_NAME)
1868            .schema_name(DEFAULT_SCHEMA_NAME)
1869            .meta(
1870                TableMetaBuilder::empty()
1871                    .schema(Arc::new(new_test_schema()))
1872                    .primary_key_indices(vec![0])
1873                    .engine("engine")
1874                    .next_column_id(3)
1875                    .options(table_options)
1876                    .build()
1877                    .unwrap(),
1878            )
1879            .build()
1880            .unwrap();
1881
1882        assert!(
1883            !table_info
1884                .to_region_options()
1885                .contains_key(REPARTITION_COLUMN_HINT_KEY)
1886        );
1887    }
1888
1889    #[test]
1890    fn test_set_auto_flush_interval() {
1891        let schema = Arc::new(new_test_schema());
1892        let table_options = TableOptions::default();
1893        let meta = TableMetaBuilder::empty()
1894            .schema(schema)
1895            .primary_key_indices(vec![0])
1896            .engine("engine")
1897            .next_column_id(3)
1898            .options(table_options)
1899            .build()
1900            .unwrap();
1901
1902        let alter_kind = AlterKind::SetTableOptions {
1903            options: vec![SetRegionOption::AutoFlushInterval(Some(
1904                std::time::Duration::from_secs(300),
1905            ))],
1906        };
1907        let new_meta = meta
1908            .builder_with_alter_kind("my_table", &alter_kind)
1909            .unwrap()
1910            .build()
1911            .unwrap();
1912
1913        assert_eq!(
1914            Some("5m"),
1915            new_meta
1916                .options
1917                .extra_options
1918                .get(AUTO_FLUSH_INTERVAL_KEY)
1919                .map(String::as_str)
1920        );
1921    }
1922
1923    #[test]
1924    fn test_set_auto_flush_interval_none_removes_existing() {
1925        let schema = Arc::new(new_test_schema());
1926        let mut table_options = TableOptions::default();
1927        table_options
1928            .extra_options
1929            .insert(AUTO_FLUSH_INTERVAL_KEY.to_string(), "5m".to_string());
1930        let meta = TableMetaBuilder::empty()
1931            .schema(schema)
1932            .primary_key_indices(vec![0])
1933            .engine("engine")
1934            .next_column_id(3)
1935            .options(table_options)
1936            .build()
1937            .unwrap();
1938
1939        let alter_kind = AlterKind::SetTableOptions {
1940            options: vec![SetRegionOption::AutoFlushInterval(None)],
1941        };
1942        let new_meta = meta
1943            .builder_with_alter_kind("my_table", &alter_kind)
1944            .unwrap()
1945            .build()
1946            .unwrap();
1947
1948        assert!(
1949            !new_meta
1950                .options
1951                .extra_options
1952                .contains_key(AUTO_FLUSH_INTERVAL_KEY)
1953        );
1954    }
1955
1956    #[test]
1957    fn test_add_columns_multiple_times() {
1958        let schema = Arc::new(new_test_schema());
1959        let meta = TableMetaBuilder::empty()
1960            .schema(schema)
1961            .primary_key_indices(vec![0])
1962            .engine("engine")
1963            .next_column_id(3)
1964            .build()
1965            .unwrap();
1966
1967        let alter_kind = AlterKind::AddColumns {
1968            columns: vec![
1969                AddColumnRequest {
1970                    column_schema: ColumnSchema::new(
1971                        "col3",
1972                        ConcreteDataType::int32_datatype(),
1973                        true,
1974                    ),
1975                    is_key: true,
1976                    location: None,
1977                    add_if_not_exists: true,
1978                },
1979                AddColumnRequest {
1980                    column_schema: ColumnSchema::new(
1981                        "col3",
1982                        ConcreteDataType::int32_datatype(),
1983                        true,
1984                    ),
1985                    is_key: true,
1986                    location: None,
1987                    add_if_not_exists: true,
1988                },
1989            ],
1990        };
1991        let err = meta
1992            .builder_with_alter_kind("my_table", &alter_kind)
1993            .err()
1994            .unwrap();
1995        assert_eq!(StatusCode::InvalidArguments, err.status_code());
1996    }
1997
1998    #[test]
1999    fn test_remove_columns() {
2000        let schema = Arc::new(new_test_schema());
2001        let meta = TableMetaBuilder::empty()
2002            .schema(schema.clone())
2003            .primary_key_indices(vec![0])
2004            .engine("engine")
2005            .next_column_id(3)
2006            .build()
2007            .unwrap();
2008        // Add more columns so we have enough candidate columns to remove.
2009        let meta = add_columns_to_meta(&meta);
2010
2011        let alter_kind = AlterKind::DropColumns {
2012            names: vec![String::from("col2"), String::from("my_field")],
2013        };
2014        let new_meta = meta
2015            .builder_with_alter_kind("my_table", &alter_kind)
2016            .unwrap()
2017            .build()
2018            .unwrap();
2019
2020        let names: Vec<String> = new_meta
2021            .schema
2022            .column_schemas()
2023            .iter()
2024            .map(|column_schema| column_schema.name.clone())
2025            .collect();
2026        assert_eq!(&["col1", "ts", "my_tag"], &names[..]);
2027        assert_eq!(&[0, 2], &new_meta.primary_key_indices[..]);
2028        assert_eq!(&[1], &new_meta.value_indices[..]);
2029        assert_eq!(
2030            schema.timestamp_column(),
2031            new_meta.schema.timestamp_column()
2032        );
2033    }
2034
2035    #[test]
2036    fn test_remove_multiple_columns_before_timestamp() {
2037        let column_schemas = vec![
2038            ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2039            ColumnSchema::new("col2", ConcreteDataType::int32_datatype(), true),
2040            ColumnSchema::new("col3", ConcreteDataType::int32_datatype(), true),
2041            ColumnSchema::new(
2042                "ts",
2043                ConcreteDataType::timestamp_millisecond_datatype(),
2044                false,
2045            )
2046            .with_time_index(true),
2047        ];
2048        let schema = Arc::new(
2049            SchemaBuilder::try_from(column_schemas)
2050                .unwrap()
2051                .version(123)
2052                .build()
2053                .unwrap(),
2054        );
2055        let meta = TableMetaBuilder::empty()
2056            .schema(schema.clone())
2057            .primary_key_indices(vec![1])
2058            .engine("engine")
2059            .next_column_id(4)
2060            .build()
2061            .unwrap();
2062
2063        // Remove columns in reverse order to test whether timestamp index is valid.
2064        let alter_kind = AlterKind::DropColumns {
2065            names: vec![String::from("col3"), String::from("col1")],
2066        };
2067        let new_meta = meta
2068            .builder_with_alter_kind("my_table", &alter_kind)
2069            .unwrap()
2070            .build()
2071            .unwrap();
2072
2073        let names: Vec<String> = new_meta
2074            .schema
2075            .column_schemas()
2076            .iter()
2077            .map(|column_schema| column_schema.name.clone())
2078            .collect();
2079        assert_eq!(&["col2", "ts"], &names[..]);
2080        assert_eq!(&[0], &new_meta.primary_key_indices[..]);
2081        assert_eq!(&[1], &new_meta.value_indices[..]);
2082        assert_eq!(
2083            schema.timestamp_column(),
2084            new_meta.schema.timestamp_column()
2085        );
2086    }
2087
2088    #[test]
2089    fn test_add_existing_column() {
2090        let schema = Arc::new(new_test_schema());
2091        let meta = TableMetaBuilder::empty()
2092            .schema(schema)
2093            .primary_key_indices(vec![0])
2094            .engine("engine")
2095            .next_column_id(3)
2096            .build()
2097            .unwrap();
2098
2099        let alter_kind = AlterKind::AddColumns {
2100            columns: vec![AddColumnRequest {
2101                column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2102                is_key: false,
2103                location: None,
2104                add_if_not_exists: false,
2105            }],
2106        };
2107
2108        let err = meta
2109            .builder_with_alter_kind("my_table", &alter_kind)
2110            .err()
2111            .unwrap();
2112        assert_eq!(StatusCode::TableColumnExists, err.status_code());
2113
2114        // Add if not exists
2115        let alter_kind = AlterKind::AddColumns {
2116            columns: vec![AddColumnRequest {
2117                column_schema: ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
2118                is_key: true,
2119                location: None,
2120                add_if_not_exists: true,
2121            }],
2122        };
2123        let new_meta = meta
2124            .builder_with_alter_kind("my_table", &alter_kind)
2125            .unwrap()
2126            .build()
2127            .unwrap();
2128        assert_eq!(
2129            meta.schema.column_schemas(),
2130            new_meta.schema.column_schemas()
2131        );
2132        assert_eq!(meta.schema.version() + 1, new_meta.schema.version());
2133    }
2134
2135    #[test]
2136    fn test_add_different_type_column() {
2137        let schema = Arc::new(new_test_schema());
2138        let meta = TableMetaBuilder::empty()
2139            .schema(schema)
2140            .primary_key_indices(vec![0])
2141            .engine("engine")
2142            .next_column_id(3)
2143            .build()
2144            .unwrap();
2145
2146        // Add if not exists, but different type.
2147        let alter_kind = AlterKind::AddColumns {
2148            columns: vec![AddColumnRequest {
2149                column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
2150                is_key: false,
2151                location: None,
2152                add_if_not_exists: true,
2153            }],
2154        };
2155        let err = meta
2156            .builder_with_alter_kind("my_table", &alter_kind)
2157            .err()
2158            .unwrap();
2159        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2160    }
2161
2162    #[test]
2163    fn test_add_invalid_column() {
2164        let schema = Arc::new(new_test_schema());
2165        let meta = TableMetaBuilder::empty()
2166            .schema(schema)
2167            .primary_key_indices(vec![0])
2168            .engine("engine")
2169            .next_column_id(3)
2170            .build()
2171            .unwrap();
2172
2173        // Not nullable and no default value.
2174        let alter_kind = AlterKind::AddColumns {
2175            columns: vec![AddColumnRequest {
2176                column_schema: ColumnSchema::new(
2177                    "weny",
2178                    ConcreteDataType::string_datatype(),
2179                    false,
2180                ),
2181                is_key: false,
2182                location: None,
2183                add_if_not_exists: false,
2184            }],
2185        };
2186
2187        let err = meta
2188            .builder_with_alter_kind("my_table", &alter_kind)
2189            .err()
2190            .unwrap();
2191        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2192    }
2193
2194    #[test]
2195    fn test_remove_unknown_column() {
2196        let schema = Arc::new(new_test_schema());
2197        let meta = TableMetaBuilder::empty()
2198            .schema(schema)
2199            .primary_key_indices(vec![0])
2200            .engine("engine")
2201            .next_column_id(3)
2202            .build()
2203            .unwrap();
2204
2205        let alter_kind = AlterKind::DropColumns {
2206            names: vec![String::from("unknown")],
2207        };
2208
2209        let err = meta
2210            .builder_with_alter_kind("my_table", &alter_kind)
2211            .err()
2212            .unwrap();
2213        assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2214    }
2215
2216    #[test]
2217    fn test_change_unknown_column_data_type() {
2218        let schema = Arc::new(new_test_schema());
2219        let meta = TableMetaBuilder::empty()
2220            .schema(schema)
2221            .primary_key_indices(vec![0])
2222            .engine("engine")
2223            .next_column_id(3)
2224            .build()
2225            .unwrap();
2226
2227        let alter_kind = AlterKind::ModifyColumnTypes {
2228            columns: vec![ModifyColumnTypeRequest {
2229                column_name: "unknown".to_string(),
2230                target_type: ConcreteDataType::string_datatype(),
2231            }],
2232        };
2233
2234        let err = meta
2235            .builder_with_alter_kind("my_table", &alter_kind)
2236            .err()
2237            .unwrap();
2238        assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
2239    }
2240
2241    #[test]
2242    fn test_remove_key_column() {
2243        let schema = Arc::new(new_test_schema());
2244        let meta = TableMetaBuilder::empty()
2245            .schema(schema)
2246            .primary_key_indices(vec![0])
2247            .engine("engine")
2248            .next_column_id(3)
2249            .build()
2250            .unwrap();
2251
2252        // Remove column in primary key.
2253        let alter_kind = AlterKind::DropColumns {
2254            names: vec![String::from("col1")],
2255        };
2256
2257        let err = meta
2258            .builder_with_alter_kind("my_table", &alter_kind)
2259            .err()
2260            .unwrap();
2261        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2262
2263        // Remove timestamp column.
2264        let alter_kind = AlterKind::DropColumns {
2265            names: vec![String::from("ts")],
2266        };
2267
2268        let err = meta
2269            .builder_with_alter_kind("my_table", &alter_kind)
2270            .err()
2271            .unwrap();
2272        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2273    }
2274
2275    #[test]
2276    fn test_remove_partition_column() {
2277        let schema = Arc::new(new_test_schema());
2278        let meta = TableMetaBuilder::empty()
2279            .schema(schema)
2280            .primary_key_indices(vec![])
2281            .partition_key_indices(vec![0])
2282            .engine("engine")
2283            .next_column_id(3)
2284            .build()
2285            .unwrap();
2286        // Remove column in primary key.
2287        let alter_kind = AlterKind::DropColumns {
2288            names: vec![String::from("col1")],
2289        };
2290
2291        let err = meta
2292            .builder_with_alter_kind("my_table", &alter_kind)
2293            .err()
2294            .unwrap();
2295        assert_matches!(err, Error::RemovePartitionColumn { .. });
2296    }
2297
2298    #[test]
2299    fn test_change_key_column_data_type() {
2300        let schema = Arc::new(new_test_schema());
2301        let meta = TableMetaBuilder::empty()
2302            .schema(schema)
2303            .primary_key_indices(vec![0])
2304            .engine("engine")
2305            .next_column_id(3)
2306            .build()
2307            .unwrap();
2308
2309        // Remove column in primary key.
2310        let alter_kind = AlterKind::ModifyColumnTypes {
2311            columns: vec![ModifyColumnTypeRequest {
2312                column_name: "col1".to_string(),
2313                target_type: ConcreteDataType::string_datatype(),
2314            }],
2315        };
2316
2317        let err = meta
2318            .builder_with_alter_kind("my_table", &alter_kind)
2319            .err()
2320            .unwrap();
2321        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2322
2323        // Remove timestamp column.
2324        let alter_kind = AlterKind::ModifyColumnTypes {
2325            columns: vec![ModifyColumnTypeRequest {
2326                column_name: "ts".to_string(),
2327                target_type: ConcreteDataType::string_datatype(),
2328            }],
2329        };
2330
2331        let err = meta
2332            .builder_with_alter_kind("my_table", &alter_kind)
2333            .err()
2334            .unwrap();
2335        assert_eq!(StatusCode::InvalidArguments, err.status_code());
2336    }
2337
2338    #[test]
2339    fn test_alloc_new_column() {
2340        let schema = Arc::new(new_test_schema());
2341        let mut meta = TableMetaBuilder::empty()
2342            .schema(schema)
2343            .primary_key_indices(vec![0])
2344            .engine("engine")
2345            .next_column_id(3)
2346            .build()
2347            .unwrap();
2348        assert_eq!(3, meta.next_column_id);
2349
2350        let column_schema = ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true);
2351        let desc = meta.alloc_new_column("test_table", &column_schema).unwrap();
2352
2353        assert_eq!(4, meta.next_column_id);
2354        assert_eq!(column_schema.name, desc.name);
2355    }
2356
2357    #[test]
2358    fn test_add_columns_with_location() {
2359        let schema = Arc::new(new_test_schema());
2360        let meta = TableMetaBuilder::empty()
2361            .schema(schema)
2362            .primary_key_indices(vec![0])
2363            // partition col: col1, col2
2364            .partition_key_indices(vec![0, 2])
2365            .engine("engine")
2366            .next_column_id(3)
2367            .build()
2368            .unwrap();
2369
2370        let new_meta = add_columns_to_meta_with_location(&meta);
2371        let names: Vec<String> = new_meta
2372            .schema
2373            .column_schemas()
2374            .iter()
2375            .map(|column_schema| column_schema.name.clone())
2376            .collect();
2377        assert_eq!(
2378            &[
2379                "my_tag_first",               // primary key column
2380                "col1",                       // partition column
2381                "ts",                         // timestamp column
2382                "yet_another_field_after_ts", // primary key column
2383                "my_field_after_ts",          // value column
2384                "col2",                       // partition column
2385            ],
2386            &names[..]
2387        );
2388        assert_eq!(&[0, 1, 3], &new_meta.primary_key_indices[..]);
2389        assert_eq!(&[2, 4, 5], &new_meta.value_indices[..]);
2390        assert_eq!(&[1, 5], &new_meta.partition_key_indices[..]);
2391    }
2392
2393    #[test]
2394    fn test_modify_column_fulltext_options() {
2395        let schema = Arc::new(new_test_schema());
2396        let meta = TableMetaBuilder::empty()
2397            .schema(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::SetIndexes {
2405            options: vec![SetIndexOption::Fulltext {
2406                column_name: "col1".to_string(),
2407                options: FulltextOptions::default(),
2408            }],
2409        };
2410        let err = meta
2411            .builder_with_alter_kind("my_table", &alter_kind)
2412            .err()
2413            .unwrap();
2414        assert_eq!(
2415            "Invalid column option, column name: col1, error: FULLTEXT index only supports string type",
2416            err.to_string()
2417        );
2418
2419        // Add a string column and make it fulltext indexed
2420        let new_meta = add_columns_to_meta_with_location(&meta);
2421        let alter_kind = AlterKind::SetIndexes {
2422            options: vec![SetIndexOption::Fulltext {
2423                column_name: "my_tag_first".to_string(),
2424                options: FulltextOptions::new_unchecked(
2425                    true,
2426                    FulltextAnalyzer::Chinese,
2427                    true,
2428                    FulltextBackend::Bloom,
2429                    1000,
2430                    0.01,
2431                ),
2432            }],
2433        };
2434        let new_meta = new_meta
2435            .builder_with_alter_kind("my_table", &alter_kind)
2436            .unwrap()
2437            .build()
2438            .unwrap();
2439        let column_schema = new_meta
2440            .schema
2441            .column_schema_by_name("my_tag_first")
2442            .unwrap();
2443        let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
2444        assert!(fulltext_options.enable);
2445        assert_eq!(
2446            datatypes::schema::FulltextAnalyzer::Chinese,
2447            fulltext_options.analyzer
2448        );
2449        assert!(fulltext_options.case_sensitive);
2450
2451        let alter_kind = AlterKind::UnsetIndexes {
2452            options: vec![UnsetIndexOption::Fulltext {
2453                column_name: "my_tag_first".to_string(),
2454            }],
2455        };
2456        let new_meta = new_meta
2457            .builder_with_alter_kind("my_table", &alter_kind)
2458            .unwrap()
2459            .build()
2460            .unwrap();
2461        let column_schema = new_meta
2462            .schema
2463            .column_schema_by_name("my_tag_first")
2464            .unwrap();
2465        let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
2466        assert!(!fulltext_options.enable);
2467    }
2468
2469    #[test]
2470    fn test_table_info_serde_compatibility() {
2471        // "serialized" is generated by the following codes before this refactor (PR 7626):
2472        //
2473        // ```Rust
2474        // serde_json::to_string(&RawTableInfo::from(TableInfo {
2475        //     ident: TableIdent {
2476        //         table_id: 1024,
2477        //         version: 1,
2478        //     },
2479        //     name: "foo".to_string(),
2480        //     desc: Some("my table".to_string()),
2481        //     catalog_name: "greptime".to_string(),
2482        //     schema_name: "public".to_string(),
2483        //     meta: TableMeta {
2484        //         schema: Arc::new(new_test_schema()),
2485        //         primary_key_indices: vec![0],
2486        //         value_indices: vec![1, 2],
2487        //         engine: "mito".to_string(),
2488        //         next_column_id: 3,
2489        //         options: TableOptions {
2490        //             ttl: Some(common_time::TimeToLive::Duration(
2491        //                 std::time::Duration::from_secs(3600),
2492        //             )),
2493        //             ..Default::default()
2494        //         },
2495        //         created_on: DateTime::<Utc>::MIN_UTC,
2496        //         updated_on: DateTime::<Utc>::MAX_UTC,
2497        //         partition_key_indices: vec![2],
2498        //         column_ids: vec![0, 1, 2],
2499        //     },
2500        //     table_type: TableType::Base,
2501        // }))
2502        // ```
2503        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"}"#;
2504
2505        let actual: TableInfo = serde_json::from_str(serialized).unwrap();
2506        let expected = TableInfo {
2507            ident: TableIdent {
2508                table_id: 1024,
2509                version: 1,
2510            },
2511            name: "foo".to_string(),
2512            desc: Some("my table".to_string()),
2513            catalog_name: "greptime".to_string(),
2514            schema_name: "public".to_string(),
2515            meta: TableMeta {
2516                schema: Arc::new(new_test_schema()),
2517                primary_key_indices: vec![0],
2518                value_indices: vec![1, 2],
2519                engine: "mito".to_string(),
2520                next_column_id: 3,
2521                options: TableOptions {
2522                    ttl: Some(common_time::TimeToLive::Duration(
2523                        std::time::Duration::from_secs(3600),
2524                    )),
2525                    ..Default::default()
2526                },
2527                created_on: DateTime::<Utc>::MIN_UTC,
2528                updated_on: DateTime::<Utc>::MAX_UTC,
2529                partition_key_indices: vec![2],
2530                column_ids: vec![0, 1, 2],
2531            },
2532            table_type: TableType::Base,
2533        };
2534        assert_eq!(actual, expected);
2535    }
2536}