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