Skip to content

Snowflake create database #1939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ast/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
// under the License.
pub mod attached_token;
pub mod key_value_options;
pub mod stmt_create_database;
pub mod stmt_create_table;
pub mod stmt_data_loading;
324 changes: 324 additions & 0 deletions src/ast/helpers/stmt_create_database.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

use crate::ast::{
CatalogSyncNamespaceMode, ContactEntry, ObjectName, Statement, StorageSerializationPolicy, Tag,
};
use crate::parser::ParserError;

/// Builder for create database statement variant ([1]).
///
/// This structure helps building and accessing a create database with more ease, without needing to:
/// - Match the enum itself a lot of times; or
/// - Moving a lot of variables around the code.
///
/// # Example
/// ```rust
/// use sqlparser::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
/// use sqlparser::ast::{ColumnDef, Ident, ObjectName};
/// let builder = CreateDatabaseBuilder::new(ObjectName::from(vec![Ident::new("database_name")]))
/// .if_not_exists(true);
/// // You can access internal elements with ease
/// assert!(builder.if_not_exists);
/// // Convert to a statement
/// assert_eq!(
/// builder.build().to_string(),
/// "CREATE DATABASE IF NOT EXISTS database_name"
/// )
/// ```
///
/// [1]: Statement::CreateDatabase
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateDatabaseBuilder {
pub db_name: ObjectName,
pub if_not_exists: bool,
pub location: Option<String>,
pub managed_location: Option<String>,
pub or_replace: bool,
pub transient: bool,
pub clone: Option<ObjectName>,
pub data_retention_time_in_days: Option<u64>,
pub max_data_extension_time_in_days: Option<u64>,
pub external_volume: Option<String>,
pub catalog: Option<String>,
pub replace_invalid_characters: Option<bool>,
pub default_ddl_collation: Option<String>,
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
pub comment: Option<String>,
pub catalog_sync: Option<String>,
pub catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
pub catalog_sync_namespace_flatten_delimiter: Option<String>,
pub with_tags: Option<Vec<Tag>>,
pub with_contacts: Option<Vec<ContactEntry>>,
}

impl CreateDatabaseBuilder {
pub fn new(name: ObjectName) -> Self {
Self {
db_name: name,
if_not_exists: false,
location: None,
managed_location: None,
or_replace: false,
transient: false,
clone: None,
data_retention_time_in_days: None,
max_data_extension_time_in_days: None,
external_volume: None,
catalog: None,
replace_invalid_characters: None,
default_ddl_collation: None,
storage_serialization_policy: None,
comment: None,
catalog_sync: None,
catalog_sync_namespace_mode: None,
catalog_sync_namespace_flatten_delimiter: None,
with_tags: None,
with_contacts: None,
}
}

pub fn location(mut self, location: Option<String>) -> Self {
self.location = location;
self
}

pub fn managed_location(mut self, managed_location: Option<String>) -> Self {
self.managed_location = managed_location;
self
}

pub fn or_replace(mut self, or_replace: bool) -> Self {
self.or_replace = or_replace;
self
}

pub fn transient(mut self, transient: bool) -> Self {
self.transient = transient;
self
}

pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
self.if_not_exists = if_not_exists;
self
}

pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
self.clone = clone;
self
}

pub fn data_retention_time_in_days(mut self, data_retention_time_in_days: Option<u64>) -> Self {
self.data_retention_time_in_days = data_retention_time_in_days;
self
}

pub fn max_data_extension_time_in_days(
mut self,
max_data_extension_time_in_days: Option<u64>,
) -> Self {
self.max_data_extension_time_in_days = max_data_extension_time_in_days;
self
}

pub fn external_volume(mut self, external_volume: Option<String>) -> Self {
self.external_volume = external_volume;
self
}

pub fn catalog(mut self, catalog: Option<String>) -> Self {
self.catalog = catalog;
self
}

pub fn replace_invalid_characters(mut self, replace_invalid_characters: Option<bool>) -> Self {
self.replace_invalid_characters = replace_invalid_characters;
self
}

pub fn default_ddl_collation(mut self, default_ddl_collation: Option<String>) -> Self {
self.default_ddl_collation = default_ddl_collation;
self
}

