1use std::collections::{HashMap, HashSet};
18use std::fmt;
19use std::str::FromStr;
20
21use common_base::readable_size::ReadableSize;
22use common_datasource::object_store::oss::is_supported_in_oss;
23use common_datasource::object_store::s3::is_supported_in_s3;
24use common_query::AddColumnLocation;
25use common_time::TimeToLive;
26use common_time::range::TimestampRange;
27use datatypes::data_type::ConcreteDataType;
28use datatypes::prelude::VectorRef;
29use datatypes::schema::{
30 ColumnDefaultConstraint, ColumnSchema, FulltextOptions, SkippingIndexOptions,
31};
32use greptime_proto::v1::region::compact_request;
33use once_cell::sync::Lazy;
34use serde::{Deserialize, Serialize};
35use store_api::metric_engine_consts::{
36 LOGICAL_TABLE_METADATA_KEY, PHYSICAL_TABLE_METADATA_KEY, is_metric_engine_option_key,
37};
38use store_api::mito_engine_options::{
39 APPEND_MODE_KEY, COMPACTION_TYPE, MEMTABLE_BULK_ENCODE_BYTES_THRESHOLD,
40 MEMTABLE_BULK_ENCODE_ROW_THRESHOLD, MEMTABLE_BULK_MAX_MERGE_GROUPS,
41 MEMTABLE_BULK_MERGE_THRESHOLD, MEMTABLE_TYPE, MERGE_MODE_KEY, SST_FORMAT_KEY,
42 TWCS_FALLBACK_TO_LOCAL, TWCS_MAX_OUTPUT_FILE_SIZE, TWCS_TIME_WINDOW, TWCS_TRIGGER_FILE_NUM,
43 is_mito_engine_option_key,
44};
45use store_api::region_request::{SetRegionOption, UnsetRegionOption};
46
47use crate::error::{ParseTableOptionSnafu, Result};
48use crate::metadata::{TableId, TableVersion};
49use crate::table_reference::TableReference;
50
51mod semantic;
52pub use semantic::*;
53
54pub const FILE_TABLE_META_KEY: &str = "__private.file_table_meta";
55pub const FILE_TABLE_LOCATION_KEY: &str = "location";
56pub const FILE_TABLE_PATTERN_KEY: &str = "pattern";
57pub const FILE_TABLE_FORMAT_KEY: &str = "format";
58
59pub const TABLE_DATA_MODEL: &str = "table_data_model";
60pub const TABLE_DATA_MODEL_TRACE_V1: &str = "greptime_trace_v1";
61
62pub fn is_trace_v1_table(table_info: &crate::metadata::TableInfo) -> bool {
66 table_info
67 .meta
68 .options
69 .extra_options
70 .get(TABLE_DATA_MODEL)
71 .map(|v| v == TABLE_DATA_MODEL_TRACE_V1)
72 .unwrap_or(false)
73}
74
75pub const OTLP_METRIC_COMPAT_KEY: &str = "otlp_metric_compat";
76pub const OTLP_METRIC_COMPAT_PROM: &str = "prom";
77
78pub const VALID_TABLE_OPTION_KEYS: [&str; 14] = [
79 WRITE_BUFFER_SIZE_KEY,
81 TTL_KEY,
82 STORAGE_KEY,
83 COMMENT_KEY,
84 SKIP_WAL_KEY,
85 SST_FORMAT_KEY,
86 FILE_TABLE_LOCATION_KEY,
88 FILE_TABLE_FORMAT_KEY,
89 FILE_TABLE_PATTERN_KEY,
90 PHYSICAL_TABLE_METADATA_KEY,
92 LOGICAL_TABLE_METADATA_KEY,
93 TABLE_DATA_MODEL,
95 OTLP_METRIC_COMPAT_KEY,
96 REPARTITION_COLUMN_HINT_KEY,
97];
98
99pub const DDL_TIMEOUT: &str = "timeout";
100pub const DDL_WAIT: &str = "wait";
101
102pub const VALID_DDL_OPTION_KEYS: [&str; 2] = [DDL_TIMEOUT, DDL_WAIT];
103
104static VALID_DB_OPT_KEYS: Lazy<HashSet<&str>> = Lazy::new(|| {
106 let mut set = HashSet::new();
107 set.insert(TTL_KEY);
108 set.insert(STORAGE_KEY);
109 set.insert(MEMTABLE_TYPE);
110 set.insert(MEMTABLE_BULK_MERGE_THRESHOLD);
111 set.insert(MEMTABLE_BULK_ENCODE_ROW_THRESHOLD);
112 set.insert(MEMTABLE_BULK_ENCODE_BYTES_THRESHOLD);
113 set.insert(MEMTABLE_BULK_MAX_MERGE_GROUPS);
114 set.insert(APPEND_MODE_KEY);
115 set.insert(MERGE_MODE_KEY);
116 set.insert(SKIP_WAL_KEY);
117 set.insert(COMPACTION_TYPE);
118 set.insert(TWCS_FALLBACK_TO_LOCAL);
119 set.insert(TWCS_TIME_WINDOW);
120 set.insert(TWCS_TRIGGER_FILE_NUM);
121 set.insert(TWCS_MAX_OUTPUT_FILE_SIZE);
122 set.insert(SST_FORMAT_KEY);
123 set
124});
125
126pub fn validate_database_option(key: &str) -> bool {
128 VALID_DB_OPT_KEYS.contains(&key)
129}
130
131pub fn validate_table_option(key: &str) -> bool {
133 if is_supported_in_s3(key) {
134 return true;
135 }
136
137 if is_supported_in_oss(key) {
138 return true;
139 }
140
141 if is_mito_engine_option_key(key) {
142 return true;
143 }
144
145 if is_metric_engine_option_key(key) {
146 return true;
147 }
148
149 if is_semantic_option_key(key) {
152 return true;
153 }
154
155 VALID_TABLE_OPTION_KEYS.contains(&key) || VALID_DDL_OPTION_KEYS.contains(&key)
156}
157
158#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
159#[serde(default)]
160pub struct TableOptions {
161 pub write_buffer_size: Option<ReadableSize>,
163 pub ttl: Option<TimeToLive>,
165 pub skip_wal: bool,
167 pub extra_options: HashMap<String, String>,
169}
170
171pub const WRITE_BUFFER_SIZE_KEY: &str = store_api::mito_engine_options::WRITE_BUFFER_SIZE_KEY;
172pub const TTL_KEY: &str = store_api::mito_engine_options::TTL_KEY;
173pub const STORAGE_KEY: &str = "storage";
174pub const COMMENT_KEY: &str = "comment";
175pub const AUTO_CREATE_TABLE_KEY: &str = "auto_create_table";
176pub const SKIP_WAL_KEY: &str = store_api::mito_engine_options::SKIP_WAL_KEY;
177pub const TRACE_TABLE_PARTITIONS_HINT_KEY: &str = "trace_table_partitions";
178pub const REPARTITION_COLUMN_HINT_KEY: &str = "repartition.column.hint";
179
180impl TableOptions {
181 pub fn try_from_iter<T: ToString, U: IntoIterator<Item = (T, T)>>(
182 iter: U,
183 ) -> Result<TableOptions> {
184 let mut options = TableOptions::default();
185
186 let kvs: HashMap<String, String> = iter
187 .into_iter()
188 .map(|(k, v)| (k.to_string(), v.to_string()))
189 .collect();
190
191 if let Some(write_buffer_size) = kvs.get(WRITE_BUFFER_SIZE_KEY) {
192 let size = ReadableSize::from_str(write_buffer_size).map_err(|_| {
193 ParseTableOptionSnafu {
194 key: WRITE_BUFFER_SIZE_KEY,
195 value: write_buffer_size,
196 }
197 .build()
198 })?;
199 options.write_buffer_size = Some(size)
200 }
201
202 if let Some(ttl) = kvs.get(TTL_KEY) {
203 let ttl_value = TimeToLive::from_humantime_or_str(ttl).map_err(|_| {
204 ParseTableOptionSnafu {
205 key: TTL_KEY,
206 value: ttl,
207 }
208 .build()
209 })?;
210 options.ttl = Some(ttl_value);
211 }
212
213 if let Some(skip_wal) = kvs.get(SKIP_WAL_KEY) {
214 options.skip_wal = skip_wal.parse().map_err(|_| {
215 ParseTableOptionSnafu {
216 key: SKIP_WAL_KEY,
217 value: skip_wal,
218 }
219 .build()
220 })?;
221 }
222
223 options.extra_options = HashMap::from_iter(
224 kvs.into_iter()
225 .filter(|(k, _)| k != WRITE_BUFFER_SIZE_KEY && k != TTL_KEY),
226 );
227
228 Ok(options)
229 }
230}
231
232impl fmt::Display for TableOptions {
233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234 let mut key_vals = vec![];
235 if let Some(size) = self.write_buffer_size {
236 key_vals.push(format!("{}={}", WRITE_BUFFER_SIZE_KEY, size));
237 }
238
239 if let Some(ttl) = self.ttl.map(|ttl| ttl.to_string()) {
240 key_vals.push(format!("{}={}", TTL_KEY, ttl));
241 }
242
243 if self.skip_wal && !self.extra_options.contains_key(SKIP_WAL_KEY) {
244 key_vals.push(format!("{}={}", SKIP_WAL_KEY, self.skip_wal));
245 }
246
247 for (k, v) in &self.extra_options {
248 key_vals.push(format!("{}={}", k, v));
249 }
250
251 write!(f, "{}", key_vals.join(" "))
252 }
253}
254
255impl From<&TableOptions> for HashMap<String, String> {
256 fn from(opts: &TableOptions) -> Self {
257 let mut res = HashMap::with_capacity(3 + opts.extra_options.len());
258 if let Some(write_buffer_size) = opts.write_buffer_size {
259 let _ = res.insert(
260 WRITE_BUFFER_SIZE_KEY.to_string(),
261 write_buffer_size.to_string(),
262 );
263 }
264 if let Some(ttl_str) = opts.ttl.map(|ttl| ttl.to_string()) {
265 let _ = res.insert(TTL_KEY.to_string(), ttl_str);
266 }
267 if opts.skip_wal {
268 let _ = res.insert(SKIP_WAL_KEY.to_string(), true.to_string());
269 }
270 res.extend(opts.extra_options.clone());
271 res
272 }
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct AlterTableRequest {
278 pub catalog_name: String,
279 pub schema_name: String,
280 pub table_name: String,
281 pub table_id: TableId,
282 pub alter_kind: AlterKind,
283 pub table_version: Option<TableVersion>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct AddColumnRequest {
290 pub column_schema: ColumnSchema,
291 pub is_key: bool,
292 pub location: Option<AddColumnLocation>,
293 pub add_if_not_exists: bool,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct ModifyColumnTypeRequest {
300 pub column_name: String,
301 pub target_type: ConcreteDataType,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub enum AlterKind {
306 AddColumns {
307 columns: Vec<AddColumnRequest>,
308 },
309 DropColumns {
310 names: Vec<String>,
311 },
312 ModifyColumnTypes {
313 columns: Vec<ModifyColumnTypeRequest>,
314 },
315 RenameTable {
316 new_table_name: String,
317 },
318 SetTableOptions {
319 options: Vec<SetRegionOption>,
320 },
321 UnsetTableOptions {
322 keys: Vec<UnsetRegionOption>,
323 },
324 SetRepartitionColumnHint {
325 column_name: String,
326 },
327 UnsetRepartitionColumnHint,
328 SetIndexes {
329 options: Vec<SetIndexOption>,
330 },
331 UnsetIndexes {
332 options: Vec<UnsetIndexOption>,
333 },
334 DropDefaults {
335 names: Vec<String>,
336 },
337 SetDefaults {
338 defaults: Vec<SetDefaultRequest>,
339 },
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct SetDefaultRequest {
344 pub column_name: String,
345 pub default_constraint: Option<ColumnDefaultConstraint>,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub enum SetIndexOption {
350 Fulltext {
351 column_name: String,
352 options: FulltextOptions,
353 },
354 Inverted {
355 column_name: String,
356 },
357 Skipping {
358 column_name: String,
359 options: SkippingIndexOptions,
360 },
361}
362
363impl SetIndexOption {
364 pub fn column_name(&self) -> &str {
366 match self {
367 SetIndexOption::Fulltext { column_name, .. } => column_name,
368 SetIndexOption::Inverted { column_name, .. } => column_name,
369 SetIndexOption::Skipping { column_name, .. } => column_name,
370 }
371 }
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub enum UnsetIndexOption {
376 Fulltext { column_name: String },
377 Inverted { column_name: String },
378 Skipping { column_name: String },
379}
380
381impl UnsetIndexOption {
382 pub fn column_name(&self) -> &str {
384 match self {
385 UnsetIndexOption::Fulltext { column_name, .. } => column_name,
386 UnsetIndexOption::Inverted { column_name, .. } => column_name,
387 UnsetIndexOption::Skipping { column_name, .. } => column_name,
388 }
389 }
390}
391
392#[derive(Debug)]
393pub struct InsertRequest {
394 pub catalog_name: String,
395 pub schema_name: String,
396 pub table_name: String,
397 pub columns_values: HashMap<String, VectorRef>,
398}
399
400#[derive(Debug)]
402pub struct DeleteRequest {
403 pub catalog_name: String,
404 pub schema_name: String,
405 pub table_name: String,
406 pub key_column_values: HashMap<String, VectorRef>,
410}
411
412#[derive(Debug)]
413pub enum CopyDirection {
414 Export,
415 Import,
416}
417
418#[derive(Debug)]
420pub struct CopyTableRequest {
421 pub catalog_name: String,
422 pub schema_name: String,
423 pub table_name: String,
424 pub location: String,
425 pub with: HashMap<String, String>,
426 pub connection: HashMap<String, String>,
427 pub pattern: Option<String>,
428 pub direction: CopyDirection,
429 pub timestamp_range: Option<TimestampRange>,
430 pub limit: Option<u64>,
431}
432
433#[derive(Debug, Clone, Default)]
434pub struct FlushTableRequest {
435 pub catalog_name: String,
436 pub schema_name: String,
437 pub table_name: String,
438}
439
440#[derive(Debug, Clone, Default)]
441pub struct BuildIndexTableRequest {
442 pub catalog_name: String,
443 pub schema_name: String,
444 pub table_name: String,
445}
446
447#[derive(Debug, Clone, PartialEq)]
448pub struct CompactTableRequest {
449 pub catalog_name: String,
450 pub schema_name: String,
451 pub table_name: String,
452 pub compact_options: compact_request::Options,
453 pub parallelism: u32,
454 pub time_range: Option<TimestampRange>,
455}
456
457impl Default for CompactTableRequest {
458 fn default() -> Self {
459 Self {
460 catalog_name: Default::default(),
461 schema_name: Default::default(),
462 table_name: Default::default(),
463 compact_options: compact_request::Options::Regular(Default::default()),
464 parallelism: 1,
465 time_range: None,
466 }
467 }
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct TruncateTableRequest {
473 pub catalog_name: String,
474 pub schema_name: String,
475 pub table_name: String,
476 pub table_id: TableId,
477}
478
479impl TruncateTableRequest {
480 pub fn table_ref(&self) -> TableReference<'_> {
481 TableReference {
482 catalog: &self.catalog_name,
483 schema: &self.schema_name,
484 table: &self.table_name,
485 }
486 }
487}
488
489#[derive(Debug, Clone, Default, Deserialize, Serialize)]
490pub struct CopyDatabaseRequest {
491 pub catalog_name: String,
492 pub schema_name: String,
493 pub location: String,
494 pub with: HashMap<String, String>,
495 pub connection: HashMap<String, String>,
496 pub time_range: Option<TimestampRange>,
497}
498
499#[derive(Debug, Clone, Default, Deserialize, Serialize)]
500pub struct CopyQueryToRequest {
501 pub location: String,
502 pub with: HashMap<String, String>,
503 pub connection: HashMap<String, String>,
504}
505
506#[cfg(test)]
507mod tests {
508 use std::time::Duration;
509
510 use super::*;
511
512 #[test]
513 fn test_validate_table_option() {
514 assert!(validate_table_option(FILE_TABLE_LOCATION_KEY));
515 assert!(validate_table_option(FILE_TABLE_FORMAT_KEY));
516 assert!(validate_table_option(FILE_TABLE_PATTERN_KEY));
517 assert!(validate_table_option(TTL_KEY));
518 assert!(validate_table_option(WRITE_BUFFER_SIZE_KEY));
519 assert!(validate_table_option(STORAGE_KEY));
520 assert!(validate_table_option(MEMTABLE_BULK_MERGE_THRESHOLD));
521 assert!(validate_table_option(REPARTITION_COLUMN_HINT_KEY));
522 assert!(!validate_table_option("foo"));
523
524 assert!(validate_table_option(SEMANTIC_SIGNAL_TYPE));
526 assert!(validate_table_option(SEMANTIC_METRIC_TYPE));
527 assert!(!validate_table_option("greptime.semantic.future.key"));
529 assert!(!validate_table_option("greptime.semanticx"));
530 assert!(!validate_table_option(SEMANTIC_PER_TABLE_INDEX_KEY));
531 }
532
533 #[test]
534 fn test_validate_database_option() {
535 assert!(validate_database_option(MEMTABLE_TYPE));
536 assert!(validate_database_option(MEMTABLE_BULK_MERGE_THRESHOLD));
537 assert!(validate_database_option(MEMTABLE_BULK_ENCODE_ROW_THRESHOLD));
538 assert!(validate_database_option(
539 MEMTABLE_BULK_ENCODE_BYTES_THRESHOLD
540 ));
541 assert!(validate_database_option(MEMTABLE_BULK_MAX_MERGE_GROUPS));
542 assert!(!validate_database_option("foo"));
543 }
544
545 #[test]
546 fn test_serialize_table_options() {
547 let options = TableOptions {
548 write_buffer_size: None,
549 ttl: Some(Duration::from_secs(1000).into()),
550 extra_options: HashMap::new(),
551 skip_wal: false,
552 };
553 let serialized = serde_json::to_string(&options).unwrap();
554 let deserialized: TableOptions = serde_json::from_str(&serialized).unwrap();
555 assert_eq!(options, deserialized);
556 }
557
558 #[test]
559 fn test_convert_hashmap_between_table_options() {
560 let options = TableOptions {
561 write_buffer_size: Some(ReadableSize::mb(128)),
562 ttl: Some(Duration::from_secs(1000).into()),
563 extra_options: HashMap::new(),
564 skip_wal: false,
565 };
566 let serialized_map = HashMap::from(&options);
567 let serialized = TableOptions::try_from_iter(&serialized_map).unwrap();
568 assert_eq!(options, serialized);
569
570 let options = TableOptions {
571 write_buffer_size: None,
572 ttl: None,
573 extra_options: HashMap::from([(SKIP_WAL_KEY.to_string(), true.to_string())]),
574 skip_wal: true,
575 };
576 let serialized_map = HashMap::from(&options);
577 assert_eq!(
578 Some("true"),
579 serialized_map.get(SKIP_WAL_KEY).map(String::as_str)
580 );
581 let serialized = TableOptions::try_from_iter(&serialized_map).unwrap();
582 assert_eq!(options, serialized);
583
584 let options = TableOptions {
585 write_buffer_size: None,
586 ttl: Default::default(),
587 extra_options: HashMap::new(),
588 skip_wal: false,
589 };
590 let serialized_map = HashMap::from(&options);
591 let serialized = TableOptions::try_from_iter(&serialized_map).unwrap();
592 assert_eq!(options, serialized);
593
594 let options = TableOptions {
595 write_buffer_size: Some(ReadableSize::mb(128)),
596 ttl: Some(Duration::from_secs(1000).into()),
597 extra_options: HashMap::from([("a".to_string(), "A".to_string())]),
598 skip_wal: false,
599 };
600 let serialized_map = HashMap::from(&options);
601 let serialized = TableOptions::try_from_iter(&serialized_map).unwrap();
602 assert_eq!(options, serialized);
603
604 let options = TableOptions {
605 extra_options: HashMap::from([(SKIP_WAL_KEY.to_string(), false.to_string())]),
606 skip_wal: false,
607 ..Default::default()
608 };
609 let serialized_map = HashMap::from(&options);
610 let serialized = TableOptions::try_from_iter(&serialized_map).unwrap();
611 assert_eq!(options, serialized);
612 }
613
614 #[test]
615 fn test_table_options_to_string() {
616 let options = TableOptions {
617 write_buffer_size: Some(ReadableSize::mb(128)),
618 ttl: Some(Duration::from_secs(1000).into()),
619 extra_options: HashMap::new(),
620 skip_wal: false,
621 };
622
623 assert_eq!(
624 "write_buffer_size=128.0MiB ttl=16m 40s",
625 options.to_string()
626 );
627
628 let options = TableOptions {
629 write_buffer_size: Some(ReadableSize::mb(128)),
630 ttl: Some(Duration::from_secs(1000).into()),
631 extra_options: HashMap::from([("a".to_string(), "A".to_string())]),
632 skip_wal: false,
633 };
634
635 assert_eq!(
636 "write_buffer_size=128.0MiB ttl=16m 40s a=A",
637 options.to_string()
638 );
639
640 let options = TableOptions {
641 write_buffer_size: Some(ReadableSize::mb(128)),
642 ttl: Some(Duration::from_secs(1000).into()),
643 extra_options: HashMap::new(),
644 skip_wal: true,
645 };
646 assert_eq!(
647 "write_buffer_size=128.0MiB ttl=16m 40s skip_wal=true",
648 options.to_string()
649 );
650
651 let options = TableOptions {
652 write_buffer_size: None,
653 ttl: None,
654 extra_options: HashMap::from([(SKIP_WAL_KEY.to_string(), "false".to_string())]),
655 skip_wal: false,
656 };
657 assert_eq!("skip_wal=false", options.to_string());
658 }
659}