diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index ba9cf251b..49f6ae4bd 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1096,7 +1096,7 @@ impl fmt::Display for ColumnOptionDef { } } -/// Identity is a column option for defining an identity or autoincrement column in a creating table statement. +/// Identity is a column option for defining an identity or autoincrement column in a `CREATE TABLE` statement. /// Syntax /// ```sql /// { IDENTITY | AUTOINCREMENT } [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ] @@ -1106,17 +1106,53 @@ impl fmt::Display for ColumnOptionDef { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub enum Identity { +pub enum IdentityPropertyKind { + /// An identity property declared via the `AUTOINCREMENT` key word + /// Example: + /// ```sql + /// AUTOINCREMENT(100, 1) NOORDER + /// AUTOINCREMENT START 100 INCREMENT 1 ORDER + /// ``` + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table Autoincrement(IdentityProperty), + /// An identity property declared via the `IDENTITY` key word + /// Example, [MS SQL Server] or [Snowflake]: + /// ```sql + /// IDENTITY(100, 1) + /// ``` + /// [Snowflake] + /// ```sql + /// IDENTITY(100, 1) ORDER + /// IDENTITY START 100 INCREMENT 1 NOORDER + /// ``` + /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table Identity(IdentityProperty), } +impl fmt::Display for IdentityPropertyKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (command, property) = match self { + IdentityPropertyKind::Identity(property) => ("IDENTITY", property), + IdentityPropertyKind::Autoincrement(property) => ("AUTOINCREMENT", property), + }; + write!(f, "{command}")?; + if let Some(parameters) = &property.parameters { + write!(f, "{parameters}")?; + } + if let Some(order) = &property.order { + write!(f, "{order}")?; + } + Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct IdentityProperty { - pub parameters: Option, - pub order: Option, + pub parameters: Option, + pub order: Option, } /// A format of parameters of identity column. @@ -1136,11 +1172,40 @@ pub struct IdentityProperty { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub enum IdentityFormat { +pub enum IdentityPropertyFormatKind { + /// A parameters of identity column declared like parameters of function call + /// Example: + /// ```sql + /// (100, 1) + /// ``` + /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table FunctionCall(IdentityParameters), + /// A parameters of identity column declared with keywords `START` and `INCREMENT` + /// Example: + /// ```sql + /// START 100 INCREMENT 1 + /// ``` + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table StartAndIncrement(IdentityParameters), } +impl fmt::Display for IdentityPropertyFormatKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + IdentityPropertyFormatKind::FunctionCall(parameters) => { + write!(f, "({}, {})", parameters.seed, parameters.increment) + } + IdentityPropertyFormatKind::StartAndIncrement(parameters) => { + write!( + f, + " START {} INCREMENT {}", + parameters.seed, parameters.increment + ) + } + } + } +} #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -1149,53 +1214,25 @@ pub struct IdentityParameters { pub increment: Expr, } +/// The identity column option specifies how values are generated for the auto-incremented column, either in increasing or decreasing order. +/// Syntax +/// ```sql +/// ORDER | NOORDER +/// ``` +/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub enum IdentityOrder { +pub enum IdentityPropertyOrder { Order, NoOrder, } -impl fmt::Display for Identity { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (command, property) = match self { - Identity::Identity(property) => ("IDENTITY", property), - Identity::Autoincrement(property) => ("AUTOINCREMENT", property), - }; - write!(f, "{command}")?; - if let Some(parameters) = &property.parameters { - write!(f, "{parameters}")?; - } - if let Some(order) = &property.order { - write!(f, "{order}")?; - } - Ok(()) - } -} - -impl fmt::Display for IdentityFormat { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - IdentityFormat::FunctionCall(parameters) => { - write!(f, "({}, {})", parameters.seed, parameters.increment) - } - IdentityFormat::StartAndIncrement(parameters) => { - write!( - f, - " START {} INCREMENT {}", - parameters.seed, parameters.increment - ) - } - } - } -} - -impl fmt::Display for IdentityOrder { +impl fmt::Display for IdentityPropertyOrder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - IdentityOrder::Order => write!(f, " ORDER"), - IdentityOrder::NoOrder => write!(f, " NOORDER"), + IdentityPropertyOrder::Order => write!(f, " ORDER"), + IdentityPropertyOrder::NoOrder => write!(f, " NOORDER"), } } } @@ -1215,15 +1252,6 @@ pub enum ColumnPolicy { ProjectionPolicy(ColumnPolicyProperty), } -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub struct ColumnPolicyProperty { - pub with: bool, - pub policy_name: Ident, - pub using_columns: Option>, -} - impl fmt::Display for ColumnPolicy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (command, property) = match self { @@ -1241,6 +1269,21 @@ impl fmt::Display for ColumnPolicy { } } +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct ColumnPolicyProperty { + /// This flag indicates that the column policy option is declared using the `WITH` prefix. + /// Example + /// ```sql + /// WITH PROJECTION POLICY sample_policy + /// ``` + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table + pub with: bool, + pub policy_name: Ident, + pub using_columns: Option>, +} + /// Tags option of column /// Syntax /// ```sql @@ -1251,6 +1294,12 @@ impl fmt::Display for ColumnPolicy { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct TagsColumnOption { + /// This flag indicates that the tags option is declared using the `WITH` prefix. + /// Example: + /// ```sql + /// WITH TAG (A = 'Tag A') + /// ``` + /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table pub with: bool, pub tags: Vec, } @@ -1342,7 +1391,7 @@ pub enum ColumnOption { /// ``` /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table - Identity(Identity), + Identity(IdentityPropertyKind), /// SQLite specific: ON CONFLICT option on column definition /// OnConflict(Keyword), diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a249baec0..4533beff6 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -42,10 +42,10 @@ pub use self::ddl::{ AlterColumnOperation, AlterIndexOperation, AlterPolicyOperation, AlterTableOperation, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, Deduplicate, DeferrableInitial, GeneratedAs, - GeneratedExpressionMode, Identity, IdentityFormat, IdentityOrder, IdentityParameters, - IdentityProperty, IndexOption, IndexType, KeyOrIndexDisplay, Owner, Partition, ProcedureParam, - ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef, - UserDefinedTypeRepresentation, ViewColumnDef, + GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, + IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, Owner, + Partition, ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption, + UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef, }; pub use self::dml::{CreateIndex, CreateTable, Delete, Insert}; pub use self::operator::{BinaryOperator, UnaryOperator}; diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index dc8f49945..6d6f06ef6 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -23,9 +23,9 @@ use crate::ast::helpers::stmt_data_loading::{ StageParamsObject, }; use crate::ast::{ - ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, Identity, IdentityFormat, - IdentityOrder, IdentityParameters, IdentityProperty, ObjectName, RowAccessPolicy, Statement, - WrappedCollection, + ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, IdentityParameters, IdentityProperty, + IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, ObjectName, + RowAccessPolicy, Statement, TagsColumnOption, WrappedCollection, }; use crate::dialect::{Dialect, Precedence}; use crate::keywords::Keyword; @@ -162,13 +162,14 @@ impl Dialect for SnowflakeDialect { if parser.parse_keyword(Keyword::IDENTITY) { Some( parse_identity_property(parser) - .map(|p| Some(ColumnOption::Identity(Identity::Identity(p)))), + .map(|p| Some(ColumnOption::Identity(IdentityPropertyKind::Identity(p)))), ) } else if parser.parse_keyword(Keyword::AUTOINCREMENT) { - Some( - parse_identity_property(parser) - .map(|p| Some(ColumnOption::Identity(Identity::Autoincrement(p)))), - ) + Some(parse_identity_property(parser).map(|p| { + Some(ColumnOption::Identity(IdentityPropertyKind::Autoincrement( + p, + ))) + })) } else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) { Some( parse_column_policy_property(parser, with) @@ -179,6 +180,8 @@ impl Dialect for SnowflakeDialect { parse_column_policy_property(parser, with) .map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))), ) + } else if parser.parse_keywords(&[Keyword::TAG]) { + Some(parse_column_tags(parser, with).map(|p| Some(ColumnOption::Tags(p)))) } else { // needs to revert initial state of parser if dialect finds any matching if with { @@ -821,25 +824,23 @@ fn parse_identity_property(parser: &mut Parser) -> Result Some(IdentityOrder::Order), - Some(Keyword::NOORDER) => Some(IdentityOrder::NoOrder), + Some(Keyword::ORDER) => Some(IdentityPropertyOrder::Order), + Some(Keyword::NOORDER) => Some(IdentityPropertyOrder::NoOrder), _ => None, }; Ok(IdentityProperty { parameters, order }) @@ -871,3 +872,17 @@ fn parse_column_policy_property( using_columns, }) } + +/// Parsing tags list of column +/// Syntax: +/// ```sql +/// ( = '' [ , = '' , ... ] ) +/// ``` +/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table +fn parse_column_tags(parser: &mut Parser, with: bool) -> Result { + parser.expect_token(&Token::LParen)?; + let tags = parser.parse_comma_separated(Parser::parse_tag)?; + parser.expect_token(&Token::RParen)?; + + Ok(TagsColumnOption { with, tags }) +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 955674b80..8e33486e8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6104,8 +6104,6 @@ impl<'a> Parser<'a> { return option; } - let with = self.parse_keyword(Keyword::WITH); - if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { Ok(Some(ColumnOption::CharacterSet( self.parse_object_name(false)?, @@ -6239,19 +6237,18 @@ impl<'a> Parser<'a> { let increment = self.parse_number()?; self.expect_token(&Token::RParen)?; - Some(IdentityFormat::FunctionCall(IdentityParameters { - seed, - increment, - })) + Some(IdentityPropertyFormatKind::FunctionCall( + IdentityParameters { seed, increment }, + )) } else { None }; - Ok(Some(ColumnOption::Identity(Identity::Identity( - IdentityProperty { + Ok(Some(ColumnOption::Identity( + IdentityPropertyKind::Identity(IdentityProperty { parameters, order: None, - }, - )))) + }), + ))) } else if dialect_of!(self is SQLiteDialect | GenericDialect) && self.parse_keywords(&[Keyword::ON, Keyword::CONFLICT]) { @@ -6265,18 +6262,7 @@ impl<'a> Parser<'a> { Keyword::REPLACE, ])?, ))) - } else if self.parse_keywords(&[Keyword::TAG]) - && dialect_of!(self is SnowflakeDialect | GenericDialect) - { - self.expect_token(&Token::LParen)?; - let tags = self.parse_comma_separated(Self::parse_tag)?; - self.expect_token(&Token::RParen)?; - - Ok(Some(ColumnOption::Tags(TagsColumnOption { with, tags }))) } else { - if with { - self.prev_token(); - } Ok(None) } } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index da887a472..ef89a4768 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -921,10 +921,12 @@ fn parse_create_table_with_identity_column() { vec![ ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Identity(IdentityProperty { - parameters: None, - order: None, - })), + option: ColumnOption::Identity(IdentityPropertyKind::Identity( + IdentityProperty { + parameters: None, + order: None, + }, + )), }, ColumnOptionDef { name: None, @@ -937,13 +939,17 @@ fn parse_create_table_with_identity_column() { vec![ ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Identity(IdentityProperty { - parameters: Some(IdentityFormat::FunctionCall(IdentityParameters { - seed: Expr::Value(number("1")), - increment: Expr::Value(number("1")), - })), - order: None, - })), + option: ColumnOption::Identity(IdentityPropertyKind::Identity( + IdentityProperty { + parameters: Some(IdentityPropertyFormatKind::FunctionCall( + IdentityParameters { + seed: Expr::Value(number("1")), + increment: Expr::Value(number("1")), + }, + )), + order: None, + }, + )), }, ColumnOptionDef { name: None, diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index cc9b934d5..d7e967ffe 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -547,10 +547,10 @@ fn test_snowflake_create_table_with_autoincrement_columns() { collation: None, options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Autoincrement( + option: ColumnOption::Identity(IdentityPropertyKind::Autoincrement( IdentityProperty { parameters: None, - order: Some(IdentityOrder::Order), + order: Some(IdentityPropertyOrder::Order), } )) }] @@ -561,15 +561,15 @@ fn test_snowflake_create_table_with_autoincrement_columns() { collation: None, options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Autoincrement( + option: ColumnOption::Identity(IdentityPropertyKind::Autoincrement( IdentityProperty { - parameters: Some(IdentityFormat::FunctionCall( + parameters: Some(IdentityPropertyFormatKind::FunctionCall( IdentityParameters { seed: Expr::Value(number("100")), increment: Expr::Value(number("1")), } )), - order: Some(IdentityOrder::NoOrder), + order: Some(IdentityPropertyOrder::NoOrder), } )) }] @@ -580,10 +580,12 @@ fn test_snowflake_create_table_with_autoincrement_columns() { collation: None, options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Identity(IdentityProperty { - parameters: None, - order: None, - })) + option: ColumnOption::Identity(IdentityPropertyKind::Identity( + IdentityProperty { + parameters: None, + order: None, + } + )) }] }, ColumnDef { @@ -592,15 +594,19 @@ fn test_snowflake_create_table_with_autoincrement_columns() { collation: None, options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Identity(IdentityProperty { - parameters: Some(IdentityFormat::StartAndIncrement( - IdentityParameters { - seed: Expr::Value(number("100")), - increment: Expr::Value(number("1")), - } - )), - order: Some(IdentityOrder::Order), - })) + option: ColumnOption::Identity(IdentityPropertyKind::Identity( + IdentityProperty { + parameters: Some( + IdentityPropertyFormatKind::StartAndIncrement( + IdentityParameters { + seed: Expr::Value(number("100")), + increment: Expr::Value(number("1")), + } + ) + ), + order: Some(IdentityPropertyOrder::Order), + } + )) }] }, ] @@ -725,7 +731,7 @@ fn test_snowflake_create_table_with_columns_tags() { false, ), ] { - match snowflake_and_generic().verified_stmt(sql) { + match snowflake().verified_stmt(sql) { Statement::CreateTable(CreateTable { columns, .. }) => { assert_eq!( columns, @@ -771,7 +777,7 @@ fn test_snowflake_create_table_with_several_column_options() { options: vec![ ColumnOptionDef { name: None, - option: ColumnOption::Identity(Identity::Identity( + option: ColumnOption::Identity(IdentityPropertyKind::Identity( IdentityProperty { parameters: None, order: None