mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-25 08:24:05 +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,
|
is_primary: bool,
|
||||||
},
|
},
|
||||||
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
|
/// 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 {
|
ForeignKey {
|
||||||
name: Option<Ident>,
|
name: Option<Ident>,
|
||||||
columns: Vec<Ident>,
|
columns: Vec<Ident>,
|
||||||
foreign_table: ObjectName,
|
foreign_table: ObjectName,
|
||||||
referred_columns: Vec<Ident>,
|
referred_columns: Vec<Ident>,
|
||||||
|
on_delete: Option<ReferentialAction>,
|
||||||
|
on_update: Option<ReferentialAction>,
|
||||||
},
|
},
|
||||||
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
|
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
|
||||||
Check {
|
Check {
|
||||||
|
@ -169,14 +174,25 @@ impl fmt::Display for TableConstraint {
|
||||||
columns,
|
columns,
|
||||||
foreign_table,
|
foreign_table,
|
||||||
referred_columns,
|
referred_columns,
|
||||||
} => write!(
|
on_delete,
|
||||||
|
on_update,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}FOREIGN KEY ({}) REFERENCES {}({})",
|
"{}FOREIGN KEY ({}) REFERENCES {}({})",
|
||||||
display_constraint_name(name),
|
display_constraint_name(name),
|
||||||
display_comma_separated(columns),
|
display_comma_separated(columns),
|
||||||
foreign_table,
|
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 } => {
|
TableConstraint::Check { name, expr } => {
|
||||||
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
|
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1741,11 +1741,26 @@ impl<'a> Parser<'a> {
|
||||||
self.expect_keyword(Keyword::REFERENCES)?;
|
self.expect_keyword(Keyword::REFERENCES)?;
|
||||||
let foreign_table = self.parse_object_name()?;
|
let foreign_table = self.parse_object_name()?;
|
||||||
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
|
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 {
|
Ok(Some(TableConstraint::ForeignKey {
|
||||||
name,
|
name,
|
||||||
columns,
|
columns,
|
||||||
foreign_table,
|
foreign_table,
|
||||||
referred_columns,
|
referred_columns,
|
||||||
|
on_delete,
|
||||||
|
on_update,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Token::Word(w) if w.keyword == Keyword::CHECK => {
|
Token::Word(w) if w.keyword == Keyword::CHECK => {
|
||||||
|
|
|
@ -1162,7 +1162,11 @@ fn parse_create_table() {
|
||||||
lng DOUBLE,
|
lng DOUBLE,
|
||||||
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0),
|
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0),
|
||||||
ref INT REFERENCES othertable (a, b),\
|
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(
|
let ast = one_statement_parses_to(
|
||||||
sql,
|
sql,
|
||||||
|
@ -1172,7 +1176,11 @@ fn parse_create_table() {
|
||||||
lng DOUBLE, \
|
lng DOUBLE, \
|
||||||
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \
|
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \
|
||||||
ref INT REFERENCES othertable (a, b), \
|
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 {
|
match ast {
|
||||||
Statement::CreateTable {
|
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![]);
|
assert_eq!(with_options, vec![]);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -1290,6 +1334,18 @@ fn parse_create_table() {
|
||||||
.contains("Expected constraint details after CONSTRAINT <name>"));
|
.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]
|
#[test]
|
||||||
fn parse_create_table_with_multiple_on_delete_fails() {
|
fn parse_create_table_with_multiple_on_delete_fails() {
|
||||||
parse_sql_statements(
|
parse_sql_statements(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue