diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index c6ab7ad10..85f3202e0 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -30,8 +30,9 @@ use sqlparser_derive::{Visit, VisitMut}; use crate::ast::value::escape_single_quote_string; use crate::ast::{ - display_comma_separated, display_separated, table_constraints::TableConstraint, ArgMode, - CommentDef, ConditionalStatements, CreateFunctionBody, CreateFunctionUsing, + display_comma_separated, display_separated, + table_constraints::{PrimaryKeyConstraint, TableConstraint, UniqueConstraint}, + ArgMode, CommentDef, ConditionalStatements, CreateFunctionBody, CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, DataType, Expr, FileFormat, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InitializeKind, MySQLColumnPosition, @@ -53,6 +54,22 @@ pub struct IndexColumn { pub operator_class: Option, } +impl From for IndexColumn { + fn from(c: Ident) -> Self { + Self { + column: OrderByExpr::from(c), + operator_class: None, + } + } +} + +impl<'a> From<&'a str> for IndexColumn { + fn from(c: &'a str) -> Self { + let ident = Ident::new(c); + ident.into() + } +} + impl fmt::Display for IndexColumn { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.column)?; @@ -1553,11 +1570,10 @@ pub enum ColumnOption { /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/create/table#default_values) Alias(Expr), - /// `{ PRIMARY KEY | UNIQUE } []` - Unique { - is_primary: bool, - characteristics: Option, - }, + /// `PRIMARY KEY []` + PrimaryKey(PrimaryKeyConstraint), + /// `UNIQUE []` + Unique(UniqueConstraint), /// A referential integrity constraint (`[FOREIGN KEY REFERENCES /// () /// { [ON DELETE ] [ON UPDATE ] | @@ -1642,6 +1658,18 @@ pub enum ColumnOption { Invisible, } +impl From for ColumnOption { + fn from(c: UniqueConstraint) -> Self { + ColumnOption::Unique(c) + } +} + +impl From for ColumnOption { + fn from(c: PrimaryKeyConstraint) -> Self { + ColumnOption::PrimaryKey(c) + } +} + impl fmt::Display for ColumnOption { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use ColumnOption::*; @@ -1658,12 +1686,16 @@ impl fmt::Display for ColumnOption { } } Alias(expr) => write!(f, "ALIAS {expr}"), - Unique { - is_primary, - characteristics, - } => { - write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })?; - if let Some(characteristics) = characteristics { + PrimaryKey(constraint) => { + write!(f, "PRIMARY KEY")?; + if let Some(characteristics) = &constraint.characteristics { + write!(f, " {characteristics}")?; + } + Ok(()) + } + Unique(constraint) => { + write!(f, "UNIQUE")?; + if let Some(characteristics) = &constraint.characteristics { write!(f, " {characteristics}")?; } Ok(()) diff --git a/src/ast/query.rs b/src/ast/query.rs index 3b414cc6e..599b013ab 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2506,6 +2506,16 @@ pub struct OrderByExpr { pub with_fill: Option, } +impl From for OrderByExpr { + fn from(ident: Ident) -> Self { + OrderByExpr { + expr: Expr::Identifier(ident), + options: OrderByOptions::default(), + with_fill: None, + } + } +} + impl fmt::Display for OrderByExpr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.expr, self.options)?; @@ -2574,7 +2584,7 @@ impl fmt::Display for InterpolateExpr { } } -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct OrderByOptions { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 0a303fcfd..d93bc6e30 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -809,7 +809,8 @@ impl Spanned for RaiseStatementValue { /// - [ColumnOption::Null] /// - [ColumnOption::NotNull] /// - [ColumnOption::Comment] -/// - [ColumnOption::Unique]ยจ +/// - [ColumnOption::PrimaryKey] +/// - [ColumnOption::Unique] /// - [ColumnOption::DialectSpecific] /// - [ColumnOption::Generated] impl Spanned for ColumnOption { @@ -821,7 +822,8 @@ impl Spanned for ColumnOption { ColumnOption::Materialized(expr) => expr.span(), ColumnOption::Ephemeral(expr) => expr.as_ref().map_or(Span::empty(), |e| e.span()), ColumnOption::Alias(expr) => expr.span(), - ColumnOption::Unique { .. } => Span::empty(), + ColumnOption::PrimaryKey(constraint) => constraint.span(), + ColumnOption::Unique(constraint) => constraint.span(), ColumnOption::ForeignKey { foreign_table, referred_columns, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d1e9b1e78..caeb68ee1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7919,7 +7919,7 @@ impl<'a> Parser<'a> { } pub fn parse_column_def(&mut self) -> Result { - let name = self.parse_identifier()?; + let col_name = self.parse_identifier()?; let data_type = if self.is_column_type_sqlite_unspecified() { DataType::Unspecified } else { @@ -7929,7 +7929,7 @@ impl<'a> Parser<'a> { loop { if self.parse_keyword(Keyword::CONSTRAINT) { let name = Some(self.parse_identifier()?); - if let Some(option) = self.parse_optional_column_option()? { + if let Some(option) = self.parse_optional_column_option(&col_name)? { options.push(ColumnOptionDef { name, option }); } else { return self.expected( @@ -7937,14 +7937,14 @@ impl<'a> Parser<'a> { self.peek_token(), ); } - } else if let Some(option) = self.parse_optional_column_option()? { + } else if let Some(option) = self.parse_optional_column_option(&col_name)? { options.push(ColumnOptionDef { name: None, option }); } else { break; }; } Ok(ColumnDef { - name, + name: col_name, data_type, options, }) @@ -7973,7 +7973,10 @@ impl<'a> Parser<'a> { } } - pub fn parse_optional_column_option(&mut self) -> Result, ParserError> { + pub fn parse_optional_column_option( + &mut self, + column_name: &Ident, + ) -> Result, ParserError> { if let Some(option) = self.dialect.parse_column_option(self)? { return option; } @@ -7981,12 +7984,15 @@ impl<'a> Parser<'a> { self.with_state( ColumnDefinition, |parser| -> Result, ParserError> { - parser.parse_optional_column_option_inner() + parser.parse_optional_column_option_inner(column_name) }, ) } - fn parse_optional_column_option_inner(&mut self) -> Result, ParserError> { + fn parse_optional_column_option_inner( + &mut self, + column_name: &Ident, + ) -> Result, ParserError> { if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { Ok(Some(ColumnOption::CharacterSet( self.parse_object_name(false)?, @@ -8029,16 +8035,32 @@ impl<'a> Parser<'a> { } } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) { let characteristics = self.parse_constraint_characteristics()?; - Ok(Some(ColumnOption::Unique { - is_primary: true, - characteristics, - })) + Ok(Some( + PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec![column_name.clone().into()], + index_options: vec![], + characteristics, + } + .into(), + )) } else if self.parse_keyword(Keyword::UNIQUE) { let characteristics = self.parse_constraint_characteristics()?; - Ok(Some(ColumnOption::Unique { - is_primary: false, - characteristics, - })) + Ok(Some( + UniqueConstraint { + name: None, + index_name: None, + index_type_display: KeyOrIndexDisplay::None, + index_type: None, + columns: vec![column_name.clone().into()], + index_options: vec![], + characteristics, + nulls_distinct: NullsDistinctOption::None, + } + .into(), + )) } else if self.parse_keyword(Keyword::REFERENCES) { let foreign_table = self.parse_object_name(false)?; // PostgreSQL allows omitting the column list and @@ -9046,7 +9068,7 @@ impl<'a> Parser<'a> { let new_name = self.parse_identifier()?; let data_type = self.parse_data_type()?; let mut options = vec![]; - while let Some(option) = self.parse_optional_column_option()? { + while let Some(option) = self.parse_optional_column_option(&new_name)? { options.push(option); } @@ -9064,7 +9086,7 @@ impl<'a> Parser<'a> { let col_name = self.parse_identifier()?; let data_type = self.parse_data_type()?; let mut options = vec![]; - while let Some(option) = self.parse_optional_column_option()? { + while let Some(option) = self.parse_optional_column_option(&col_name)? { options.push(option); } @@ -11325,7 +11347,7 @@ impl<'a> Parser<'a> { /// Parses a column definition within a view. fn parse_view_column(&mut self) -> Result { let name = self.parse_identifier()?; - let options = self.parse_view_column_options()?; + let options = self.parse_view_column_options(&name)?; let data_type = if dialect_of!(self is ClickHouseDialect) { Some(self.parse_data_type()?) } else { @@ -11338,10 +11360,13 @@ impl<'a> Parser<'a> { }) } - fn parse_view_column_options(&mut self) -> Result, ParserError> { + fn parse_view_column_options( + &mut self, + column_name: &Ident, + ) -> Result, ParserError> { let mut options = Vec::new(); loop { - let option = self.parse_optional_column_option()?; + let option = self.parse_optional_column_option(column_name)?; if let Some(option) = option { options.push(option); } else { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a1fd48d3e..89014aa69 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3763,10 +3763,14 @@ fn parse_create_table() { }, ColumnOptionDef { name: Some("pkey".into()), - option: ColumnOption::Unique { - is_primary: true, - characteristics: None - }, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec!["constrained".into()], + index_options: vec![], + characteristics: None, + }), }, ColumnOptionDef { name: None, @@ -3774,10 +3778,16 @@ fn parse_create_table() { }, ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: false, - characteristics: None - }, + option: ColumnOption::Unique(UniqueConstraint { + name: None, + index_name: None, + index_type_display: KeyOrIndexDisplay::None, + index_type: None, + columns: vec!["constrained".into()], + index_options: vec![], + characteristics: None, + nulls_distinct: NullsDistinctOption::None, + }), }, ColumnOptionDef { name: None, @@ -4086,10 +4096,16 @@ fn parse_create_table_column_constraint_characteristics() { data_type: DataType::Int(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: false, - characteristics: expected_value - } + option: ColumnOption::Unique(UniqueConstraint { + name: None, + index_name: None, + index_type_display: KeyOrIndexDisplay::None, + index_type: None, + columns: vec!["a".into()], + index_options: vec![], + characteristics: expected_value, + nulls_distinct: NullsDistinctOption::None, + }) }] }], "{message}" diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 35bac548c..602175028 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -638,10 +638,14 @@ fn parse_create_table_auto_increment() { options: vec![ ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: true, - characteristics: None - }, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec!["bar".into()], + index_options: vec![], + characteristics: None, + }), }, ColumnOptionDef { name: None, @@ -743,10 +747,14 @@ fn parse_create_table_primary_and_unique_key() { options: vec![ ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: true, - characteristics: None - }, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec!["id".into()], + index_options: vec![], + characteristics: None, + }), }, ColumnOptionDef { name: None, @@ -1382,10 +1390,14 @@ fn parse_quote_identifiers() { data_type: DataType::Int(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: true, - characteristics: None - }, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec![Ident::with_quote('`', "BEGIN").into()], + index_options: vec![], + characteristics: None, + }), }], }], columns diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 114aca03a..cbe2fb5db 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -217,10 +217,14 @@ fn parse_create_table_auto_increment() { options: vec![ ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: true, - characteristics: None - }, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec!["bar".into()], + index_options: vec![], + characteristics: None, + }), }, ColumnOptionDef { name: None, @@ -245,10 +249,14 @@ fn parse_create_table_primary_key_asc_desc() { options: vec![ ColumnOptionDef { name: None, - option: ColumnOption::Unique { - is_primary: true, + option: ColumnOption::PrimaryKey(PrimaryKeyConstraint { + name: None, + index_name: None, + index_type: None, + columns: vec!["bar".into()], + index_options: vec![], characteristics: None, - }, + }), }, ColumnOptionDef { name: None,