From b0121b74e4d2b36960439d01073f5959e462f4b9 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Sat, 19 Jul 2025 09:33:19 +0300 Subject: [PATCH] Snowflake: CREATE DATABASE/SCHEMA ... CLONE --- src/ast/mod.rs | 24 ++++++++++++++++++++++++ src/parser/mod.rs | 14 ++++++++++++++ tests/sqlparser_common.rs | 25 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1f5311aff..b321bb535 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3846,6 +3846,14 @@ pub enum Statement { /// /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_schema_statement) default_collate_spec: Option, + /// Clones a schema + /// + /// ```sql + /// CREATE SCHEMA myschema CLONE otherschema + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-clone#databases-schemas) + clone: Option, }, /// ```sql /// CREATE DATABASE @@ -3855,6 +3863,14 @@ pub enum Statement { if_not_exists: bool, location: Option, managed_location: Option, + /// Clones a database + /// + /// ```sql + /// CREATE DATABASE mydb CLONE otherdb + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-clone#databases-schemas) + clone: Option, }, /// ```sql /// CREATE FUNCTION @@ -4797,6 +4813,7 @@ impl fmt::Display for Statement { if_not_exists, location, managed_location, + clone, } => { write!(f, "CREATE DATABASE")?; if *if_not_exists { @@ -4809,6 +4826,9 @@ impl fmt::Display for Statement { if let Some(ml) = managed_location { write!(f, " MANAGEDLOCATION '{ml}'")?; } + if let Some(clone) = clone { + write!(f, " CLONE {clone}")?; + } Ok(()) } Statement::CreateFunction(create_function) => create_function.fmt(f), @@ -5730,6 +5750,7 @@ impl fmt::Display for Statement { with, options, default_collate_spec, + clone, } => { write!( f, @@ -5750,6 +5771,9 @@ impl fmt::Display for Statement { write!(f, " OPTIONS({})", display_comma_separated(options))?; } + if let Some(clone) = clone { + write!(f, " CLONE {clone}")?; + } Ok(()) } Statement::Assert { condition, message } => { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3bb913118..accc72d6e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4900,12 +4900,19 @@ impl<'a> Parser<'a> { None }; + let clone = if self.parse_keyword(Keyword::CLONE) { + Some(self.parse_object_name(false)?) + } else { + None + }; + Ok(Statement::CreateSchema { schema_name, if_not_exists, with, options, default_collate_spec, + clone, }) } @@ -4940,11 +4947,18 @@ impl<'a> Parser<'a> { _ => break, } } + let clone = if self.parse_keyword(Keyword::CLONE) { + Some(self.parse_object_name(false)?) + } else { + None + }; + Ok(Statement::CreateDatabase { db_name, if_not_exists: ine, location, managed_location, + clone, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 4183c5539..c9d857df5 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4296,6 +4296,7 @@ fn parse_create_schema() { verified_stmt(r#"CREATE SCHEMA a.b.c WITH (key1 = 'value1', key2 = 'value2')"#); verified_stmt(r#"CREATE SCHEMA IF NOT EXISTS a WITH (key1 = 'value1')"#); verified_stmt(r#"CREATE SCHEMA IF NOT EXISTS a WITH ()"#); + verified_stmt(r#"CREATE SCHEMA a CLONE b"#); } #[test] @@ -7900,11 +7901,33 @@ fn parse_create_database() { if_not_exists, location, managed_location, + clone, } => { assert_eq!("mydb", db_name.to_string()); assert!(!if_not_exists); assert_eq!(None, location); assert_eq!(None, managed_location); + assert_eq!(None, clone); + } + _ => unreachable!(), + } + let sql = "CREATE DATABASE mydb CLONE otherdb"; + match verified_stmt(sql) { + Statement::CreateDatabase { + db_name, + if_not_exists, + location, + managed_location, + clone, + } => { + assert_eq!("mydb", db_name.to_string()); + assert!(!if_not_exists); + assert_eq!(None, location); + assert_eq!(None, managed_location); + assert_eq!( + Some(ObjectName::from(vec![Ident::new("otherdb".to_string())])), + clone + ); } _ => unreachable!(), } @@ -7919,11 +7942,13 @@ fn parse_create_database_ine() { if_not_exists, location, managed_location, + clone, } => { assert_eq!("mydb", db_name.to_string()); assert!(if_not_exists); assert_eq!(None, location); assert_eq!(None, managed_location); + assert_eq!(None, clone); } _ => unreachable!(), }