diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index b5444b8da..3cb754dfc 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1050,6 +1050,20 @@ 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 struct IdentityProperty { + pub seed: Expr, + pub increment: Expr, +} + +impl fmt::Display for IdentityProperty { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}, {}", self.seed, self.increment) + } +} + /// `ColumnOption`s are modifiers that follow a column definition in a `CREATE /// TABLE` statement. #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] @@ -1120,6 +1134,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 +1242,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..0bb37677c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -36,8 +36,8 @@ pub use self::dcl::{AlterRoleOperation, ResetConfig, RoleOption, SetConfigValue, pub use self::ddl::{ AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ConstraintCharacteristics, Deduplicate, DeferrableInitial, - GeneratedAs, GeneratedExpressionMode, IndexOption, IndexType, KeyOrIndexDisplay, Owner, - Partition, ProcedureParam, ReferentialAction, TableConstraint, + GeneratedAs, GeneratedExpressionMode, IdentityProperty, IndexOption, IndexType, + KeyOrIndexDisplay, Owner, Partition, ProcedureParam, ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef, }; pub use self::dml::{CreateIndex, CreateTable, Delete, Insert}; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e2f4dd508..47076995a 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 property = if self.consume_token(&Token::LParen) { + let seed = self.parse_number()?; + self.expect_token(&Token::Comma)?; + let increment = self.parse_number()?; + self.expect_token(&Token::RParen)?; + + Some(IdentityProperty { seed, increment }) + } else { + None + }; + Ok(Some(ColumnOption::Identity(property))) } else { Ok(None) } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 0ab160f56..94dc4cdd6 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -908,6 +908,117 @@ 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(IdentityProperty { + seed: Expr::Value(Value::Number("1".to_string(), false)), + increment: Expr::Value(Value::Number("1".to_string(), false)), + })), + #[cfg(feature = "bigdecimal")] + option: ColumnOption::Identity(Some(IdentityProperty { + seed: Expr::Value(Value::Number(bigdecimal::BigDecimal::from(1), false)), + increment: Expr::Value(Value::Number( + bigdecimal::BigDecimal::from(1), + false, + )), + })), + }, + ColumnOptionDef { + name: None, + option: ColumnOption::NotNull, + }, + ], + ), + ]; + + for (sql, column_options) in with_column_options { + assert_eq!( + ms_and_generic().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: None, + },],), + columns: vec![ColumnDef { + name: Ident { + value: "columnA".to_string(), + quote_style: None, + }, + 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 {})],