mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-24 16:04:04 +00:00
Add referential actions to TableConstraint foreign key (#306)
* Add referential actions to TableConstraint foreign key * Remove copy/paste error * Add referential actions to TableConstraint foreign key * Add additional tests
This commit is contained in:
parent
f56e57470e
commit
a95c81fb13
3 changed files with 99 additions and 12 deletions
|
@ -136,12 +136,17 @@ pub enum TableConstraint {
|
|||
is_primary: bool,
|
||||
},
|
||||
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
|
||||
/// REFERENCES <foreign_table> (<referred_columns>)`)
|
||||
/// REFERENCES <foreign_table> (<referred_columns>)
|
||||
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
|
||||
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
|
||||
/// }`).
|
||||
ForeignKey {
|
||||
name: Option<Ident>,
|
||||
columns: Vec<Ident>,
|
||||
foreign_table: ObjectName,
|
||||
referred_columns: Vec<Ident>,
|
||||
on_delete: Option<ReferentialAction>,
|
||||
on_update: Option<ReferentialAction>,
|
||||
},
|
||||
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
|
||||
Check {
|
||||
|
@ -169,14 +174,25 @@ impl fmt::Display for TableConstraint {
|
|||
columns,
|
||||
foreign_table,
|
||||
referred_columns,
|
||||
} => write!(
|
||||
on_delete,
|
||||
on_update,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{}FOREIGN KEY ({}) REFERENCES {}({})",
|
||||
display_constraint_name(name),
|
||||
display_comma_separated(columns),
|
||||
foreign_table,
|
||||
display_comma_separated(referred_columns)
|
||||
),
|
||||
display_comma_separated(referred_columns),
|
||||
)?;
|
||||
if let Some(action) = on_delete {
|
||||
write!(f, " ON DELETE {}", action)?;
|
||||
}
|
||||
if let Some(action) = on_update {
|
||||
write!(f, " ON UPDATE {}", action)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TableConstraint::Check { name, expr } => {
|
||||
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
|
||||
}
|
||||
|
|
|
@ -1741,11 +1741,26 @@ impl<'a> Parser<'a> {
|
|||
self.expect_keyword(Keyword::REFERENCES)?;
|
||||
let foreign_table = self.parse_object_name()?;
|
||||
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
|
||||
let mut on_delete = None;
|
||||
let mut on_update = None;
|
||||
loop {
|
||||
if on_delete.is_none() && self.parse_keywords(&[Keyword::ON, Keyword::DELETE]) {
|
||||
on_delete = Some(self.parse_referential_action()?);
|
||||
} else if on_update.is_none()
|
||||
&& self.parse_keywords(&[Keyword::ON, Keyword::UPDATE])
|
||||
{
|
||||
on_update = Some(self.parse_referential_action()?);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Some(TableConstraint::ForeignKey {
|
||||
name,
|
||||
columns,
|
||||
foreign_table,
|
||||
referred_columns,
|
||||
on_delete,
|
||||
on_update,
|
||||
}))
|
||||
}
|
||||
Token::Word(w) if w.keyword == Keyword::CHECK => {
|
||||
|
|
|
@ -1162,7 +1162,11 @@ fn parse_create_table() {
|
|||
lng DOUBLE,
|
||||
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0),
|
||||
ref INT REFERENCES othertable (a, b),\
|
||||
ref2 INT references othertable2 on delete cascade on update no action\
|
||||
ref2 INT references othertable2 on delete cascade on update no action,\
|
||||
constraint fkey foreign key (lat) references othertable3 (lat) on delete restrict,\
|
||||
constraint fkey2 foreign key (lat) references othertable4(lat) on delete no action on update restrict, \
|
||||
foreign key (lat) references othertable4(lat) on update set default on delete cascade, \
|
||||
FOREIGN KEY (lng) REFERENCES othertable4 (longitude) ON UPDATE SET NULL
|
||||
)";
|
||||
let ast = one_statement_parses_to(
|
||||
sql,
|
||||
|
@ -1172,7 +1176,11 @@ fn parse_create_table() {
|
|||
lng DOUBLE, \
|
||||
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \
|
||||
ref INT REFERENCES othertable (a, b), \
|
||||
ref2 INT REFERENCES othertable2 ON DELETE CASCADE ON UPDATE NO ACTION)",
|
||||
ref2 INT REFERENCES othertable2 ON DELETE CASCADE ON UPDATE NO ACTION, \
|
||||
CONSTRAINT fkey FOREIGN KEY (lat) REFERENCES othertable3(lat) ON DELETE RESTRICT, \
|
||||
CONSTRAINT fkey2 FOREIGN KEY (lat) REFERENCES othertable4(lat) ON DELETE NO ACTION ON UPDATE RESTRICT, \
|
||||
FOREIGN KEY (lat) REFERENCES othertable4(lat) ON DELETE CASCADE ON UPDATE SET DEFAULT, \
|
||||
FOREIGN KEY (lng) REFERENCES othertable4(longitude) ON UPDATE SET NULL)",
|
||||
);
|
||||
match ast {
|
||||
Statement::CreateTable {
|
||||
|
@ -1271,7 +1279,43 @@ fn parse_create_table() {
|
|||
}
|
||||
]
|
||||
);
|
||||
assert!(constraints.is_empty());
|
||||
assert_eq!(
|
||||
constraints,
|
||||
vec![
|
||||
TableConstraint::ForeignKey {
|
||||
name: Some("fkey".into()),
|
||||
columns: vec!["lat".into()],
|
||||
foreign_table: ObjectName(vec!["othertable3".into()]),
|
||||
referred_columns: vec!["lat".into()],
|
||||
on_delete: Some(ReferentialAction::Restrict),
|
||||
on_update: None
|
||||
},
|
||||
TableConstraint::ForeignKey {
|
||||
name: Some("fkey2".into()),
|
||||
columns: vec!["lat".into()],
|
||||
foreign_table: ObjectName(vec!["othertable4".into()]),
|
||||
referred_columns: vec!["lat".into()],
|
||||
on_delete: Some(ReferentialAction::NoAction),
|
||||
on_update: Some(ReferentialAction::Restrict)
|
||||
},
|
||||
TableConstraint::ForeignKey {
|
||||
name: None,
|
||||
columns: vec!["lat".into()],
|
||||
foreign_table: ObjectName(vec!["othertable4".into()]),
|
||||
referred_columns: vec!["lat".into()],
|
||||
on_delete: Some(ReferentialAction::Cascade),
|
||||
on_update: Some(ReferentialAction::SetDefault)
|
||||
},
|
||||
TableConstraint::ForeignKey {
|
||||
name: None,
|
||||
columns: vec!["lng".into()],
|
||||
foreign_table: ObjectName(vec!["othertable4".into()]),
|
||||
referred_columns: vec!["longitude".into()],
|
||||
on_delete: None,
|
||||
on_update: Some(ReferentialAction::SetNull)
|
||||
},
|
||||
]
|
||||
);
|
||||
assert_eq!(with_options, vec![]);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -1290,6 +1334,18 @@ fn parse_create_table() {
|
|||
.contains("Expected constraint details after CONSTRAINT <name>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_with_multiple_on_delete_in_constraint_fails() {
|
||||
parse_sql_statements(
|
||||
"\
|
||||
create table X (\
|
||||
y_id int, \
|
||||
foreign key (y_id) references Y (id) on delete cascade on update cascade on delete no action\
|
||||
)",
|
||||
)
|
||||
.expect_err("should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_with_multiple_on_delete_fails() {
|
||||
parse_sql_statements(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue