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