Skip to content
This repository was archived by the owner on Dec 25, 2019. It is now read-only.

Commit 5fa7fe6

Browse files
committed
Parse column constraints in any order
CREATE TABLE t (a INT NOT NULL DEFAULT 1 PRIMARY KEY) is as valid as CREATE TABLE t (a INT DEFAULT 1 PRIMARY KEY NOT NULL).
1 parent db15b68 commit 5fa7fe6

File tree

5 files changed

+242
-209
lines changed

5 files changed

+242
-209
lines changed

src/sqlast/mod.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -633,28 +633,51 @@ impl ToString for SQLAssignment {
633633
pub struct SQLColumnDef {
634634
pub name: SQLIdent,
635635
pub data_type: SQLType,
636-
pub is_primary: bool,
637-
pub is_unique: bool,
638-
pub default: Option<ASTNode>,
639-
pub allow_null: bool,
636+
pub constraints: Vec<SQLColumnConstraint>,
640637
}
641638

642639
impl ToString for SQLColumnDef {
643640
fn to_string(&self) -> String {
644-
let mut s = format!("{} {}", self.name, self.data_type.to_string());
645-
if self.is_primary {
646-
s += " PRIMARY KEY";
647-
}
648-
if self.is_unique {
649-
s += " UNIQUE";
650-
}
651-
if let Some(ref default) = self.default {
652-
s += &format!(" DEFAULT {}", default.to_string());
653-
}
654-
if !self.allow_null {
655-
s += " NOT NULL";
641+
format!(
642+
"{} {}{}",
643+
self.name,
644+
self.data_type.to_string(),
645+
self.constraints
646+
.iter()
647+
.map(|c| format!(" {}", c.to_string()))
648+
.collect::<Vec<_>>()
649+
.join("")
650+
)
651+
}
652+
}
653+
654+
#[derive(Debug, Clone, PartialEq, Hash)]
655+
pub enum SQLColumnConstraint {
656+
Null,
657+
NotNull,
658+
Check(ASTNode),
659+
Default(ASTNode),
660+
Unique(Option<SQLIdent>),
661+
PrimaryKey(Option<SQLIdent>),
662+
}
663+
664+
impl ToString for SQLColumnConstraint {
665+
fn to_string(&self) -> String {
666+
use SQLColumnConstraint::*;
667+
match self {
668+
Null => "NULL".to_string(),
669+
NotNull => "NOT NULL".to_string(),
670+
Check(expr) => format!("CHECK ({})", expr.to_string()),
671+
Default(expr) => format!("DEFAULT {}", expr.to_string()),
672+
Unique(name) => match name {
673+
Some(name) => format!("CONSTRAINT {} UNIQUE", name),
674+
None => "UNIQUE".to_string(),
675+
},
676+
PrimaryKey(name) => match name {
677+
Some(name) => format!("CONSTRAINT {} PRIMARY KEY", name),
678+
None => "PRIMARY KEY".to_string(),
679+
},
656680
}
657-
s
658681
}
659682
}
660683

src/sqlast/visit.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ pub trait Visit<'ast> {
332332
visit_column_def(self, column_def)
333333
}
334334

335-
fn visit_column_default(&mut self, default: Option<&'ast ASTNode>) {
336-
visit_column_default(self, default)
335+
fn visit_column_constraint(&mut self, constraint: &'ast SQLColumnConstraint) {
336+
visit_column_constraint(self, constraint)
337337
}
338338

339339
fn visit_file_format(&mut self, _file_format: &'ast FileFormat) {}
@@ -1070,16 +1070,21 @@ pub fn visit_column_def<'ast, V: Visit<'ast> + ?Sized>(
10701070
) {
10711071
visitor.visit_identifier(&column_def.name);
10721072
visitor.visit_type(&column_def.data_type);
1073-
visitor.visit_column_default(column_def.default.as_ref());
1073+
for constraint in &column_def.constraints {
1074+
visitor.visit_column_constraint(constraint)
1075+
}
10741076
}
10751077

1076-
pub fn visit_column_default<'ast, V: Visit<'ast> + ?Sized>(
1078+
pub fn visit_column_constraint<'ast, V: Visit<'ast> + ?Sized>(
10771079
visitor: &mut V,
1078-
default: Option<&'ast ASTNode>,
1080+
constraint: &'ast SQLColumnConstraint,
10791081
) {
1080-
match default {
1081-
Some(expr) => visitor.visit_expr(expr),
1082-
None => (),
1082+
use SQLColumnConstraint::*;
1083+
match constraint {
1084+
Check(expr) => visitor.visit_expr(expr),
1085+
Default(expr) => visitor.visit_expr(expr),
1086+
Unique(Some(ident)) | PrimaryKey(Some(ident)) => visitor.visit_identifier(ident),
1087+
Unique(None) | PrimaryKey(None) | Null | NotNull => (),
10831088
}
10841089
}
10851090

src/sqlparser.rs

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -165,29 +165,6 @@ impl Parser {
165165
Ok(expr)
166166
}
167167

168-
/// Parse expression for DEFAULT clause in CREATE TABLE
169-
pub fn parse_default_expr(&mut self, precedence: u8) -> Result<ASTNode, ParserError> {
170-
debug!("parsing expr");
171-
let mut expr = self.parse_prefix()?;
172-
debug!("prefix: {:?}", expr);
173-
loop {
174-
// stop parsing on `NULL` | `NOT NULL`
175-
match self.peek_token() {
176-
Some(Token::SQLWord(ref k)) if k.keyword == "NOT" || k.keyword == "NULL" => break,
177-
_ => {}
178-
}
179-
180-
let next_precedence = self.get_next_precedence()?;
181-
debug!("next precedence: {:?}", next_precedence);
182-
if precedence >= next_precedence {
183-
break;
184-
}
185-
186-
expr = self.parse_infix(expr, next_precedence)?;
187-
}
188-
Ok(expr)
189-
}
190-
191168
/// Parse an expression prefix
192169
pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
193170
let tok = self
@@ -930,35 +907,24 @@ impl Parser {
930907
match self.next_token() {
931908
Some(Token::SQLWord(column_name)) => {
932909
let data_type = self.parse_data_type()?;
933-
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
934-
let is_unique = self.parse_keyword("UNIQUE");
935-
let default = if self.parse_keyword("DEFAULT") {
936-
let expr = self.parse_default_expr(0)?;
937-
Some(expr)
938-
} else {
939-
None
940-
};
941-
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
942-
false
943-
} else {
944-
let _ = self.parse_keyword("NULL");
945-
true
946-
};
947-
debug!("default: {:?}", default);
910+
911+
let mut constraints = vec![];
912+
loop {
913+
match self.peek_token() {
914+
None | Some(Token::Comma) | Some(Token::RParen) => break,
915+
_ => constraints.push(self.parse_column_constraint()?),
916+
}
917+
}
948918

949919
columns.push(SQLColumnDef {
950920
name: column_name.as_sql_ident(),
951921
data_type,
952-
allow_null,
953-
is_primary,
954-
is_unique,
955-
default,
922+
constraints,
956923
});
924+
957925
match self.next_token() {
958-
Some(Token::Comma) => {}
959-
Some(Token::RParen) => {
960-
break;
961-
}
926+
Some(Token::Comma) => (),
927+
Some(Token::RParen) => break,
962928
other => {
963929
return parser_err!(format!(
964930
"Expected ',' or ')' after column definition but found {:?}",
@@ -976,6 +942,44 @@ impl Parser {
976942
Ok(columns)
977943
}
978944

945+
pub fn parse_column_constraint(&mut self) -> Result<SQLColumnConstraint, ParserError> {
946+
if self.parse_keywords(vec!["NOT", "NULL"]) {
947+
return Ok(SQLColumnConstraint::NotNull);
948+
} else if self.parse_keyword("NULL") {
949+
return Ok(SQLColumnConstraint::Null);
950+
} else if self.parse_keyword("DEFAULT") {
951+
// We want to allow as many DEFAULT expressions as possible, but
952+
// calling self.parse_expr() will choke on expressions like "DEFAULT
953+
// 1 NOT NULL". Typically this would be a syntax error, as "1 NOT
954+
// NULL" is not a valid SQL expression, but in this case we know
955+
// that NOT NULL is part of the next column constraint. As it turns
956+
// out, the only expressions that cause trouble all have precedence
957+
// less than or equal to BETWEEN, so we pass BETWEEN_PREC to stop
958+
// parsing on tokens with precedence less than or equal to BETWEEN.
959+
// The same trick is employed by PostgreSQL [0].
960+
// https://github.com/postgres/postgres/blob/56b78626c/src/backend/parser/gram.y#L13366-L13370
961+
let expr = self.parse_subexpr(Self::BETWEEN_PREC)?;
962+
return Ok(SQLColumnConstraint::Default(expr));
963+
}
964+
965+
let name = if self.parse_keyword("CONSTRAINT") {
966+
Some(self.parse_identifier()?)
967+
} else {
968+
None
969+
};
970+
971+
if self.parse_keywords(vec!["PRIMARY", "KEY"]) {
972+
Ok(SQLColumnConstraint::PrimaryKey(name))
973+
} else if self.parse_keyword("UNIQUE") {
974+
Ok(SQLColumnConstraint::Unique(name))
975+
} else {
976+
parser_err!(format!(
977+
"Unexpected token in column definition: {:?}",
978+
self.peek_token()
979+
))
980+
}
981+
}
982+
979983
pub fn parse_table_key(&mut self, constraint_name: SQLIdent) -> Result<TableKey, ParserError> {
980984
let is_primary_key = self.parse_keywords(vec!["PRIMARY", "KEY"]);
981985
let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]);

tests/sqlparser_common.rs

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -714,13 +714,15 @@ fn parse_create_table() {
714714
let sql = "CREATE TABLE uk_cities (\
715715
name VARCHAR(100) NOT NULL,\
716716
lat DOUBLE NULL,\
717-
lng DOUBLE NULL)";
717+
lng DOUBLE,
718+
constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE)";
718719
let ast = one_statement_parses_to(
719720
sql,
720721
"CREATE TABLE uk_cities (\
721722
name character varying(100) NOT NULL, \
722-
lat double, \
723-
lng double)",
723+
lat double NULL, \
724+
lng double, \
725+
constrained int NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE)",
724726
);
725727
match ast {
726728
SQLStatement::SQLCreateTable {
@@ -732,23 +734,36 @@ fn parse_create_table() {
732734
location: None,
733735
} => {
734736
assert_eq!("uk_cities", name.to_string());
735-
assert_eq!(3, columns.len());
736-
737-
let c_name = &columns[0];
738-
assert_eq!("name", c_name.name);
739-
assert_eq!(SQLType::Varchar(Some(100)), c_name.data_type);
740-
assert_eq!(false, c_name.allow_null);
741-
742-
let c_lat = &columns[1];
743-
assert_eq!("lat", c_lat.name);
744-
assert_eq!(SQLType::Double, c_lat.data_type);
745-
assert_eq!(true, c_lat.allow_null);
746-
747-
let c_lng = &columns[2];
748-
assert_eq!("lng", c_lng.name);
749-
assert_eq!(SQLType::Double, c_lng.data_type);
750-
assert_eq!(true, c_lng.allow_null);
751-
737+
assert_eq!(
738+
columns,
739+
vec![
740+
SQLColumnDef {
741+
name: "name".into(),
742+
data_type: SQLType::Varchar(Some(100)),
743+
constraints: vec![SQLColumnConstraint::NotNull],
744+
},
745+
SQLColumnDef {
746+
name: "lat".into(),
747+
data_type: SQLType::Double,
748+
constraints: vec![SQLColumnConstraint::Null],
749+
},
750+
SQLColumnDef {
751+
name: "lng".into(),
752+
data_type: SQLType::Double,
753+
constraints: vec![],
754+
},
755+
SQLColumnDef {
756+
name: "constrained".into(),
757+
data_type: SQLType::Int,
758+
constraints: vec![
759+
SQLColumnConstraint::Null,
760+
SQLColumnConstraint::PrimaryKey(Some("pkey".into())),
761+
SQLColumnConstraint::NotNull,
762+
SQLColumnConstraint::Unique(None),
763+
],
764+
}
765+
]
766+
);
752767
assert_eq!(with_options, vec![]);
753768
}
754769
_ => unreachable!(),
@@ -783,13 +798,13 @@ fn parse_create_external_table() {
783798
let sql = "CREATE EXTERNAL TABLE uk_cities (\
784799
name VARCHAR(100) NOT NULL,\
785800
lat DOUBLE NULL,\
786-
lng DOUBLE NULL)\
801+
lng DOUBLE)\
787802
STORED AS TEXTFILE LOCATION '/tmp/example.csv";
788803
let ast = one_statement_parses_to(
789804
sql,
790805
"CREATE EXTERNAL TABLE uk_cities (\
791806
name character varying(100) NOT NULL, \
792-
lat double, \
807+
lat double NULL, \
793808
lng double) \
794809
STORED AS TEXTFILE LOCATION '/tmp/example.csv'",
795810
);
@@ -803,22 +818,26 @@ fn parse_create_external_table() {
803818
location,
804819
} => {
805820
assert_eq!("uk_cities", name.to_string());
806-
assert_eq!(3, columns.len());
807-
808-
let c_name = &columns[0];
809-
assert_eq!("name", c_name.name);
810-
assert_eq!(SQLType::Varchar(Some(100)), c_name.data_type);
811-
assert_eq!(false, c_name.allow_null);
812-
813-
let c_lat = &columns[1];
814-
assert_eq!("lat", c_lat.name);
815-
assert_eq!(SQLType::Double, c_lat.data_type);
816-
assert_eq!(true, c_lat.allow_null);
817-
818-
let c_lng = &columns[2];
819-
assert_eq!("lng", c_lng.name);
820-
assert_eq!(SQLType::Double, c_lng.data_type);
821-
assert_eq!(true, c_lng.allow_null);
821+
assert_eq!(
822+
columns,
823+
vec![
824+
SQLColumnDef {
825+
name: "name".into(),
826+
data_type: SQLType::Varchar(Some(100)),
827+
constraints: vec![SQLColumnConstraint::NotNull],
828+
},
829+
SQLColumnDef {
830+
name: "lat".into(),
831+
data_type: SQLType::Double,
832+
constraints: vec![SQLColumnConstraint::Null],
833+
},
834+
SQLColumnDef {
835+
name: "lng".into(),
836+
data_type: SQLType::Double,
837+
constraints: vec![],
838+
},
839+
]
840+
);
822841

823842
assert!(external);
824843
assert_eq!(FileFormat::TEXTFILE, file_format.unwrap());

0 commit comments

Comments
 (0)