tests_fuzz/translator/postgres/
alter_expr.rs1use 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 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 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 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}