tests_fuzz/ir/
alter_expr.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
15use std::fmt::Display;
16use std::str::FromStr;
17
18use common_base::readable_size::ReadableSize;
19use common_query::AddColumnLocation;
20use common_time::{Duration, FOREVER, INSTANT};
21use derive_builder::Builder;
22use serde::{Deserialize, Serialize};
23use store_api::mito_engine_options::{
24    APPEND_MODE_KEY, COMPACTION_TYPE, TTL_KEY, TWCS_MAX_OUTPUT_FILE_SIZE, TWCS_TIME_WINDOW,
25    TWCS_TRIGGER_FILE_NUM,
26};
27use strum::EnumIter;
28
29use crate::error::{self, Result};
30use crate::ir::{Column, Ident};
31
32#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
33pub struct AlterTableExpr {
34    pub table_name: Ident,
35    pub alter_kinds: AlterTableOperation,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub enum AlterTableOperation {
40    /// `ADD [ COLUMN ] <column_def> [location]`
41    AddColumn {
42        column: Column,
43        location: Option<AddColumnLocation>,
44    },
45    /// `DROP COLUMN <name>`
46    DropColumn { name: Ident },
47    /// `RENAME <new_table_name>`
48    RenameTable { new_table_name: Ident },
49    /// `MODIFY COLUMN <column_name> <column_type>`
50    ModifyDataType { column: Column },
51    /// `SET <table attrs key> = <table attr value>`
52    SetTableOptions { options: Vec<AlterTableOption> },
53    /// `UNSET <table attrs key>`
54    UnsetTableOptions { keys: Vec<String> },
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
58pub enum Ttl {
59    Duration(Duration),
60    Instant,
61    #[default]
62    Forever,
63}
64
65impl Display for Ttl {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            Ttl::Duration(d) => write!(f, "{}", d),
69            Ttl::Instant => write!(f, "{}", INSTANT),
70            Ttl::Forever => write!(f, "{}", FOREVER),
71        }
72    }
73}
74
75#[derive(Debug, EnumIter, Clone, Serialize, Deserialize, PartialEq, Eq)]
76pub enum AlterTableOption {
77    Ttl(Ttl),
78    TwcsTimeWindow(Duration),
79    TwcsMaxOutputFileSize(ReadableSize),
80    TwcsTriggerFileNum(u64),
81}
82
83impl AlterTableOption {
84    pub fn key(&self) -> &str {
85        match self {
86            AlterTableOption::Ttl(_) => TTL_KEY,
87            AlterTableOption::TwcsTimeWindow(_) => TWCS_TIME_WINDOW,
88            AlterTableOption::TwcsMaxOutputFileSize(_) => TWCS_MAX_OUTPUT_FILE_SIZE,
89            AlterTableOption::TwcsTriggerFileNum(_) => TWCS_TRIGGER_FILE_NUM,
90        }
91    }
92
93    /// Parses the AlterTableOption from a key-value pair
94    fn parse_kv(key: &str, value: &str) -> Result<Self> {
95        match key {
96            TTL_KEY => {
97                let ttl = if value.to_lowercase() == INSTANT {
98                    Ttl::Instant
99                } else if value.to_lowercase() == FOREVER {
100                    Ttl::Forever
101                } else {
102                    let duration = humantime::parse_duration(value).unwrap();
103                    Ttl::Duration(duration.into())
104                };
105                Ok(AlterTableOption::Ttl(ttl))
106            }
107            TWCS_TRIGGER_FILE_NUM => {
108                let files = value.parse().unwrap();
109                Ok(AlterTableOption::TwcsTriggerFileNum(files))
110            }
111            TWCS_MAX_OUTPUT_FILE_SIZE => {
112                // may be "1M" instead of "1 MiB"
113                let value = if value.ends_with("B") {
114                    value.to_string()
115                } else {
116                    format!("{}B", value)
117                };
118                let size = ReadableSize::from_str(&value).unwrap();
119                Ok(AlterTableOption::TwcsMaxOutputFileSize(size))
120            }
121            TWCS_TIME_WINDOW => {
122                let time = humantime::parse_duration(value).unwrap();
123                Ok(AlterTableOption::TwcsTimeWindow(time.into()))
124            }
125            _ => error::UnexpectedSnafu {
126                violated: format!("Unknown table option key: {}", key),
127            }
128            .fail(),
129        }
130    }
131
132    /// Parses the AlterTableOption from comma-separated string
133    pub fn parse_kv_pairs(option_string: &str) -> Result<Vec<Self>> {
134        let mut options = vec![];
135        for pair in option_string.split(',') {
136            let pair = pair.trim();
137            let (key, value) = pair.split_once('=').unwrap();
138            let key = key.trim().replace("\'", "");
139            let value = value.trim().replace('\'', "");
140            // Currently we have only one compaction type, so we ignore it
141            // Cautious: COMPACTION_TYPE may be kept even if there are no compaction options enabled
142            if key == COMPACTION_TYPE || key == APPEND_MODE_KEY {
143                continue;
144            } else {
145                let option = AlterTableOption::parse_kv(&key, &value)?;
146                options.push(option);
147            }
148        }
149        Ok(options)
150    }
151}
152
153impl Display for AlterTableOption {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        match self {
156            AlterTableOption::Ttl(d) => write!(f, "'{}' = '{}'", TTL_KEY, d),
157            AlterTableOption::TwcsTimeWindow(d) => write!(f, "'{}' = '{}'", TWCS_TIME_WINDOW, d),
158            AlterTableOption::TwcsMaxOutputFileSize(s) => {
159                // Caution: to_string loses precision for ReadableSize
160                write!(f, "'{}' = '{}'", TWCS_MAX_OUTPUT_FILE_SIZE, s)
161            }
162            AlterTableOption::TwcsTriggerFileNum(u) => {
163                write!(f, "'{}' = '{}'", TWCS_TRIGGER_FILE_NUM, u)
164            }
165        }
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_parse_kv_pairs() {
175        let option_string =
176            "compaction.twcs.max_output_file_size = '1M', compaction.type = 'twcs', ttl = 'forever'";
177        let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
178        assert_eq!(options.len(), 2);
179        assert_eq!(
180            options,
181            vec![
182                AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1MB").unwrap()),
183                AlterTableOption::Ttl(Ttl::Forever),
184            ]
185        );
186
187        let option_string = "compaction.twcs.trigger_file_num = '5030469694939972912',
188  compaction.twcs.max_output_file_size = '15686.4PiB',
189  compaction.twcs.time_window = '2061999256ms',
190  compaction.type = 'twcs',
191  ttl = '1month 3days 15h 49m 8s 279ms'";
192        let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
193        assert_eq!(options.len(), 4);
194        let expected = vec![
195            AlterTableOption::TwcsTriggerFileNum(5030469694939972912),
196            AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("15686.4PiB").unwrap()),
197            AlterTableOption::TwcsTimeWindow(Duration::new_nanosecond(2_061_999_256_000_000)),
198            AlterTableOption::Ttl(Ttl::Duration(Duration::new_millisecond(
199                // A month is 2_630_016 seconds
200                2_630_016 * 1000
201                    + 3 * 24 * 60 * 60 * 1000
202                    + 15 * 60 * 60 * 1000
203                    + 49 * 60 * 1000
204                    + 8 * 1000
205                    + 279,
206            ))),
207        ];
208        assert_eq!(options, expected);
209    }
210}