tests_fuzz/translator/mysql/
create_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 datatypes::data_type::ConcreteDataType;
16use datatypes::value::Value;
17use partition::partition::PartitionBound;
18use sql::statements::concrete_data_type_to_sql_data_type;
19
20use crate::error::{Error, Result};
21use crate::ir::create_expr::ColumnOption;
22use crate::ir::{Column, CreateDatabaseExpr, CreateTableExpr};
23use crate::translator::DslTranslator;
24
25pub struct CreateTableExprTranslator;
26
27impl DslTranslator<CreateTableExpr, String> for CreateTableExprTranslator {
28    type Error = Error;
29
30    fn translate(&self, input: &CreateTableExpr) -> Result<String> {
31        Ok(format!(
32            "CREATE TABLE{}{}(\n{}\n)\n{}{};",
33            Self::create_if_not_exists(input),
34            input.table_name,
35            Self::format_columns(input),
36            Self::format_table_options(input),
37            Self::format_with_clause(input),
38        ))
39    }
40}
41
42impl CreateTableExprTranslator {
43    fn create_if_not_exists(input: &CreateTableExpr) -> &str {
44        if input.if_not_exists {
45            " IF NOT EXISTS "
46        } else {
47            " "
48        }
49    }
50
51    fn format_columns(input: &CreateTableExpr) -> String {
52        let mut output =
53            Vec::with_capacity(input.columns.len() + (!input.primary_keys.is_empty()) as usize);
54        for column in &input.columns {
55            output.push(Self::format_column(column));
56        }
57        if let Some(primary_keys) = Self::format_primary_keys(input) {
58            output.push(primary_keys);
59        }
60        output.join(",\n")
61    }
62
63    fn format_column(column: &Column) -> String {
64        vec![
65            column.name.to_string(),
66            Self::format_column_type(&column.column_type),
67            Self::format_column_options(&column.options),
68        ]
69        .into_iter()
70        .filter(|s| !s.is_empty())
71        .collect::<Vec<_>>()
72        .join(" ")
73    }
74
75    fn format_partition(input: &CreateTableExpr) -> Option<String> {
76        input.partition.as_ref().map(|partition| {
77            format!(
78                "PARTITION ON COLUMNS({}) (\n{}\n)",
79                partition.partition_columns().join(", "),
80                partition
81                    .partition_bounds()
82                    .iter()
83                    .map(Self::format_partition_bound)
84                    .collect::<Vec<_>>()
85                    .join(",\n")
86            )
87        })
88    }
89
90    fn format_partition_bound(bound: &PartitionBound) -> String {
91        match bound {
92            PartitionBound::Value(v) => match v {
93                Value::String(v) => format!("'{}'", v.as_utf8()),
94                _ => format!("{v}"),
95            },
96            PartitionBound::MaxValue => "MAXVALUE".to_string(),
97            PartitionBound::Expr(expr) => expr.to_parser_expr().to_string(),
98        }
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        let mut output = Vec::with_capacity(options.len());
110        for option in options {
111            if option != &ColumnOption::PrimaryKey {
112                output.push(option.to_string());
113            }
114        }
115        output.join(" ")
116    }
117
118    fn format_primary_keys(input: &CreateTableExpr) -> Option<String> {
119        if input.primary_keys.is_empty() {
120            None
121        } else {
122            Some(format!(
123                "PRIMARY KEY({})",
124                input
125                    .primary_keys
126                    .iter()
127                    .map(|idx| input.columns[*idx].name.to_string())
128                    .collect::<Vec<_>>()
129                    .join(", ")
130            ))
131        }
132    }
133
134    fn format_table_options(input: &CreateTableExpr) -> String {
135        let mut output = vec![];
136        if let Some(partition) = Self::format_partition(input) {
137            output.push(partition);
138        }
139        if !input.engine.is_empty() {
140            output.push(format!("ENGINE={}", input.engine));
141        }
142
143        output.join("\n")
144    }
145
146    fn format_with_clause(input: &CreateTableExpr) -> String {
147        if input.options.is_empty() {
148            String::new()
149        } else {
150            let mut output = vec![];
151            for (key, value) in &input.options {
152                output.push(format!("\"{key}\" = \"{value}\""));
153            }
154            format!(" with ({})", output.join(",\n"))
155        }
156    }
157}
158
159pub struct CreateDatabaseExprTranslator;
160
161impl DslTranslator<CreateDatabaseExpr, String> for CreateDatabaseExprTranslator {
162    type Error = Error;
163
164    fn translate(&self, input: &CreateDatabaseExpr) -> Result<String> {
165        Ok(format!(
166            "CREATE DATABASE{}{};",
167            Self::create_if_not_exists(input),
168            input.database_name
169        ))
170    }
171}
172
173impl CreateDatabaseExprTranslator {
174    fn create_if_not_exists(input: &CreateDatabaseExpr) -> &str {
175        if input.if_not_exists {
176            " IF NOT EXISTS "
177        } else {
178            " "
179        }
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use partition::expr::{Operand, PartitionExpr, RestrictedOp};
186    use partition::partition::{PartitionBound, PartitionDef};
187
188    use super::CreateTableExprTranslator;
189    use crate::ir::create_expr::{CreateDatabaseExprBuilder, CreateTableExprBuilder};
190    use crate::test_utils;
191    use crate::translator::DslTranslator;
192
193    #[test]
194    fn test_create_table_expr_translator() {
195        let test_ctx = test_utils::new_test_ctx();
196        let create_table_expr = CreateTableExprBuilder::default()
197            .columns(test_ctx.columns.clone())
198            .table_name("system_metrics")
199            .engine("mito")
200            .primary_keys(vec![0, 1])
201            .partition(PartitionDef::new(
202                vec!["idc".to_string()],
203                vec![
204                    PartitionBound::Expr(PartitionExpr::new(
205                        Operand::Column("idc".to_string()),
206                        RestrictedOp::Lt,
207                        Operand::Value(datatypes::value::Value::Int32(10)),
208                    )),
209                    PartitionBound::Expr(PartitionExpr::new(
210                        Operand::Expr(PartitionExpr::new(
211                            Operand::Column("idc".to_string()),
212                            RestrictedOp::GtEq,
213                            Operand::Value(datatypes::value::Value::Int32(10)),
214                        )),
215                        RestrictedOp::And,
216                        Operand::Expr(PartitionExpr::new(
217                            Operand::Column("idc".to_string()),
218                            RestrictedOp::Lt,
219                            Operand::Value(datatypes::value::Value::Int32(50)),
220                        )),
221                    )),
222                    PartitionBound::Expr(PartitionExpr::new(
223                        Operand::Column("idc".to_string()),
224                        RestrictedOp::GtEq,
225                        Operand::Value(datatypes::value::Value::Int32(50)),
226                    )),
227                ],
228            ))
229            .build()
230            .unwrap();
231
232        let output = CreateTableExprTranslator
233            .translate(&create_table_expr)
234            .unwrap();
235        assert_eq!(
236            "CREATE TABLE system_metrics(
237host STRING,
238idc STRING,
239cpu_util DOUBLE,
240memory_util DOUBLE,
241disk_util DOUBLE,
242ts TIMESTAMP(3) TIME INDEX,
243PRIMARY KEY(host, idc)
244)
245PARTITION ON COLUMNS(idc) (
246idc < 10,
247idc >= 10 AND idc < 50,
248idc >= 50
249)
250ENGINE=mito;",
251            output
252        );
253    }
254
255    #[test]
256    fn test_create_database_expr_translator() {
257        let create_database_expr = CreateDatabaseExprBuilder::default()
258            .database_name("all_metrics")
259            .if_not_exists(true)
260            .build()
261            .unwrap();
262
263        let output = super::CreateDatabaseExprTranslator
264            .translate(&create_database_expr)
265            .unwrap();
266
267        assert_eq!("CREATE DATABASE IF NOT EXISTS all_metrics;", output);
268    }
269}