tests_fuzz/translator/mysql/
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;
16
17use common_query::AddColumnLocation;
18use datatypes::data_type::ConcreteDataType;
19use sql::statements::concrete_data_type_to_sql_data_type;
20
21use crate::error::{Error, Result};
22use crate::ir::alter_expr::AlterTableOperation;
23use crate::ir::create_expr::ColumnOption;
24use crate::ir::{AlterTableExpr, Column};
25use crate::translator::common::CommonAlterTableTranslator;
26use crate::translator::DslTranslator;
27
28pub struct AlterTableExprTranslator;
29
30impl DslTranslator<AlterTableExpr, String> for AlterTableExprTranslator {
31    type Error = Error;
32
33    fn translate(&self, input: &AlterTableExpr) -> Result<String> {
34        Ok(match &input.alter_kinds {
35            AlterTableOperation::AddColumn { column, location } => {
36                Self::format_add_column(&input.table_name, column, location)
37            }
38            AlterTableOperation::RenameTable { new_table_name } => {
39                Self::format_rename(&input.table_name, new_table_name)
40            }
41            AlterTableOperation::ModifyDataType { column } => {
42                Self::format_modify_data_type(&input.table_name, column)
43            }
44            _ => CommonAlterTableTranslator.translate(input)?,
45        })
46    }
47}
48
49impl AlterTableExprTranslator {
50    fn format_rename(name: impl Display, new_name: impl Display) -> String {
51        format!("ALTER TABLE {name} RENAME {new_name};")
52    }
53
54    fn format_add_column(
55        name: impl Display,
56        column: &Column,
57        location: &Option<AddColumnLocation>,
58    ) -> String {
59        format!(
60            "{};",
61            vec![
62                format!(
63                    "ALTER TABLE {name} ADD COLUMN {}",
64                    Self::format_column(column)
65                ),
66                Self::format_location(location).unwrap_or_default(),
67            ]
68            .into_iter()
69            .filter(|s| !s.is_empty())
70            .collect::<Vec<_>>()
71            .join(" ")
72        )
73    }
74
75    fn format_modify_data_type(name: impl Display, column: &Column) -> String {
76        format!(
77            "ALTER TABLE {name} MODIFY COLUMN {};",
78            Self::format_column(column)
79        )
80    }
81
82    fn format_location(location: &Option<AddColumnLocation>) -> Option<String> {
83        location.as_ref().map(|location| match location {
84            AddColumnLocation::First => "FIRST".to_string(),
85            AddColumnLocation::After { column_name } => format!("AFTER {column_name}"),
86        })
87    }
88
89    fn format_column(column: &Column) -> String {
90        vec![
91            column.name.to_string(),
92            Self::format_column_type(&column.column_type),
93            Self::format_column_options(&column.options),
94        ]
95        .into_iter()
96        .filter(|s| !s.is_empty())
97        .collect::<Vec<_>>()
98        .join(" ")
99    }
100
101    fn format_column_type(column_type: &ConcreteDataType) -> String {
102        // Safety: We don't use the `Dictionary` type
103        concrete_data_type_to_sql_data_type(column_type)
104            .unwrap()
105            .to_string()
106    }
107
108    fn format_column_options(options: &[ColumnOption]) -> String {
109        options
110            .iter()
111            .map(|option| option.to_string())
112            .collect::<Vec<_>>()
113            .join(" ")
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use std::str::FromStr;
120
121    use common_base::readable_size::ReadableSize;
122    use common_query::AddColumnLocation;
123    use common_time::Duration;
124    use datatypes::data_type::ConcreteDataType;
125
126    use super::AlterTableExprTranslator;
127    use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption, Ttl};
128    use crate::ir::create_expr::ColumnOption;
129    use crate::ir::{AlterTableExpr, Column};
130    use crate::translator::DslTranslator;
131
132    #[test]
133    fn test_alter_table_expr() {
134        let alter_expr = AlterTableExpr {
135            table_name: "test".into(),
136            alter_kinds: AlterTableOperation::AddColumn {
137                column: Column {
138                    name: "host".into(),
139                    column_type: ConcreteDataType::string_datatype(),
140                    options: vec![ColumnOption::PrimaryKey],
141                },
142                location: Some(AddColumnLocation::First),
143            },
144        };
145
146        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
147        assert_eq!(
148            "ALTER TABLE test ADD COLUMN host STRING PRIMARY KEY FIRST;",
149            output
150        );
151
152        let alter_expr = AlterTableExpr {
153            table_name: "test".into(),
154            alter_kinds: AlterTableOperation::RenameTable {
155                new_table_name: "foo".into(),
156            },
157        };
158
159        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
160        assert_eq!("ALTER TABLE test RENAME foo;", output);
161
162        let alter_expr = AlterTableExpr {
163            table_name: "test".into(),
164            alter_kinds: AlterTableOperation::DropColumn { name: "foo".into() },
165        };
166
167        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
168        assert_eq!("ALTER TABLE test DROP COLUMN foo;", output);
169
170        let alter_expr = AlterTableExpr {
171            table_name: "test".into(),
172            alter_kinds: AlterTableOperation::ModifyDataType {
173                column: Column {
174                    name: "host".into(),
175                    column_type: ConcreteDataType::string_datatype(),
176                    options: vec![],
177                },
178            },
179        };
180
181        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
182        assert_eq!("ALTER TABLE test MODIFY COLUMN host STRING;", output);
183    }
184
185    #[test]
186    fn test_alter_table_expr_set_table_options() {
187        let alter_expr = AlterTableExpr {
188            table_name: "test".into(),
189            alter_kinds: AlterTableOperation::SetTableOptions {
190                options: vec![
191                    AlterTableOption::Ttl(Ttl::Duration(Duration::new_second(60))),
192                    AlterTableOption::TwcsTimeWindow(Duration::new_second(60)),
193                    AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1GB").unwrap()),
194                    AlterTableOption::TwcsTriggerFileNum(5),
195                ],
196            },
197        };
198
199        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
200        let expected = concat!(
201            "ALTER TABLE test SET 'ttl' = '60s', ",
202            "'compaction.twcs.time_window' = '60s', ",
203            "'compaction.twcs.max_output_file_size' = '1.0GiB', ",
204            "'compaction.twcs.trigger_file_num' = '5';"
205        );
206        assert_eq!(expected, output);
207    }
208
209    #[test]
210    fn test_alter_table_expr_unset_table_options() {
211        let alter_expr = AlterTableExpr {
212            table_name: "test".into(),
213            alter_kinds: AlterTableOperation::UnsetTableOptions {
214                keys: vec!["ttl".into(), "compaction.twcs.time_window".into()],
215            },
216        };
217
218        let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
219        let expected = "ALTER TABLE test UNSET 'ttl', 'compaction.twcs.time_window';";
220        assert_eq!(expected, output);
221    }
222}