pub fn storage_serialization_policy(
mut self,
storage_serialization_policy: Option<StorageSerializationPolicy>,
) -> Self {
self.storage_serialization_policy = storage_serialization_policy;
self
}

pub fn comment(mut self, comment: Option<String>) -> Self {
self.comment = comment;
self
}

pub fn catalog_sync(mut self, catalog_sync: Option<String>) -> Self {
self.catalog_sync = catalog_sync;
self
}

pub fn catalog_sync_namespace_mode(
mut self,
catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
) -> Self {
self.catalog_sync_namespace_mode = catalog_sync_namespace_mode;
self
}

pub fn catalog_sync_namespace_flatten_delimiter(
mut self,
catalog_sync_namespace_flatten_delimiter: Option<String>,
) -> Self {
self.catalog_sync_namespace_flatten_delimiter = catalog_sync_namespace_flatten_delimiter;
self
}

pub fn with_tags(mut self, with_tags: Option<Vec<Tag>>) -> Self {
self.with_tags = with_tags;
self
}

pub fn with_contacts(mut self, with_contacts: Option<Vec<ContactEntry>>) -> Self {
self.with_contacts = with_contacts;
self
}

pub fn build(self) -> Statement {
Statement::CreateDatabase {
db_name: self.db_name,
if_not_exists: self.if_not_exists,
managed_location: self.managed_location,
location: self.location,
or_replace: self.or_replace,
transient: self.transient,
clone: self.clone,
data_retention_time_in_days: self.data_retention_time_in_days,
max_data_extension_time_in_days: self.max_data_extension_time_in_days,
external_volume: self.external_volume,
catalog: self.catalog,
replace_invalid_characters: self.replace_invalid_characters,
default_ddl_collation: self.default_ddl_collation,
storage_serialization_policy: self.storage_serialization_policy,
comment: self.comment,
catalog_sync: self.catalog_sync,
catalog_sync_namespace_mode: self.catalog_sync_namespace_mode,
catalog_sync_namespace_flatten_delimiter: self.catalog_sync_namespace_flatten_delimiter,
with_tags: self.with_tags,
with_contacts: self.with_contacts,
}
}
}

impl TryFrom<Statement> for CreateDatabaseBuilder {
type Error = ParserError;

fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
match stmt {
Statement::CreateDatabase {
db_name,
if_not_exists,
location,
managed_location,
or_replace,
transient,
clone,
data_retention_time_in_days,
max_data_extension_time_in_days,
external_volume,
catalog,
replace_invalid_characters,
default_ddl_collation,
storage_serialization_policy,
comment,
catalog_sync,
catalog_sync_namespace_mode,
catalog_sync_namespace_flatten_delimiter,
with_tags,
with_contacts,
} => Ok(Self {
db_name,
if_not_exists,
location,
managed_location,
or_replace,
transient,
clone,
data_retention_time_in_days,
max_data_extension_time_in_days,
external_volume,
catalog,
replace_invalid_characters,
default_ddl_collation,
storage_serialization_policy,
comment,
catalog_sync,
catalog_sync_namespace_mode,
catalog_sync_namespace_flatten_delimiter,
with_tags,
with_contacts,
}),
_ => Err(ParserError::ParserError(format!(
"Expected create database statement, but received: {stmt}"
))),
}
}
}

#[cfg(test)]
mod tests {
use crate::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
use crate::ast::{Ident, ObjectName, Statement};
use crate::parser::ParserError;

#[test]
pub fn test_from_valid_statement() {
let builder = CreateDatabaseBuilder::new(ObjectName::from(vec![Ident::new("db_name")]));

let stmt = builder.clone().build();

assert_eq!(builder, CreateDatabaseBuilder::try_from(stmt).unwrap());
}

#[test]
pub fn test_from_invalid_statement() {
let stmt = Statement::Commit {
chain: false,
end: false,
modifier: None,
};

assert_eq!(
CreateDatabaseBuilder::try_from(stmt).unwrap_err(),
ParserError::ParserError(
"Expected create database statement, but received: COMMIT".to_owned()
)
);
}
}
Loading