1use 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 AddConstraint(TableConstraint),
64 AddColumns {
66 add_columns: Vec<AddColumn>,
67 },
68 ModifyColumnType {
70 column_name: Ident,
71 target_type: DataType,
72 },
73 SetTableOptions {
75 options: Vec<KeyValueOption>,
76 },
77 UnsetTableOptions {
79 keys: Vec<String>,
80 },
81 DropColumn {
83 name: Ident,
84 },
85 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 Fulltext {
101 column_name: Ident,
102 options: FulltextOptions,
103 },
104 Inverted { column_name: Ident },
106 Skipping {
108 column_name: Ident,
109 options: SkippingIndexOptions,
110 },
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
114pub enum UnsetIndexOperation {
115 Fulltext { column_name: Ident },
117 Inverted { column_name: Ident },
119 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}