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