Skip to content

Commit 961b1be

Browse files
Merge branch 'main' into unique-unique-constraint
2 parents b9326fe + c8531d4 commit 961b1be

File tree

8 files changed

+171
-54
lines changed

8 files changed

+171
-54
lines changed

src/ast/ddl.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use sqlparser_derive::{Visit, VisitMut};
3131
use crate::ast::value::escape_single_quote_string;
3232
use crate::ast::{
3333
display_comma_separated, display_separated,
34-
table_constraints::{PrimaryKeyConstraint, TableConstraint, UniqueConstraint},
34+
table_constraints::{
35+
ForeignKeyConstraint, PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
36+
},
3537
ArgMode, AttachedToken, CommentDef, ConditionalStatements, CreateFunctionBody,
3638
CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, CreateViewParams, DataType, Expr,
3739
FileFormat, FunctionBehavior, FunctionCalledOnNull, FunctionDesc, FunctionDeterminismSpecifier,
@@ -1575,20 +1577,14 @@ pub enum ColumnOption {
15751577
PrimaryKey(PrimaryKeyConstraint),
15761578
/// `UNIQUE [<constraint_characteristics>]`
15771579
Unique(UniqueConstraint),
1578-
/// A referential integrity constraint (`[FOREIGN KEY REFERENCES
1579-
/// <foreign_table> (<referred_columns>)
1580+
/// A referential integrity constraint (`REFERENCES <foreign_table> (<referred_columns>)
1581+
/// [ MATCH { FULL | PARTIAL | SIMPLE } ]
15801582
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
15811583
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
1582-
/// }
1584+
/// }
15831585
/// [<constraint_characteristics>]
15841586
/// `).
1585-
ForeignKey {
1586-
foreign_table: ObjectName,
1587-
referred_columns: Vec<Ident>,
1588-
on_delete: Option<ReferentialAction>,
1589-
on_update: Option<ReferentialAction>,
1590-
characteristics: Option<ConstraintCharacteristics>,
1591-
},
1587+
ForeignKey(ForeignKeyConstraint),
15921588
/// `CHECK (<expr>)`
15931589
Check(Expr),
15941590
/// Dialect-specific options, such as:
@@ -1671,6 +1667,12 @@ impl From<PrimaryKeyConstraint> for ColumnOption {
16711667
}
16721668
}
16731669

1670+
impl From<ForeignKeyConstraint> for ColumnOption {
1671+
fn from(fk: ForeignKeyConstraint) -> Self {
1672+
ColumnOption::ForeignKey(fk)
1673+
}
1674+
}
1675+
16741676
impl fmt::Display for ColumnOption {
16751677
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16761678
use ColumnOption::*;
@@ -1701,24 +1703,25 @@ impl fmt::Display for ColumnOption {
17011703
}
17021704
Ok(())
17031705
}
1704-
ForeignKey {
1705-
foreign_table,
1706-
referred_columns,
1707-
on_delete,
1708-
on_update,
1709-
characteristics,
1710-
} => {
1711-
write!(f, "REFERENCES {foreign_table}")?;
1712-
if !referred_columns.is_empty() {
1713-
write!(f, " ({})", display_comma_separated(referred_columns))?;
1706+
ForeignKey(constraint) => {
1707+
write!(f, "REFERENCES {}", constraint.foreign_table)?;
1708+
if !constraint.referred_columns.is_empty() {
1709+
write!(
1710+
f,
1711+
" ({})",
1712+
display_comma_separated(&constraint.referred_columns)
1713+
)?;
17141714
}
1715-
if let Some(action) = on_delete {
1715+
if let Some(match_kind) = &constraint.match_kind {
1716+
write!(f, " {match_kind}")?;
1717+
}
1718+
if let Some(action) = &constraint.on_delete {
17161719
write!(f, " ON DELETE {action}")?;
17171720
}
1718-
if let Some(action) = on_update {
1721+
if let Some(action) = &constraint.on_update {
17191722
write!(f, " ON UPDATE {action}")?;
17201723
}
1721-
if let Some(characteristics) = characteristics {
1724+
if let Some(characteristics) = &constraint.characteristics {
17221725
write!(f, " {characteristics}")?;
17231726
}
17241727
Ok(())

src/ast/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,31 @@ pub enum CastKind {
657657
DoubleColon,
658658
}
659659

660+
/// `MATCH` type for constraint references
661+
///
662+
/// See: <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES>
663+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
664+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
665+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
666+
pub enum ConstraintReferenceMatchKind {
667+
/// `MATCH FULL`
668+
Full,
669+
/// `MATCH PARTIAL`
670+
Partial,
671+
/// `MATCH SIMPLE`
672+
Simple,
673+
}
674+
675+
impl fmt::Display for ConstraintReferenceMatchKind {
676+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
677+
match self {
678+
Self::Full => write!(f, "MATCH FULL"),
679+
Self::Partial => write!(f, "MATCH PARTIAL"),
680+
Self::Simple => write!(f, "MATCH SIMPLE"),
681+
}
682+
}
683+
}
684+
660685
/// `EXTRACT` syntax variants.
661686
///
662687
/// In Snowflake dialect, the `EXTRACT` expression can support either the `from` syntax

src/ast/spans.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -743,19 +743,7 @@ impl Spanned for ColumnOption {
743743
ColumnOption::Alias(expr) => expr.span(),
744744
ColumnOption::PrimaryKey(constraint) => constraint.span(),
745745
ColumnOption::Unique(constraint) => constraint.span(),
746-
ColumnOption::ForeignKey {
747-
foreign_table,
748-
referred_columns,
749-
on_delete,
750-
on_update,
751-
characteristics,
752-
} => union_spans(
753-
core::iter::once(foreign_table.span())
754-
.chain(referred_columns.iter().map(|i| i.span))
755-
.chain(on_delete.iter().map(|i| i.span()))
756-
.chain(on_update.iter().map(|i| i.span()))
757-
.chain(characteristics.iter().map(|i| i.span())),
758-
),
746+
ColumnOption::ForeignKey(constraint) => constraint.span(),
759747
ColumnOption::Check(expr) => expr.span(),
760748
ColumnOption::DialectSpecific(_) => Span::empty(),
761749
ColumnOption::CharacterSet(object_name) => object_name.span(),

src/ast/table_constraints.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
//! SQL Abstract Syntax Tree (AST) types for table constraints
1919
2020
use crate::ast::{
21-
display_comma_separated, display_separated, ConstraintCharacteristics, Expr, Ident,
22-
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, ObjectName,
23-
ReferentialAction,
21+
display_comma_separated, display_separated, ConstraintCharacteristics,
22+
ConstraintReferenceMatchKind, Expr, Ident, IndexColumn, IndexOption, IndexType,
23+
KeyOrIndexDisplay, NullsDistinctOption, ObjectName, ReferentialAction,
2424
};
2525
use crate::tokenizer::Span;
2626
use core::fmt;
@@ -189,7 +189,7 @@ impl crate::ast::Spanned for CheckConstraint {
189189
}
190190

191191
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
192-
/// REFERENCES <foreign_table> (<referred_columns>)
192+
/// REFERENCES <foreign_table> (<referred_columns>) [ MATCH { FULL | PARTIAL | SIMPLE } ]
193193
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
194194
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
195195
/// }`).
@@ -206,6 +206,7 @@ pub struct ForeignKeyConstraint {
206206
pub referred_columns: Vec<Ident>,
207207
pub on_delete: Option<ReferentialAction>,
208208
pub on_update: Option<ReferentialAction>,
209+
pub match_kind: Option<ConstraintReferenceMatchKind>,
209210
pub characteristics: Option<ConstraintCharacteristics>,
210211
}
211212

@@ -223,6 +224,9 @@ impl fmt::Display for ForeignKeyConstraint {
223224
if !self.referred_columns.is_empty() {
224225
write!(f, "({})", display_comma_separated(&self.referred_columns))?;
225226
}
227+
if let Some(match_kind) = &self.match_kind {
228+
write!(f, " {match_kind}")?;
229+
}
226230
if let Some(action) = &self.on_delete {
227231
write!(f, " ON DELETE {action}")?;
228232
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ define_keywords!(
713713
PARAMETER,
714714
PARQUET,
715715
PART,
716+
PARTIAL,
716717
PARTITION,
717718
PARTITIONED,
718719
PARTITIONS,
@@ -885,6 +886,7 @@ define_keywords!(
885886
SHOW,
886887
SIGNED,
887888
SIMILAR,
889+
SIMPLE,
888890
SKIP,
889891
SLOW,
890892
SMALLINT,

src/parser/mod.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8081,10 +8081,15 @@ impl<'a> Parser<'a> {
80818081
// PostgreSQL allows omitting the column list and
80828082
// uses the primary key column of the foreign table by default
80838083
let referred_columns = self.parse_parenthesized_column_list(Optional, false)?;
8084+
let mut match_kind = None;
80848085
let mut on_delete = None;
80858086
let mut on_update = None;
80868087
loop {
8087-
if on_delete.is_none() && self.parse_keywords(&[Keyword::ON, Keyword::DELETE]) {
8088+
if match_kind.is_none() && self.parse_keyword(Keyword::MATCH) {
8089+
match_kind = Some(self.parse_match_kind()?);
8090+
} else if on_delete.is_none()
8091+
&& self.parse_keywords(&[Keyword::ON, Keyword::DELETE])
8092+
{
80888093
on_delete = Some(self.parse_referential_action()?);
80898094
} else if on_update.is_none()
80908095
&& self.parse_keywords(&[Keyword::ON, Keyword::UPDATE])
@@ -8096,13 +8101,20 @@ impl<'a> Parser<'a> {
80968101
}
80978102
let characteristics = self.parse_constraint_characteristics()?;
80988103

8099-
Ok(Some(ColumnOption::ForeignKey {
8100-
foreign_table,
8101-
referred_columns,
8102-
on_delete,
8103-
on_update,
8104-
characteristics,
8105-
}))
8104+
Ok(Some(
8105+
ForeignKeyConstraint {
8106+
name: None, // Column-level constraints don't have names
8107+
index_name: None, // Not applicable for column-level constraints
8108+
columns: vec![], // Not applicable for column-level constraints
8109+
foreign_table,
8110+
referred_columns,
8111+
on_delete,
8112+
on_update,
8113+
match_kind,
8114+
characteristics,
8115+
}
8116+
.into(),
8117+
))
81068118
} else if self.parse_keyword(Keyword::CHECK) {
81078119
self.expect_token(&Token::LParen)?;
81088120
// since `CHECK` requires parentheses, we can parse the inner expression in ParserState::Normal
@@ -8376,6 +8388,18 @@ impl<'a> Parser<'a> {
83768388
}
83778389
}
83788390

8391+
pub fn parse_match_kind(&mut self) -> Result<ConstraintReferenceMatchKind, ParserError> {
8392+
if self.parse_keyword(Keyword::FULL) {
8393+
Ok(ConstraintReferenceMatchKind::Full)
8394+
} else if self.parse_keyword(Keyword::PARTIAL) {
8395+
Ok(ConstraintReferenceMatchKind::Partial)
8396+
} else if self.parse_keyword(Keyword::SIMPLE) {
8397+
Ok(ConstraintReferenceMatchKind::Simple)
8398+
} else {
8399+
self.expected("one of FULL, PARTIAL or SIMPLE", self.peek_token())
8400+
}
8401+
}
8402+
83798403
pub fn parse_constraint_characteristics(
83808404
&mut self,
83818405
) -> Result<Option<ConstraintCharacteristics>, ParserError> {
@@ -8486,10 +8510,15 @@ impl<'a> Parser<'a> {
84868510
self.expect_keyword_is(Keyword::REFERENCES)?;
84878511
let foreign_table = self.parse_object_name(false)?;
84888512
let referred_columns = self.parse_parenthesized_column_list(Optional, false)?;
8513+
let mut match_kind = None;
84898514
let mut on_delete = None;
84908515
let mut on_update = None;
84918516
loop {
8492-
if on_delete.is_none() && self.parse_keywords(&[Keyword::ON, Keyword::DELETE]) {
8517+
if match_kind.is_none() && self.parse_keyword(Keyword::MATCH) {
8518+
match_kind = Some(self.parse_match_kind()?);
8519+
} else if on_delete.is_none()
8520+
&& self.parse_keywords(&[Keyword::ON, Keyword::DELETE])
8521+
{
84938522
on_delete = Some(self.parse_referential_action()?);
84948523
} else if on_update.is_none()
84958524
&& self.parse_keywords(&[Keyword::ON, Keyword::UPDATE])
@@ -8511,6 +8540,7 @@ impl<'a> Parser<'a> {
85118540
referred_columns,
85128541
on_delete,
85138542
on_update,
8543+
match_kind,
85148544
characteristics,
85158545
}
85168546
.into(),

tests/sqlparser_common.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,27 +3800,35 @@ fn parse_create_table() {
38003800
data_type: DataType::Int(None),
38013801
options: vec![ColumnOptionDef {
38023802
name: None,
3803-
option: ColumnOption::ForeignKey {
3803+
option: ColumnOption::ForeignKey(ForeignKeyConstraint {
3804+
name: None,
3805+
index_name: None,
3806+
columns: vec![],
38043807
foreign_table: ObjectName::from(vec!["othertable".into()]),
38053808
referred_columns: vec!["a".into(), "b".into()],
38063809
on_delete: None,
38073810
on_update: None,
3811+
match_kind: None,
38083812
characteristics: None,
3809-
},
3813+
}),
38103814
}],
38113815
},
38123816
ColumnDef {
38133817
name: "ref2".into(),
38143818
data_type: DataType::Int(None),
38153819
options: vec![ColumnOptionDef {
38163820
name: None,
3817-
option: ColumnOption::ForeignKey {
3821+
option: ColumnOption::ForeignKey(ForeignKeyConstraint {
3822+
name: None,
3823+
index_name: None,
3824+
columns: vec![],
38183825
foreign_table: ObjectName::from(vec!["othertable2".into()]),
38193826
referred_columns: vec![],
38203827
on_delete: Some(ReferentialAction::Cascade),
38213828
on_update: Some(ReferentialAction::NoAction),
3829+
match_kind: None,
38223830
characteristics: None,
3823-
},
3831+
}),
38243832
},],
38253833
},
38263834
]
@@ -3836,6 +3844,7 @@ fn parse_create_table() {
38363844
referred_columns: vec!["lat".into()],
38373845
on_delete: Some(ReferentialAction::Restrict),
38383846
on_update: None,
3847+
match_kind: None,
38393848
characteristics: None,
38403849
}
38413850
.into(),
@@ -3847,6 +3856,7 @@ fn parse_create_table() {
38473856
referred_columns: vec!["lat".into()],
38483857
on_delete: Some(ReferentialAction::NoAction),
38493858
on_update: Some(ReferentialAction::Restrict),
3859+
match_kind: None,
38503860
characteristics: None,
38513861
}
38523862
.into(),
@@ -3858,6 +3868,7 @@ fn parse_create_table() {
38583868
referred_columns: vec!["lat".into()],
38593869
on_delete: Some(ReferentialAction::Cascade),
38603870
on_update: Some(ReferentialAction::SetDefault),
3871+
match_kind: None,
38613872
characteristics: None,
38623873
}
38633874
.into(),
@@ -3869,6 +3880,7 @@ fn parse_create_table() {
38693880
referred_columns: vec!["longitude".into()],
38703881
on_delete: None,
38713882
on_update: Some(ReferentialAction::SetNull),
3883+
match_kind: None,
38723884
characteristics: None,
38733885
}
38743886
.into(),
@@ -3967,6 +3979,7 @@ fn parse_create_table_with_constraint_characteristics() {
39673979
referred_columns: vec!["lat".into()],
39683980
on_delete: Some(ReferentialAction::Restrict),
39693981
on_update: None,
3982+
match_kind: None,
39703983
characteristics: Some(ConstraintCharacteristics {
39713984
deferrable: Some(true),
39723985
initially: Some(DeferrableInitial::Deferred),
@@ -3982,6 +3995,7 @@ fn parse_create_table_with_constraint_characteristics() {
39823995
referred_columns: vec!["lat".into()],
39833996
on_delete: Some(ReferentialAction::NoAction),
39843997
on_update: Some(ReferentialAction::Restrict),
3998+
match_kind: None,
39853999
characteristics: Some(ConstraintCharacteristics {
39864000
deferrable: Some(true),
39874001
initially: Some(DeferrableInitial::Immediate),
@@ -3997,6 +4011,7 @@ fn parse_create_table_with_constraint_characteristics() {
39974011
referred_columns: vec!["lat".into()],
39984012
on_delete: Some(ReferentialAction::Cascade),
39994013
on_update: Some(ReferentialAction::SetDefault),
4014+
match_kind: None,
40004015
characteristics: Some(ConstraintCharacteristics {
40014016
deferrable: Some(false),
40024017
initially: Some(DeferrableInitial::Deferred),
@@ -4012,6 +4027,7 @@ fn parse_create_table_with_constraint_characteristics() {
40124027
referred_columns: vec!["longitude".into()],
40134028
on_delete: None,
40144029
on_update: Some(ReferentialAction::SetNull),
4030+
match_kind: None,
40154031
characteristics: Some(ConstraintCharacteristics {
40164032
deferrable: Some(false),
40174033
initially: Some(DeferrableInitial::Immediate),

0 commit comments

Comments
 (0)