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