sqlparser/ast/helpers/
stmt_create_database.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[cfg(not(feature = "std"))]
19use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
20
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24#[cfg(feature = "visitor")]
25use yachtsql_sqlparser_derive::{Visit, VisitMut};
26
27use crate::ast::{
28    CatalogSyncNamespaceMode, ContactEntry, ObjectName, Statement, StorageSerializationPolicy, Tag,
29};
30use crate::parser::ParserError;
31
32/// Builder for create database statement variant ([1]).
33///
34/// This structure helps building and accessing a create database with more ease, without needing to:
35/// - Match the enum itself a lot of times; or
36/// - Moving a lot of variables around the code.
37///
38/// # Example
39/// ```rust
40/// use sqlparser::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
41/// use sqlparser::ast::{ColumnDef, Ident, ObjectName};
42/// let builder = CreateDatabaseBuilder::new(ObjectName::from(vec![Ident::new("database_name")]))
43///    .if_not_exists(true);
44/// // You can access internal elements with ease
45/// assert!(builder.if_not_exists);
46/// // Convert to a statement
47/// assert_eq!(
48///    builder.build().to_string(),
49///    "CREATE DATABASE IF NOT EXISTS database_name"
50/// )
51/// ```
52///
53/// [1]: Statement::CreateDatabase
54#[derive(Debug, Clone, PartialEq, Eq, Hash)]
55#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
57pub struct CreateDatabaseBuilder {
58    pub db_name: ObjectName,
59    pub if_not_exists: bool,
60    pub location: Option<String>,
61    pub managed_location: Option<String>,
62    pub or_replace: bool,
63    pub transient: bool,
64    pub clone: Option<ObjectName>,
65    pub data_retention_time_in_days: Option<u64>,
66    pub max_data_extension_time_in_days: Option<u64>,
67    pub external_volume: Option<String>,
68    pub catalog: Option<String>,
69    pub replace_invalid_characters: Option<bool>,
70    pub default_ddl_collation: Option<String>,
71    pub storage_serialization_policy: Option<StorageSerializationPolicy>,
72    pub comment: Option<String>,
73    pub catalog_sync: Option<String>,
74    pub catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
75    pub catalog_sync_namespace_flatten_delimiter: Option<String>,
76    pub with_tags: Option<Vec<Tag>>,
77    pub with_contacts: Option<Vec<ContactEntry>>,
78}
79
80impl CreateDatabaseBuilder {
81    pub fn new(name: ObjectName) -> Self {
82        Self {
83            db_name: name,
84            if_not_exists: false,
85            location: None,
86            managed_location: None,
87            or_replace: false,
88            transient: false,
89            clone: None,
90            data_retention_time_in_days: None,
91            max_data_extension_time_in_days: None,
92            external_volume: None,
93            catalog: None,
94            replace_invalid_characters: None,
95            default_ddl_collation: None,
96            storage_serialization_policy: None,
97            comment: None,
98            catalog_sync: None,
99            catalog_sync_namespace_mode: None,
100            catalog_sync_namespace_flatten_delimiter: None,
101            with_tags: None,
102            with_contacts: None,
103        }
104    }
105
106    pub fn location(mut self, location: Option<String>) -> Self {
107        self.location = location;
108        self
109    }
110
111    pub fn managed_location(mut self, managed_location: Option<String>) -> Self {
112        self.managed_location = managed_location;
113        self
114    }
115
116    pub fn or_replace(mut self, or_replace: bool) -> Self {
117        self.or_replace = or_replace;
118        self
119    }
120
121    pub fn transient(mut self, transient: bool) -> Self {
122        self.transient = transient;
123        self
124    }
125
126    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
127        self.if_not_exists = if_not_exists;
128        self
129    }
130
131    pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
132        self.clone = clone;
133        self
134    }
135
136    pub fn data_retention_time_in_days(mut self, data_retention_time_in_days: Option<u64>) -> Self {
137        self.data_retention_time_in_days = data_retention_time_in_days;
138        self
139    }
140
141    pub fn max_data_extension_time_in_days(
142        mut self,
143        max_data_extension_time_in_days: Option<u64>,
144    ) -> Self {
145        self.max_data_extension_time_in_days = max_data_extension_time_in_days;
146        self
147    }
148
149    pub fn external_volume(mut self, external_volume: Option<String>) -> Self {
150        self.external_volume = external_volume;
151        self
152    }
153
154    pub fn catalog(mut self, catalog: Option<String>) -> Self {
155        self.catalog = catalog;
156        self
157    }
158
159    pub fn replace_invalid_characters(mut self, replace_invalid_characters: Option<bool>) -> Self {
160        self.replace_invalid_characters = replace_invalid_characters;
161        self
162    }
163
164    pub fn default_ddl_collation(mut self, default_ddl_collation: Option<String>) -> Self {
165        self.default_ddl_collation = default_ddl_collation;
166        self
167    }
168
169    pub fn storage_serialization_policy(
170        mut self,
171        storage_serialization_policy: Option<StorageSerializationPolicy>,
172    ) -> Self {
173        self.storage_serialization_policy = storage_serialization_policy;
174        self
175    }
176
177    pub fn comment(mut self, comment: Option<String>) -> Self {
178        self.comment = comment;
179        self
180    }
181
182    pub fn catalog_sync(mut self, catalog_sync: Option<String>) -> Self {
183        self.catalog_sync = catalog_sync;
184        self
185    }
186
187    pub fn catalog_sync_namespace_mode(
188        mut self,
189        catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
190    ) -> Self {
191        self.catalog_sync_namespace_mode = catalog_sync_namespace_mode;
192        self
193    }
194
195    pub fn catalog_sync_namespace_flatten_delimiter(
196        mut self,
197        catalog_sync_namespace_flatten_delimiter: Option<String>,
198    ) -> Self {
199        self.catalog_sync_namespace_flatten_delimiter = catalog_sync_namespace_flatten_delimiter;
200        self
201    }
202
203    pub fn with_tags(mut self, with_tags: Option<Vec<Tag>>) -> Self {
204        self.with_tags = with_tags;
205        self
206    }
207
208    pub fn with_contacts(mut self, with_contacts: Option<Vec<ContactEntry>>) -> Self {
209        self.with_contacts = with_contacts;
210        self
211    }
212
213    pub fn build(self) -> Statement {
214        Statement::CreateDatabase {
215            db_name: self.db_name,
216            if_not_exists: self.if_not_exists,
217            managed_location: self.managed_location,
218            location: self.location,
219            or_replace: self.or_replace,
220            transient: self.transient,
221            clone: self.clone,
222            data_retention_time_in_days: self.data_retention_time_in_days,
223            max_data_extension_time_in_days: self.max_data_extension_time_in_days,
224            external_volume: self.external_volume,
225            catalog: self.catalog,
226            replace_invalid_characters: self.replace_invalid_characters,
227            default_ddl_collation: self.default_ddl_collation,
228            storage_serialization_policy: self.storage_serialization_policy,
229            comment: self.comment,
230            catalog_sync: self.catalog_sync,
231            catalog_sync_namespace_mode: self.catalog_sync_namespace_mode,
232            catalog_sync_namespace_flatten_delimiter: self.catalog_sync_namespace_flatten_delimiter,
233            with_tags: self.with_tags,
234            with_contacts: self.with_contacts,
235        }
236    }
237}
238
239impl TryFrom<Statement> for CreateDatabaseBuilder {
240    type Error = ParserError;
241
242    fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
243        match stmt {
244            Statement::CreateDatabase {
245                db_name,
246                if_not_exists,
247                location,
248                managed_location,
249                or_replace,
250                transient,
251                clone,
252                data_retention_time_in_days,
253                max_data_extension_time_in_days,
254                external_volume,
255                catalog,
256                replace_invalid_characters,
257                default_ddl_collation,
258                storage_serialization_policy,
259                comment,
260                catalog_sync,
261                catalog_sync_namespace_mode,
262                catalog_sync_namespace_flatten_delimiter,
263                with_tags,
264                with_contacts,
265            } => Ok(Self {
266                db_name,
267                if_not_exists,
268                location,
269                managed_location,
270                or_replace,
271                transient,
272                clone,
273                data_retention_time_in_days,
274                max_data_extension_time_in_days,
275                external_volume,
276                catalog,
277                replace_invalid_characters,
278                default_ddl_collation,
279                storage_serialization_policy,
280                comment,
281                catalog_sync,
282                catalog_sync_namespace_mode,
283                catalog_sync_namespace_flatten_delimiter,
284                with_tags,
285                with_contacts,
286            }),
287            _ => Err(ParserError::ParserError(format!(
288                "Expected create database statement, but received: {stmt}"
289            ))),
290        }
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use crate::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
297    use crate::ast::{Ident, ObjectName, Statement};
298    use crate::parser::ParserError;
299
300    #[test]
301    pub fn test_from_valid_statement() {
302        let builder = CreateDatabaseBuilder::new(ObjectName::from(vec![Ident::new("db_name")]));
303
304        let stmt = builder.clone().build();
305
306        assert_eq!(builder, CreateDatabaseBuilder::try_from(stmt).unwrap());
307    }
308
309    #[test]
310    pub fn test_from_invalid_statement() {
311        let stmt = Statement::Commit {
312            chain: false,
313            end: false,
314            modifier: None,
315        };
316
317        assert_eq!(
318            CreateDatabaseBuilder::try_from(stmt).unwrap_err(),
319            ParserError::ParserError(
320                "Expected create database statement, but received: COMMIT".to_owned()
321            )
322        );
323    }
324}