Skip to content

Commit 16f42c1

Browse files
committed
add support for "on delete cascade" column option
1 parent 5ad578e commit 16f42c1

File tree

6 files changed

+136
-22
lines changed

6 files changed

+136
-22
lines changed

examples/cli.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
#![warn(clippy::all)]
1414

15-
use simple_logger;
16-
1715
///! A small command-line app to run the parser.
1816
/// Run with `cargo run --example cli`
1917
use std::fs;

src/ast/ddl.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,13 @@ pub enum ColumnOption {
158158
/// <foreign_table> (<referred_columns>)`).
159159
ForeignKey {
160160
foreign_table: ObjectName,
161-
referred_columns: Vec<Ident>,
161+
referred_columns: Option<Vec<Ident>>,
162+
},
163+
// ON { UPDATE | DELETE }
164+
// { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }
165+
ReferenceChange {
166+
change_type: ReferenceChangeType,
167+
action: ReferenceChangeAction,
162168
},
163169
// `CHECK (<expr>)`
164170
Check(Expr),
@@ -176,14 +182,19 @@ impl fmt::Display for ColumnOption {
176182
}
177183
ForeignKey {
178184
foreign_table,
179-
referred_columns,
180-
} => write!(
181-
f,
185+
referred_columns: Some(referred_columns),
186+
} => write!(f,
182187
"REFERENCES {} ({})",
183188
foreign_table,
184189
display_comma_separated(referred_columns)
185190
),
191+
ForeignKey {
192+
foreign_table,
193+
referred_columns: None,
194+
} => write!(f, "REFERENCES {}", foreign_table),
186195
Check(expr) => write!(f, "CHECK ({})", expr),
196+
ReferenceChange { change_type, action } =>
197+
write!(f, "ON {} {}", change_type, action),
187198
}
188199
}
189200
}
@@ -200,3 +211,41 @@ fn display_constraint_name<'a>(name: &'a Option<Ident>) -> impl fmt::Display + '
200211
}
201212
ConstraintName(name)
202213
}
214+
215+
// e.g. on delete/update cascade
216+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
217+
pub enum ReferenceChangeType {
218+
Delete,
219+
Update,
220+
}
221+
222+
impl fmt::Display for ReferenceChangeType {
223+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224+
match self {
225+
ReferenceChangeType::Delete => write!(f, "DELETE"),
226+
ReferenceChangeType::Update => write!(f, "UPDATE"),
227+
}
228+
}
229+
}
230+
231+
// e.g. on delete cascade/(set null)
232+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
233+
pub enum ReferenceChangeAction {
234+
Restrict,
235+
Cascade,
236+
SetNull,
237+
NoAction,
238+
SetDefault,
239+
}
240+
241+
impl fmt::Display for ReferenceChangeAction {
242+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
243+
match self {
244+
ReferenceChangeAction::Restrict => write!(f, "RESTRICT"),
245+
ReferenceChangeAction::Cascade => write!(f, "CASCADE"),
246+
ReferenceChangeAction::SetNull => write!(f, "SET NULL"),
247+
ReferenceChangeAction::NoAction => write!(f, "NO ACTION"),
248+
ReferenceChangeAction::SetDefault => write!(f, "SET DEFAULT"),
249+
}
250+
}
251+
}

