mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-31 12:20:38 +00:00
* bump edition Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * format Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * gen keyword Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * lifetime and env var Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * one more gen fix Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * lifetime of temporaries in tail expressions Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * format again Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * clippy nested if Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * clippy let and return Signed-off-by: Ruihang Xia <waynestxia@gmail.com> --------- Signed-off-by: Ruihang Xia <waynestxia@gmail.com>
210 lines
7.5 KiB
Rust
210 lines
7.5 KiB
Rust
// Copyright 2023 Greptime Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use std::fmt::Display;
|
|
use std::str::FromStr;
|
|
|
|
use common_base::readable_size::ReadableSize;
|
|
use common_query::AddColumnLocation;
|
|
use common_time::{Duration, FOREVER, INSTANT};
|
|
use derive_builder::Builder;
|
|
use serde::{Deserialize, Serialize};
|
|
use store_api::mito_engine_options::{
|
|
APPEND_MODE_KEY, COMPACTION_TYPE, TTL_KEY, TWCS_MAX_OUTPUT_FILE_SIZE, TWCS_TIME_WINDOW,
|
|
TWCS_TRIGGER_FILE_NUM,
|
|
};
|
|
use strum::EnumIter;
|
|
|
|
use crate::error::{self, Result};
|
|
use crate::ir::{Column, Ident};
|
|
|
|
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
|
|
pub struct AlterTableExpr {
|
|
pub table_name: Ident,
|
|
pub alter_kinds: AlterTableOperation,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum AlterTableOperation {
|
|
/// `ADD [ COLUMN ] <column_def> [location]`
|
|
AddColumn {
|
|
column: Column,
|
|
location: Option<AddColumnLocation>,
|
|
},
|
|
/// `DROP COLUMN <name>`
|
|
DropColumn { name: Ident },
|
|
/// `RENAME <new_table_name>`
|
|
RenameTable { new_table_name: Ident },
|
|
/// `MODIFY COLUMN <column_name> <column_type>`
|
|
ModifyDataType { column: Column },
|
|
/// `SET <table attrs key> = <table attr value>`
|
|
SetTableOptions { options: Vec<AlterTableOption> },
|
|
/// `UNSET <table attrs key>`
|
|
UnsetTableOptions { keys: Vec<String> },
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
|
|
pub enum Ttl {
|
|
Duration(Duration),
|
|
Instant,
|
|
#[default]
|
|
Forever,
|
|
}
|
|
|
|
impl Display for Ttl {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Ttl::Duration(d) => write!(f, "{}", d),
|
|
Ttl::Instant => write!(f, "{}", INSTANT),
|
|
Ttl::Forever => write!(f, "{}", FOREVER),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, EnumIter, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
pub enum AlterTableOption {
|
|
Ttl(Ttl),
|
|
TwcsTimeWindow(Duration),
|
|
TwcsMaxOutputFileSize(ReadableSize),
|
|
TwcsTriggerFileNum(u64),
|
|
}
|
|
|
|
impl AlterTableOption {
|
|
pub fn key(&self) -> &str {
|
|
match self {
|
|
AlterTableOption::Ttl(_) => TTL_KEY,
|
|
AlterTableOption::TwcsTimeWindow(_) => TWCS_TIME_WINDOW,
|
|
AlterTableOption::TwcsMaxOutputFileSize(_) => TWCS_MAX_OUTPUT_FILE_SIZE,
|
|
AlterTableOption::TwcsTriggerFileNum(_) => TWCS_TRIGGER_FILE_NUM,
|
|
}
|
|
}
|
|
|
|
/// Parses the AlterTableOption from a key-value pair
|
|
fn parse_kv(key: &str, value: &str) -> Result<Self> {
|
|
match key {
|
|
TTL_KEY => {
|
|
let ttl = if value.to_lowercase() == INSTANT {
|
|
Ttl::Instant
|
|
} else if value.to_lowercase() == FOREVER {
|
|
Ttl::Forever
|
|
} else {
|
|
let duration = humantime::parse_duration(value).unwrap();
|
|
Ttl::Duration(duration.into())
|
|
};
|
|
Ok(AlterTableOption::Ttl(ttl))
|
|
}
|
|
TWCS_TRIGGER_FILE_NUM => {
|
|
let files = value.parse().unwrap();
|
|
Ok(AlterTableOption::TwcsTriggerFileNum(files))
|
|
}
|
|
TWCS_MAX_OUTPUT_FILE_SIZE => {
|
|
// may be "1M" instead of "1 MiB"
|
|
let value = if value.ends_with("B") {
|
|
value.to_string()
|
|
} else {
|
|
format!("{}B", value)
|
|
};
|
|
let size = ReadableSize::from_str(&value).unwrap();
|
|
Ok(AlterTableOption::TwcsMaxOutputFileSize(size))
|
|
}
|
|
TWCS_TIME_WINDOW => {
|
|
let time = humantime::parse_duration(value).unwrap();
|
|
Ok(AlterTableOption::TwcsTimeWindow(time.into()))
|
|
}
|
|
_ => error::UnexpectedSnafu {
|
|
violated: format!("Unknown table option key: {}", key),
|
|
}
|
|
.fail(),
|
|
}
|
|
}
|
|
|
|
/// Parses the AlterTableOption from comma-separated string
|
|
pub fn parse_kv_pairs(option_string: &str) -> Result<Vec<Self>> {
|
|
let mut options = vec![];
|
|
for pair in option_string.split(',') {
|
|
let pair = pair.trim();
|
|
let (key, value) = pair.split_once('=').unwrap();
|
|
let key = key.trim().replace("\'", "");
|
|
let value = value.trim().replace('\'', "");
|
|
// Currently we have only one compaction type, so we ignore it
|
|
// Cautious: COMPACTION_TYPE may be kept even if there are no compaction options enabled
|
|
if key == COMPACTION_TYPE || key == APPEND_MODE_KEY {
|
|
continue;
|
|
} else {
|
|
let option = AlterTableOption::parse_kv(&key, &value)?;
|
|
options.push(option);
|
|
}
|
|
}
|
|
Ok(options)
|
|
}
|
|
}
|
|
|
|
impl Display for AlterTableOption {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
AlterTableOption::Ttl(d) => write!(f, "'{}' = '{}'", TTL_KEY, d),
|
|
AlterTableOption::TwcsTimeWindow(d) => write!(f, "'{}' = '{}'", TWCS_TIME_WINDOW, d),
|
|
AlterTableOption::TwcsMaxOutputFileSize(s) => {
|
|
// Caution: to_string loses precision for ReadableSize
|
|
write!(f, "'{}' = '{}'", TWCS_MAX_OUTPUT_FILE_SIZE, s)
|
|
}
|
|
AlterTableOption::TwcsTriggerFileNum(u) => {
|
|
write!(f, "'{}' = '{}'", TWCS_TRIGGER_FILE_NUM, u)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_kv_pairs() {
|
|
let option_string = "compaction.twcs.max_output_file_size = '1M', compaction.type = 'twcs', ttl = 'forever'";
|
|
let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
|
|
assert_eq!(options.len(), 2);
|
|
assert_eq!(
|
|
options,
|
|
vec![
|
|
AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1MB").unwrap()),
|
|
AlterTableOption::Ttl(Ttl::Forever),
|
|
]
|
|
);
|
|
|
|
let option_string = "compaction.twcs.trigger_file_num = '5030469694939972912',
|
|
compaction.twcs.max_output_file_size = '15686.4PiB',
|
|
compaction.twcs.time_window = '2061999256ms',
|
|
compaction.type = 'twcs',
|
|
ttl = '1month 3days 15h 49m 8s 279ms'";
|
|
let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
|
|
assert_eq!(options.len(), 4);
|
|
let expected = vec![
|
|
AlterTableOption::TwcsTriggerFileNum(5030469694939972912),
|
|
AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("15686.4PiB").unwrap()),
|
|
AlterTableOption::TwcsTimeWindow(Duration::new_nanosecond(2_061_999_256_000_000)),
|
|
AlterTableOption::Ttl(Ttl::Duration(Duration::new_millisecond(
|
|
// A month is 2_630_016 seconds
|
|
2_630_016 * 1000
|
|
+ 3 * 24 * 60 * 60 * 1000
|
|
+ 15 * 60 * 60 * 1000
|
|
+ 49 * 60 * 1000
|
|
+ 8 * 1000
|
|
+ 279,
|
|
))),
|
|
];
|
|
assert_eq!(options, expected);
|
|
}
|
|
}
|