Skip to main content

store_api/
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
15//! Metadata of region and column.
16//!
17//! This mod has its own error type [MetadataError] for validation and codec exceptions.
18
19use std::any::Any;
20use std::collections::{HashMap, HashSet};
21use std::sync::Arc;
22use std::{fmt, mem};
23
24use api::v1::SemanticType;
25use api::v1::column_def::try_as_column_schema;
26use api::v1::region::RegionColumnDef;
27use common_base::hash::partition_expr_version;
28use common_error::ext::ErrorExt;
29use common_error::status_code::StatusCode;
30use common_macro::stack_trace_debug;
31use datatypes::arrow;
32use datatypes::arrow::datatypes::FieldRef;
33use datatypes::extension::json::json2_metadata_with_updated_settings;
34use datatypes::schema::{ColumnSchema, FulltextOptions, Schema, SchemaRef};
35use datatypes::types::TimestampType;
36use itertools::Itertools;
37use serde::de::Error;
38use serde::{Deserialize, Deserializer, Serialize};
39use snafu::{Location, OptionExt, ResultExt, Snafu, ensure};
40
41use crate::codec::PrimaryKeyEncoding;
42use crate::region_request::{
43    AddColumn, AddColumnLocation, AlterKind, ModifyColumnType, SetIndexOption, UnsetIndexOption,
44};
45use crate::storage::consts::is_internal_column;
46use crate::storage::{ColumnId, RegionId};
47
48pub type Result<T> = std::result::Result<T, MetadataError>;
49
50/// Metadata of a column.
51#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
52pub struct ColumnMetadata {
53    /// Schema of this column. Is the same as `column_schema` in [SchemaRef].
54    pub column_schema: ColumnSchema,
55    /// Semantic type of this column (e.g. tag or timestamp).
56    pub semantic_type: SemanticType,
57    /// Immutable and unique id of a region.
58    pub column_id: ColumnId,
59}
60
61impl fmt::Debug for ColumnMetadata {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(
64            f,
65            "[{:?} {:?} {:?}]",
66            self.column_schema, self.semantic_type, self.column_id,
67        )
68    }
69}
70
71impl ColumnMetadata {
72    /// Construct `Self` from protobuf struct [RegionColumnDef]
73    pub fn try_from_column_def(column_def: RegionColumnDef) -> Result<Self> {
74        let column_id = column_def.column_id;
75        let column_def = column_def
76            .column_def
77            .context(InvalidRawRegionRequestSnafu {
78                err: "column_def is absent",
79            })?;
80        let semantic_type = column_def.semantic_type();
81        let column_schema = try_as_column_schema(&column_def).context(ConvertColumnSchemaSnafu)?;
82
83        Ok(Self {
84            column_schema,
85            semantic_type,
86            column_id,
87        })
88    }
89
90    /// Encodes a vector of `ColumnMetadata` into a JSON byte vector.
91    pub fn encode_list(columns: &[Self]) -> serde_json::Result<Vec<u8>> {
92        serde_json::to_vec(columns)
93    }
94
95    /// Decodes a JSON byte vector into a vector of `ColumnMetadata`.
96    pub fn decode_list(bytes: &[u8]) -> serde_json::Result<Vec<Self>> {
97        serde_json::from_slice(bytes)
98    }
99
100    pub fn is_same_datatype(&self, other: &Self) -> bool {
101        self.column_schema.data_type == other.column_schema.data_type
102    }
103
104    /// Returns the estimated memory footprint of this metadata.
105    pub fn estimated_size(&self) -> usize {
106        mem::size_of_val(self) - mem::size_of_val(&self.column_schema)
107            + self.column_schema.estimated_size()
108    }
109}
110
111#[cfg_attr(doc, aquamarine::aquamarine)]
112/// General static metadata of a region.
113///
114/// This struct implements [Serialize] and [Deserialize] traits.
115/// To build a [RegionMetadata] object, use [RegionMetadataBuilder].
116///
117/// ```mermaid
118/// class RegionMetadata {
119///     +RegionId region_id
120///     +SchemaRef schema
121///     +Vec&lt;ColumnMetadata&gt; column_metadatas
122///     +Vec&lt;ColumnId&gt; primary_key
123/// }
124/// class Schema
125/// class ColumnMetadata {
126///     +ColumnSchema column_schema
127///     +SemanticTyle semantic_type
128///     +ColumnId column_id
129/// }
130/// class SemanticType
131/// RegionMetadata o-- Schema
132/// RegionMetadata o-- ColumnMetadata
133/// ColumnMetadata o-- SemanticType
134/// ```
135#[derive(Clone, PartialEq, Eq, Serialize)]
136pub struct RegionMetadata {
137    /// Latest schema constructed from [column_metadatas](RegionMetadata::column_metadatas).
138    #[serde(skip)]
139    pub schema: SchemaRef,
140
141    // We don't pub `time_index` and `id_to_index` and always construct them via [SkippedFields]
142    // so we can assumes they are valid.
143    /// Id of the time index column.
144    #[serde(skip)]
145    time_index: ColumnId,
146    /// Map column id to column's index in [column_metadatas](RegionMetadata::column_metadatas).
147    #[serde(skip)]
148    id_to_index: HashMap<ColumnId, usize>,
149
150    /// Columns in the region. Has the same order as columns
151    /// in [schema](RegionMetadata::schema).
152    pub column_metadatas: Vec<ColumnMetadata>,
153    /// Maintains an ordered list of primary keys
154    pub primary_key: Vec<ColumnId>,
155
156    /// Immutable and unique id of a region.
157    pub region_id: RegionId,
158    /// Current version of the region schema.
159    ///
160    /// The version starts from 0. Altering the schema bumps the version.
161    pub schema_version: u64,
162
163    /// Primary key encoding mode.
164    pub primary_key_encoding: PrimaryKeyEncoding,
165
166    /// Partition expression serialized as a JSON string.
167    /// Compatibility behavior:
168    /// - None: no partition expr was ever set in the manifest (legacy regions).
169    /// - Some(""): an explicit “single-region/no-partition” designation. This is distinct from None and should be preserved as-is.
170    pub partition_expr: Option<String>,
171    #[serde(skip)]
172    pub partition_expr_version: u64,
173}
174
175impl fmt::Debug for RegionMetadata {
176    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177        f.debug_struct("RegionMetadata")
178            .field("column_metadatas", &self.column_metadatas)
179            .field("time_index", &self.time_index)
180            .field("primary_key", &self.primary_key)
181            .field("region_id", &self.region_id)
182            .field("schema_version", &self.schema_version)
183            .field("partition_expr", &self.partition_expr)
184            .finish()
185    }
186}
187
188pub type RegionMetadataRef = Arc<RegionMetadata>;
189
190impl<'de> Deserialize<'de> for RegionMetadata {
191    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
192    where
193        D: Deserializer<'de>,
194    {
195        // helper internal struct for deserialization
196        #[derive(Deserialize)]
197        struct RegionMetadataWithoutSchema {
198            column_metadatas: Vec<ColumnMetadata>,
199            primary_key: Vec<ColumnId>,
200            region_id: RegionId,
201            schema_version: u64,
202            #[serde(default)]
203            primary_key_encoding: PrimaryKeyEncoding,
204            #[serde(default)]
205            partition_expr: Option<String>,
206        }
207
208        let without_schema = RegionMetadataWithoutSchema::deserialize(deserializer)?;
209        let skipped =
210            SkippedFields::new(&without_schema.column_metadatas).map_err(D::Error::custom)?;
211
212        let partition_expr_version =
213            partition_expr_version(without_schema.partition_expr.as_deref());
214
215        Ok(Self {
216            schema: skipped.schema,
217            time_index: skipped.time_index,
218            id_to_index: skipped.id_to_index,
219            column_metadatas: without_schema.column_metadatas,
220            primary_key: without_schema.primary_key,
221            region_id: without_schema.region_id,
222            schema_version: without_schema.schema_version,
223            primary_key_encoding: without_schema.primary_key_encoding,
224            partition_expr: without_schema.partition_expr,
225            partition_expr_version,
226        })
227    }
228}
229
230impl RegionMetadata {
231    /// Decode the metadata from a JSON str.
232    pub fn from_json(s: &str) -> Result<Self> {
233        serde_json::from_str(s).context(SerdeJsonSnafu)
234    }
235
236    /// Returns the estimated memory footprint of this metadata.
237    pub fn estimated_size(&self) -> usize {
238        mem::size_of_val(self)
239            + mem::size_of::<ColumnMetadata>() * self.column_metadatas.capacity()
240            + self
241                .column_metadatas
242                .iter()
243                .map(|column| column.estimated_size() - mem::size_of::<ColumnMetadata>())
244                .sum::<usize>()
245            + mem::size_of::<ColumnId>() * self.primary_key.capacity()
246            + mem::size_of::<(ColumnId, usize)>() * self.id_to_index.capacity()
247            + self.schema.estimated_size()
248            + self
249                .partition_expr
250                .as_ref()
251                .map(|expr| expr.capacity())
252                .unwrap_or_default()
253    }
254
255    /// Encode the metadata to a JSON string.
256    pub fn to_json(&self) -> Result<String> {
257        serde_json::to_string(&self).context(SerdeJsonSnafu)
258    }
259
260    pub fn set_partition_expr(&mut self, expr: Option<String>) {
261        self.partition_expr_version = partition_expr_version(expr.as_deref());
262        self.partition_expr = expr;
263    }
264
265    /// Find column by id.
266    pub fn column_by_id(&self, column_id: ColumnId) -> Option<&ColumnMetadata> {
267        self.id_to_index
268            .get(&column_id)
269            .map(|index| &self.column_metadatas[*index])
270    }
271
272    /// Find column index by id.
273    pub fn column_index_by_id(&self, column_id: ColumnId) -> Option<usize> {
274        self.id_to_index.get(&column_id).copied()
275    }
276
277    /// Find column index by name.
278    pub fn column_index_by_name(&self, column_name: &str) -> Option<usize> {
279        self.column_metadatas
280            .iter()
281            .position(|col| col.column_schema.name == column_name)
282    }
283
284    /// Returns the time index column
285    ///
286    /// # Panics
287    /// Panics if the time index column id is invalid.
288    pub fn time_index_column(&self) -> &ColumnMetadata {
289        let index = self.id_to_index[&self.time_index];
290        &self.column_metadatas[index]
291    }
292
293    /// Returns timestamp type of time index column
294    ///
295    /// # Panics
296    /// Panics if the time index column id is invalid.
297    pub fn time_index_type(&self) -> TimestampType {
298        let index = self.id_to_index[&self.time_index];
299        self.column_metadatas[index]
300            .column_schema
301            .data_type
302            .as_timestamp()
303            .unwrap()
304    }
305
306    /// Returns the position of the time index.
307    pub fn time_index_column_pos(&self) -> usize {
308        self.id_to_index[&self.time_index]
309    }
310
311    /// Returns the arrow field of the time index column.
312    pub fn time_index_field(&self) -> FieldRef {
313        let index = self.id_to_index[&self.time_index];
314        self.schema.arrow_schema().fields[index].clone()
315    }
316
317    /// Finds a column by name.
318    pub fn column_by_name(&self, name: &str) -> Option<&ColumnMetadata> {
319        self.schema
320            .column_index_by_name(name)
321            .map(|index| &self.column_metadatas[index])
322    }
323
324    /// Returns all primary key columns.
325    pub fn primary_key_columns(&self) -> impl Iterator<Item = &ColumnMetadata> {
326        // safety: RegionMetadata::validate ensures every primary key exists.
327        self.primary_key
328            .iter()
329            .map(|id| self.column_by_id(*id).unwrap())
330    }
331
332    /// Returns all field columns before projection.
333    ///
334    /// **Use with caution**. On read path where might have projection, this method
335    /// can return columns that not present in data batch.
336    pub fn field_columns(&self) -> impl Iterator<Item = &ColumnMetadata> {
337        self.column_metadatas
338            .iter()
339            .filter(|column| column.semantic_type == SemanticType::Field)
340    }
341
342    /// Returns a column's index in primary key if it is a primary key column.
343    ///
344    /// This does a linear search.
345    pub fn primary_key_index(&self, column_id: ColumnId) -> Option<usize> {
346        self.primary_key.iter().position(|id| *id == column_id)
347    }
348
349    /// Project the metadata to a new one using specified column ids.
350    ///
351    /// [RegionId] and schema version are preserved.
352    pub fn project(&self, projection: &[ColumnId]) -> Result<RegionMetadata> {
353        // check time index
354        ensure!(
355            projection.contains(&self.time_index),
356            TimeIndexNotFoundSnafu
357        );
358
359        // prepare new indices
360        let indices_to_preserve = projection
361            .iter()
362            .map(|id| {
363                self.column_index_by_id(*id)
364                    .with_context(|| InvalidRegionRequestSnafu {
365                        region_id: self.region_id,
366                        err: format!("column id {} not found", id),
367                    })
368            })
369            .collect::<Result<Vec<_>>>()?;
370
371        // project schema
372        let projected_schema =
373            self.schema
374                .try_project(&indices_to_preserve)
375                .with_context(|_| SchemaProjectSnafu {
376                    origin_schema: self.schema.clone(),
377                    projection: projection.to_vec(),
378                })?;
379
380        // project columns, generate projected primary key and new id_to_index
381        let mut projected_column_metadatas = Vec::with_capacity(indices_to_preserve.len());
382        let mut projected_primary_key = vec![];
383        let mut projected_id_to_index = HashMap::with_capacity(indices_to_preserve.len());
384        for index in indices_to_preserve {
385            let col = self.column_metadatas[index].clone();
386            if col.semantic_type == SemanticType::Tag {
387                projected_primary_key.push(col.column_id);
388            }
389            projected_id_to_index.insert(col.column_id, projected_column_metadatas.len());
390            projected_column_metadatas.push(col);
391        }
392
393        Ok(RegionMetadata {
394            schema: Arc::new(projected_schema),
395            time_index: self.time_index,
396            id_to_index: projected_id_to_index,
397            column_metadatas: projected_column_metadatas,
398            primary_key: projected_primary_key,
399            region_id: self.region_id,
400            schema_version: self.schema_version,
401            primary_key_encoding: self.primary_key_encoding,
402            partition_expr: self.partition_expr.clone(),
403            partition_expr_version: partition_expr_version(self.partition_expr.as_deref()),
404        })
405    }
406
407    /// Gets the column ids to be indexed by inverted index.
408    pub fn inverted_indexed_column_ids<'a>(
409        &self,
410        ignore_column_ids: impl Iterator<Item = &'a ColumnId>,
411    ) -> HashSet<ColumnId> {
412        let mut inverted_index = self
413            .column_metadatas
414            .iter()
415            .filter(|column| column.column_schema.is_inverted_indexed())
416            .map(|column| column.column_id)
417            .collect::<HashSet<_>>();
418
419        for ignored in ignore_column_ids {
420            inverted_index.remove(ignored);
421        }
422
423        inverted_index
424    }
425
426    /// Checks whether the metadata is valid.
427    fn validate(&self) -> Result<()> {
428        // Id to name.
429        let mut id_names = HashMap::with_capacity(self.column_metadatas.len());
430        for col in &self.column_metadatas {
431            // Validate each column.
432            Self::validate_column_metadata(col)?;
433
434            // Check whether column id is duplicated. We already check column name
435            // is unique in `Schema` so we only check column id here.
436            ensure!(
437                !id_names.contains_key(&col.column_id),
438                InvalidMetaSnafu {
439                    reason: format!(
440                        "column {} and {} have the same column id {}",
441                        id_names[&col.column_id], col.column_schema.name, col.column_id,
442                    ),
443                }
444            );
445            id_names.insert(col.column_id, &col.column_schema.name);
446        }
447
448        // Checks there is only one time index.
449        let time_indexes = self
450            .column_metadatas
451            .iter()
452            .filter(|col| col.semantic_type == SemanticType::Timestamp)
453            .collect::<Vec<_>>();
454        ensure!(
455            time_indexes.len() == 1,
456            InvalidMetaSnafu {
457                reason: format!(
458                    "expect only one time index, found {}: {}",
459                    time_indexes.len(),
460                    time_indexes
461                        .iter()
462                        .map(|c| &c.column_schema.name)
463                        .join(", ")
464                ),
465            }
466        );
467
468        // Checks the time index column is not nullable.
469        ensure!(
470            !self.time_index_column().column_schema.is_nullable(),
471            InvalidMetaSnafu {
472                reason: format!(
473                    "time index column {} must be NOT NULL",
474                    self.time_index_column().column_schema.name
475                ),
476            }
477        );
478
479        if !self.primary_key.is_empty() {
480            let mut pk_ids = HashSet::with_capacity(self.primary_key.len());
481            // Checks column ids in the primary key is valid.
482            for column_id in &self.primary_key {
483                // Checks whether the column id exists.
484                ensure!(
485                    id_names.contains_key(column_id),
486                    InvalidMetaSnafu {
487                        reason: format!("unknown column id {}", column_id),
488                    }
489                );
490
491                // Safety: Column with specific id must exist.
492                let column = self.column_by_id(*column_id).unwrap();
493                // Checks duplicate.
494                ensure!(
495                    !pk_ids.contains(&column_id),
496                    InvalidMetaSnafu {
497                        reason: format!(
498                            "duplicate column {} in primary key",
499                            column.column_schema.name
500                        ),
501                    }
502                );
503
504                // Checks this is not a time index column.
505                ensure!(
506                    *column_id != self.time_index,
507                    InvalidMetaSnafu {
508                        reason: format!(
509                            "column {} is already a time index column",
510                            column.column_schema.name,
511                        ),
512                    }
513                );
514
515                // Checks semantic type.
516                ensure!(
517                    column.semantic_type == SemanticType::Tag,
518                    InvalidMetaSnafu {
519                        reason: format!(
520                            "semantic type of column {} should be Tag, not {:?}",
521                            column.column_schema.name, column.semantic_type
522                        ),
523                    }
524                );
525
526                pk_ids.insert(column_id);
527            }
528        }
529
530        // Checks tag semantic type.
531        let num_tag = self
532            .column_metadatas
533            .iter()
534            .filter(|col| col.semantic_type == SemanticType::Tag)
535            .count();
536        ensure!(
537            num_tag == self.primary_key.len(),
538            InvalidMetaSnafu {
539                reason: format!(
540                    "number of primary key columns {} not equal to tag columns {}",
541                    self.primary_key.len(),
542                    num_tag
543                ),
544            }
545        );
546
547        Ok(())
548    }
549
550    /// Checks whether it is a valid column.
551    fn validate_column_metadata(column_metadata: &ColumnMetadata) -> Result<()> {
552        if column_metadata.semantic_type == SemanticType::Timestamp {
553            ensure!(
554                column_metadata.column_schema.data_type.is_timestamp(),
555                InvalidMetaSnafu {
556                    reason: format!(
557                        "column `{}` is not timestamp type",
558                        column_metadata.column_schema.name
559                    ),
560                }
561            );
562        }
563
564        ensure!(
565            !is_internal_column(&column_metadata.column_schema.name),
566            InvalidMetaSnafu {
567                reason: format!(
568                    "{} is internal column name that can not be used",
569                    column_metadata.column_schema.name
570                ),
571            }
572        );
573
574        Ok(())
575    }
576}
577
578/// Builder to build [RegionMetadata].
579pub struct RegionMetadataBuilder {
580    region_id: RegionId,
581    column_metadatas: Vec<ColumnMetadata>,
582    primary_key: Vec<ColumnId>,
583    schema_version: u64,
584    primary_key_encoding: PrimaryKeyEncoding,
585    partition_expr: Option<String>,
586}
587
588impl RegionMetadataBuilder {
589    /// Returns a new builder.
590    pub fn new(id: RegionId) -> Self {
591        Self {
592            region_id: id,
593            column_metadatas: vec![],
594            primary_key: vec![],
595            schema_version: 0,
596            primary_key_encoding: PrimaryKeyEncoding::Dense,
597            partition_expr: None,
598        }
599    }
600
601    /// Creates a builder from existing [RegionMetadata].
602    pub fn from_existing(existing: RegionMetadata) -> Self {
603        Self {
604            column_metadatas: existing.column_metadatas,
605            primary_key: existing.primary_key,
606            region_id: existing.region_id,
607            schema_version: existing.schema_version,
608            primary_key_encoding: existing.primary_key_encoding,
609            partition_expr: existing.partition_expr,
610        }
611    }
612
613    /// Sets the primary key encoding mode.
614    pub fn primary_key_encoding(&mut self, encoding: PrimaryKeyEncoding) -> &mut Self {
615        self.primary_key_encoding = encoding;
616        self
617    }
618
619    /// Sets the partition expression in JSON string form.
620    pub fn partition_expr_json(&mut self, expr_json: Option<String>) -> &mut Self {
621        self.partition_expr = expr_json;
622        self
623    }
624
625    /// Pushes a new column metadata to this region's metadata.
626    pub fn push_column_metadata(&mut self, column_metadata: ColumnMetadata) -> &mut Self {
627        self.column_metadatas.push(column_metadata);
628        self
629    }
630
631    /// Sets the primary key of the region.
632    pub fn primary_key(&mut self, key: Vec<ColumnId>) -> &mut Self {
633        self.primary_key = key;
634        self
635    }
636
637    /// Increases the schema version by 1.
638    pub fn bump_version(&mut self) -> &mut Self {
639        self.schema_version += 1;
640        self
641    }
642
643    /// Applies the alter `kind` to the builder.
644    ///
645    /// The `kind` should be valid.
646    pub fn alter(&mut self, kind: AlterKind) -> Result<&mut Self> {
647        match kind {
648            AlterKind::AddColumns { columns } => self.add_columns(columns)?,
649            AlterKind::DropColumns { names } => self.drop_columns(&names),
650            AlterKind::ModifyColumnTypes { columns } => self.modify_column_types(columns)?,
651            AlterKind::SetJsonSettings {
652                column_name,
653                settings,
654            } => self.set_json_settings(column_name, settings)?,
655            AlterKind::SetIndexes { options } => self.set_indexes(options)?,
656            AlterKind::UnsetIndexes { options } => self.unset_indexes(options)?,
657            AlterKind::SetRegionOptions { options: _ } => {
658                // nothing to be done with RegionMetadata
659            }
660            AlterKind::UnsetRegionOptions { keys: _ } => {
661                // nothing to be done with RegionMetadata
662            }
663            AlterKind::DropDefaults { names } => {
664                self.drop_defaults(names)?;
665            }
666            AlterKind::SetDefaults { columns } => self.set_defaults(&columns)?,
667            AlterKind::SyncColumns { column_metadatas } => {
668                self.primary_key = column_metadatas
669                    .iter()
670                    .filter_map(|column_metadata| {
671                        if column_metadata.semantic_type == SemanticType::Tag {
672                            Some(column_metadata.column_id)
673                        } else {
674                            None
675                        }
676                    })
677                    .collect::<Vec<_>>();
678                self.column_metadatas = column_metadatas;
679            }
680        }
681        Ok(self)
682    }
683
684    /// Consumes the builder and build a [RegionMetadata].
685    pub fn build(self) -> Result<RegionMetadata> {
686        self.build_with_options(true)
687    }
688
689    /// Builds metadata without running validation.
690    ///
691    /// Intended for file/external engines that should accept arbitrary schemas
692    /// coming from files.
693    pub fn build_without_validation(self) -> Result<RegionMetadata> {
694        self.build_with_options(false)
695    }
696
697    fn build_with_options(self, validate: bool) -> Result<RegionMetadata> {
698        let skipped = SkippedFields::new(&self.column_metadatas)?;
699
700        let partition_expr_version = partition_expr_version(self.partition_expr.as_deref());
701        let meta = RegionMetadata {
702            schema: skipped.schema,
703            time_index: skipped.time_index,
704            id_to_index: skipped.id_to_index,
705            column_metadatas: self.column_metadatas,
706            primary_key: self.primary_key,
707            region_id: self.region_id,
708            schema_version: self.schema_version,
709            primary_key_encoding: self.primary_key_encoding,
710            partition_expr: self.partition_expr,
711            partition_expr_version,
712        };
713
714        if validate {
715            meta.validate()?;
716        }
717
718        Ok(meta)
719    }
720
721    /// Adds columns to the metadata if not exist.
722    fn add_columns(&mut self, columns: Vec<AddColumn>) -> Result<()> {
723        let mut names: HashSet<_> = self
724            .column_metadatas
725            .iter()
726            .map(|col| col.column_schema.name.clone())
727            .collect();
728
729        for add_column in columns {
730            if names.contains(&add_column.column_metadata.column_schema.name) {
731                // Column already exists.
732                continue;
733            }
734
735            let column_id = add_column.column_metadata.column_id;
736            let semantic_type = add_column.column_metadata.semantic_type;
737            let column_name = add_column.column_metadata.column_schema.name.clone();
738            match add_column.location {
739                None => {
740                    self.column_metadatas.push(add_column.column_metadata);
741                }
742                Some(AddColumnLocation::First) => {
743                    self.column_metadatas.insert(0, add_column.column_metadata);
744                }
745                Some(AddColumnLocation::After { column_name }) => {
746                    let pos = self
747                        .column_metadatas
748                        .iter()
749                        .position(|col| col.column_schema.name == column_name)
750                        .context(InvalidRegionRequestSnafu {
751                            region_id: self.region_id,
752                            err: format!(
753                                "column {} not found, failed to add column {} after it",
754                                column_name, add_column.column_metadata.column_schema.name
755                            ),
756                        })?;
757                    // Insert after pos.
758                    self.column_metadatas
759                        .insert(pos + 1, add_column.column_metadata);
760                }
761            }
762            names.insert(column_name);
763            if semantic_type == SemanticType::Tag {
764                // For a new tag, we extend the primary key.
765                self.primary_key.push(column_id);
766            }
767        }
768
769        Ok(())
770    }
771
772    /// Drops columns from the metadata if exist.
773    fn drop_columns(&mut self, names: &[String]) {
774        let name_set: HashSet<_> = names.iter().collect();
775        self.column_metadatas
776            .retain(|col| !name_set.contains(&col.column_schema.name));
777    }
778
779    /// Changes columns type to the metadata if exist.
780    fn modify_column_types(&mut self, columns: Vec<ModifyColumnType>) -> Result<()> {
781        let mut change_type_map: HashMap<_, _> = columns
782            .into_iter()
783            .map(
784                |ModifyColumnType {
785                     column_name,
786                     target_type,
787                 }| (column_name, target_type),
788            )
789            .collect();
790
791        for column_meta in self.column_metadatas.iter_mut() {
792            if let Some(target_type) = change_type_map.remove(&column_meta.column_schema.name) {
793                column_meta.column_schema.data_type = target_type.clone();
794                // also cast default value to target_type if default value exist
795                let new_default =
796                    if let Some(default_value) = column_meta.column_schema.default_constraint() {
797                        Some(
798                            default_value
799                                .cast_to_datatype(&target_type)
800                                .with_context(|_| CastDefaultValueSnafu {
801                                    reason: format!(
802                                        "Failed to cast default value from {:?} to type {:?}",
803                                        default_value, target_type
804                                    ),
805                                })?,
806                        )
807                    } else {
808                        None
809                    };
810                column_meta.column_schema = column_meta
811                    .column_schema
812                    .clone()
813                    .with_default_constraint(new_default.clone())
814                    .with_context(|_| CastDefaultValueSnafu {
815                        reason: format!("Failed to set new default: {:?}", new_default),
816                    })?;
817            }
818        }
819
820        Ok(())
821    }
822
823    fn set_json_settings(
824        &mut self,
825        col_name: String,
826        settings: datatypes::json::JsonSettings,
827    ) -> Result<()> {
828        let Some(col_meta) = self
829            .column_metadatas
830            .iter_mut()
831            .find(|col| col.column_schema.name == col_name)
832        else {
833            return InvalidRegionRequestSnafu {
834                region_id: self.region_id,
835                err: format!("column {col_name} not found"),
836            }
837            .fail();
838        };
839
840        let old_metadata = col_meta.column_schema.metadata();
841        let new_metadata =
842            json2_metadata_with_updated_settings(old_metadata, settings).map_err(|err| {
843                InvalidRegionRequestSnafu {
844                    region_id: self.region_id,
845                    err: err.to_string(),
846                }
847                .build()
848            })?;
849
850        *col_meta.column_schema.mut_metadata() = new_metadata;
851        Ok(())
852    }
853
854    fn set_indexes(&mut self, options: Vec<SetIndexOption>) -> Result<()> {
855        let mut set_index_map: HashMap<_, Vec<_>> = HashMap::new();
856        for option in &options {
857            set_index_map
858                .entry(option.column_name())
859                .or_default()
860                .push(option);
861        }
862
863        for column_metadata in self.column_metadatas.iter_mut() {
864            if let Some(options) = set_index_map.remove(&column_metadata.column_schema.name) {
865                for option in options {
866                    Self::set_index(column_metadata, option)?;
867                }
868            }
869        }
870
871        Ok(())
872    }
873
874    fn unset_indexes(&mut self, options: Vec<UnsetIndexOption>) -> Result<()> {
875        let mut unset_index_map: HashMap<_, Vec<_>> = HashMap::new();
876        for option in &options {
877            unset_index_map
878                .entry(option.column_name())
879                .or_default()
880                .push(option);
881        }
882
883        for column_metadata in self.column_metadatas.iter_mut() {
884            if let Some(options) = unset_index_map.remove(&column_metadata.column_schema.name) {
885                for option in options {
886                    Self::unset_index(column_metadata, option)?;
887                }
888            }
889        }
890
891        Ok(())
892    }
893
894    fn set_index(column_metadata: &mut ColumnMetadata, options: &SetIndexOption) -> Result<()> {
895        match options {
896            SetIndexOption::Fulltext {
897                column_name,
898                options,
899            } => {
900                ensure!(
901                    column_metadata.column_schema.data_type.is_string(),
902                    InvalidColumnOptionSnafu {
903                        column_name,
904                        msg: "FULLTEXT index only supports string type".to_string(),
905                    }
906                );
907                let current_fulltext_options = column_metadata
908                    .column_schema
909                    .fulltext_options()
910                    .with_context(|_| GetFulltextOptionsSnafu {
911                        column_name: column_name.clone(),
912                    })?;
913                set_column_fulltext_options(
914                    column_metadata,
915                    column_name,
916                    options,
917                    current_fulltext_options,
918                )?;
919            }
920            SetIndexOption::Inverted { .. } => {
921                column_metadata.column_schema.set_inverted_index(true)
922            }
923            SetIndexOption::Skipping {
924                column_name,
925                options,
926            } => {
927                column_metadata
928                    .column_schema
929                    .set_skipping_options(options)
930                    .context(UnsetSkippingIndexOptionsSnafu { column_name })?;
931            }
932        }
933
934        Ok(())
935    }
936
937    fn unset_index(column_metadata: &mut ColumnMetadata, options: &UnsetIndexOption) -> Result<()> {
938        match options {
939            UnsetIndexOption::Fulltext { column_name } => {
940                ensure!(
941                    column_metadata.column_schema.data_type.is_string(),
942                    InvalidColumnOptionSnafu {
943                        column_name,
944                        msg: "FULLTEXT index only supports string type".to_string(),
945                    }
946                );
947
948                let current_fulltext_options = column_metadata
949                    .column_schema
950                    .fulltext_options()
951                    .with_context(|_| GetFulltextOptionsSnafu {
952                        column_name: column_name.clone(),
953                    })?;
954
955                unset_column_fulltext_options(
956                    column_metadata,
957                    column_name,
958                    current_fulltext_options,
959                )?;
960            }
961            UnsetIndexOption::Inverted { .. } => {
962                column_metadata.column_schema.set_inverted_index(false)
963            }
964            UnsetIndexOption::Skipping { column_name } => {
965                column_metadata
966                    .column_schema
967                    .unset_skipping_options()
968                    .context(UnsetSkippingIndexOptionsSnafu { column_name })?;
969            }
970        }
971
972        Ok(())
973    }
974
975    fn drop_defaults(&mut self, column_names: Vec<String>) -> Result<()> {
976        for name in column_names.iter() {
977            let meta = self
978                .column_metadatas
979                .iter_mut()
980                .find(|col| col.column_schema.name == *name);
981            if let Some(meta) = meta {
982                if !meta.column_schema.is_nullable() {
983                    return InvalidRegionRequestSnafu {
984                        region_id: self.region_id,
985                        err: format!(
986                            "column {name} is not nullable and `default` cannot be dropped",
987                        ),
988                    }
989                    .fail();
990                }
991                meta.column_schema = meta
992                    .column_schema
993                    .clone()
994                    .with_default_constraint(None)
995                    .with_context(|_| CastDefaultValueSnafu {
996                        reason: format!("Failed to drop default : {name:?}"),
997                    })?;
998            } else {
999                return InvalidRegionRequestSnafu {
1000                    region_id: self.region_id,
1001                    err: format!("column {name} not found",),
1002                }
1003                .fail();
1004            }
1005        }
1006        Ok(())
1007    }
1008
1009    fn set_defaults(&mut self, set_defaults: &[crate::region_request::SetDefault]) -> Result<()> {
1010        for set_default in set_defaults.iter() {
1011            let meta = self
1012                .column_metadatas
1013                .iter_mut()
1014                .find(|col| col.column_schema.name == set_default.name);
1015            if let Some(meta) = meta {
1016                let default_constraint = common_sql::convert::deserialize_default_constraint(
1017                    set_default.default_constraint.as_slice(),
1018                    &meta.column_schema.name,
1019                    &meta.column_schema.data_type,
1020                )
1021                .context(SqlCommonSnafu)?;
1022
1023                meta.column_schema = meta
1024                    .column_schema
1025                    .clone()
1026                    .with_default_constraint(default_constraint)
1027                    .with_context(|_| CastDefaultValueSnafu {
1028                        reason: format!("Failed to set default : {set_default:?}"),
1029                    })?;
1030            } else {
1031                return InvalidRegionRequestSnafu {
1032                    region_id: self.region_id,
1033                    err: format!("column {} not found", set_default.name),
1034                }
1035                .fail();
1036            }
1037        }
1038        Ok(())
1039    }
1040}
1041
1042/// Fields skipped in serialization.
1043struct SkippedFields {
1044    /// Last schema.
1045    schema: SchemaRef,
1046    /// Id of the time index column.
1047    time_index: ColumnId,
1048    /// Map column id to column's index in [column_metadatas](RegionMetadata::column_metadatas).
1049    id_to_index: HashMap<ColumnId, usize>,
1050}
1051
1052impl SkippedFields {
1053    /// Constructs skipped fields from `column_metadatas`.
1054    fn new(column_metadatas: &[ColumnMetadata]) -> Result<SkippedFields> {
1055        let column_schemas = column_metadatas
1056            .iter()
1057            .map(|column_metadata| column_metadata.column_schema.clone())
1058            .collect();
1059        let schema = Arc::new(Schema::try_new(column_schemas).context(InvalidSchemaSnafu)?);
1060        let time_index = column_metadatas
1061            .iter()
1062            .find_map(|col| {
1063                if col.semantic_type == SemanticType::Timestamp {
1064                    Some(col.column_id)
1065                } else {
1066                    None
1067                }
1068            })
1069            .context(InvalidMetaSnafu {
1070                reason: "time index not found",
1071            })?;
1072        let id_to_index = column_metadatas
1073            .iter()
1074            .enumerate()
1075            .map(|(idx, col)| (col.column_id, idx))
1076            .collect();
1077
1078        Ok(SkippedFields {
1079            schema,
1080            time_index,
1081            id_to_index,
1082        })
1083    }
1084}
1085
1086#[derive(Snafu)]
1087#[snafu(visibility(pub))]
1088#[stack_trace_debug]
1089pub enum MetadataError {
1090    #[snafu(display("Invalid schema"))]
1091    InvalidSchema {
1092        source: datatypes::error::Error,
1093        #[snafu(implicit)]
1094        location: Location,
1095    },
1096
1097    #[snafu(display("Invalid metadata, {}", reason))]
1098    InvalidMeta {
1099        reason: String,
1100        #[snafu(implicit)]
1101        location: Location,
1102    },
1103
1104    #[snafu(display("Failed to ser/de json object"))]
1105    SerdeJson {
1106        #[snafu(implicit)]
1107        location: Location,
1108        #[snafu(source)]
1109        error: serde_json::Error,
1110    },
1111
1112    #[snafu(display("Invalid raw region request, err: {}", err))]
1113    InvalidRawRegionRequest {
1114        err: String,
1115        #[snafu(implicit)]
1116        location: Location,
1117    },
1118
1119    #[snafu(display("Invalid region request, region_id: {}, err: {}", region_id, err))]
1120    InvalidRegionRequest {
1121        region_id: RegionId,
1122        err: String,
1123        #[snafu(implicit)]
1124        location: Location,
1125    },
1126
1127    #[snafu(display("Unexpected schema error during project"))]
1128    SchemaProject {
1129        origin_schema: SchemaRef,
1130        projection: Vec<ColumnId>,
1131        #[snafu(implicit)]
1132        location: Location,
1133        source: datatypes::Error,
1134    },
1135
1136    #[snafu(display("Time index column not found"))]
1137    TimeIndexNotFound {
1138        #[snafu(implicit)]
1139        location: Location,
1140    },
1141
1142    #[snafu(display("Change column {} not exists in region: {}", column_name, region_id))]
1143    ChangeColumnNotFound {
1144        column_name: String,
1145        region_id: RegionId,
1146        #[snafu(implicit)]
1147        location: Location,
1148    },
1149
1150    #[snafu(display("Failed to convert column schema"))]
1151    ConvertColumnSchema {
1152        source: api::error::Error,
1153        #[snafu(implicit)]
1154        location: Location,
1155    },
1156
1157    #[snafu(display("Failed to convert TimeRanges"))]
1158    ConvertTimeRanges {
1159        source: api::error::Error,
1160        #[snafu(implicit)]
1161        location: Location,
1162    },
1163
1164    #[snafu(display("Invalid set region option request, key: {}, value: {}", key, value))]
1165    InvalidSetRegionOptionRequest {
1166        key: String,
1167        value: String,
1168        #[snafu(implicit)]
1169        location: Location,
1170    },
1171
1172    #[snafu(display("Invalid set region option request, key: {}", key))]
1173    InvalidUnsetRegionOptionRequest {
1174        key: String,
1175        #[snafu(implicit)]
1176        location: Location,
1177    },
1178
1179    #[snafu(display("Failed to decode protobuf"))]
1180    DecodeProto {
1181        #[snafu(source)]
1182        error: prost::UnknownEnumValue,
1183        #[snafu(implicit)]
1184        location: Location,
1185    },
1186
1187    #[snafu(display("Invalid column option, column name: {}, error: {}", column_name, msg))]
1188    InvalidColumnOption {
1189        column_name: String,
1190        msg: String,
1191        #[snafu(implicit)]
1192        location: Location,
1193    },
1194
1195    #[snafu(display("Failed to set fulltext options for column {}", column_name))]
1196    SetFulltextOptions {
1197        column_name: String,
1198        source: datatypes::Error,
1199        #[snafu(implicit)]
1200        location: Location,
1201    },
1202
1203    #[snafu(display("Failed to get fulltext options for column {}", column_name))]
1204    GetFulltextOptions {
1205        column_name: String,
1206        source: datatypes::Error,
1207        #[snafu(implicit)]
1208        location: Location,
1209    },
1210
1211    #[snafu(display("Failed to set skipping index options for column {}", column_name))]
1212    SetSkippingIndexOptions {
1213        column_name: String,
1214        source: datatypes::Error,
1215        #[snafu(implicit)]
1216        location: Location,
1217    },
1218
1219    #[snafu(display("Failed to unset skipping index options for column {}", column_name))]
1220    UnsetSkippingIndexOptions {
1221        column_name: String,
1222        source: datatypes::Error,
1223        #[snafu(implicit)]
1224        location: Location,
1225    },
1226
1227    #[snafu(display("Failed to decode arrow ipc record batches"))]
1228    DecodeArrowIpc {
1229        #[snafu(source)]
1230        error: arrow::error::ArrowError,
1231        #[snafu(implicit)]
1232        location: Location,
1233    },
1234
1235    #[snafu(display("Failed to cast default value, reason: {}", reason))]
1236    CastDefaultValue {
1237        reason: String,
1238        source: datatypes::Error,
1239        #[snafu(implicit)]
1240        location: Location,
1241    },
1242
1243    #[snafu(display("Unexpected: {}", reason))]
1244    Unexpected {
1245        reason: String,
1246        #[snafu(implicit)]
1247        location: Location,
1248    },
1249
1250    #[snafu(display("Failed to encode/decode flight message"))]
1251    FlightCodec {
1252        source: common_grpc::Error,
1253        #[snafu(implicit)]
1254        location: Location,
1255    },
1256
1257    #[snafu(display("Invalid index option"))]
1258    InvalidIndexOption {
1259        #[snafu(implicit)]
1260        location: Location,
1261        #[snafu(source)]
1262        error: datatypes::error::Error,
1263    },
1264
1265    #[snafu(display("Sql common error"))]
1266    SqlCommon {
1267        source: common_sql::error::Error,
1268        #[snafu(implicit)]
1269        location: Location,
1270    },
1271}
1272
1273impl ErrorExt for MetadataError {
1274    fn status_code(&self) -> StatusCode {
1275        match self {
1276            Self::SqlCommon { source, .. } => source.status_code(),
1277            _ => StatusCode::InvalidArguments,
1278        }
1279    }
1280
1281    fn as_any(&self) -> &dyn Any {
1282        self
1283    }
1284}
1285
1286/// Set column fulltext options if it passed the validation.
1287///
1288/// Options allowed to modify:
1289/// * backend
1290///
1291/// Options not allowed to modify:
1292/// * analyzer
1293/// * case_sensitive
1294fn set_column_fulltext_options(
1295    column_meta: &mut ColumnMetadata,
1296    column_name: &str,
1297    options: &FulltextOptions,
1298    current_options: Option<FulltextOptions>,
1299) -> Result<()> {
1300    if let Some(current_options) = current_options {
1301        ensure!(
1302            current_options.analyzer == options.analyzer
1303                && current_options.case_sensitive == options.case_sensitive,
1304            InvalidColumnOptionSnafu {
1305                column_name,
1306                msg: format!(
1307                    "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1308                    current_options.analyzer, current_options.case_sensitive
1309                ),
1310            }
1311        );
1312    }
1313
1314    column_meta
1315        .column_schema
1316        .set_fulltext_options(options)
1317        .context(SetFulltextOptionsSnafu { column_name })?;
1318
1319    Ok(())
1320}
1321
1322fn unset_column_fulltext_options(
1323    column_meta: &mut ColumnMetadata,
1324    column_name: &str,
1325    current_options: Option<FulltextOptions>,
1326) -> Result<()> {
1327    if let Some(mut current_options) = current_options
1328        && current_options.enable
1329    {
1330        current_options.enable = false;
1331        column_meta
1332            .column_schema
1333            .set_fulltext_options(&current_options)
1334            .context(SetFulltextOptionsSnafu { column_name })?;
1335    } else {
1336        return InvalidColumnOptionSnafu {
1337            column_name,
1338            msg: "FULLTEXT index already disabled",
1339        }
1340        .fail();
1341    }
1342
1343    Ok(())
1344}
1345
1346#[cfg(test)]
1347mod test {
1348    use datatypes::prelude::ConcreteDataType;
1349    use datatypes::schema::{
1350        ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextBackend,
1351    };
1352    use datatypes::value::Value;
1353
1354    use super::*;
1355
1356    fn create_builder() -> RegionMetadataBuilder {
1357        RegionMetadataBuilder::new(RegionId::new(1234, 5678))
1358    }
1359
1360    fn build_test_region_metadata() -> RegionMetadata {
1361        let mut builder = create_builder();
1362        builder
1363            .push_column_metadata(ColumnMetadata {
1364                column_schema: ColumnSchema::new("a", ConcreteDataType::int64_datatype(), false),
1365                semantic_type: SemanticType::Tag,
1366                column_id: 1,
1367            })
1368            .push_column_metadata(ColumnMetadata {
1369                column_schema: ColumnSchema::new("b", ConcreteDataType::float64_datatype(), false),
1370                semantic_type: SemanticType::Field,
1371                column_id: 2,
1372            })
1373            .push_column_metadata(ColumnMetadata {
1374                column_schema: ColumnSchema::new(
1375                    "c",
1376                    ConcreteDataType::timestamp_millisecond_datatype(),
1377                    false,
1378                ),
1379                semantic_type: SemanticType::Timestamp,
1380                column_id: 3,
1381            })
1382            .primary_key(vec![1])
1383            .partition_expr_json(Some("".to_string()));
1384        builder.build().unwrap()
1385    }
1386
1387    #[test]
1388    fn test_region_metadata() {
1389        let region_metadata = build_test_region_metadata();
1390        assert_eq!("c", region_metadata.time_index_column().column_schema.name);
1391        assert_eq!(
1392            "a",
1393            region_metadata.column_by_id(1).unwrap().column_schema.name
1394        );
1395        assert_eq!(None, region_metadata.column_by_id(10));
1396    }
1397
1398    #[test]
1399    fn test_region_metadata_serde() {
1400        let region_metadata = build_test_region_metadata();
1401        let serialized = serde_json::to_string(&region_metadata).unwrap();
1402        let deserialized: RegionMetadata = serde_json::from_str(&serialized).unwrap();
1403        assert_eq!(region_metadata, deserialized);
1404    }
1405
1406    #[test]
1407    fn test_column_metadata_validate() {
1408        let mut builder = create_builder();
1409        let col = ColumnMetadata {
1410            column_schema: ColumnSchema::new("ts", ConcreteDataType::string_datatype(), false),
1411            semantic_type: SemanticType::Timestamp,
1412            column_id: 1,
1413        };
1414
1415        builder.push_column_metadata(col);
1416        let err = builder.build().unwrap_err();
1417        assert!(
1418            err.to_string()
1419                .contains("column `ts` is not timestamp type"),
1420            "unexpected err: {err}",
1421        );
1422    }
1423
1424    #[test]
1425    fn test_empty_region_metadata() {
1426        let builder = create_builder();
1427        let err = builder.build().unwrap_err();
1428        // A region must have a time index.
1429        assert!(
1430            err.to_string().contains("time index not found"),
1431            "unexpected err: {err}",
1432        );
1433    }
1434
1435    #[test]
1436    fn test_same_column_id() {
1437        let mut builder = create_builder();
1438        builder
1439            .push_column_metadata(ColumnMetadata {
1440                column_schema: ColumnSchema::new("a", ConcreteDataType::int64_datatype(), false),
1441                semantic_type: SemanticType::Tag,
1442                column_id: 1,
1443            })
1444            .push_column_metadata(ColumnMetadata {
1445                column_schema: ColumnSchema::new(
1446                    "b",
1447                    ConcreteDataType::timestamp_millisecond_datatype(),
1448                    false,
1449                ),
1450                semantic_type: SemanticType::Timestamp,
1451                column_id: 1,
1452            });
1453        let err = builder.build().unwrap_err();
1454        assert!(
1455            err.to_string()
1456                .contains("column a and b have the same column id"),
1457            "unexpected err: {err}",
1458        );
1459    }
1460
1461    #[test]
1462    fn test_duplicate_time_index() {
1463        let mut builder = create_builder();
1464        builder
1465            .push_column_metadata(ColumnMetadata {
1466                column_schema: ColumnSchema::new(
1467                    "a",
1468                    ConcreteDataType::timestamp_millisecond_datatype(),
1469                    false,
1470                ),
1471                semantic_type: SemanticType::Timestamp,
1472                column_id: 1,
1473            })
1474            .push_column_metadata(ColumnMetadata {
1475                column_schema: ColumnSchema::new(
1476                    "b",
1477                    ConcreteDataType::timestamp_millisecond_datatype(),
1478                    false,
1479                ),
1480                semantic_type: SemanticType::Timestamp,
1481                column_id: 2,
1482            });
1483        let err = builder.build().unwrap_err();
1484        assert!(
1485            err.to_string().contains("expect only one time index"),
1486            "unexpected err: {err}",
1487        );
1488    }
1489
1490    #[test]
1491    fn test_unknown_primary_key() {
1492        let mut builder = create_builder();
1493        builder
1494            .push_column_metadata(ColumnMetadata {
1495                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), false),
1496                semantic_type: SemanticType::Tag,
1497                column_id: 1,
1498            })
1499            .push_column_metadata(ColumnMetadata {
1500                column_schema: ColumnSchema::new(
1501                    "b",
1502                    ConcreteDataType::timestamp_millisecond_datatype(),
1503                    false,
1504                ),
1505                semantic_type: SemanticType::Timestamp,
1506                column_id: 2,
1507            })
1508            .primary_key(vec![3]);
1509        let err = builder.build().unwrap_err();
1510        assert!(
1511            err.to_string().contains("unknown column id 3"),
1512            "unexpected err: {err}",
1513        );
1514    }
1515
1516    #[test]
1517    fn test_same_primary_key() {
1518        let mut builder = create_builder();
1519        builder
1520            .push_column_metadata(ColumnMetadata {
1521                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), false),
1522                semantic_type: SemanticType::Tag,
1523                column_id: 1,
1524            })
1525            .push_column_metadata(ColumnMetadata {
1526                column_schema: ColumnSchema::new(
1527                    "b",
1528                    ConcreteDataType::timestamp_millisecond_datatype(),
1529                    false,
1530                ),
1531                semantic_type: SemanticType::Timestamp,
1532                column_id: 2,
1533            })
1534            .primary_key(vec![1, 1]);
1535        let err = builder.build().unwrap_err();
1536        assert!(
1537            err.to_string()
1538                .contains("duplicate column a in primary key"),
1539            "unexpected err: {err}",
1540        );
1541    }
1542
1543    #[test]
1544    fn test_in_time_index() {
1545        let mut builder = create_builder();
1546        builder
1547            .push_column_metadata(ColumnMetadata {
1548                column_schema: ColumnSchema::new(
1549                    "ts",
1550                    ConcreteDataType::timestamp_millisecond_datatype(),
1551                    false,
1552                ),
1553                semantic_type: SemanticType::Timestamp,
1554                column_id: 1,
1555            })
1556            .primary_key(vec![1]);
1557        let err = builder.build().unwrap_err();
1558        assert!(
1559            err.to_string()
1560                .contains("column ts is already a time index column"),
1561            "unexpected err: {err}",
1562        );
1563    }
1564
1565    #[test]
1566    fn test_nullable_time_index() {
1567        let mut builder = create_builder();
1568        builder.push_column_metadata(ColumnMetadata {
1569            column_schema: ColumnSchema::new(
1570                "ts",
1571                ConcreteDataType::timestamp_millisecond_datatype(),
1572                true,
1573            ),
1574            semantic_type: SemanticType::Timestamp,
1575            column_id: 1,
1576        });
1577        let err = builder.build().unwrap_err();
1578        assert!(
1579            err.to_string()
1580                .contains("time index column ts must be NOT NULL"),
1581            "unexpected err: {err}",
1582        );
1583    }
1584
1585    #[test]
1586    fn test_primary_key_semantic_type() {
1587        let mut builder = create_builder();
1588        builder
1589            .push_column_metadata(ColumnMetadata {
1590                column_schema: ColumnSchema::new(
1591                    "ts",
1592                    ConcreteDataType::timestamp_millisecond_datatype(),
1593                    false,
1594                ),
1595                semantic_type: SemanticType::Timestamp,
1596                column_id: 1,
1597            })
1598            .push_column_metadata(ColumnMetadata {
1599                column_schema: ColumnSchema::new("a", ConcreteDataType::float64_datatype(), true),
1600                semantic_type: SemanticType::Field,
1601                column_id: 2,
1602            })
1603            .primary_key(vec![2]);
1604        let err = builder.build().unwrap_err();
1605        assert!(
1606            err.to_string()
1607                .contains("semantic type of column a should be Tag, not Field"),
1608            "unexpected err: {err}",
1609        );
1610    }
1611
1612    #[test]
1613    fn test_primary_key_tag_num() {
1614        let mut builder = create_builder();
1615        builder
1616            .push_column_metadata(ColumnMetadata {
1617                column_schema: ColumnSchema::new(
1618                    "ts",
1619                    ConcreteDataType::timestamp_millisecond_datatype(),
1620                    false,
1621                ),
1622                semantic_type: SemanticType::Timestamp,
1623                column_id: 1,
1624            })
1625            .push_column_metadata(ColumnMetadata {
1626                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), true),
1627                semantic_type: SemanticType::Tag,
1628                column_id: 2,
1629            })
1630            .push_column_metadata(ColumnMetadata {
1631                column_schema: ColumnSchema::new("b", ConcreteDataType::string_datatype(), true),
1632                semantic_type: SemanticType::Tag,
1633                column_id: 3,
1634            })
1635            .primary_key(vec![2]);
1636        let err = builder.build().unwrap_err();
1637        assert!(
1638            err.to_string()
1639                .contains("number of primary key columns 1 not equal to tag columns 2"),
1640            "unexpected err: {err}",
1641        );
1642    }
1643
1644    #[test]
1645    fn test_bump_version() {
1646        let mut region_metadata = build_test_region_metadata();
1647        let mut builder = RegionMetadataBuilder::from_existing(region_metadata.clone());
1648        builder.bump_version();
1649        let new_meta = builder.build().unwrap();
1650        region_metadata.schema_version += 1;
1651        assert_eq!(region_metadata, new_meta);
1652    }
1653
1654    fn new_column_metadata(name: &str, is_tag: bool, column_id: ColumnId) -> ColumnMetadata {
1655        let semantic_type = if is_tag {
1656            SemanticType::Tag
1657        } else {
1658            SemanticType::Field
1659        };
1660        ColumnMetadata {
1661            column_schema: ColumnSchema::new(name, ConcreteDataType::string_datatype(), true),
1662            semantic_type,
1663            column_id,
1664        }
1665    }
1666
1667    fn check_columns(metadata: &RegionMetadata, names: &[&str]) {
1668        let actual: Vec<_> = metadata
1669            .column_metadatas
1670            .iter()
1671            .map(|col| &col.column_schema.name)
1672            .collect();
1673        assert_eq!(names, actual);
1674    }
1675
1676    fn get_columns_default_constraint(
1677        metadata: &RegionMetadata,
1678        name: String,
1679    ) -> Option<Option<&ColumnDefaultConstraint>> {
1680        metadata.column_metadatas.iter().find_map(|col| {
1681            if col.column_schema.name == name {
1682                Some(col.column_schema.default_constraint())
1683            } else {
1684                None
1685            }
1686        })
1687    }
1688
1689    #[test]
1690    fn test_alter() {
1691        // a (tag), b (field), c (ts)
1692        let metadata = build_test_region_metadata();
1693        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1694        // tag d
1695        builder
1696            .alter(AlterKind::AddColumns {
1697                columns: vec![AddColumn {
1698                    column_metadata: new_column_metadata("d", true, 4),
1699                    location: None,
1700                }],
1701            })
1702            .unwrap();
1703        let metadata = builder.build().unwrap();
1704        check_columns(&metadata, &["a", "b", "c", "d"]);
1705        assert_eq!([1, 4], &metadata.primary_key[..]);
1706
1707        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1708        builder
1709            .alter(AlterKind::AddColumns {
1710                columns: vec![AddColumn {
1711                    column_metadata: new_column_metadata("e", false, 5),
1712                    location: Some(AddColumnLocation::First),
1713                }],
1714            })
1715            .unwrap();
1716        let metadata = builder.build().unwrap();
1717        check_columns(&metadata, &["e", "a", "b", "c", "d"]);
1718
1719        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1720        builder
1721            .alter(AlterKind::AddColumns {
1722                columns: vec![AddColumn {
1723                    column_metadata: new_column_metadata("f", false, 6),
1724                    location: Some(AddColumnLocation::After {
1725                        column_name: "b".to_string(),
1726                    }),
1727                }],
1728            })
1729            .unwrap();
1730        let metadata = builder.build().unwrap();
1731        check_columns(&metadata, &["e", "a", "b", "f", "c", "d"]);
1732
1733        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1734        builder
1735            .alter(AlterKind::AddColumns {
1736                columns: vec![AddColumn {
1737                    column_metadata: new_column_metadata("g", false, 7),
1738                    location: Some(AddColumnLocation::After {
1739                        column_name: "d".to_string(),
1740                    }),
1741                }],
1742            })
1743            .unwrap();
1744        let metadata = builder.build().unwrap();
1745        check_columns(&metadata, &["e", "a", "b", "f", "c", "d", "g"]);
1746
1747        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1748        builder
1749            .alter(AlterKind::DropColumns {
1750                names: vec!["g".to_string(), "e".to_string()],
1751            })
1752            .unwrap();
1753        let metadata = builder.build().unwrap();
1754        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1755
1756        let mut builder = RegionMetadataBuilder::from_existing(metadata.clone());
1757        builder
1758            .alter(AlterKind::DropColumns {
1759                names: vec!["a".to_string()],
1760            })
1761            .unwrap();
1762        // Build returns error as the primary key contains a.
1763        let err = builder.build().unwrap_err();
1764        assert_eq!(StatusCode::InvalidArguments, err.status_code());
1765
1766        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1767        let mut column_metadata = new_column_metadata("g", false, 8);
1768        let default_constraint = Some(ColumnDefaultConstraint::Value(Value::from("g")));
1769        column_metadata.column_schema = column_metadata
1770            .column_schema
1771            .with_default_constraint(default_constraint.clone())
1772            .unwrap();
1773        builder
1774            .alter(AlterKind::AddColumns {
1775                columns: vec![AddColumn {
1776                    column_metadata,
1777                    location: None,
1778                }],
1779            })
1780            .unwrap();
1781        let metadata = builder.build().unwrap();
1782        assert_eq!(
1783            get_columns_default_constraint(&metadata, "g".to_string()).unwrap(),
1784            default_constraint.as_ref()
1785        );
1786        check_columns(&metadata, &["a", "b", "f", "c", "d", "g"]);
1787
1788        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1789        builder
1790            .alter(AlterKind::DropDefaults {
1791                names: vec!["g".to_string()],
1792            })
1793            .unwrap();
1794        let metadata = builder.build().unwrap();
1795        assert_eq!(
1796            get_columns_default_constraint(&metadata, "g".to_string()).unwrap(),
1797            None
1798        );
1799        check_columns(&metadata, &["a", "b", "f", "c", "d", "g"]);
1800
1801        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1802        builder
1803            .alter(AlterKind::DropColumns {
1804                names: vec!["g".to_string()],
1805            })
1806            .unwrap();
1807        let metadata = builder.build().unwrap();
1808        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1809
1810        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1811        builder
1812            .alter(AlterKind::ModifyColumnTypes {
1813                columns: vec![ModifyColumnType {
1814                    column_name: "b".to_string(),
1815                    target_type: ConcreteDataType::string_datatype(),
1816                }],
1817            })
1818            .unwrap();
1819        let metadata = builder.build().unwrap();
1820        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1821        let b_type = &metadata
1822            .column_by_name("b")
1823            .unwrap()
1824            .column_schema
1825            .data_type;
1826        assert_eq!(ConcreteDataType::string_datatype(), *b_type);
1827
1828        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1829        builder
1830            .alter(AlterKind::SetIndexes {
1831                options: vec![SetIndexOption::Fulltext {
1832                    column_name: "b".to_string(),
1833                    options: FulltextOptions::new_unchecked(
1834                        true,
1835                        FulltextAnalyzer::Chinese,
1836                        true,
1837                        FulltextBackend::Bloom,
1838                        1000,
1839                        0.01,
1840                    ),
1841                }],
1842            })
1843            .unwrap();
1844        let metadata = builder.build().unwrap();
1845        let a_fulltext_options = metadata
1846            .column_by_name("b")
1847            .unwrap()
1848            .column_schema
1849            .fulltext_options()
1850            .unwrap()
1851            .unwrap();
1852        assert!(a_fulltext_options.enable);
1853        assert_eq!(
1854            datatypes::schema::FulltextAnalyzer::Chinese,
1855            a_fulltext_options.analyzer
1856        );
1857        assert!(a_fulltext_options.case_sensitive);
1858
1859        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1860        builder
1861            .alter(AlterKind::UnsetIndexes {
1862                options: vec![UnsetIndexOption::Fulltext {
1863                    column_name: "b".to_string(),
1864                }],
1865            })
1866            .unwrap();
1867        let metadata = builder.build().unwrap();
1868        let a_fulltext_options = metadata
1869            .column_by_name("b")
1870            .unwrap()
1871            .column_schema
1872            .fulltext_options()
1873            .unwrap()
1874            .unwrap();
1875        assert!(!a_fulltext_options.enable);
1876        assert_eq!(
1877            datatypes::schema::FulltextAnalyzer::Chinese,
1878            a_fulltext_options.analyzer
1879        );
1880        assert!(a_fulltext_options.case_sensitive);
1881    }
1882
1883    #[test]
1884    fn test_add_if_not_exists() {
1885        // a (tag), b (field), c (ts)
1886        let metadata = build_test_region_metadata();
1887        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1888        // tag d
1889        builder
1890            .alter(AlterKind::AddColumns {
1891                columns: vec![
1892                    AddColumn {
1893                        column_metadata: new_column_metadata("d", true, 4),
1894                        location: None,
1895                    },
1896                    AddColumn {
1897                        column_metadata: new_column_metadata("d", true, 4),
1898                        location: None,
1899                    },
1900                ],
1901            })
1902            .unwrap();
1903        let metadata = builder.build().unwrap();
1904        check_columns(&metadata, &["a", "b", "c", "d"]);
1905        assert_eq!([1, 4], &metadata.primary_key[..]);
1906
1907        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1908        // field b.
1909        builder
1910            .alter(AlterKind::AddColumns {
1911                columns: vec![AddColumn {
1912                    column_metadata: new_column_metadata("b", false, 2),
1913                    location: None,
1914                }],
1915            })
1916            .unwrap();
1917        let metadata = builder.build().unwrap();
1918        check_columns(&metadata, &["a", "b", "c", "d"]);
1919    }
1920
1921    #[test]
1922    fn test_add_column_with_inverted_index() {
1923        // only set inverted index to true explicitly will this column be inverted indexed
1924
1925        // a (tag), b (field), c (ts)
1926        let metadata = build_test_region_metadata();
1927        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1928        // tag d, e
1929        let mut col = new_column_metadata("d", true, 4);
1930        col.column_schema.set_inverted_index(true);
1931        builder
1932            .alter(AlterKind::AddColumns {
1933                columns: vec![
1934                    AddColumn {
1935                        column_metadata: col,
1936                        location: None,
1937                    },
1938                    AddColumn {
1939                        column_metadata: new_column_metadata("e", true, 5),
1940                        location: None,
1941                    },
1942                ],
1943            })
1944            .unwrap();
1945        let metadata = builder.build().unwrap();
1946        check_columns(&metadata, &["a", "b", "c", "d", "e"]);
1947        assert_eq!([1, 4, 5], &metadata.primary_key[..]);
1948        let column_metadata = metadata.column_by_name("a").unwrap();
1949        assert!(!column_metadata.column_schema.is_inverted_indexed());
1950        let column_metadata = metadata.column_by_name("b").unwrap();
1951        assert!(!column_metadata.column_schema.is_inverted_indexed());
1952        let column_metadata = metadata.column_by_name("c").unwrap();
1953        assert!(!column_metadata.column_schema.is_inverted_indexed());
1954        let column_metadata = metadata.column_by_name("d").unwrap();
1955        assert!(column_metadata.column_schema.is_inverted_indexed());
1956        let column_metadata = metadata.column_by_name("e").unwrap();
1957        assert!(!column_metadata.column_schema.is_inverted_indexed());
1958    }
1959
1960    #[test]
1961    fn test_drop_if_exists() {
1962        // a (tag), b (field), c (ts)
1963        let metadata = build_test_region_metadata();
1964        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1965        // field d, e
1966        builder
1967            .alter(AlterKind::AddColumns {
1968                columns: vec![
1969                    AddColumn {
1970                        column_metadata: new_column_metadata("d", false, 4),
1971                        location: None,
1972                    },
1973                    AddColumn {
1974                        column_metadata: new_column_metadata("e", false, 5),
1975                        location: None,
1976                    },
1977                ],
1978            })
1979            .unwrap();
1980        let metadata = builder.build().unwrap();
1981        check_columns(&metadata, &["a", "b", "c", "d", "e"]);
1982
1983        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1984        builder
1985            .alter(AlterKind::DropColumns {
1986                names: vec!["b".to_string(), "b".to_string()],
1987            })
1988            .unwrap();
1989        let metadata = builder.build().unwrap();
1990        check_columns(&metadata, &["a", "c", "d", "e"]);
1991
1992        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1993        builder
1994            .alter(AlterKind::DropColumns {
1995                names: vec!["b".to_string(), "e".to_string()],
1996            })
1997            .unwrap();
1998        let metadata = builder.build().unwrap();
1999        check_columns(&metadata, &["a", "c", "d"]);
2000    }
2001
2002    #[test]
2003    fn test_invalid_column_name() {
2004        let mut builder = create_builder();
2005        builder.push_column_metadata(ColumnMetadata {
2006            column_schema: ColumnSchema::new(
2007                "__sequence",
2008                ConcreteDataType::timestamp_millisecond_datatype(),
2009                false,
2010            ),
2011            semantic_type: SemanticType::Timestamp,
2012            column_id: 1,
2013        });
2014        let err = builder.build().unwrap_err();
2015        assert!(
2016            err.to_string()
2017                .contains("internal column name that can not be used"),
2018            "unexpected err: {err}",
2019        );
2020    }
2021
2022    #[test]
2023    fn test_allow_internal_column_name() {
2024        let mut builder = create_builder();
2025        builder
2026            .push_column_metadata(ColumnMetadata {
2027                column_schema: ColumnSchema::new(
2028                    "__primary_key",
2029                    ConcreteDataType::string_datatype(),
2030                    false,
2031                ),
2032                semantic_type: SemanticType::Tag,
2033                column_id: 1,
2034            })
2035            .push_column_metadata(ColumnMetadata {
2036                column_schema: ColumnSchema::new(
2037                    "ts",
2038                    ConcreteDataType::timestamp_millisecond_datatype(),
2039                    false,
2040                ),
2041                semantic_type: SemanticType::Timestamp,
2042                column_id: 2,
2043            })
2044            .primary_key(vec![1]);
2045
2046        let metadata = builder.build_without_validation().unwrap();
2047        assert_eq!(
2048            "__primary_key",
2049            metadata.column_metadatas[0].column_schema.name
2050        );
2051    }
2052
2053    #[test]
2054    fn test_build_without_validation() {
2055        // Primary key points to a Field column, which would normally fail validation.
2056        let mut builder = create_builder();
2057        builder
2058            .push_column_metadata(ColumnMetadata {
2059                column_schema: ColumnSchema::new(
2060                    "ts",
2061                    ConcreteDataType::timestamp_millisecond_datatype(),
2062                    false,
2063                ),
2064                semantic_type: SemanticType::Timestamp,
2065                column_id: 1,
2066            })
2067            .push_column_metadata(ColumnMetadata {
2068                column_schema: ColumnSchema::new(
2069                    "field",
2070                    ConcreteDataType::string_datatype(),
2071                    true,
2072                ),
2073                semantic_type: SemanticType::Field,
2074                column_id: 2,
2075            })
2076            .primary_key(vec![2]);
2077
2078        // Unvalidated build should succeed.
2079        let metadata = builder.build_without_validation().unwrap();
2080        assert_eq!(vec![2], metadata.primary_key);
2081
2082        // Validated build still rejects it.
2083        let mut builder = create_builder();
2084        builder
2085            .push_column_metadata(ColumnMetadata {
2086                column_schema: ColumnSchema::new(
2087                    "ts",
2088                    ConcreteDataType::timestamp_millisecond_datatype(),
2089                    false,
2090                ),
2091                semantic_type: SemanticType::Timestamp,
2092                column_id: 1,
2093            })
2094            .push_column_metadata(ColumnMetadata {
2095                column_schema: ColumnSchema::new(
2096                    "field",
2097                    ConcreteDataType::string_datatype(),
2098                    true,
2099                ),
2100                semantic_type: SemanticType::Field,
2101                column_id: 2,
2102            })
2103            .primary_key(vec![2]);
2104        let err = builder.build().unwrap_err();
2105        assert!(
2106            err.to_string()
2107                .contains("semantic type of column field should be Tag"),
2108            "unexpected err: {err}"
2109        );
2110    }
2111
2112    #[test]
2113    fn test_debug_for_column_metadata() {
2114        let region_metadata = build_test_region_metadata();
2115        let formatted = format!("{:?}", region_metadata);
2116        assert_eq!(
2117            formatted,
2118            "RegionMetadata { column_metadatas: [[a Int64 not null Tag 1], [b Float64 not null Field 2], [c TimestampMillisecond not null Timestamp 3]], time_index: 3, primary_key: [1], region_id: 5299989648942(1234, 5678), schema_version: 0, partition_expr: Some(\"\") }"
2119        );
2120    }
2121
2122    #[test]
2123    fn test_region_metadata_deserialize_default_primary_key_encoding() {
2124        let serialize = r#"{"column_metadatas":[{"column_schema":{"name":"a","data_type":{"Int64":{}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Tag","column_id":1},{"column_schema":{"name":"b","data_type":{"Float64":{}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Field","column_id":2},{"column_schema":{"name":"c","data_type":{"Timestamp":{"Millisecond":null}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Timestamp","column_id":3}],"primary_key":[1],"region_id":5299989648942,"schema_version":0}"#;
2125        let deserialized: RegionMetadata = serde_json::from_str(serialize).unwrap();
2126        assert_eq!(deserialized.primary_key_encoding, PrimaryKeyEncoding::Dense);
2127
2128        let serialize = r#"{"column_metadatas":[{"column_schema":{"name":"a","data_type":{"Int64":{}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Tag","column_id":1},{"column_schema":{"name":"b","data_type":{"Float64":{}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Field","column_id":2},{"column_schema":{"name":"c","data_type":{"Timestamp":{"Millisecond":null}},"is_nullable":false,"is_time_index":false,"default_constraint":null,"metadata":{}},"semantic_type":"Timestamp","column_id":3}],"primary_key":[1],"region_id":5299989648942,"schema_version":0,"primary_key_encoding":"sparse"}"#;
2129        let deserialized: RegionMetadata = serde_json::from_str(serialize).unwrap();
2130        assert_eq!(
2131            deserialized.primary_key_encoding,
2132            PrimaryKeyEncoding::Sparse
2133        );
2134    }
2135}