sql/statements/
alter.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::{Debug, Display};
16
17use api::v1;
18use common_query::AddColumnLocation;
19use datatypes::schema::{FulltextOptions, SkippingIndexOptions};
20use itertools::Itertools;
21use serde::Serialize;
22use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint};
23use sqlparser_derive::{Visit, VisitMut};
24
25#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
26pub struct AlterTable {
27    pub table_name: ObjectName,
28    pub alter_operation: AlterTableOperation,
29}
30
31impl AlterTable {
32    pub(crate) fn new(table_name: ObjectName, alter_operation: AlterTableOperation) -> Self {
33        Self {
34            table_name,
35            alter_operation,
36        }
37    }
38
39    pub fn table_name(&self) -> &ObjectName {
40        &self.table_name
41    }
42
43    pub fn alter_operation(&self) -> &AlterTableOperation {
44        &self.alter_operation
45    }
46
47    pub fn alter_operation_mut(&mut self) -> &mut AlterTableOperation {
48        &mut self.alter_operation
49    }
50}
51
52impl Display for AlterTable {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        let table_name = self.table_name();
55        let alter_operation = self.alter_operation();
56        write!(f, r#"ALTER TABLE {table_name} {alter_operation}"#)
57    }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
61pub enum AlterTableOperation {
62    /// `ADD <table_constraint>`
63    AddConstraint(TableConstraint),
64    /// `ADD [ COLUMN ] <column_def> [location]`
65    AddColumns {
66        add_columns: Vec<AddColumn>,
67    },
68    /// `MODIFY <column_name> [target_type]`
69    ModifyColumnType {
70        column_name: Ident,
71        target_type: DataType,
72    },
73    /// `SET <table attrs key> = <table attr value>`
74    SetTableOptions {
75        options: Vec<KeyValueOption>,
76    },
77    /// `UNSET <table attrs key>`
78    UnsetTableOptions {
79        keys: Vec<String>,
80    },
81    /// `DROP COLUMN <name>`
82    DropColumn {
83        name: Ident,
84    },
85    /// `RENAME <new_table_name>`
86    RenameTable {
87        new_table_name: String,
88    },
89    SetIndex {
90        options: SetIndexOperation,
91    },
92    UnsetIndex {
93        options: UnsetIndexOperation,
94    },
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
98pub enum SetIndexOperation {
99    /// `MODIFY COLUMN <column_name> SET FULLTEXT INDEX [WITH <options>]`
100    Fulltext {
101        column_name: Ident,
102        options: FulltextOptions,
103    },
104    /// `MODIFY COLUMN <column_name> SET INVERTED INDEX`
105    Inverted { column_name: Ident },
106    /// `MODIFY COLUMN <column_name> SET SKIPPING INDEX`
107    Skipping {
108        column_name: Ident,
109        options: SkippingIndexOptions,
110    },
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
114pub enum UnsetIndexOperation {
115    /// `MODIFY COLUMN <column_name> UNSET FULLTEXT INDEX`
116    Fulltext { column_name: Ident },
117    /// `MODIFY COLUMN <column_name> UNSET INVERTED INDEX`
118    Inverted { column_name: Ident },
119    /// `MODIFY COLUMN <column_name> UNSET SKIPPING INDEX`
120    Skipping { column_name: Ident },
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
124pub struct AddColumn {
125    pub column_def: ColumnDef,
126    pub location: Option<AddColumnLocation>,
127    pub add_if_not_exists: bool,
128}
129
130impl Display for AddColumn {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        if let Some(location) = &self.location {
133            write!(f, "{} {location}", self.column_def)
134        } else {
135            write!(f, "{}", self.column_def)
136        }
137    }
138}
139
140impl Display for AlterTableOperation {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        match self {
143            AlterTableOperation::AddConstraint(constraint) => write!(f, r#"ADD {constraint}"#),
144            AlterTableOperation::AddColumns { add_columns } => {
145                let columns = add_columns
146                    .iter()
147                    .map(|add_column| format!("ADD COLUMN {add_column}"))
148                    .join(", ");
149                write!(f, "{columns}")
150            }
151            AlterTableOperation::DropColumn { name } => write!(f, r#"DROP COLUMN {name}"#),
152            AlterTableOperation::RenameTable { new_table_name } => {
153                write!(f, r#"RENAME {new_table_name}"#)
154            }
155            AlterTableOperation::ModifyColumnType {
156                column_name,
157                target_type,
158            } => {
159                write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)
160            }
161            AlterTableOperation::SetTableOptions { options } => {
162                let kvs = options
163                    .iter()
164                    .map(|KeyValueOption { key, value }| {
165                        if !value.is_empty() {
166                            format!("'{key}'='{value}'")
167                        } else {
168                            format!("'{key}'=NULL")
169                        }
170                    })
171                    .join(",");
172
173                write!(f, "SET {kvs}")
174            }
175            AlterTableOperation::UnsetTableOptions { keys } => {
176                let keys = keys.iter().map(|k| format!("'{k}'")).join(",");
177                write!(f, "UNSET {keys}")
178            }
179            AlterTableOperation::SetIndex { options } => match options {
180                SetIndexOperation::Fulltext {
181                    column_name,
182                    options,
183                } => {
184                    write!(f, "MODIFY COLUMN {column_name} SET FULLTEXT INDEX WITH(analyzer={0}, case_sensitive={1}, backend={2})", options.analyzer, options.case_sensitive, options.backend)
185                }
186                SetIndexOperation::Inverted { column_name } => {
187                    write!(f, "MODIFY COLUMN {column_name} SET INVERTED INDEX")
188                }
189                SetIndexOperation::Skipping {
190                    column_name,
191                    options,
192                } => {
193                    write!(f, "MODIFY COLUMN {column_name} SET SKIPPING INDEX WITH(granularity={0}, index_type={1})", options.granularity, options.index_type)
194                }
195            },
196            AlterTableOperation::UnsetIndex { options } => match options {
197                UnsetIndexOperation::Fulltext { column_name } => {
198                    write!(f, "MODIFY COLUMN {column_name} UNSET FULLTEXT INDEX")
199                }
200                UnsetIndexOperation::Inverted { column_name } => {
201                    write!(f, "MODIFY COLUMN {column_name} UNSET INVERTED INDEX")
202                }
203                UnsetIndexOperation::Skipping { column_name } => {
204                    write!(f, "MODIFY COLUMN {column_name} UNSET SKIPPING INDEX")
205                }
206            },
207        }
208    }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
212pub struct KeyValueOption {
213    pub key: String,
214    pub value: String,
215}
216
217impl From<KeyValueOption> for v1::Option {
218    fn from(c: KeyValueOption) -> Self {
219        v1::Option {
220            key: c.key,
221            value: c.value,
222        }
223    }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
227pub struct AlterDatabase {
228    pub database_name: ObjectName,
229    pub alter_operation: AlterDatabaseOperation,
230}
231
232impl AlterDatabase {
233    pub(crate) fn new(database_name: ObjectName, alter_operation: AlterDatabaseOperation) -> Self {
234        Self {
235            database_name,
236            alter_operation,
237        }
238    }
239
240    pub fn database_name(&self) -> &ObjectName {
241        &self.database_name
242    }
243
244    pub fn alter_operation(&self) -> &AlterDatabaseOperation {
245        &self.alter_operation
246    }
247}
248
249impl Display for AlterDatabase {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        let database_name = self.database_name();
252        let alter_operation = self.alter_operation();
253        write!(f, r#"ALTER DATABASE {database_name} {alter_operation}"#)
254    }
255}
256
257#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
258pub enum AlterDatabaseOperation {
259    SetDatabaseOption { options: Vec<KeyValueOption> },
260    UnsetDatabaseOption { keys: Vec<String> },
261}
262
263impl Display for AlterDatabaseOperation {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        match self {
266            AlterDatabaseOperation::SetDatabaseOption { options } => {
267                let kvs = options
268                    .iter()
269                    .map(|KeyValueOption { key, value }| {
270                        if !value.is_empty() {
271                            format!("'{key}'='{value}'")
272                        } else {
273                            format!("'{key}'=NULL")
274                        }
275                    })
276                    .join(",");
277
278                write!(f, "SET {kvs}")?;
279
280                Ok(())
281            }
282            AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
283                let keys = keys.iter().map(|key| format!("'{key}'")).join(",");
284                write!(f, "UNSET {keys}")?;
285
286                Ok(())
287            }
288        }
289    }
290}
291
292#[cfg(test)]
293mod tests {
294    use std::assert_matches::assert_matches;
295
296    use crate::dialect::GreptimeDbDialect;
297    use crate::parser::{ParseOptions, ParserContext};
298    use crate::statements::statement::Statement;
299
300    #[test]
301    fn test_display_alter() {
302        let sql = r"ALTER DATABASE db SET 'a' = 'b', 'c' = 'd'";
303        let stmts =
304            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
305                .unwrap();
306        assert_eq!(1, stmts.len());
307        assert_matches!(&stmts[0], Statement::AlterDatabase { .. });
308
309        match &stmts[0] {
310            Statement::AlterDatabase(set) => {
311                let new_sql = format!("\n{}", set);
312                assert_eq!(
313                    r#"
314ALTER DATABASE db SET 'a'='b','c'='d'"#,
315                    &new_sql
316                );
317            }
318            _ => {
319                unreachable!();
320            }
321        }
322
323        let sql = r"ALTER DATABASE db UNSET 'a', 'c'";
324        let stmts =
325            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
326                .unwrap();
327        assert_eq!(1, stmts.len());
328
329        match &stmts[0] {
330            Statement::AlterDatabase(set) => {
331                let new_sql = format!("\n{}", set);
332                assert_eq!(
333                    r#"
334ALTER DATABASE db UNSET 'a','c'"#,
335                    &new_sql
336                );
337            }
338            _ => {
339                unreachable!();
340            }
341        }
342
343        let sql =
344            r"alter table monitor add column app string default 'shop' primary key, add foo INT;";
345        let stmts =
346            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
347                .unwrap();
348        assert_eq!(1, stmts.len());
349        assert_matches!(&stmts[0], Statement::AlterTable { .. });
350
351        match &stmts[0] {
352            Statement::AlterTable(set) => {
353                let new_sql = format!("\n{}", set);
354                assert_eq!(
355                    r#"
356ALTER TABLE monitor ADD COLUMN app STRING DEFAULT 'shop' PRIMARY KEY, ADD COLUMN foo INT"#,
357                    &new_sql
358                );
359            }
360            _ => {
361                unreachable!();
362            }
363        }
364
365        let sql = r"alter table monitor modify column load_15 string;";
366        let stmts =
367            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
368                .unwrap();
369        assert_eq!(1, stmts.len());
370        assert_matches!(&stmts[0], Statement::AlterTable { .. });
371
372        match &stmts[0] {
373            Statement::AlterTable(set) => {
374                let new_sql = format!("\n{}", set);
375                assert_eq!(
376                    r#"
377ALTER TABLE monitor MODIFY COLUMN load_15 STRING"#,
378                    &new_sql
379                );
380            }
381            _ => {
382                unreachable!();
383            }
384        }
385
386        let sql = r"alter table monitor drop column load_15;";
387        let stmts =
388            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
389                .unwrap();
390        assert_eq!(1, stmts.len());
391        assert_matches!(&stmts[0], Statement::AlterTable { .. });
392
393        match &stmts[0] {
394            Statement::AlterTable(set) => {
395                let new_sql = format!("\n{}", set);
396                assert_eq!(
397                    r#"
398ALTER TABLE monitor DROP COLUMN load_15"#,
399                    &new_sql
400                );
401            }
402            _ => {
403                unreachable!();
404            }
405        }
406
407        let sql = r"alter table monitor rename monitor_new;";
408        let stmts =
409            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
410                .unwrap();
411        assert_eq!(1, stmts.len());
412        assert_matches!(&stmts[0], Statement::AlterTable { .. });
413
414        match &stmts[0] {
415            Statement::AlterTable(set) => {
416                let new_sql = format!("\n{}", set);
417                assert_eq!(
418                    r#"
419ALTER TABLE monitor RENAME monitor_new"#,
420                    &new_sql
421                );
422            }
423            _ => {
424                unreachable!();
425            }
426        }
427
428        let sql = "ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false',backend='bloom')";
429        let stmts =
430            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
431                .unwrap();
432        assert_eq!(1, stmts.len());
433        assert_matches!(&stmts[0], Statement::AlterTable { .. });
434
435        match &stmts[0] {
436            Statement::AlterTable(set) => {
437                let new_sql = format!("\n{}", set);
438                assert_eq!(
439                    r#"
440ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer=English, case_sensitive=false, backend=bloom)"#,
441                    &new_sql
442                );
443            }
444            _ => {
445                unreachable!();
446            }
447        }
448
449        let sql = "ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT INDEX";
450        let stmts =
451            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
452                .unwrap();
453        assert_eq!(1, stmts.len());
454        assert_matches!(&stmts[0], Statement::AlterTable { .. });
455
456        match &stmts[0] {
457            Statement::AlterTable(set) => {
458                let new_sql = format!("\n{}", set);
459                assert_eq!(
460                    r#"
461ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT INDEX"#,
462                    &new_sql
463                );
464            }
465            _ => {
466                unreachable!();
467            }
468        }
469
470        let sql = "ALTER TABLE monitor MODIFY COLUMN a SET INVERTED INDEX";
471        let stmts =
472            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
473                .unwrap();
474        assert_eq!(1, stmts.len());
475        assert_matches!(&stmts[0], Statement::AlterTable { .. });
476
477        match &stmts[0] {
478            Statement::AlterTable(set) => {
479                let new_sql = format!("\n{}", set);
480                assert_eq!(
481                    r#"
482ALTER TABLE monitor MODIFY COLUMN a SET INVERTED INDEX"#,
483                    &new_sql
484                );
485            }
486            _ => {
487                unreachable!();
488            }
489        }
490    }
491}