From c41a356d93629eb2f620cb89a6bf5f4a1e686596 Mon Sep 17 00:00:00 2001 From: "aleksei.p" Date: Wed, 18 Sep 2024 18:05:09 +0200 Subject: [PATCH] MS SQL Server: add support for IDENTITY column option --- src/ast/ddl.rs | 14 +++++ src/ast/mod.rs | 10 ++++ src/parser/mod.rs | 14 +++++ tests/sqlparser_mssql.rs | 108 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index b5444b8da..3884803fc 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1120,6 +1120,13 @@ pub enum ColumnOption { /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list /// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list Options(Vec), + /// MS SQL Server specific: Creates an identity column in a table. + /// Syntax + /// ```sql + /// IDENTITY [ (seed , increment) ] + /// ``` + /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property + Identity(Option), } impl fmt::Display for ColumnOption { @@ -1221,6 +1228,13 @@ impl fmt::Display for ColumnOption { Options(options) => { write!(f, "OPTIONS({})", display_comma_separated(options)) } + Identity(parameters) => { + write!(f, "IDENTITY")?; + if let Some(parameters) = parameters { + write!(f, "({parameters})")?; + } + Ok(()) + } } } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6c851906c..5be6405fa 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -5818,6 +5818,13 @@ pub enum SqlOption { range_direction: Option, for_values: Vec, }, + /// MS SQL Server specific: Optional parameters of identity column + /// E.g. + /// + /// IDENTITY(1, 2) + /// + /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property + Identity { seed: Value, increment: Value }, } impl fmt::Display for SqlOption { @@ -5849,6 +5856,9 @@ impl fmt::Display for SqlOption { display_comma_separated(for_values) ) } + SqlOption::Identity { seed, increment } => { + write!(f, "{}, {}", seed, increment) + } } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e2f4dd508..ea67d4784 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6043,6 +6043,20 @@ impl<'a> Parser<'a> { && dialect_of!(self is MySqlDialect | SQLiteDialect | DuckDbDialect | GenericDialect) { self.parse_optional_column_option_as() + } else if self.parse_keyword(Keyword::IDENTITY) + && dialect_of!(self is MsSqlDialect | GenericDialect) + { + let parameters = if self.expect_token(&Token::LParen).is_ok() { + let seed = self.parse_number_value()?; + self.expect_token(&Token::Comma)?; + let increment = self.parse_number_value()?; + self.expect_token(&Token::RParen)?; + + Some(SqlOption::Identity { seed, increment }) + } else { + None + }; + Ok(Some(ColumnOption::Identity(parameters))) } else { Ok(None) } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 0ab160f56..9ba29bcd4 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -908,6 +908,114 @@ fn parse_create_table_with_invalid_options() { } } +#[test] +fn parse_create_table_with_identity_column() { + let with_column_options = [ + ( + r#"CREATE TABLE [mytable] ([columnA] INT IDENTITY NOT NULL)"#, + vec![ + ColumnOptionDef { + name: None, + option: ColumnOption::Identity(None), + }, + ColumnOptionDef { + name: None, + option: ColumnOption::NotNull, + }, + ], + ), + ( + r#"CREATE TABLE [mytable] ([columnA] INT IDENTITY(1, 1) NOT NULL)"#, + vec![ + ColumnOptionDef { + name: None, + #[cfg(not(feature = "bigdecimal"))] + option: ColumnOption::Identity(Some(SqlOption::Identity { + seed: Value::Number("1".to_string(), false), + increment: Value::Number("1".to_string(), false), + })), + #[cfg(feature = "bigdecimal")] + option: ColumnOption::Identity(Some(SqlOption::Identity { + seed: Value::Number(bigdecimal::BigDecimal::from(1), false), + increment: Value::Number(bigdecimal::BigDecimal::from(1), false), + })), + }, + ColumnOptionDef { + name: None, + option: ColumnOption::NotNull, + }, + ], + ), + ]; + + for (sql, column_options) in with_column_options { + assert_eq!( + ms().verified_stmt(sql), + Statement::CreateTable(CreateTable { + or_replace: false, + temporary: false, + external: false, + global: None, + if_not_exists: false, + transient: false, + volatile: false, + name: ObjectName(vec![Ident { + value: "mytable".to_string(), + quote_style: Some('['), + },],), + columns: vec![ColumnDef { + name: Ident { + value: "columnA".to_string(), + quote_style: Some('['), + }, + data_type: Int(None,), + collation: None, + options: column_options, + },], + constraints: vec![], + hive_distribution: HiveDistributionStyle::NONE, + hive_formats: Some(HiveFormat { + row_format: None, + serde_properties: None, + storage: None, + location: None, + },), + table_properties: vec![], + with_options: vec![], + file_format: None, + location: None, + query: None, + without_rowid: false, + like: None, + clone: None, + engine: None, + comment: None, + auto_increment_offset: None, + default_charset: None, + collation: None, + on_commit: None, + on_cluster: None, + primary_key: None, + order_by: None, + partition_by: None, + cluster_by: None, + clustered_by: None, + options: None, + strict: false, + copy_grants: false, + enable_schema_evolution: None, + change_tracking: None, + data_retention_time_in_days: None, + max_data_extension_time_in_days: None, + default_ddl_collation: None, + with_aggregation_policy: None, + with_row_access_policy: None, + with_tags: None, + }), + ); + } +} + fn ms() -> TestedDialects { TestedDialects { dialects: vec![Box::new(MsSqlDialect {})],