src/ast/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::fmt;
2323
pub use self::data_type::DataType;
2424
pub use self::ddl::{
2525
AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, TableConstraint,
26+
ReferenceChangeAction, ReferenceChangeType,
2627
};
2728
pub use self::operator::{BinaryOperator, UnaryOperator};
2829
pub use self::query::{

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ macro_rules! define_keywords {
5151

5252
define_keywords!(
5353
ABS,
54+
ACTION,
5455
ADD,
5556
ASC,
5657
ALL,

src/parser.rs

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,9 @@ impl Parser {
893893
// Many dialects support `OR REPLACE` | `OR ALTER` right after `CREATE`, but we don't (yet).
894894
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
895895
let name = self.parse_object_name()?;
896-
let columns = self.parse_parenthesized_column_list(Optional)?;
896+
let columns =
897+
self.parse_parenthesized_column_list(Optional)?
898+
.unwrap_or(vec![]);
897899
let with_options = self.parse_with_options()?;
898900
self.expect_keyword("AS")?;
899901
let query = Box::new(self.parse_query()?);
@@ -1017,11 +1019,32 @@ impl Parser {
10171019
ColumnOption::Unique { is_primary: false }
10181020
} else if self.parse_keyword("REFERENCES") {
10191021
let foreign_table = self.parse_object_name()?;
1020-
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
1022+
let referred_columns =
1023+
self.parse_parenthesized_column_list(Optional)?;
10211024
ColumnOption::ForeignKey {
10221025
foreign_table,
10231026
referred_columns,
10241027
}
1028+
} else if self.parse_keyword("ON") {
1029+
let change_type =
1030+
if self.parse_keyword("DELETE") { ReferenceChangeType::Delete }
1031+
else if self.parse_keyword("UPDATE") { ReferenceChangeType::Update }
1032+
else {
1033+
return self.expected("DELETE or UPDATE", self.peek_token());
1034+
};
1035+
let action =
1036+
if self.parse_keyword("RESTRICT") { ReferenceChangeAction::Restrict }
1037+
else if self.parse_keyword("CASCADE") { ReferenceChangeAction::Cascade }
1038+
else if self.parse_keywords(vec!["SET", "NULL"]) { ReferenceChangeAction::SetNull }
1039+
else if self.parse_keywords(vec!["NO", "ACTION"]) { ReferenceChangeAction::NoAction }
1040+
else if self.parse_keywords(vec!["SET", "DEFAULT"]) { ReferenceChangeAction::SetDefault }
1041+
else {
1042+
return self.expected("one of RESTRICT, CASCADE, SET NULL, NO ACTION or SET DEFAULT", self.peek_token())
1043+
};
1044+
ColumnOption::ReferenceChange {
1045+
change_type,
1046+
action,
1047+
}
10251048
} else if self.parse_keyword("CHECK") {
10261049
self.expect_token(&Token::LParen)?;
10271050
let expr = self.parse_expr()?;
@@ -1048,7 +1071,8 @@ impl Parser {
10481071
if is_primary {
10491072
self.expect_keyword("KEY")?;
10501073
}
1051-
let columns = self.parse_parenthesized_column_list(Mandatory)?;
1074+
let columns =
1075+
self.parse_parenthesized_column_list(Mandatory)?.unwrap();
10521076
Ok(Some(TableConstraint::Unique {
10531077
name,
10541078
columns,
@@ -1057,10 +1081,12 @@ impl Parser {
10571081
}
10581082
Some(Token::Word(ref k)) if k.keyword == "FOREIGN" => {
10591083
self.expect_keyword("KEY")?;
1060-
let columns = self.parse_parenthesized_column_list(Mandatory)?;
1084+
let columns =
1085+
self.parse_parenthesized_column_list(Mandatory)?.unwrap();
10611086
self.expect_keyword("REFERENCES")?;
10621087
let foreign_table = self.parse_object_name()?;
1063-
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
1088+
let referred_columns =
1089+
self.parse_parenthesized_column_list(Mandatory)?.unwrap();
10641090
Ok(Some(TableConstraint::ForeignKey {
10651091
name,
10661092
columns,
@@ -1125,7 +1151,9 @@ impl Parser {
11251151
/// Parse a copy statement
11261152
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
11271153
let table_name = self.parse_object_name()?;
1128-
let columns = self.parse_parenthesized_column_list(Optional)?;
1154+
let columns =
1155+
self.parse_parenthesized_column_list(Optional)?
1156+
.unwrap_or(vec![]);
11291157
self.expect_keywords(&["FROM", "STDIN"])?;
11301158
self.expect_token(&Token::SemiColon)?;
11311159
let values = self.parse_tsv()?;
@@ -1345,7 +1373,9 @@ impl Parser {
13451373
) -> Result<Option<TableAlias>, ParserError> {
13461374
match self.parse_optional_alias(reserved_kwds)? {
13471375
Some(name) => {
1348-
let columns = self.parse_parenthesized_column_list(Optional)?;
1376+
let columns =
1377+
self.parse_parenthesized_column_list(Optional)?
1378+
.unwrap_or(vec![]);
13491379
Ok(Some(TableAlias { name, columns }))
13501380
}
13511381
None => Ok(None),
@@ -1377,13 +1407,13 @@ impl Parser {
13771407
pub fn parse_parenthesized_column_list(
13781408
&mut self,
13791409
optional: IsOptional,
1380-
) -> Result<Vec<Ident>, ParserError> {
1410+
) -> Result<Option<Vec<Ident>>, ParserError> {
13811411
if self.consume_token(&Token::LParen) {
13821412
let cols = self.parse_comma_separated(Parser::parse_identifier)?;
13831413
self.expect_token(&Token::RParen)?;
1384-
Ok(cols)
1414+
Ok(Some(cols))
13851415
} else if optional == Optional {
1386-
Ok(vec![])
1416+
Ok(None)
13871417
} else {
13881418
self.expected("a list of columns in parentheses", self.peek_token())
13891419
}
@@ -1483,7 +1513,8 @@ impl Parser {
14831513
fn parse_cte(&mut self) -> Result<Cte, ParserError> {
14841514
let alias = TableAlias {
14851515
name: self.parse_identifier()?,
1486-
columns: self.parse_parenthesized_column_list(Optional)?,
1516+
columns: self.parse_parenthesized_column_list(Optional)?
1517+
.unwrap_or(vec![]),
14871518
};
14881519
self.expect_keyword("AS")?;
14891520
self.expect_token(&Token::LParen)?;
@@ -1856,7 +1887,8 @@ impl Parser {
18561887
let constraint = self.parse_expr()?;
18571888
Ok(JoinConstraint::On(constraint))
18581889
} else if self.parse_keyword("USING") {
1859-
let columns = self.parse_parenthesized_column_list(Mandatory)?;
1890+
let columns =
1891+
self.parse_parenthesized_column_list(Mandatory)?.unwrap();
18601892
Ok(JoinConstraint::Using(columns))
18611893
} else {
18621894
self.expected("ON, or USING after JOIN", self.peek_token())
@@ -1867,7 +1899,9 @@ impl Parser {
18671899
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
18681900
self.expect_keyword("INTO")?;
18691901
let table_name = self.parse_object_name()?;
1870-
let columns = self.parse_parenthesized_column_list(Optional)?;
1902+
let columns =
1903+
self.parse_parenthesized_column_list(Optional)?
1904+
.unwrap_or(vec![]);
18711905
let source = Box::new(self.parse_query()?);
18721906
Ok(Statement::Insert {
18731907
table_name,

tests/sqlparser_common.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -893,15 +893,18 @@ fn parse_create_table() {
893893
lat DOUBLE NULL,\
894894
lng DOUBLE,
895895
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0),
896-
ref INT REFERENCES othertable (a, b))";
896+
ref INT REFERENCES othertable (a, b),\
897+
ref2 INT references othertable2 on delete cascade on update no action\
898+
)";
897899
let ast = one_statement_parses_to(
898900
sql,
899901
"CREATE TABLE uk_cities (\
900902
name character varying(100) NOT NULL, \
901903
lat double NULL, \
902904
lng double, \
903905
constrained int NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \
904-
ref int REFERENCES othertable (a, b))",
906+
ref int REFERENCES othertable (a, b), \
907+
ref2 int REFERENCES othertable2 ON DELETE CASCADE ON UPDATE NO ACTION)",
905908
);
906909
match ast {
907910
Statement::CreateTable {
@@ -977,9 +980,37 @@ fn parse_create_table() {
977980
name: None,
978981
option: ColumnOption::ForeignKey {
979982
foreign_table: ObjectName(vec!["othertable".into()]),
980-
referred_columns: vec!["a".into(), "b".into(),],
983+
referred_columns: Some(vec!["a".into(), "b".into(),]),
981984
}
982985
}]
986+
},
987+
ColumnDef {
988+
name: "ref2".into(),
989+
data_type: DataType::Int,
990+
collation: None,
991+
options: vec![
992+
ColumnOptionDef {
993+
name: None,
994+
option: ColumnOption::ForeignKey {
995+
foreign_table: ObjectName(vec!["othertable2".into()]),
996+
referred_columns: None,
997+
}
998+
},
999+
ColumnOptionDef {
1000+
name: None,
1001+
option: ColumnOption::ReferenceChange {
1002+
change_type: ReferenceChangeType::Delete,
1003+
action: ReferenceChangeAction::Cascade,
1004+
}
1005+
},
1006+
ColumnOptionDef {
1007+
name: None,
1008+
option: ColumnOption::ReferenceChange {
1009+
change_type: ReferenceChangeType::Update,
1010+
action: ReferenceChangeAction::NoAction,
1011+
}
1012+
}
1013+
]
9831014
}
9841015
]
9851016
);

0 commit comments

Comments
 (0)