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, VectorIndexOptions};
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    /// Gets the column IDs that have vector indexes along with their options.
427    /// Returns a map from column ID to the vector index options.
428    pub fn vector_indexed_column_ids(&self) -> HashMap<ColumnId, VectorIndexOptions> {
429        self.column_metadatas
430            .iter()
431            .filter_map(|column| {
432                column
433                    .column_schema
434                    .vector_index_options()
435                    .ok()
436                    .flatten()
437                    .map(|options| (column.column_id, options))
438            })
439            .collect()
440    }
441
442    /// Checks whether the metadata is valid.
443    fn validate(&self) -> Result<()> {
444        // Id to name.
445        let mut id_names = HashMap::with_capacity(self.column_metadatas.len());
446        for col in &self.column_metadatas {
447            // Validate each column.
448            Self::validate_column_metadata(col)?;
449
450            // Check whether column id is duplicated. We already check column name
451            // is unique in `Schema` so we only check column id here.
452            ensure!(
453                !id_names.contains_key(&col.column_id),
454                InvalidMetaSnafu {
455                    reason: format!(
456                        "column {} and {} have the same column id {}",
457                        id_names[&col.column_id], col.column_schema.name, col.column_id,
458                    ),
459                }
460            );
461            id_names.insert(col.column_id, &col.column_schema.name);
462        }
463
464        // Checks there is only one time index.
465        let time_indexes = self
466            .column_metadatas
467            .iter()
468            .filter(|col| col.semantic_type == SemanticType::Timestamp)
469            .collect::<Vec<_>>();
470        ensure!(
471            time_indexes.len() == 1,
472            InvalidMetaSnafu {
473                reason: format!(
474                    "expect only one time index, found {}: {}",
475                    time_indexes.len(),
476                    time_indexes
477                        .iter()
478                        .map(|c| &c.column_schema.name)
479                        .join(", ")
480                ),
481            }
482        );
483
484        // Checks the time index column is not nullable.
485        ensure!(
486            !self.time_index_column().column_schema.is_nullable(),
487            InvalidMetaSnafu {
488                reason: format!(
489                    "time index column {} must be NOT NULL",
490                    self.time_index_column().column_schema.name
491                ),
492            }
493        );
494
495        if !self.primary_key.is_empty() {
496            let mut pk_ids = HashSet::with_capacity(self.primary_key.len());
497            // Checks column ids in the primary key is valid.
498            for column_id in &self.primary_key {
499                // Checks whether the column id exists.
500                ensure!(
501                    id_names.contains_key(column_id),
502                    InvalidMetaSnafu {
503                        reason: format!("unknown column id {}", column_id),
504                    }
505                );
506
507                // Safety: Column with specific id must exist.
508                let column = self.column_by_id(*column_id).unwrap();
509                // Checks duplicate.
510                ensure!(
511                    !pk_ids.contains(&column_id),
512                    InvalidMetaSnafu {
513                        reason: format!(
514                            "duplicate column {} in primary key",
515                            column.column_schema.name
516                        ),
517                    }
518                );
519
520                // Checks this is not a time index column.
521                ensure!(
522                    *column_id != self.time_index,
523                    InvalidMetaSnafu {
524                        reason: format!(
525                            "column {} is already a time index column",
526                            column.column_schema.name,
527                        ),
528                    }
529                );
530
531                // Checks semantic type.
532                ensure!(
533                    column.semantic_type == SemanticType::Tag,
534                    InvalidMetaSnafu {
535                        reason: format!(
536                            "semantic type of column {} should be Tag, not {:?}",
537                            column.column_schema.name, column.semantic_type
538                        ),
539                    }
540                );
541
542                pk_ids.insert(column_id);
543            }
544        }
545
546        // Checks tag semantic type.
547        let num_tag = self
548            .column_metadatas
549            .iter()
550            .filter(|col| col.semantic_type == SemanticType::Tag)
551            .count();
552        ensure!(
553            num_tag == self.primary_key.len(),
554            InvalidMetaSnafu {
555                reason: format!(
556                    "number of primary key columns {} not equal to tag columns {}",
557                    self.primary_key.len(),
558                    num_tag
559                ),
560            }
561        );
562
563        Ok(())
564    }
565
566    /// Checks whether it is a valid column.
567    fn validate_column_metadata(column_metadata: &ColumnMetadata) -> Result<()> {
568        if column_metadata.semantic_type == SemanticType::Timestamp {
569            ensure!(
570                column_metadata.column_schema.data_type.is_timestamp(),
571                InvalidMetaSnafu {
572                    reason: format!(
573                        "column `{}` is not timestamp type",
574                        column_metadata.column_schema.name
575                    ),
576                }
577            );
578        }
579
580        ensure!(
581            !is_internal_column(&column_metadata.column_schema.name),
582            InvalidMetaSnafu {
583                reason: format!(
584                    "{} is internal column name that can not be used",
585                    column_metadata.column_schema.name
586                ),
587            }
588        );
589
590        Ok(())
591    }
592}
593
594/// Builder to build [RegionMetadata].
595pub struct RegionMetadataBuilder {
596    region_id: RegionId,
597    column_metadatas: Vec<ColumnMetadata>,
598    primary_key: Vec<ColumnId>,
599    schema_version: u64,
600    primary_key_encoding: PrimaryKeyEncoding,
601    partition_expr: Option<String>,
602}
603
604impl RegionMetadataBuilder {
605    /// Returns a new builder.
606    pub fn new(id: RegionId) -> Self {
607        Self {
608            region_id: id,
609            column_metadatas: vec![],
610            primary_key: vec![],
611            schema_version: 0,
612            primary_key_encoding: PrimaryKeyEncoding::Dense,
613            partition_expr: None,
614        }
615    }
616
617    /// Creates a builder from existing [RegionMetadata].
618    pub fn from_existing(existing: RegionMetadata) -> Self {
619        Self {
620            column_metadatas: existing.column_metadatas,
621            primary_key: existing.primary_key,
622            region_id: existing.region_id,
623            schema_version: existing.schema_version,
624            primary_key_encoding: existing.primary_key_encoding,
625            partition_expr: existing.partition_expr,
626        }
627    }
628
629    /// Sets the primary key encoding mode.
630    pub fn primary_key_encoding(&mut self, encoding: PrimaryKeyEncoding) -> &mut Self {
631        self.primary_key_encoding = encoding;
632        self
633    }
634
635    /// Sets the partition expression in JSON string form.
636    pub fn partition_expr_json(&mut self, expr_json: Option<String>) -> &mut Self {
637        self.partition_expr = expr_json;
638        self
639    }
640
641    /// Pushes a new column metadata to this region's metadata.
642    pub fn push_column_metadata(&mut self, column_metadata: ColumnMetadata) -> &mut Self {
643        self.column_metadatas.push(column_metadata);
644        self
645    }
646
647    /// Sets the primary key of the region.
648    pub fn primary_key(&mut self, key: Vec<ColumnId>) -> &mut Self {
649        self.primary_key = key;
650        self
651    }
652
653    /// Increases the schema version by 1.
654    pub fn bump_version(&mut self) -> &mut Self {
655        self.schema_version += 1;
656        self
657    }
658
659    /// Applies the alter `kind` to the builder.
660    ///
661    /// The `kind` should be valid.
662    pub fn alter(&mut self, kind: AlterKind) -> Result<&mut Self> {
663        match kind {
664            AlterKind::AddColumns { columns } => self.add_columns(columns)?,
665            AlterKind::DropColumns { names } => self.drop_columns(&names),
666            AlterKind::ModifyColumnTypes { columns } => self.modify_column_types(columns)?,
667            AlterKind::SetJsonSettings {
668                column_name,
669                settings,
670            } => self.set_json_settings(column_name, settings)?,
671            AlterKind::SetIndexes { options } => self.set_indexes(options)?,
672            AlterKind::UnsetIndexes { options } => self.unset_indexes(options)?,
673            AlterKind::SetRegionOptions { options: _ } => {
674                // nothing to be done with RegionMetadata
675            }
676            AlterKind::UnsetRegionOptions { keys: _ } => {
677                // nothing to be done with RegionMetadata
678            }
679            AlterKind::DropDefaults { names } => {
680                self.drop_defaults(names)?;
681            }
682            AlterKind::SetDefaults { columns } => self.set_defaults(&columns)?,
683            AlterKind::SyncColumns { column_metadatas } => {
684                self.primary_key = column_metadatas
685                    .iter()
686                    .filter_map(|column_metadata| {
687                        if column_metadata.semantic_type == SemanticType::Tag {
688                            Some(column_metadata.column_id)
689                        } else {
690                            None
691                        }
692                    })
693                    .collect::<Vec<_>>();
694                self.column_metadatas = column_metadatas;
695            }
696        }
697        Ok(self)
698    }
699
700    /// Consumes the builder and build a [RegionMetadata].
701    pub fn build(self) -> Result<RegionMetadata> {
702        self.build_with_options(true)
703    }
704
705    /// Builds metadata without running validation.
706    ///
707    /// Intended for file/external engines that should accept arbitrary schemas
708    /// coming from files.
709    pub fn build_without_validation(self) -> Result<RegionMetadata> {
710        self.build_with_options(false)
711    }
712
713    fn build_with_options(self, validate: bool) -> Result<RegionMetadata> {
714        let skipped = SkippedFields::new(&self.column_metadatas)?;
715
716        let partition_expr_version = partition_expr_version(self.partition_expr.as_deref());
717        let meta = RegionMetadata {
718            schema: skipped.schema,
719            time_index: skipped.time_index,
720            id_to_index: skipped.id_to_index,
721            column_metadatas: self.column_metadatas,
722            primary_key: self.primary_key,
723            region_id: self.region_id,
724            schema_version: self.schema_version,
725            primary_key_encoding: self.primary_key_encoding,
726            partition_expr: self.partition_expr,
727            partition_expr_version,
728        };
729
730        if validate {
731            meta.validate()?;
732        }
733
734        Ok(meta)
735    }
736
737    /// Adds columns to the metadata if not exist.
738    fn add_columns(&mut self, columns: Vec<AddColumn>) -> Result<()> {
739        let mut names: HashSet<_> = self
740            .column_metadatas
741            .iter()
742            .map(|col| col.column_schema.name.clone())
743            .collect();
744
745        for add_column in columns {
746            if names.contains(&add_column.column_metadata.column_schema.name) {
747                // Column already exists.
748                continue;
749            }
750
751            let column_id = add_column.column_metadata.column_id;
752            let semantic_type = add_column.column_metadata.semantic_type;
753            let column_name = add_column.column_metadata.column_schema.name.clone();
754            match add_column.location {
755                None => {
756                    self.column_metadatas.push(add_column.column_metadata);
757                }
758                Some(AddColumnLocation::First) => {
759                    self.column_metadatas.insert(0, add_column.column_metadata);
760                }
761                Some(AddColumnLocation::After { column_name }) => {
762                    let pos = self
763                        .column_metadatas
764                        .iter()
765                        .position(|col| col.column_schema.name == column_name)
766                        .context(InvalidRegionRequestSnafu {
767                            region_id: self.region_id,
768                            err: format!(
769                                "column {} not found, failed to add column {} after it",
770                                column_name, add_column.column_metadata.column_schema.name
771                            ),
772                        })?;
773                    // Insert after pos.
774                    self.column_metadatas
775                        .insert(pos + 1, add_column.column_metadata);
776                }
777            }
778            names.insert(column_name);
779            if semantic_type == SemanticType::Tag {
780                // For a new tag, we extend the primary key.
781                self.primary_key.push(column_id);
782            }
783        }
784
785        Ok(())
786    }
787
788    /// Drops columns from the metadata if exist.
789    fn drop_columns(&mut self, names: &[String]) {
790        let name_set: HashSet<_> = names.iter().collect();
791        self.column_metadatas
792            .retain(|col| !name_set.contains(&col.column_schema.name));
793    }
794
795    /// Changes columns type to the metadata if exist.
796    fn modify_column_types(&mut self, columns: Vec<ModifyColumnType>) -> Result<()> {
797        let mut change_type_map: HashMap<_, _> = columns
798            .into_iter()
799            .map(
800                |ModifyColumnType {
801                     column_name,
802                     target_type,
803                 }| (column_name, target_type),
804            )
805            .collect();
806
807        for column_meta in self.column_metadatas.iter_mut() {
808            if let Some(target_type) = change_type_map.remove(&column_meta.column_schema.name) {
809                column_meta.column_schema.data_type = target_type.clone();
810                // also cast default value to target_type if default value exist
811                let new_default =
812                    if let Some(default_value) = column_meta.column_schema.default_constraint() {
813                        Some(
814                            default_value
815                                .cast_to_datatype(&target_type)
816                                .with_context(|_| CastDefaultValueSnafu {
817                                    reason: format!(
818                                        "Failed to cast default value from {:?} to type {:?}",
819                                        default_value, target_type
820                                    ),
821                                })?,
822                        )
823                    } else {
824                        None
825                    };
826                column_meta.column_schema = column_meta
827                    .column_schema
828                    .clone()
829                    .with_default_constraint(new_default.clone())
830                    .with_context(|_| CastDefaultValueSnafu {
831                        reason: format!("Failed to set new default: {:?}", new_default),
832                    })?;
833            }
834        }
835
836        Ok(())
837    }
838
839    fn set_json_settings(
840        &mut self,
841        col_name: String,
842        settings: datatypes::json::JsonSettings,
843    ) -> Result<()> {
844        let Some(col_meta) = self
845            .column_metadatas
846            .iter_mut()
847            .find(|col| col.column_schema.name == col_name)
848        else {
849            return InvalidRegionRequestSnafu {
850                region_id: self.region_id,
851                err: format!("column {col_name} not found"),
852            }
853            .fail();
854        };
855
856        let old_metadata = col_meta.column_schema.metadata();
857        let new_metadata =
858            json2_metadata_with_updated_settings(old_metadata, settings).map_err(|err| {
859                InvalidRegionRequestSnafu {
860                    region_id: self.region_id,
861                    err: err.to_string(),
862                }
863                .build()
864            })?;
865
866        *col_meta.column_schema.mut_metadata() = new_metadata;
867        Ok(())
868    }
869
870    fn set_indexes(&mut self, options: Vec<SetIndexOption>) -> Result<()> {
871        let mut set_index_map: HashMap<_, Vec<_>> = HashMap::new();
872        for option in &options {
873            set_index_map
874                .entry(option.column_name())
875                .or_default()
876                .push(option);
877        }
878
879        for column_metadata in self.column_metadatas.iter_mut() {
880            if let Some(options) = set_index_map.remove(&column_metadata.column_schema.name) {
881                for option in options {
882                    Self::set_index(column_metadata, option)?;
883                }
884            }
885        }
886
887        Ok(())
888    }
889
890    fn unset_indexes(&mut self, options: Vec<UnsetIndexOption>) -> Result<()> {
891        let mut unset_index_map: HashMap<_, Vec<_>> = HashMap::new();
892        for option in &options {
893            unset_index_map
894                .entry(option.column_name())
895                .or_default()
896                .push(option);
897        }
898
899        for column_metadata in self.column_metadatas.iter_mut() {
900            if let Some(options) = unset_index_map.remove(&column_metadata.column_schema.name) {
901                for option in options {
902                    Self::unset_index(column_metadata, option)?;
903                }
904            }
905        }
906
907        Ok(())
908    }
909
910    fn set_index(column_metadata: &mut ColumnMetadata, options: &SetIndexOption) -> Result<()> {
911        match options {
912            SetIndexOption::Fulltext {
913                column_name,
914                options,
915            } => {
916                ensure!(
917                    column_metadata.column_schema.data_type.is_string(),
918                    InvalidColumnOptionSnafu {
919                        column_name,
920                        msg: "FULLTEXT index only supports string type".to_string(),
921                    }
922                );
923                let current_fulltext_options = column_metadata
924                    .column_schema
925                    .fulltext_options()
926                    .with_context(|_| GetFulltextOptionsSnafu {
927                        column_name: column_name.clone(),
928                    })?;
929                set_column_fulltext_options(
930                    column_metadata,
931                    column_name,
932                    options,
933                    current_fulltext_options,
934                )?;
935            }
936            SetIndexOption::Inverted { .. } => {
937                column_metadata.column_schema.set_inverted_index(true)
938            }
939            SetIndexOption::Skipping {
940                column_name,
941                options,
942            } => {
943                column_metadata
944                    .column_schema
945                    .set_skipping_options(options)
946                    .context(UnsetSkippingIndexOptionsSnafu { column_name })?;
947            }
948        }
949
950        Ok(())
951    }
952
953    fn unset_index(column_metadata: &mut ColumnMetadata, options: &UnsetIndexOption) -> Result<()> {
954        match options {
955            UnsetIndexOption::Fulltext { column_name } => {
956                ensure!(
957                    column_metadata.column_schema.data_type.is_string(),
958                    InvalidColumnOptionSnafu {
959                        column_name,
960                        msg: "FULLTEXT index only supports string type".to_string(),
961                    }
962                );
963
964                let current_fulltext_options = column_metadata
965                    .column_schema
966                    .fulltext_options()
967                    .with_context(|_| GetFulltextOptionsSnafu {
968                        column_name: column_name.clone(),
969                    })?;
970
971                unset_column_fulltext_options(
972                    column_metadata,
973                    column_name,
974                    current_fulltext_options,
975                )?;
976            }
977            UnsetIndexOption::Inverted { .. } => {
978                column_metadata.column_schema.set_inverted_index(false)
979            }
980            UnsetIndexOption::Skipping { column_name } => {
981                column_metadata
982                    .column_schema
983                    .unset_skipping_options()
984                    .context(UnsetSkippingIndexOptionsSnafu { column_name })?;
985            }
986        }
987
988        Ok(())
989    }
990
991    fn drop_defaults(&mut self, column_names: Vec<String>) -> Result<()> {
992        for name in column_names.iter() {
993            let meta = self
994                .column_metadatas
995                .iter_mut()
996                .find(|col| col.column_schema.name == *name);
997            if let Some(meta) = meta {
998                if !meta.column_schema.is_nullable() {
999                    return InvalidRegionRequestSnafu {
1000                        region_id: self.region_id,
1001                        err: format!(
1002                            "column {name} is not nullable and `default` cannot be dropped",
1003                        ),
1004                    }
1005                    .fail();
1006                }
1007                meta.column_schema = meta
1008                    .column_schema
1009                    .clone()
1010                    .with_default_constraint(None)
1011                    .with_context(|_| CastDefaultValueSnafu {
1012                        reason: format!("Failed to drop default : {name:?}"),
1013                    })?;
1014            } else {
1015                return InvalidRegionRequestSnafu {
1016                    region_id: self.region_id,
1017                    err: format!("column {name} not found",),
1018                }
1019                .fail();
1020            }
1021        }
1022        Ok(())
1023    }
1024
1025    fn set_defaults(&mut self, set_defaults: &[crate::region_request::SetDefault]) -> Result<()> {
1026        for set_default in set_defaults.iter() {
1027            let meta = self
1028                .column_metadatas
1029                .iter_mut()
1030                .find(|col| col.column_schema.name == set_default.name);
1031            if let Some(meta) = meta {
1032                let default_constraint = common_sql::convert::deserialize_default_constraint(
1033                    set_default.default_constraint.as_slice(),
1034                    &meta.column_schema.name,
1035                    &meta.column_schema.data_type,
1036                )
1037                .context(SqlCommonSnafu)?;
1038
1039                meta.column_schema = meta
1040                    .column_schema
1041                    .clone()
1042                    .with_default_constraint(default_constraint)
1043                    .with_context(|_| CastDefaultValueSnafu {
1044                        reason: format!("Failed to set default : {set_default:?}"),
1045                    })?;
1046            } else {
1047                return InvalidRegionRequestSnafu {
1048                    region_id: self.region_id,
1049                    err: format!("column {} not found", set_default.name),
1050                }
1051                .fail();
1052            }
1053        }
1054        Ok(())
1055    }
1056}
1057
1058/// Fields skipped in serialization.
1059struct SkippedFields {
1060    /// Last schema.
1061    schema: SchemaRef,
1062    /// Id of the time index column.
1063    time_index: ColumnId,
1064    /// Map column id to column's index in [column_metadatas](RegionMetadata::column_metadatas).
1065    id_to_index: HashMap<ColumnId, usize>,
1066}
1067
1068impl SkippedFields {
1069    /// Constructs skipped fields from `column_metadatas`.
1070    fn new(column_metadatas: &[ColumnMetadata]) -> Result<SkippedFields> {
1071        let column_schemas = column_metadatas
1072            .iter()
1073            .map(|column_metadata| column_metadata.column_schema.clone())
1074            .collect();
1075        let schema = Arc::new(Schema::try_new(column_schemas).context(InvalidSchemaSnafu)?);
1076        let time_index = column_metadatas
1077            .iter()
1078            .find_map(|col| {
1079                if col.semantic_type == SemanticType::Timestamp {
1080                    Some(col.column_id)
1081                } else {
1082                    None
1083                }
1084            })
1085            .context(InvalidMetaSnafu {
1086                reason: "time index not found",
1087            })?;
1088        let id_to_index = column_metadatas
1089            .iter()
1090            .enumerate()
1091            .map(|(idx, col)| (col.column_id, idx))
1092            .collect();
1093
1094        Ok(SkippedFields {
1095            schema,
1096            time_index,
1097            id_to_index,
1098        })
1099    }
1100}
1101
1102#[derive(Snafu)]
1103#[snafu(visibility(pub))]
1104#[stack_trace_debug]
1105pub enum MetadataError {
1106    #[snafu(display("Invalid schema"))]
1107    InvalidSchema {
1108        source: datatypes::error::Error,
1109        #[snafu(implicit)]
1110        location: Location,
1111    },
1112
1113    #[snafu(display("Invalid metadata, {}", reason))]
1114    InvalidMeta {
1115        reason: String,
1116        #[snafu(implicit)]
1117        location: Location,
1118    },
1119
1120    #[snafu(display("Failed to ser/de json object"))]
1121    SerdeJson {
1122        #[snafu(implicit)]
1123        location: Location,
1124        #[snafu(source)]
1125        error: serde_json::Error,
1126    },
1127
1128    #[snafu(display("Invalid raw region request, err: {}", err))]
1129    InvalidRawRegionRequest {
1130        err: String,
1131        #[snafu(implicit)]
1132        location: Location,
1133    },
1134
1135    #[snafu(display("Invalid region request, region_id: {}, err: {}", region_id, err))]
1136    InvalidRegionRequest {
1137        region_id: RegionId,
1138        err: String,
1139        #[snafu(implicit)]
1140        location: Location,
1141    },
1142
1143    #[snafu(display("Unexpected schema error during project"))]
1144    SchemaProject {
1145        origin_schema: SchemaRef,
1146        projection: Vec<ColumnId>,
1147        #[snafu(implicit)]
1148        location: Location,
1149        source: datatypes::Error,
1150    },
1151
1152    #[snafu(display("Time index column not found"))]
1153    TimeIndexNotFound {
1154        #[snafu(implicit)]
1155        location: Location,
1156    },
1157
1158    #[snafu(display("Change column {} not exists in region: {}", column_name, region_id))]
1159    ChangeColumnNotFound {
1160        column_name: String,
1161        region_id: RegionId,
1162        #[snafu(implicit)]
1163        location: Location,
1164    },
1165
1166    #[snafu(display("Failed to convert column schema"))]
1167    ConvertColumnSchema {
1168        source: api::error::Error,
1169        #[snafu(implicit)]
1170        location: Location,
1171    },
1172
1173    #[snafu(display("Failed to convert TimeRanges"))]
1174    ConvertTimeRanges {
1175        source: api::error::Error,
1176        #[snafu(implicit)]
1177        location: Location,
1178    },
1179
1180    #[snafu(display("Invalid set region option request, key: {}, value: {}", key, value))]
1181    InvalidSetRegionOptionRequest {
1182        key: String,
1183        value: String,
1184        #[snafu(implicit)]
1185        location: Location,
1186    },
1187
1188    #[snafu(display("Invalid set region option request, key: {}", key))]
1189    InvalidUnsetRegionOptionRequest {
1190        key: String,
1191        #[snafu(implicit)]
1192        location: Location,
1193    },
1194
1195    #[snafu(display("Failed to decode protobuf"))]
1196    DecodeProto {
1197        #[snafu(source)]
1198        error: prost::UnknownEnumValue,
1199        #[snafu(implicit)]
1200        location: Location,
1201    },
1202
1203    #[snafu(display("Invalid column option, column name: {}, error: {}", column_name, msg))]
1204    InvalidColumnOption {
1205        column_name: String,
1206        msg: String,
1207        #[snafu(implicit)]
1208        location: Location,
1209    },
1210
1211    #[snafu(display("Failed to set fulltext options for column {}", column_name))]
1212    SetFulltextOptions {
1213        column_name: String,
1214        source: datatypes::Error,
1215        #[snafu(implicit)]
1216        location: Location,
1217    },
1218
1219    #[snafu(display("Failed to get fulltext options for column {}", column_name))]
1220    GetFulltextOptions {
1221        column_name: String,
1222        source: datatypes::Error,
1223        #[snafu(implicit)]
1224        location: Location,
1225    },
1226
1227    #[snafu(display("Failed to set skipping index options for column {}", column_name))]
1228    SetSkippingIndexOptions {
1229        column_name: String,
1230        source: datatypes::Error,
1231        #[snafu(implicit)]
1232        location: Location,
1233    },
1234
1235    #[snafu(display("Failed to unset skipping index options for column {}", column_name))]
1236    UnsetSkippingIndexOptions {
1237        column_name: String,
1238        source: datatypes::Error,
1239        #[snafu(implicit)]
1240        location: Location,
1241    },
1242
1243    #[snafu(display("Failed to decode arrow ipc record batches"))]
1244    DecodeArrowIpc {
1245        #[snafu(source)]
1246        error: arrow::error::ArrowError,
1247        #[snafu(implicit)]
1248        location: Location,
1249    },
1250
1251    #[snafu(display("Failed to cast default value, reason: {}", reason))]
1252    CastDefaultValue {
1253        reason: String,
1254        source: datatypes::Error,
1255        #[snafu(implicit)]
1256        location: Location,
1257    },
1258
1259    #[snafu(display("Unexpected: {}", reason))]
1260    Unexpected {
1261        reason: String,
1262        #[snafu(implicit)]
1263        location: Location,
1264    },
1265
1266    #[snafu(display("Failed to encode/decode flight message"))]
1267    FlightCodec {
1268        source: common_grpc::Error,
1269        #[snafu(implicit)]
1270        location: Location,
1271    },
1272
1273    #[snafu(display("Invalid index option"))]
1274    InvalidIndexOption {
1275        #[snafu(implicit)]
1276        location: Location,
1277        #[snafu(source)]
1278        error: datatypes::error::Error,
1279    },
1280
1281    #[snafu(display("Sql common error"))]
1282    SqlCommon {
1283        source: common_sql::error::Error,
1284        #[snafu(implicit)]
1285        location: Location,
1286    },
1287}
1288
1289impl ErrorExt for MetadataError {
1290    fn status_code(&self) -> StatusCode {
1291        match self {
1292            Self::SqlCommon { source, .. } => source.status_code(),
1293            _ => StatusCode::InvalidArguments,
1294        }
1295    }
1296
1297    fn as_any(&self) -> &dyn Any {
1298        self
1299    }
1300}
1301
1302/// Set column fulltext options if it passed the validation.
1303///
1304/// Options allowed to modify:
1305/// * backend
1306///
1307/// Options not allowed to modify:
1308/// * analyzer
1309/// * case_sensitive
1310fn set_column_fulltext_options(
1311    column_meta: &mut ColumnMetadata,
1312    column_name: &str,
1313    options: &FulltextOptions,
1314    current_options: Option<FulltextOptions>,
1315) -> Result<()> {
1316    if let Some(current_options) = current_options {
1317        ensure!(
1318            current_options.analyzer == options.analyzer
1319                && current_options.case_sensitive == options.case_sensitive,
1320            InvalidColumnOptionSnafu {
1321                column_name,
1322                msg: format!(
1323                    "Cannot change analyzer or case_sensitive if FULLTEXT index is set before. Previous analyzer: {}, previous case_sensitive: {}",
1324                    current_options.analyzer, current_options.case_sensitive
1325                ),
1326            }
1327        );
1328    }
1329
1330    column_meta
1331        .column_schema
1332        .set_fulltext_options(options)
1333        .context(SetFulltextOptionsSnafu { column_name })?;
1334
1335    Ok(())
1336}
1337
1338fn unset_column_fulltext_options(
1339    column_meta: &mut ColumnMetadata,
1340    column_name: &str,
1341    current_options: Option<FulltextOptions>,
1342) -> Result<()> {
1343    if let Some(mut current_options) = current_options
1344        && current_options.enable
1345    {
1346        current_options.enable = false;
1347        column_meta
1348            .column_schema
1349            .set_fulltext_options(&current_options)
1350            .context(SetFulltextOptionsSnafu { column_name })?;
1351    } else {
1352        return InvalidColumnOptionSnafu {
1353            column_name,
1354            msg: "FULLTEXT index already disabled",
1355        }
1356        .fail();
1357    }
1358
1359    Ok(())
1360}
1361
1362#[cfg(test)]
1363mod test {
1364    use datatypes::prelude::ConcreteDataType;
1365    use datatypes::schema::{
1366        ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextBackend,
1367    };
1368    use datatypes::value::Value;
1369
1370    use super::*;
1371
1372    fn create_builder() -> RegionMetadataBuilder {
1373        RegionMetadataBuilder::new(RegionId::new(1234, 5678))
1374    }
1375
1376    fn build_test_region_metadata() -> RegionMetadata {
1377        let mut builder = create_builder();
1378        builder
1379            .push_column_metadata(ColumnMetadata {
1380                column_schema: ColumnSchema::new("a", ConcreteDataType::int64_datatype(), false),
1381                semantic_type: SemanticType::Tag,
1382                column_id: 1,
1383            })
1384            .push_column_metadata(ColumnMetadata {
1385                column_schema: ColumnSchema::new("b", ConcreteDataType::float64_datatype(), false),
1386                semantic_type: SemanticType::Field,
1387                column_id: 2,
1388            })
1389            .push_column_metadata(ColumnMetadata {
1390                column_schema: ColumnSchema::new(
1391                    "c",
1392                    ConcreteDataType::timestamp_millisecond_datatype(),
1393                    false,
1394                ),
1395                semantic_type: SemanticType::Timestamp,
1396                column_id: 3,
1397            })
1398            .primary_key(vec![1])
1399            .partition_expr_json(Some("".to_string()));
1400        builder.build().unwrap()
1401    }
1402
1403    #[test]
1404    fn test_region_metadata() {
1405        let region_metadata = build_test_region_metadata();
1406        assert_eq!("c", region_metadata.time_index_column().column_schema.name);
1407        assert_eq!(
1408            "a",
1409            region_metadata.column_by_id(1).unwrap().column_schema.name
1410        );
1411        assert_eq!(None, region_metadata.column_by_id(10));
1412    }
1413
1414    #[test]
1415    fn test_region_metadata_serde() {
1416        let region_metadata = build_test_region_metadata();
1417        let serialized = serde_json::to_string(&region_metadata).unwrap();
1418        let deserialized: RegionMetadata = serde_json::from_str(&serialized).unwrap();
1419        assert_eq!(region_metadata, deserialized);
1420    }
1421
1422    #[test]
1423    fn test_column_metadata_validate() {
1424        let mut builder = create_builder();
1425        let col = ColumnMetadata {
1426            column_schema: ColumnSchema::new("ts", ConcreteDataType::string_datatype(), false),
1427            semantic_type: SemanticType::Timestamp,
1428            column_id: 1,
1429        };
1430
1431        builder.push_column_metadata(col);
1432        let err = builder.build().unwrap_err();
1433        assert!(
1434            err.to_string()
1435                .contains("column `ts` is not timestamp type"),
1436            "unexpected err: {err}",
1437        );
1438    }
1439
1440    #[test]
1441    fn test_empty_region_metadata() {
1442        let builder = create_builder();
1443        let err = builder.build().unwrap_err();
1444        // A region must have a time index.
1445        assert!(
1446            err.to_string().contains("time index not found"),
1447            "unexpected err: {err}",
1448        );
1449    }
1450
1451    #[test]
1452    fn test_same_column_id() {
1453        let mut builder = create_builder();
1454        builder
1455            .push_column_metadata(ColumnMetadata {
1456                column_schema: ColumnSchema::new("a", ConcreteDataType::int64_datatype(), false),
1457                semantic_type: SemanticType::Tag,
1458                column_id: 1,
1459            })
1460            .push_column_metadata(ColumnMetadata {
1461                column_schema: ColumnSchema::new(
1462                    "b",
1463                    ConcreteDataType::timestamp_millisecond_datatype(),
1464                    false,
1465                ),
1466                semantic_type: SemanticType::Timestamp,
1467                column_id: 1,
1468            });
1469        let err = builder.build().unwrap_err();
1470        assert!(
1471            err.to_string()
1472                .contains("column a and b have the same column id"),
1473            "unexpected err: {err}",
1474        );
1475    }
1476
1477    #[test]
1478    fn test_duplicate_time_index() {
1479        let mut builder = create_builder();
1480        builder
1481            .push_column_metadata(ColumnMetadata {
1482                column_schema: ColumnSchema::new(
1483                    "a",
1484                    ConcreteDataType::timestamp_millisecond_datatype(),
1485                    false,
1486                ),
1487                semantic_type: SemanticType::Timestamp,
1488                column_id: 1,
1489            })
1490            .push_column_metadata(ColumnMetadata {
1491                column_schema: ColumnSchema::new(
1492                    "b",
1493                    ConcreteDataType::timestamp_millisecond_datatype(),
1494                    false,
1495                ),
1496                semantic_type: SemanticType::Timestamp,
1497                column_id: 2,
1498            });
1499        let err = builder.build().unwrap_err();
1500        assert!(
1501            err.to_string().contains("expect only one time index"),
1502            "unexpected err: {err}",
1503        );
1504    }
1505
1506    #[test]
1507    fn test_unknown_primary_key() {
1508        let mut builder = create_builder();
1509        builder
1510            .push_column_metadata(ColumnMetadata {
1511                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), false),
1512                semantic_type: SemanticType::Tag,
1513                column_id: 1,
1514            })
1515            .push_column_metadata(ColumnMetadata {
1516                column_schema: ColumnSchema::new(
1517                    "b",
1518                    ConcreteDataType::timestamp_millisecond_datatype(),
1519                    false,
1520                ),
1521                semantic_type: SemanticType::Timestamp,
1522                column_id: 2,
1523            })
1524            .primary_key(vec![3]);
1525        let err = builder.build().unwrap_err();
1526        assert!(
1527            err.to_string().contains("unknown column id 3"),
1528            "unexpected err: {err}",
1529        );
1530    }
1531
1532    #[test]
1533    fn test_same_primary_key() {
1534        let mut builder = create_builder();
1535        builder
1536            .push_column_metadata(ColumnMetadata {
1537                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), false),
1538                semantic_type: SemanticType::Tag,
1539                column_id: 1,
1540            })
1541            .push_column_metadata(ColumnMetadata {
1542                column_schema: ColumnSchema::new(
1543                    "b",
1544                    ConcreteDataType::timestamp_millisecond_datatype(),
1545                    false,
1546                ),
1547                semantic_type: SemanticType::Timestamp,
1548                column_id: 2,
1549            })
1550            .primary_key(vec![1, 1]);
1551        let err = builder.build().unwrap_err();
1552        assert!(
1553            err.to_string()
1554                .contains("duplicate column a in primary key"),
1555            "unexpected err: {err}",
1556        );
1557    }
1558
1559    #[test]
1560    fn test_in_time_index() {
1561        let mut builder = create_builder();
1562        builder
1563            .push_column_metadata(ColumnMetadata {
1564                column_schema: ColumnSchema::new(
1565                    "ts",
1566                    ConcreteDataType::timestamp_millisecond_datatype(),
1567                    false,
1568                ),
1569                semantic_type: SemanticType::Timestamp,
1570                column_id: 1,
1571            })
1572            .primary_key(vec![1]);
1573        let err = builder.build().unwrap_err();
1574        assert!(
1575            err.to_string()
1576                .contains("column ts is already a time index column"),
1577            "unexpected err: {err}",
1578        );
1579    }
1580
1581    #[test]
1582    fn test_nullable_time_index() {
1583        let mut builder = create_builder();
1584        builder.push_column_metadata(ColumnMetadata {
1585            column_schema: ColumnSchema::new(
1586                "ts",
1587                ConcreteDataType::timestamp_millisecond_datatype(),
1588                true,
1589            ),
1590            semantic_type: SemanticType::Timestamp,
1591            column_id: 1,
1592        });
1593        let err = builder.build().unwrap_err();
1594        assert!(
1595            err.to_string()
1596                .contains("time index column ts must be NOT NULL"),
1597            "unexpected err: {err}",
1598        );
1599    }
1600
1601    #[test]
1602    fn test_primary_key_semantic_type() {
1603        let mut builder = create_builder();
1604        builder
1605            .push_column_metadata(ColumnMetadata {
1606                column_schema: ColumnSchema::new(
1607                    "ts",
1608                    ConcreteDataType::timestamp_millisecond_datatype(),
1609                    false,
1610                ),
1611                semantic_type: SemanticType::Timestamp,
1612                column_id: 1,
1613            })
1614            .push_column_metadata(ColumnMetadata {
1615                column_schema: ColumnSchema::new("a", ConcreteDataType::float64_datatype(), true),
1616                semantic_type: SemanticType::Field,
1617                column_id: 2,
1618            })
1619            .primary_key(vec![2]);
1620        let err = builder.build().unwrap_err();
1621        assert!(
1622            err.to_string()
1623                .contains("semantic type of column a should be Tag, not Field"),
1624            "unexpected err: {err}",
1625        );
1626    }
1627
1628    #[test]
1629    fn test_primary_key_tag_num() {
1630        let mut builder = create_builder();
1631        builder
1632            .push_column_metadata(ColumnMetadata {
1633                column_schema: ColumnSchema::new(
1634                    "ts",
1635                    ConcreteDataType::timestamp_millisecond_datatype(),
1636                    false,
1637                ),
1638                semantic_type: SemanticType::Timestamp,
1639                column_id: 1,
1640            })
1641            .push_column_metadata(ColumnMetadata {
1642                column_schema: ColumnSchema::new("a", ConcreteDataType::string_datatype(), true),
1643                semantic_type: SemanticType::Tag,
1644                column_id: 2,
1645            })
1646            .push_column_metadata(ColumnMetadata {
1647                column_schema: ColumnSchema::new("b", ConcreteDataType::string_datatype(), true),
1648                semantic_type: SemanticType::Tag,
1649                column_id: 3,
1650            })
1651            .primary_key(vec![2]);
1652        let err = builder.build().unwrap_err();
1653        assert!(
1654            err.to_string()
1655                .contains("number of primary key columns 1 not equal to tag columns 2"),
1656            "unexpected err: {err}",
1657        );
1658    }
1659
1660    #[test]
1661    fn test_bump_version() {
1662        let mut region_metadata = build_test_region_metadata();
1663        let mut builder = RegionMetadataBuilder::from_existing(region_metadata.clone());
1664        builder.bump_version();
1665        let new_meta = builder.build().unwrap();
1666        region_metadata.schema_version += 1;
1667        assert_eq!(region_metadata, new_meta);
1668    }
1669
1670    fn new_column_metadata(name: &str, is_tag: bool, column_id: ColumnId) -> ColumnMetadata {
1671        let semantic_type = if is_tag {
1672            SemanticType::Tag
1673        } else {
1674            SemanticType::Field
1675        };
1676        ColumnMetadata {
1677            column_schema: ColumnSchema::new(name, ConcreteDataType::string_datatype(), true),
1678            semantic_type,
1679            column_id,
1680        }
1681    }
1682
1683    fn check_columns(metadata: &RegionMetadata, names: &[&str]) {
1684        let actual: Vec<_> = metadata
1685            .column_metadatas
1686            .iter()
1687            .map(|col| &col.column_schema.name)
1688            .collect();
1689        assert_eq!(names, actual);
1690    }
1691
1692    fn get_columns_default_constraint(
1693        metadata: &RegionMetadata,
1694        name: String,
1695    ) -> Option<Option<&ColumnDefaultConstraint>> {
1696        metadata.column_metadatas.iter().find_map(|col| {
1697            if col.column_schema.name == name {
1698                Some(col.column_schema.default_constraint())
1699            } else {
1700                None
1701            }
1702        })
1703    }
1704
1705    #[test]
1706    fn test_alter() {
1707        // a (tag), b (field), c (ts)
1708        let metadata = build_test_region_metadata();
1709        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1710        // tag d
1711        builder
1712            .alter(AlterKind::AddColumns {
1713                columns: vec![AddColumn {
1714                    column_metadata: new_column_metadata("d", true, 4),
1715                    location: None,
1716                }],
1717            })
1718            .unwrap();
1719        let metadata = builder.build().unwrap();
1720        check_columns(&metadata, &["a", "b", "c", "d"]);
1721        assert_eq!([1, 4], &metadata.primary_key[..]);
1722
1723        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1724        builder
1725            .alter(AlterKind::AddColumns {
1726                columns: vec![AddColumn {
1727                    column_metadata: new_column_metadata("e", false, 5),
1728                    location: Some(AddColumnLocation::First),
1729                }],
1730            })
1731            .unwrap();
1732        let metadata = builder.build().unwrap();
1733        check_columns(&metadata, &["e", "a", "b", "c", "d"]);
1734
1735        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1736        builder
1737            .alter(AlterKind::AddColumns {
1738                columns: vec![AddColumn {
1739                    column_metadata: new_column_metadata("f", false, 6),
1740                    location: Some(AddColumnLocation::After {
1741                        column_name: "b".to_string(),
1742                    }),
1743                }],
1744            })
1745            .unwrap();
1746        let metadata = builder.build().unwrap();
1747        check_columns(&metadata, &["e", "a", "b", "f", "c", "d"]);
1748
1749        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1750        builder
1751            .alter(AlterKind::AddColumns {
1752                columns: vec![AddColumn {
1753                    column_metadata: new_column_metadata("g", false, 7),
1754                    location: Some(AddColumnLocation::After {
1755                        column_name: "d".to_string(),
1756                    }),
1757                }],
1758            })
1759            .unwrap();
1760        let metadata = builder.build().unwrap();
1761        check_columns(&metadata, &["e", "a", "b", "f", "c", "d", "g"]);
1762
1763        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1764        builder
1765            .alter(AlterKind::DropColumns {
1766                names: vec!["g".to_string(), "e".to_string()],
1767            })
1768            .unwrap();
1769        let metadata = builder.build().unwrap();
1770        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1771
1772        let mut builder = RegionMetadataBuilder::from_existing(metadata.clone());
1773        builder
1774            .alter(AlterKind::DropColumns {
1775                names: vec!["a".to_string()],
1776            })
1777            .unwrap();
1778        // Build returns error as the primary key contains a.
1779        let err = builder.build().unwrap_err();
1780        assert_eq!(StatusCode::InvalidArguments, err.status_code());
1781
1782        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1783        let mut column_metadata = new_column_metadata("g", false, 8);
1784        let default_constraint = Some(ColumnDefaultConstraint::Value(Value::from("g")));
1785        column_metadata.column_schema = column_metadata
1786            .column_schema
1787            .with_default_constraint(default_constraint.clone())
1788            .unwrap();
1789        builder
1790            .alter(AlterKind::AddColumns {
1791                columns: vec![AddColumn {
1792                    column_metadata,
1793                    location: None,
1794                }],
1795            })
1796            .unwrap();
1797        let metadata = builder.build().unwrap();
1798        assert_eq!(
1799            get_columns_default_constraint(&metadata, "g".to_string()).unwrap(),
1800            default_constraint.as_ref()
1801        );
1802        check_columns(&metadata, &["a", "b", "f", "c", "d", "g"]);
1803
1804        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1805        builder
1806            .alter(AlterKind::DropDefaults {
1807                names: vec!["g".to_string()],
1808            })
1809            .unwrap();
1810        let metadata = builder.build().unwrap();
1811        assert_eq!(
1812            get_columns_default_constraint(&metadata, "g".to_string()).unwrap(),
1813            None
1814        );
1815        check_columns(&metadata, &["a", "b", "f", "c", "d", "g"]);
1816
1817        let mut builder: RegionMetadataBuilder = RegionMetadataBuilder::from_existing(metadata);
1818        builder
1819            .alter(AlterKind::DropColumns {
1820                names: vec!["g".to_string()],
1821            })
1822            .unwrap();
1823        let metadata = builder.build().unwrap();
1824        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1825
1826        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1827        builder
1828            .alter(AlterKind::ModifyColumnTypes {
1829                columns: vec![ModifyColumnType {
1830                    column_name: "b".to_string(),
1831                    target_type: ConcreteDataType::string_datatype(),
1832                }],
1833            })
1834            .unwrap();
1835        let metadata = builder.build().unwrap();
1836        check_columns(&metadata, &["a", "b", "f", "c", "d"]);
1837        let b_type = &metadata
1838            .column_by_name("b")
1839            .unwrap()
1840            .column_schema
1841            .data_type;
1842        assert_eq!(ConcreteDataType::string_datatype(), *b_type);
1843
1844        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1845        builder
1846            .alter(AlterKind::SetIndexes {
1847                options: vec![SetIndexOption::Fulltext {
1848                    column_name: "b".to_string(),
1849                    options: FulltextOptions::new_unchecked(
1850                        true,
1851                        FulltextAnalyzer::Chinese,
1852                        true,
1853                        FulltextBackend::Bloom,
1854                        1000,
1855                        0.01,
1856                    ),
1857                }],
1858            })
1859            .unwrap();
1860        let metadata = builder.build().unwrap();
1861        let a_fulltext_options = metadata
1862            .column_by_name("b")
1863            .unwrap()
1864            .column_schema
1865            .fulltext_options()
1866            .unwrap()
1867            .unwrap();
1868        assert!(a_fulltext_options.enable);
1869        assert_eq!(
1870            datatypes::schema::FulltextAnalyzer::Chinese,
1871            a_fulltext_options.analyzer
1872        );
1873        assert!(a_fulltext_options.case_sensitive);
1874
1875        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1876        builder
1877            .alter(AlterKind::UnsetIndexes {
1878                options: vec![UnsetIndexOption::Fulltext {
1879                    column_name: "b".to_string(),
1880                }],
1881            })
1882            .unwrap();
1883        let metadata = builder.build().unwrap();
1884        let a_fulltext_options = metadata
1885            .column_by_name("b")
1886            .unwrap()
1887            .column_schema
1888            .fulltext_options()
1889            .unwrap()
1890            .unwrap();
1891        assert!(!a_fulltext_options.enable);
1892        assert_eq!(
1893            datatypes::schema::FulltextAnalyzer::Chinese,
1894            a_fulltext_options.analyzer
1895        );
1896        assert!(a_fulltext_options.case_sensitive);
1897    }
1898
1899    #[test]
1900    fn test_add_if_not_exists() {
1901        // a (tag), b (field), c (ts)
1902        let metadata = build_test_region_metadata();
1903        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1904        // tag d
1905        builder
1906            .alter(AlterKind::AddColumns {
1907                columns: vec![
1908                    AddColumn {
1909                        column_metadata: new_column_metadata("d", true, 4),
1910                        location: None,
1911                    },
1912                    AddColumn {
1913                        column_metadata: new_column_metadata("d", true, 4),
1914                        location: None,
1915                    },
1916                ],
1917            })
1918            .unwrap();
1919        let metadata = builder.build().unwrap();
1920        check_columns(&metadata, &["a", "b", "c", "d"]);
1921        assert_eq!([1, 4], &metadata.primary_key[..]);
1922
1923        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1924        // field b.
1925        builder
1926            .alter(AlterKind::AddColumns {
1927                columns: vec![AddColumn {
1928                    column_metadata: new_column_metadata("b", false, 2),
1929                    location: None,
1930                }],
1931            })
1932            .unwrap();
1933        let metadata = builder.build().unwrap();
1934        check_columns(&metadata, &["a", "b", "c", "d"]);
1935    }
1936
1937    #[test]
1938    fn test_add_column_with_inverted_index() {
1939        // only set inverted index to true explicitly will this column be inverted indexed
1940
1941        // a (tag), b (field), c (ts)
1942        let metadata = build_test_region_metadata();
1943        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1944        // tag d, e
1945        let mut col = new_column_metadata("d", true, 4);
1946        col.column_schema.set_inverted_index(true);
1947        builder
1948            .alter(AlterKind::AddColumns {
1949                columns: vec![
1950                    AddColumn {
1951                        column_metadata: col,
1952                        location: None,
1953                    },
1954                    AddColumn {
1955                        column_metadata: new_column_metadata("e", true, 5),
1956                        location: None,
1957                    },
1958                ],
1959            })
1960            .unwrap();
1961        let metadata = builder.build().unwrap();
1962        check_columns(&metadata, &["a", "b", "c", "d", "e"]);
1963        assert_eq!([1, 4, 5], &metadata.primary_key[..]);
1964        let column_metadata = metadata.column_by_name("a").unwrap();
1965        assert!(!column_metadata.column_schema.is_inverted_indexed());
1966        let column_metadata = metadata.column_by_name("b").unwrap();
1967        assert!(!column_metadata.column_schema.is_inverted_indexed());
1968        let column_metadata = metadata.column_by_name("c").unwrap();
1969        assert!(!column_metadata.column_schema.is_inverted_indexed());
1970        let column_metadata = metadata.column_by_name("d").unwrap();
1971        assert!(column_metadata.column_schema.is_inverted_indexed());
1972        let column_metadata = metadata.column_by_name("e").unwrap();
1973        assert!(!column_metadata.column_schema.is_inverted_indexed());
1974    }
1975
1976    #[test]
1977    fn test_drop_if_exists() {
1978        // a (tag), b (field), c (ts)
1979        let metadata = build_test_region_metadata();
1980        let mut builder = RegionMetadataBuilder::from_existing(metadata);
1981        // field d, e
1982        builder
1983            .alter(AlterKind::AddColumns {
1984                columns: vec![
1985                    AddColumn {
1986                        column_metadata: new_column_metadata("d", false, 4),
1987                        location: None,
1988                    },
1989                    AddColumn {
1990                        column_metadata: new_column_metadata("e", false, 5),
1991                        location: None,
1992                    },
1993                ],
1994            })
1995            .unwrap();
1996        let metadata = builder.build().unwrap();
1997        check_columns(&metadata, &["a", "b", "c", "d", "e"]);
1998
1999        let mut builder = RegionMetadataBuilder::from_existing(metadata);
2000        builder
2001            .alter(AlterKind::DropColumns {
2002                names: vec!["b".to_string(), "b".to_string()],
2003            })
2004            .unwrap();
2005        let metadata = builder.build().unwrap();
2006        check_columns(&metadata, &["a", "c", "d", "e"]);
2007
2008        let mut builder = RegionMetadataBuilder::from_existing(metadata);
2009        builder
2010            .alter(AlterKind::DropColumns {
2011                names: vec!["b".to_string(), "e".to_string()],
2012            })
2013            .unwrap();
2014        let metadata = builder.build().unwrap();
2015        check_columns(&metadata, &["a", "c", "d"]);
2016    }
2017
2018    #[test]
2019    fn test_invalid_column_name() {
2020        let mut builder = create_builder();
2021        builder.push_column_metadata(ColumnMetadata {
2022            column_schema: ColumnSchema::new(
2023                "__sequence",
2024                ConcreteDataType::timestamp_millisecond_datatype(),
2025                false,
2026            ),
2027            semantic_type: SemanticType::Timestamp,
2028            column_id: 1,
2029        });
2030        let err = builder.build().unwrap_err();
2031        assert!(
2032            err.to_string()
2033                .contains("internal column name that can not be used"),
2034            "unexpected err: {err}",
2035        );
2036    }
2037
2038    #[test]
2039    fn test_allow_internal_column_name() {
2040        let mut builder = create_builder();
2041        builder
2042            .push_column_metadata(ColumnMetadata {
2043                column_schema: ColumnSchema::new(
2044                    "__primary_key",
2045                    ConcreteDataType::string_datatype(),
2046                    false,
2047                ),
2048                semantic_type: SemanticType::Tag,
2049                column_id: 1,
2050            })
2051            .push_column_metadata(ColumnMetadata {
2052                column_schema: ColumnSchema::new(
2053                    "ts",
2054                    ConcreteDataType::timestamp_millisecond_datatype(),
2055                    false,
2056                ),
2057                semantic_type: SemanticType::Timestamp,
2058                column_id: 2,
2059            })
2060            .primary_key(vec![1]);
2061
2062        let metadata = builder.build_without_validation().unwrap();
2063        assert_eq!(
2064            "__primary_key",
2065            metadata.column_metadatas[0].column_schema.name
2066        );
2067    }
2068
2069    #[test]
2070    fn test_build_without_validation() {
2071        // Primary key points to a Field column, which would normally fail validation.
2072        let mut builder = create_builder();
2073        builder
2074            .push_column_metadata(ColumnMetadata {
2075                column_schema: ColumnSchema::new(
2076                    "ts",
2077                    ConcreteDataType::timestamp_millisecond_datatype(),
2078                    false,
2079                ),
2080                semantic_type: SemanticType::Timestamp,
2081                column_id: 1,
2082            })
2083            .push_column_metadata(ColumnMetadata {
2084                column_schema: ColumnSchema::new(
2085                    "field",
2086                    ConcreteDataType::string_datatype(),
2087                    true,
2088                ),
2089                semantic_type: SemanticType::Field,
2090                column_id: 2,
2091            })
2092            .primary_key(vec![2]);
2093
2094        // Unvalidated build should succeed.
2095        let metadata = builder.build_without_validation().unwrap();
2096        assert_eq!(vec![2], metadata.primary_key);
2097
2098        // Validated build still rejects it.
2099        let mut builder = create_builder();
2100        builder
2101            .push_column_metadata(ColumnMetadata {
2102                column_schema: ColumnSchema::new(
2103                    "ts",
2104                    ConcreteDataType::timestamp_millisecond_datatype(),
2105                    false,
2106                ),
2107                semantic_type: SemanticType::Timestamp,
2108                column_id: 1,
2109            })
2110            .push_column_metadata(ColumnMetadata {
2111                column_schema: ColumnSchema::new(
2112                    "field",
2113                    ConcreteDataType::string_datatype(),
2114                    true,
2115                ),
2116                semantic_type: SemanticType::Field,
2117                column_id: 2,
2118            })
2119            .primary_key(vec![2]);
2120        let err = builder.build().unwrap_err();
2121        assert!(
2122            err.to_string()
2123                .contains("semantic type of column field should be Tag"),
2124            "unexpected err: {err}"
2125        );
2126    }
2127
2128    #[test]
2129    fn test_debug_for_column_metadata() {
2130        let region_metadata = build_test_region_metadata();
2131        let formatted = format!("{:?}", region_metadata);
2132        assert_eq!(
2133            formatted,
2134            "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(\"\") }"
2135        );
2136    }
2137
2138    #[test]
2139    fn test_region_metadata_deserialize_default_primary_key_encoding() {
2140        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}"#;
2141        let deserialized: RegionMetadata = serde_json::from_str(serialize).unwrap();
2142        assert_eq!(deserialized.primary_key_encoding, PrimaryKeyEncoding::Dense);
2143
2144        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"}"#;
2145        let deserialized: RegionMetadata = serde_json::from_str(serialize).unwrap();
2146        assert_eq!(
2147            deserialized.primary_key_encoding,
2148            PrimaryKeyEncoding::Sparse
2149        );
2150    }
2151}