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