scylladb_parse/statements/
ddl.rs

1use super::*;
2use crate::{
3    ColumnDefinition,
4    Constant,
5    PrimaryKey,
6    TerminatingList,
7};
8
9#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq)]
10#[parse_via(TaggedDataDefinitionStatement)]
11pub enum DataDefinitionStatement {
12    Use(UseStatement),
13    CreateKeyspace(CreateKeyspaceStatement),
14    AlterKeyspace(AlterKeyspaceStatement),
15    DropKeyspace(DropKeyspaceStatement),
16    CreateTable(CreateTableStatement),
17    AlterTable(AlterTableStatement),
18    DropTable(DropTableStatement),
19    Truncate(TruncateStatement),
20}
21
22impl TryFrom<TaggedDataDefinitionStatement> for DataDefinitionStatement {
23    type Error = anyhow::Error;
24    fn try_from(value: TaggedDataDefinitionStatement) -> Result<Self, Self::Error> {
25        Ok(match value {
26            TaggedDataDefinitionStatement::Use(value) => DataDefinitionStatement::Use(value.try_into()?),
27            TaggedDataDefinitionStatement::CreateKeyspace(value) => {
28                DataDefinitionStatement::CreateKeyspace(value.try_into()?)
29            }
30            TaggedDataDefinitionStatement::AlterKeyspace(value) => {
31                DataDefinitionStatement::AlterKeyspace(value.try_into()?)
32            }
33            TaggedDataDefinitionStatement::DropKeyspace(value) => {
34                DataDefinitionStatement::DropKeyspace(value.try_into()?)
35            }
36            TaggedDataDefinitionStatement::CreateTable(value) => {
37                DataDefinitionStatement::CreateTable(value.try_into()?)
38            }
39            TaggedDataDefinitionStatement::AlterTable(value) => DataDefinitionStatement::AlterTable(value.try_into()?),
40            TaggedDataDefinitionStatement::DropTable(value) => DataDefinitionStatement::DropTable(value.try_into()?),
41            TaggedDataDefinitionStatement::Truncate(value) => DataDefinitionStatement::Truncate(value.try_into()?),
42        })
43    }
44}
45
46#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq)]
47#[tokenize_as(DataDefinitionStatement)]
48pub enum TaggedDataDefinitionStatement {
49    Use(TaggedUseStatement),
50    CreateKeyspace(TaggedCreateKeyspaceStatement),
51    AlterKeyspace(TaggedAlterKeyspaceStatement),
52    DropKeyspace(TaggedDropKeyspaceStatement),
53    CreateTable(TaggedCreateTableStatement),
54    AlterTable(TaggedAlterTableStatement),
55    DropTable(TaggedDropTableStatement),
56    Truncate(TaggedTruncateStatement),
57}
58
59impl Parse for TaggedDataDefinitionStatement {
60    type Output = Self;
61    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
62        Ok(if let Some(keyword) = s.find::<ReservedKeyword>() {
63            match keyword {
64                ReservedKeyword::USE => Self::Use(s.parse()?),
65                ReservedKeyword::CREATE | ReservedKeyword::ALTER | ReservedKeyword::DROP => {
66                    if let Some((_, keyword2)) = s.find::<(ReservedKeyword, ReservedKeyword)>() {
67                        match (keyword, keyword2) {
68                            (ReservedKeyword::CREATE, ReservedKeyword::KEYSPACE) => Self::CreateKeyspace(s.parse()?),
69                            (ReservedKeyword::CREATE, ReservedKeyword::TABLE) => Self::CreateTable(s.parse()?),
70                            (ReservedKeyword::ALTER, ReservedKeyword::KEYSPACE) => Self::AlterKeyspace(s.parse()?),
71                            (ReservedKeyword::ALTER, ReservedKeyword::TABLE) => Self::AlterTable(s.parse()?),
72                            (ReservedKeyword::DROP, ReservedKeyword::KEYSPACE) => Self::DropKeyspace(s.parse()?),
73                            (ReservedKeyword::DROP, ReservedKeyword::TABLE) => Self::DropTable(s.parse()?),
74                            _ => anyhow::bail!("Unexpected keyword following {}: {}", keyword, keyword2),
75                        }
76                    } else {
77                        anyhow::bail!("Unexpected token following {}: {}", keyword, s.info())
78                    }
79                }
80                ReservedKeyword::TRUNCATE => Self::Truncate(s.parse()?),
81                _ => anyhow::bail!("Expected a data definition statement, found {}", s.info()),
82            }
83        } else {
84            anyhow::bail!("Expected a data definition statement, found {}", s.info())
85        })
86    }
87}
88
89impl Display for DataDefinitionStatement {
90    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Self::Use(s) => s.fmt(f),
93            Self::CreateKeyspace(s) => s.fmt(f),
94            Self::AlterKeyspace(s) => s.fmt(f),
95            Self::DropKeyspace(s) => s.fmt(f),
96            Self::CreateTable(s) => s.fmt(f),
97            Self::AlterTable(s) => s.fmt(f),
98            Self::DropTable(s) => s.fmt(f),
99            Self::Truncate(s) => s.fmt(f),
100        }
101    }
102}
103
104#[derive(ParseFromStr, Clone, Debug, ToTokens, PartialEq, Eq)]
105#[parse_via(TaggedUseStatement)]
106pub struct UseStatement {
107    pub keyspace: Name,
108}
109
110impl TryFrom<TaggedUseStatement> for UseStatement {
111    type Error = anyhow::Error;
112    fn try_from(value: TaggedUseStatement) -> Result<Self, Self::Error> {
113        Ok(Self {
114            keyspace: value.keyspace.into_value()?,
115        })
116    }
117}
118
119#[derive(ParseFromStr, Clone, Debug, ToTokens, PartialEq, Eq)]
120#[tokenize_as(UseStatement)]
121pub struct TaggedUseStatement {
122    pub keyspace: Tag<Name>,
123}
124
125impl Parse for TaggedUseStatement {
126    type Output = Self;
127    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
128        s.parse::<USE>()?;
129        let keyspace = s.parse()?;
130        s.parse::<Option<Semicolon>>()?;
131        Ok(Self { keyspace })
132    }
133}
134
135impl Display for UseStatement {
136    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137        write!(f, "USE {}", self.keyspace)
138    }
139}
140
141impl<T: Into<Name>> From<T> for UseStatement {
142    fn from(name: T) -> Self {
143        Self { keyspace: name.into() }
144    }
145}
146
147#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq, Default)]
148#[parse_via(TaggedKeyspaceOpts)]
149pub struct KeyspaceOpts {
150    #[builder(setter(into))]
151    pub replication: Replication,
152    #[builder(setter(strip_option), default)]
153    pub durable_writes: Option<bool>,
154}
155
156impl TryFrom<TaggedKeyspaceOpts> for KeyspaceOpts {
157    type Error = anyhow::Error;
158    fn try_from(value: TaggedKeyspaceOpts) -> Result<Self, Self::Error> {
159        Ok(Self {
160            replication: value.replication.into_value()?.try_into()?,
161            durable_writes: value.durable_writes.map(|v| v.into_value()).transpose()?,
162        })
163    }
164}
165
166#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq, Default)]
167#[tokenize_as(KeyspaceOpts)]
168pub struct TaggedKeyspaceOpts {
169    pub replication: Tag<TaggedReplication>,
170    #[builder(setter(strip_option), default)]
171    pub durable_writes: Option<Tag<bool>>,
172}
173
174impl Parse for TaggedKeyspaceOpts {
175    type Output = Self;
176    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
177        let mut res = TaggedKeyspaceOptsBuilder::default();
178        for TaggedStatementOpt { name, value } in s.parse_from::<List<TaggedStatementOpt, AND>>()? {
179            let (Name::Quoted(n) | Name::Unquoted(n)) = &name;
180            match n.as_str() {
181                "replication" => {
182                    if res.replication.is_some() {
183                        anyhow::bail!("Duplicate replication option");
184                    } else if let TaggedStatementOptValue::Map(m) = value {
185                        res.replication(match m {
186                            Tag::Tag(t) => Tag::Tag(t),
187                            Tag::Value(m) => Tag::Value(TaggedReplication::try_from(m)?),
188                        });
189                    } else {
190                        anyhow::bail!("Invalid replication value: {}", value);
191                    }
192                }
193                "durable_writes" => {
194                    if res.durable_writes.is_some() {
195                        anyhow::bail!("Duplicate durable writes option");
196                    } else if let TaggedStatementOptValue::Constant(b) = value {
197                        res.durable_writes(match b {
198                            Tag::Tag(t) => Tag::Tag(t),
199                            Tag::Value(b) => {
200                                if let Constant::Boolean(b) = b {
201                                    Tag::Value(b)
202                                } else {
203                                    anyhow::bail!("Invalid durable writes value: {}", b);
204                                }
205                            }
206                        });
207                    } else {
208                        anyhow::bail!("Invalid durable writes value: {}", value);
209                    }
210                }
211                _ => anyhow::bail!("Invalid table option: {}", name),
212            }
213        }
214        Ok(res
215            .build()
216            .map_err(|e| anyhow::anyhow!("Invalid Keyspace Options: {}", e))?)
217    }
218}
219
220impl Display for KeyspaceOpts {
221    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
222        write!(f, "replication = {}", self.replication)?;
223        if let Some(d) = self.durable_writes {
224            write!(f, " AND durable_writes = {}", d)?;
225        }
226        Ok(())
227    }
228}
229
230#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
231#[parse_via(TaggedCreateKeyspaceStatement)]
232pub struct CreateKeyspaceStatement {
233    #[builder(setter(name = "set_if_not_exists"), default)]
234    pub if_not_exists: bool,
235    #[builder(setter(into))]
236    pub keyspace: Name,
237    pub options: KeyspaceOpts,
238}
239
240impl TryFrom<TaggedCreateKeyspaceStatement> for CreateKeyspaceStatement {
241    type Error = anyhow::Error;
242    fn try_from(value: TaggedCreateKeyspaceStatement) -> Result<Self, Self::Error> {
243        Ok(Self {
244            if_not_exists: value.if_not_exists,
245            keyspace: value.keyspace.into_value()?,
246            options: value.options.into_value()?.try_into()?,
247        })
248    }
249}
250
251#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
252#[tokenize_as(CreateKeyspaceStatement)]
253pub struct TaggedCreateKeyspaceStatement {
254    #[builder(setter(name = "set_if_not_exists"), default)]
255    pub if_not_exists: bool,
256    pub keyspace: Tag<Name>,
257    pub options: Tag<TaggedKeyspaceOpts>,
258}
259
260impl CreateKeyspaceStatementBuilder {
261    /// Set IF NOT EXISTS on the statement.
262    /// To undo this, use `set_if_not_exists(false)`.
263    pub fn if_not_exists(&mut self) -> &mut Self {
264        self.if_not_exists.replace(true);
265        self
266    }
267}
268
269impl Parse for TaggedCreateKeyspaceStatement {
270    type Output = Self;
271    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
272        s.parse::<(CREATE, KEYSPACE)>()?;
273        let mut res = TaggedCreateKeyspaceStatementBuilder::default();
274        res.set_if_not_exists(s.parse::<Option<(IF, NOT, EXISTS)>>()?.is_some())
275            .keyspace(s.parse()?);
276        s.parse::<WITH>()?;
277        res.options(s.parse()?);
278        s.parse::<Option<Semicolon>>()?;
279        Ok(res
280            .build()
281            .map_err(|e| anyhow::anyhow!("Invalid CREATE KEYSPACE statement: {}", e))?)
282    }
283}
284
285impl Display for CreateKeyspaceStatement {
286    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
287        write!(
288            f,
289            "CREATE KEYSPACE{} {} WITH {}",
290            if self.if_not_exists { " IF NOT EXISTS" } else { "" },
291            self.keyspace,
292            self.options
293        )
294    }
295}
296
297impl KeyspaceOptionsExt for CreateKeyspaceStatement {
298    fn keyspace_opts(&self) -> &KeyspaceOpts {
299        &self.options
300    }
301
302    fn keyspace_opts_mut(&mut self) -> &mut KeyspaceOpts {
303        &mut self.options
304    }
305}
306
307#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
308#[parse_via(TaggedAlterKeyspaceStatement)]
309pub struct AlterKeyspaceStatement {
310    #[builder(setter(into))]
311    pub keyspace: Name,
312    pub options: KeyspaceOpts,
313}
314
315impl TryFrom<TaggedAlterKeyspaceStatement> for AlterKeyspaceStatement {
316    type Error = anyhow::Error;
317    fn try_from(value: TaggedAlterKeyspaceStatement) -> Result<Self, Self::Error> {
318        Ok(Self {
319            keyspace: value.keyspace.into_value()?,
320            options: value.options,
321        })
322    }
323}
324
325#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
326#[tokenize_as(AlterKeyspaceStatement)]
327pub struct TaggedAlterKeyspaceStatement {
328    pub keyspace: Tag<Name>,
329    pub options: KeyspaceOpts,
330}
331
332impl Parse for TaggedAlterKeyspaceStatement {
333    type Output = Self;
334    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
335        s.parse::<(ALTER, KEYSPACE)>()?;
336        let mut res = TaggedAlterKeyspaceStatementBuilder::default();
337        res.keyspace(s.parse()?);
338        s.parse::<WITH>()?;
339        res.options(s.parse()?);
340        s.parse::<Option<Semicolon>>()?;
341        Ok(res
342            .build()
343            .map_err(|e| anyhow::anyhow!("Invalid ALTER KEYSPACE statement: {}", e))?)
344    }
345}
346
347impl Display for AlterKeyspaceStatement {
348    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
349        write!(f, "ALTER KEYSPACE {} WITH {}", self.keyspace, self.options)
350    }
351}
352
353#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
354#[parse_via(TaggedDropKeyspaceStatement)]
355pub struct DropKeyspaceStatement {
356    #[builder(setter(name = "set_if_exists"), default)]
357    pub if_exists: bool,
358    #[builder(setter(into))]
359    pub keyspace: Name,
360}
361
362impl TryFrom<TaggedDropKeyspaceStatement> for DropKeyspaceStatement {
363    type Error = anyhow::Error;
364    fn try_from(value: TaggedDropKeyspaceStatement) -> Result<Self, Self::Error> {
365        Ok(Self {
366            if_exists: value.if_exists,
367            keyspace: value.keyspace.into_value()?,
368        })
369    }
370}
371
372#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
373#[tokenize_as(DropKeyspaceStatement)]
374pub struct TaggedDropKeyspaceStatement {
375    #[builder(setter(name = "set_if_exists"), default)]
376    pub if_exists: bool,
377    pub keyspace: Tag<Name>,
378}
379
380impl DropKeyspaceStatementBuilder {
381    /// Set IF EXISTS on the statement.
382    /// To undo this, use `set_if_exists(false)`.
383    pub fn if_exists(&mut self) -> &mut Self {
384        self.if_exists.replace(true);
385        self
386    }
387}
388
389impl Parse for TaggedDropKeyspaceStatement {
390    type Output = Self;
391    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
392        s.parse::<(DROP, KEYSPACE)>()?;
393        let mut res = TaggedDropKeyspaceStatementBuilder::default();
394        res.set_if_exists(s.parse::<Option<(IF, EXISTS)>>()?.is_some())
395            .keyspace(s.parse()?);
396        s.parse::<Option<Semicolon>>()?;
397        Ok(res
398            .build()
399            .map_err(|e| anyhow::anyhow!("Invalid DROP KEYSPACE statement: {}", e))?)
400    }
401}
402
403impl Display for DropKeyspaceStatement {
404    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
405        write!(
406            f,
407            "DROP KEYSPACE{} {}",
408            if self.if_exists { " IF EXISTS" } else { "" },
409            self.keyspace
410        )
411    }
412}
413
414impl<T: Into<Name>> From<T> for DropKeyspaceStatement {
415    fn from(name: T) -> Self {
416        Self {
417            if_exists: Default::default(),
418            keyspace: name.into(),
419        }
420    }
421}
422
423#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
424#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
425#[parse_via(TaggedCreateTableStatement)]
426pub struct CreateTableStatement {
427    #[builder(setter(name = "set_if_not_exists"), default)]
428    pub if_not_exists: bool,
429    #[builder(setter(into))]
430    pub table: KeyspaceQualifiedName,
431    pub columns: Vec<ColumnDefinition>,
432    #[builder(setter(into), default)]
433    pub primary_key: Option<PrimaryKey>,
434    #[builder(default)]
435    pub options: Option<TableOpts>,
436}
437
438impl TryFrom<TaggedCreateTableStatement> for CreateTableStatement {
439    type Error = anyhow::Error;
440    fn try_from(value: TaggedCreateTableStatement) -> Result<Self, Self::Error> {
441        Ok(Self {
442            if_not_exists: value.if_not_exists,
443            table: value.table.try_into()?,
444            columns: value.columns,
445            primary_key: value.primary_key,
446            options: value.options.map(|v| v.into_value()).transpose()?,
447        })
448    }
449}
450
451#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
452#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
453#[tokenize_as(CreateTableStatement)]
454pub struct TaggedCreateTableStatement {
455    #[builder(setter(name = "set_if_not_exists"), default)]
456    pub if_not_exists: bool,
457    pub table: TaggedKeyspaceQualifiedName,
458    pub columns: Vec<ColumnDefinition>,
459    #[builder(default)]
460    pub primary_key: Option<PrimaryKey>,
461    #[builder(default)]
462    pub options: Option<Tag<TableOpts>>,
463}
464
465impl CreateTableStatementBuilder {
466    /// Set IF NOT EXISTS on the statement.
467    /// To undo this, use `set_if_not_exists(false)`.
468    pub fn if_not_exists(&mut self) -> &mut Self {
469        self.if_not_exists.replace(true);
470        self
471    }
472
473    fn validate(&self) -> Result<(), String> {
474        if self.columns.as_ref().map(|s| s.is_empty()).unwrap_or(false) {
475            return Err("Column definitions cannot be empty".to_string());
476        }
477        Ok(())
478    }
479}
480
481impl TaggedCreateTableStatementBuilder {
482    fn validate(&self) -> Result<(), String> {
483        if self.columns.as_ref().map(|s| s.is_empty()).unwrap_or(false) {
484            return Err("Column definitions cannot be empty".to_string());
485        }
486        Ok(())
487    }
488}
489
490impl Parse for TaggedCreateTableStatement {
491    type Output = Self;
492    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
493        s.parse::<(CREATE, TABLE)>()?;
494        let mut res = TaggedCreateTableStatementBuilder::default();
495        res.set_if_not_exists(s.parse::<Option<(IF, NOT, EXISTS)>>()?.is_some())
496            .table(s.parse()?);
497        s.parse::<LeftParen>()?;
498        res.columns(s.parse_from::<TerminatingList<ColumnDefinition, Comma, (PRIMARY, KEY)>>()?);
499        if let Some(p) = s.parse_from::<If<(PRIMARY, KEY), Parens<PrimaryKey>>>()? {
500            res.primary_key(p);
501        }
502        s.parse::<RightParen>()?;
503        if let Some(p) = s.parse_from::<If<WITH, Tag<TableOpts>>>()? {
504            res.options(p);
505        }
506        s.parse::<Option<Semicolon>>()?;
507        Ok(res
508            .build()
509            .map_err(|e| anyhow::anyhow!("Invalid CREATE TABLE statement: {}", e))?)
510    }
511}
512
513impl Display for CreateTableStatement {
514    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
515        write!(
516            f,
517            "CREATE TABLE{} {} ({}",
518            if self.if_not_exists { " IF NOT EXISTS" } else { "" },
519            self.table,
520            self.columns
521                .iter()
522                .map(|i| i.to_string())
523                .collect::<Vec<_>>()
524                .join(", "),
525        )?;
526        if let Some(ref pk) = self.primary_key {
527            write!(f, ", PRIMARY KEY ({})", pk)?;
528        }
529        write!(f, ")")?;
530        if let Some(ref options) = self.options {
531            write!(f, " WITH {}", options)?;
532        }
533        Ok(())
534    }
535}
536
537impl TableOptionsExt for CreateTableStatement {
538    fn table_opts(&self) -> &Option<TableOpts> {
539        &self.options
540    }
541
542    fn table_opts_mut(&mut self) -> &mut Option<TableOpts> {
543        &mut self.options
544    }
545}
546
547#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
548#[parse_via(TaggedAlterTableStatement)]
549pub struct AlterTableStatement {
550    #[builder(setter(into))]
551    pub table: KeyspaceQualifiedName,
552    pub instruction: AlterTableInstruction,
553}
554
555impl TryFrom<TaggedAlterTableStatement> for AlterTableStatement {
556    type Error = anyhow::Error;
557    fn try_from(value: TaggedAlterTableStatement) -> Result<Self, Self::Error> {
558        Ok(Self {
559            table: value.table.try_into()?,
560            instruction: value.instruction,
561        })
562    }
563}
564
565#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
566#[tokenize_as(AlterTableStatement)]
567pub struct TaggedAlterTableStatement {
568    pub table: TaggedKeyspaceQualifiedName,
569    pub instruction: AlterTableInstruction,
570}
571
572impl Parse for TaggedAlterTableStatement {
573    type Output = Self;
574    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
575        s.parse::<(ALTER, TABLE)>()?;
576        let mut res = TaggedAlterTableStatementBuilder::default();
577        res.table(s.parse()?).instruction(s.parse()?);
578        s.parse::<Option<Semicolon>>()?;
579        Ok(res
580            .build()
581            .map_err(|e| anyhow::anyhow!("Invalid ALTER TABLE statement: {}", e))?)
582    }
583}
584
585impl Display for AlterTableStatement {
586    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
587        write!(f, "ALTER TABLE {} {}", self.table, self.instruction)
588    }
589}
590
591#[derive(ParseFromStr, Clone, Debug, ToTokens, PartialEq)]
592pub enum AlterTableInstruction {
593    Add(Vec<ColumnDefinition>),
594    Drop(Vec<Name>),
595    Alter(Name, CqlType),
596    With(TableOpts),
597}
598
599impl AlterTableInstruction {
600    pub fn add<T: Into<ColumnDefinition>>(defs: Vec<T>) -> anyhow::Result<Self> {
601        if defs.is_empty() {
602            anyhow::bail!("Column definitions cannot be empty");
603        }
604        Ok(AlterTableInstruction::Add(defs.into_iter().map(|i| i.into()).collect()))
605    }
606
607    pub fn drop<T: Into<Name>>(names: Vec<T>) -> anyhow::Result<Self> {
608        if names.is_empty() {
609            anyhow::bail!("Column names cannot be empty");
610        }
611        Ok(AlterTableInstruction::Drop(
612            names.into_iter().map(|i| i.into()).collect(),
613        ))
614    }
615
616    pub fn alter<N: Into<Name>, T: Into<CqlType>>(name: N, cql_type: T) -> Self {
617        AlterTableInstruction::Alter(name.into(), cql_type.into())
618    }
619
620    pub fn with(opts: TableOpts) -> Self {
621        AlterTableInstruction::With(opts)
622    }
623}
624
625impl Parse for AlterTableInstruction {
626    type Output = Self;
627    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
628        Ok(
629            if s.parse::<Option<ADD>>()?.is_some() || s.check::<ColumnDefinition>() {
630                Self::Add(s.parse_from::<List<ColumnDefinition, Comma>>()?)
631            } else if s.parse::<Option<DROP>>()?.is_some() {
632                if let Some(columns) = s.parse_from::<Option<Parens<List<Name, Comma>>>>()? {
633                    Self::Drop(columns)
634                } else {
635                    Self::Drop(vec![s.parse()?])
636                }
637            } else if s.parse::<Option<ALTER>>()?.is_some() {
638                let (col, _, ty) = s.parse::<(_, TYPE, _)>()?;
639                Self::Alter(col, ty)
640            } else if s.parse::<Option<WITH>>()?.is_some() {
641                Self::With(s.parse_from::<TableOpts>()?)
642            } else {
643                anyhow::bail!("Expected ALTER TABLE instruction, found {}", s.info());
644            },
645        )
646    }
647}
648
649impl Display for AlterTableInstruction {
650    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
651        match self {
652            Self::Add(cols) => write!(
653                f,
654                "ADD {}",
655                cols.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
656            ),
657            Self::Drop(cols) => write!(
658                f,
659                "DROP ({})",
660                cols.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
661            ),
662            Self::Alter(col, ty) => write!(f, "ALTER {} TYPE {}", col, ty),
663            Self::With(options) => write!(f, "WITH {}", options),
664        }
665    }
666}
667
668#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
669#[parse_via(TaggedDropTableStatement)]
670pub struct DropTableStatement {
671    #[builder(setter(name = "set_if_exists"), default)]
672    pub if_exists: bool,
673    #[builder(setter(into))]
674    pub table: KeyspaceQualifiedName,
675}
676
677impl TryFrom<TaggedDropTableStatement> for DropTableStatement {
678    type Error = anyhow::Error;
679    fn try_from(value: TaggedDropTableStatement) -> Result<Self, Self::Error> {
680        Ok(Self {
681            if_exists: value.if_exists,
682            table: value.table.try_into()?,
683        })
684    }
685}
686
687#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
688#[tokenize_as(DropTableStatement)]
689pub struct TaggedDropTableStatement {
690    #[builder(setter(name = "set_if_exists"), default)]
691    pub if_exists: bool,
692    pub table: TaggedKeyspaceQualifiedName,
693}
694
695impl DropTableStatementBuilder {
696    /// Set IF EXISTS on the statement.
697    /// To undo this, use `set_if_exists(false)`.
698    pub fn if_exists(&mut self) -> &mut Self {
699        self.if_exists.replace(true);
700        self
701    }
702}
703
704impl Parse for TaggedDropTableStatement {
705    type Output = Self;
706    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
707        s.parse::<(DROP, TABLE)>()?;
708        let mut res = TaggedDropTableStatementBuilder::default();
709        res.set_if_exists(s.parse::<Option<(IF, EXISTS)>>()?.is_some())
710            .table(s.parse()?);
711        s.parse::<Option<Semicolon>>()?;
712        Ok(res
713            .build()
714            .map_err(|e| anyhow::anyhow!("Invalid DROP TABLE statement: {}", e))?)
715    }
716}
717
718impl Display for DropTableStatement {
719    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
720        write!(
721            f,
722            "DROP TABLE{} {}",
723            if self.if_exists { " IF EXISTS" } else { "" },
724            self.table,
725        )
726    }
727}
728
729impl<T: Into<KeyspaceQualifiedName>> From<T> for DropTableStatement {
730    fn from(name: T) -> Self {
731        Self {
732            if_exists: Default::default(),
733            table: name.into(),
734        }
735    }
736}
737
738#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
739#[parse_via(TaggedTruncateStatement)]
740pub struct TruncateStatement {
741    #[builder(setter(into))]
742    pub table: KeyspaceQualifiedName,
743}
744
745impl TryFrom<TaggedTruncateStatement> for TruncateStatement {
746    type Error = anyhow::Error;
747    fn try_from(value: TaggedTruncateStatement) -> Result<Self, Self::Error> {
748        Ok(Self {
749            table: value.table.try_into()?,
750        })
751    }
752}
753
754#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
755#[tokenize_as(TruncateStatement)]
756pub struct TaggedTruncateStatement {
757    pub table: TaggedKeyspaceQualifiedName,
758}
759
760impl Parse for TaggedTruncateStatement {
761    type Output = Self;
762    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
763        s.parse::<TRUNCATE>()?;
764        let mut res = TaggedTruncateStatementBuilder::default();
765        res.table(s.parse()?);
766        s.parse::<Option<Semicolon>>()?;
767        Ok(res
768            .build()
769            .map_err(|e| anyhow::anyhow!("Invalid TRUNCATE statement: {}", e))?)
770    }
771}
772
773impl Display for TruncateStatement {
774    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
775        write!(f, "TRUNCATE {}", self.table)
776    }
777}
778
779impl<T: Into<KeyspaceQualifiedName>> From<T> for TruncateStatement {
780    fn from(name: T) -> Self {
781        Self { table: name.into() }
782    }
783}
784
785#[cfg(test)]
786mod test {
787    use super::*;
788    use crate::{
789        Compaction,
790        Compression,
791        JavaTimeUnit,
792        KeyspaceQualifyExt,
793        NativeType,
794        Order,
795        SpeculativeRetry,
796    };
797
798    #[test]
799    fn test_parse_create_table() {
800        let mut builder = CreateTableStatementBuilder::default();
801        assert!(builder.build().is_err());
802        builder.table("test");
803        assert!(builder.build().is_err());
804        builder.columns(vec![
805            ("ascii", NativeType::Ascii).into(),
806            ("bigint", NativeType::Bigint).into(),
807            ("blob", NativeType::Blob).into(),
808            ("boolean", NativeType::Boolean).into(),
809            ("counter", NativeType::Counter).into(),
810            ("decimal", NativeType::Decimal).into(),
811            ("double", NativeType::Double).into(),
812            ("duration", NativeType::Duration).into(),
813            ("float", NativeType::Float).into(),
814            ("inet", NativeType::Inet).into(),
815            ("int", NativeType::Int).into(),
816            ("smallint", NativeType::Smallint).into(),
817            ("text", NativeType::Text).into(),
818            ("time", NativeType::Time).into(),
819            ("timestamp", NativeType::Timestamp).into(),
820            ("timeuuid", NativeType::Timeuuid).into(),
821            ("tinyint", NativeType::Tinyint).into(),
822            ("uuid", NativeType::Uuid).into(),
823            ("varchar", NativeType::Varchar).into(),
824            ("varint", NativeType::Varint).into(),
825        ]);
826        let statement = builder.build().unwrap().to_string();
827        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
828        builder.table("test".dot("test"));
829        let statement = builder.build().unwrap().to_string();
830        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
831        builder.primary_key(vec!["tinyint", "int", "bigint"]);
832        let statement = builder.build().unwrap().to_string();
833        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
834        builder.if_not_exists();
835        let statement = builder.build().unwrap().to_string();
836        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
837        let mut opts_builder = crate::TableOptsBuilder::default();
838        assert!(opts_builder.build().is_err());
839        opts_builder.clustering_order(vec![
840            ("tinyint", Order::Ascending).into(),
841            ("int", Order::Descending).into(),
842            ("bigint", Order::Ascending).into(),
843        ]);
844        builder.options(opts_builder.build().unwrap());
845        let statement = builder.build().unwrap().to_string();
846        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
847        opts_builder.comment("test");
848        builder.options(opts_builder.build().unwrap());
849        let statement = builder.build().unwrap().to_string();
850        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
851        opts_builder.compaction(Compaction::size_tiered().enabled(false).build().unwrap().into());
852        builder.options(opts_builder.build().unwrap());
853        let statement = builder.build().unwrap().to_string();
854        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
855        opts_builder.compression(Compression::build().class("LZ4Compressor").build().unwrap());
856        builder.options(opts_builder.build().unwrap());
857        let statement = builder.build().unwrap().to_string();
858        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
859        opts_builder.default_time_to_live(0);
860        builder.options(opts_builder.build().unwrap());
861        let statement = builder.build().unwrap().to_string();
862        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
863        opts_builder.gc_grace_seconds(99999);
864        builder.options(opts_builder.build().unwrap());
865        let statement = builder.build().unwrap().to_string();
866        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
867        opts_builder.memtable_flush_period_in_ms(100);
868        builder.options(opts_builder.build().unwrap());
869        let statement = builder.build().unwrap().to_string();
870        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
871        opts_builder.read_repair(true);
872        builder.options(opts_builder.build().unwrap());
873        let statement = builder.build().unwrap().to_string();
874        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
875        opts_builder.read_repair(false);
876        builder.options(opts_builder.build().unwrap());
877        let statement = builder.build().unwrap().to_string();
878        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
879        opts_builder.speculative_retry(SpeculativeRetry::Percentile(99.0));
880        builder.options(opts_builder.build().unwrap());
881        let statement = builder.build().unwrap().to_string();
882        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
883    }
884
885    #[test]
886    fn test_parse_alter_table() {
887        let mut builder = AlterTableStatementBuilder::default();
888        builder.table("test");
889        assert!(builder.build().is_err());
890        builder.instruction(
891            AlterTableInstruction::add(vec![
892                ("ascii", NativeType::Ascii),
893                ("bigint", NativeType::Bigint),
894                ("blob", NativeType::Blob),
895                ("boolean", NativeType::Boolean),
896                ("counter", NativeType::Counter),
897                ("decimal", NativeType::Decimal),
898                ("double", NativeType::Double),
899                ("duration", NativeType::Duration),
900                ("float", NativeType::Float),
901                ("inet", NativeType::Inet),
902                ("int", NativeType::Int),
903                ("smallint", NativeType::Smallint),
904                ("text", NativeType::Text),
905                ("time", NativeType::Time),
906                ("timestamp", NativeType::Timestamp),
907                ("timeuuid", NativeType::Timeuuid),
908                ("tinyint", NativeType::Tinyint),
909                ("uuid", NativeType::Uuid),
910                ("varchar", NativeType::Varchar),
911                ("varint", NativeType::Varint),
912            ])
913            .unwrap(),
914        );
915        let statement = builder.build().unwrap().to_string();
916        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
917        builder.table("test".dot("test"));
918        let statement = builder.build().unwrap().to_string();
919        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
920        builder.instruction(AlterTableInstruction::alter("ascii", NativeType::Blob));
921        let statement = builder.build().unwrap().to_string();
922        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
923        builder.instruction(AlterTableInstruction::drop(vec!["ascii", "timestamp", "varint"]).unwrap());
924        let statement = builder.build().unwrap().to_string();
925        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
926        let mut opts_builder = crate::TableOptsBuilder::default();
927        assert!(opts_builder.build().is_err());
928        opts_builder
929            .compaction(
930                Compaction::leveled()
931                    .enabled(false)
932                    .tombstone_threshold(0.99)
933                    .tombstone_compaction_interval(10)
934                    .sstable_size_in_mb(2)
935                    .fanout_size(4)
936                    .log_all(true)
937                    .unchecked_tombstone_compaction(false)
938                    .only_purge_repaired_tombstone(true)
939                    .min_threshold(1)
940                    .max_threshold(10)
941                    .build()
942                    .unwrap()
943                    .into(),
944            )
945            .compression(
946                Compression::build()
947                    .class("java.org.something.MyCompressorClass")
948                    .chunk_length_in_kb(10)
949                    .crc_check_chance(0.5)
950                    .compression_level(1)
951                    .enabled(true)
952                    .build()
953                    .unwrap(),
954            )
955            .default_time_to_live(0)
956            .gc_grace_seconds(99999)
957            .memtable_flush_period_in_ms(100)
958            .read_repair(true)
959            .speculative_retry(SpeculativeRetry::custom("3h30m"));
960        builder.instruction(AlterTableInstruction::with(opts_builder.build().unwrap()));
961        let statement = builder.build().unwrap().to_string();
962        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
963        opts_builder
964            .compaction(
965                Compaction::time_window()
966                    .enabled(true)
967                    .tombstone_threshold(0.05)
968                    .tombstone_compaction_interval(2)
969                    .compaction_window_unit(JavaTimeUnit::Days)
970                    .compaction_window_size(2)
971                    .unsafe_aggressive_sstable_expiration(true)
972                    .log_all(false)
973                    .unchecked_tombstone_compaction(true)
974                    .only_purge_repaired_tombstone(false)
975                    .min_threshold(1)
976                    .max_threshold(10)
977                    .build()
978                    .unwrap()
979                    .into(),
980            )
981            .read_repair(false)
982            .speculative_retry(SpeculativeRetry::Always);
983        builder.instruction(AlterTableInstruction::with(opts_builder.build().unwrap()));
984        let statement = builder.build().unwrap().to_string();
985        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
986    }
987
988    #[test]
989    fn test_parse_drop_table() {
990        let mut builder = DropTableStatementBuilder::default();
991        builder.table("test");
992        let statement = builder.build().unwrap().to_string();
993        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
994        builder.if_exists().table("test".dot("test"));
995        let statement = builder.build().unwrap().to_string();
996        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
997    }
998
999    #[test]
1000    fn test_parse_create_keyspace() {
1001        let mut builder = CreateKeyspaceStatementBuilder::default();
1002        builder.keyspace("test");
1003        assert!(builder.build().is_err());
1004        builder.if_not_exists();
1005        assert!(builder.build().is_err());
1006        builder.options(
1007            KeyspaceOptsBuilder::default()
1008                .replication(1)
1009                .durable_writes(true)
1010                .build()
1011                .unwrap(),
1012        );
1013        let statement = builder.build().unwrap().to_string();
1014        println!("{}", statement);
1015        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1016        builder.options(
1017            KeyspaceOptsBuilder::default()
1018                .replication(Replication::network_topology(maplit::btreemap! {
1019                    "dc1" => 1,
1020                    "dc2" => 2,
1021                }))
1022                .durable_writes(false)
1023                .build()
1024                .unwrap(),
1025        );
1026        let statement = builder.build().unwrap().to_string();
1027        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1028    }
1029
1030    #[test]
1031    fn test_parse_alter_keyspace() {
1032        let mut builder = AlterKeyspaceStatementBuilder::default();
1033        builder.keyspace("test whitespace");
1034        assert!(builder.build().is_err());
1035        builder.options(KeyspaceOptsBuilder::default().replication(2).build().unwrap());
1036        let statement = builder.build().unwrap().to_string();
1037        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1038        builder.options(
1039            KeyspaceOptsBuilder::default()
1040                .replication(Replication::network_topology(maplit::btreemap! {
1041                    "dc1" => 1,
1042                    "dc2" => 2,
1043                }))
1044                .durable_writes(true)
1045                .build()
1046                .unwrap(),
1047        );
1048        let statement = builder.build().unwrap().to_string();
1049        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1050    }
1051
1052    #[test]
1053    fn test_parse_drop_keyspace() {
1054        let mut builder = DropKeyspaceStatementBuilder::default();
1055        builder.keyspace("test");
1056        let statement = builder.build().unwrap().to_string();
1057        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1058        builder.if_exists();
1059        let statement = builder.build().unwrap().to_string();
1060        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
1061    }
1062}