Skip to content

Commit b47438d

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 fc5e662 commit b47438d

File tree

5 files changed

+358
-219
lines changed

5 files changed

+358
-219
lines changed

src/sqlast/ddl.rs

+104-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! AST types specific to CREATE/ALTER variants of `SQLStatement`
22
//! (commonly referred to as Data Definition Language, or DDL)
3-
use super::{ASTNode, SQLIdent, SQLObjectName};
3+
use super::{ASTNode, SQLIdent, SQLObjectName, SQLType};
44

55
/// An `ALTER TABLE` (`SQLStatement::SQLAlterTable`) operation
66
#[derive(Debug, Clone, PartialEq, Hash)]
@@ -48,11 +48,6 @@ pub enum TableConstraint {
4848

4949
impl ToString for TableConstraint {
5050
fn to_string(&self) -> String {
51-
fn format_constraint_name(name: &Option<SQLIdent>) -> String {
52-
name.as_ref()
53-
.map(|name| format!("CONSTRAINT {} ", name))
54-
.unwrap_or_default()
55-
}
5651
match self {
5752
TableConstraint::Unique {
5853
name,
@@ -84,3 +79,106 @@ impl ToString for TableConstraint {
8479
}
8580
}
8681
}
82+
83+
/// SQL column definition
84+
#[derive(Debug, Clone, PartialEq, Hash)]
85+
pub struct SQLColumnDef {
86+
pub name: SQLIdent,
87+
pub data_type: SQLType,
88+
pub collation: Option<SQLObjectName>,
89+
pub constraints: Vec<ColumnConstraint>,
90+
}
91+
92+
impl ToString for SQLColumnDef {
93+
fn to_string(&self) -> String {
94+
format!(
95+
"{} {}{}",
96+
self.name,
97+
self.data_type.to_string(),
98+
self.constraints
99+
.iter()
100+
.map(|c| format!(" {}", c.to_string()))
101+
.collect::<Vec<_>>()
102+
.join("")
103+
)
104+
}
105+
}
106+
107+
#[derive(Debug, Clone, PartialEq, Hash)]
108+
pub enum ColumnConstraint {
109+
/// `NULL`
110+
///
111+
/// The ANSI specification technically allows NULL constraints to have a
112+
/// name, but no known databases retain that name, if they even parse it
113+
/// at all. Just omit it until we have evidence that it's important.
114+
Null,
115+
/// `NOT NULL`
116+
///
117+
/// As with `NULL`, `NOT NULL` constraints can technically have a name,
118+
/// but we choose to omit it.
119+
NotNull,
120+
/// `[ CONSTRAINT <name> ] DEFAULT <restricted-expr>`
121+
Default {
122+
name: Option<SQLIdent>,
123+
expr: ASTNode,
124+
},
125+
/// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE }`
126+
Unique {
127+
name: Option<SQLIdent>,
128+
/// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
129+
is_primary: bool,
130+
},
131+
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY
132+
/// REFERENCES <foreign_table> (<referred_columns>)`)
133+
ForeignKey {
134+
name: Option<SQLIdent>,
135+
foreign_table: SQLObjectName,
136+
referred_columns: Vec<SQLIdent>,
137+
},
138+
// `[ CONSTRAINT <name> ] CHECK (<expr>)`
139+
Check {
140+
name: Option<SQLIdent>,
141+
expr: Box<ASTNode>,
142+
},
143+
}
144+
145+
impl ToString for ColumnConstraint {
146+
fn to_string(&self) -> String {
147+
use ColumnConstraint::*;
148+
match self {
149+
Null => "NULL".to_string(),
150+
NotNull => "NOT NULL".to_string(),
151+
Default { name, expr } => format!(
152+
"{}DEFAULT {}",
153+
format_constraint_name(name),
154+
expr.to_string()
155+
),
156+
Unique { name, is_primary } => format!(
157+
"{}{}",
158+
format_constraint_name(name),
159+
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
160+
),
161+
ForeignKey {
162+
name,
163+
foreign_table,
164+
referred_columns,
165+
} => format!(
166+
"{}REFERENCES {} ({})",
167+
format_constraint_name(name),
168+
foreign_table.to_string(),
169+
referred_columns.join(", ")
170+
),
171+
Check { name, expr } => format!(
172+
"{}CHECK ({})",
173+
format_constraint_name(name),
174+
expr.to_string()
175+
),
176+
}
177+
}
178+
}
179+
180+
fn format_constraint_name(name: &Option<SQLIdent>) -> String {
181+
name.as_ref()
182+
.map(|name| format!("CONSTRAINT {} ", name))
183+
.unwrap_or_default()
184+
}

src/sqlast/mod.rs

+1-31
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod value;
2222

2323
use std::ops::Deref;
2424

25-
pub use self::ddl::{AlterTableOperation, TableConstraint};
25+
pub use self::ddl::{AlterTableOperation, ColumnConstraint, SQLColumnDef, TableConstraint};
2626
pub use self::query::{
2727
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
2828
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableAlias, TableFactor,
@@ -580,36 +580,6 @@ impl ToString for SQLAssignment {
580580
}
581581
}
582582

583-
/// SQL column definition
584-
#[derive(Debug, Clone, PartialEq, Hash)]
585-
pub struct SQLColumnDef {
586-
pub name: SQLIdent,
587-
pub data_type: SQLType,
588-
pub is_primary: bool,
589-
pub is_unique: bool,
590-
pub default: Option<ASTNode>,
591-
pub allow_null: bool,
592-
}
593-
594-
impl ToString for SQLColumnDef {
595-
fn to_string(&self) -> String {
596-
let mut s = format!("{} {}", self.name, self.data_type.to_string());
597-
if self.is_primary {
598-
s += " PRIMARY KEY";
599-
}
600-
if self.is_unique {
601-
s += " UNIQUE";
602-
}
603-
if let Some(ref default) = self.default {
604-
s += &format!(" DEFAULT {}", default.to_string());
605-
}
606-
if !self.allow_null {
607-
s += " NOT NULL";
608-
}
609-
s
610-
}
611-
}
612-
613583
/// SQL function
614584
#[derive(Debug, Clone, PartialEq, Hash)]
615585
pub struct SQLFunction {

src/sqlparser.rs

+58-39
Original file line numberDiff line numberDiff line change
@@ -156,29 +156,6 @@ impl Parser {
156156
Ok(expr)
157157
}
158158

159-
/// Parse expression for DEFAULT clause in CREATE TABLE
160-
pub fn parse_default_expr(&mut self, precedence: u8) -> Result<ASTNode, ParserError> {
161-
debug!("parsing expr");
162-
let mut expr = self.parse_prefix()?;
163-
debug!("prefix: {:?}", expr);
164-
loop {
165-
// stop parsing on `NULL` | `NOT NULL`
166-
match self.peek_token() {
167-
Some(Token::SQLWord(ref k)) if k.keyword == "NOT" || k.keyword == "NULL" => break,
168-
_ => {}
169-
}
170-
171-
let next_precedence = self.get_next_precedence()?;
172-
debug!("next precedence: {:?}", next_precedence);
173-
if precedence >= next_precedence {
174-
break;
175-
}
176-
177-
expr = self.parse_infix(expr, next_precedence)?;
178-
}
179-
Ok(expr)
180-
}
181-
182159
/// Parse an expression prefix
183160
pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
184161
let tok = self
@@ -897,29 +874,24 @@ impl Parser {
897874
} else if let Some(Token::SQLWord(column_name)) = self.peek_token() {
898875
self.next_token();
899876
let data_type = self.parse_data_type()?;
900-
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
901-
let is_unique = self.parse_keyword("UNIQUE");
902-
let default = if self.parse_keyword("DEFAULT") {
903-
let expr = self.parse_default_expr(0)?;
904-
Some(expr)
877+
let collation = if self.parse_keyword("COLLATE") {
878+
Some(self.parse_object_name()?)
905879
} else {
906880
None
907881
};
908-
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
909-
false
910-
} else {
911-
let _ = self.parse_keyword("NULL");
912-
true
913-
};
914-
debug!("default: {:?}", default);
882+
let mut constraints = vec![];
883+
loop {
884+
match self.peek_token() {
885+
None | Some(Token::Comma) | Some(Token::RParen) => break,
886+
_ => constraints.push(self.parse_column_constraint()?),
887+
}
888+
}
915889

916890
columns.push(SQLColumnDef {
917891
name: column_name.as_sql_ident(),
918892
data_type,
919-
allow_null,
920-
is_primary,
921-
is_unique,
922-
default,
893+
collation,
894+
constraints,
923895
});
924896
} else {
925897
return self.expected("column name or constraint definition", self.peek_token());
@@ -936,6 +908,53 @@ impl Parser {
936908
Ok((columns, constraints))
937909
}
938910

911+
pub fn parse_column_constraint(&mut self) -> Result<ColumnConstraint, ParserError> {
912+
let name = if self.parse_keyword("CONSTRAINT") {
913+
Some(self.parse_identifier()?)
914+
} else {
915+
None
916+
};
917+
918+
if self.parse_keywords(vec!["NOT", "NULL"]) {
919+
Ok(ColumnConstraint::NotNull)
920+
} else if self.parse_keyword("NULL") {
921+
Ok(ColumnConstraint::Null)
922+
} else if self.parse_keyword("DEFAULT") {
923+
Ok(ColumnConstraint::Default {
924+
name,
925+
expr: self.parse_expr()?,
926+
})
927+
} else if self.parse_keywords(vec!["PRIMARY", "KEY"]) {
928+
Ok(ColumnConstraint::Unique {
929+
name,
930+
is_primary: true,
931+
})
932+
} else if self.parse_keyword("UNIQUE") {
933+
Ok(ColumnConstraint::Unique {
934+
name,
935+
is_primary: false,
936+
})
937+
} else if self.parse_keyword("REFERENCES") {
938+
let foreign_table = self.parse_object_name()?;
939+
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
940+
Ok(ColumnConstraint::ForeignKey {
941+
name,
942+
foreign_table,
943+
referred_columns,
944+
})
945+
} else if self.parse_keyword("CHECK") {
946+
self.expect_token(&Token::LParen)?;
947+
let expr = Box::new(self.parse_expr()?);
948+
self.expect_token(&Token::RParen)?;
949+
Ok(ColumnConstraint::Check { name, expr })
950+
} else {
951+
parser_err!(format!(
952+
"Unexpected token in column definition: {:?}",
953+
self.peek_token()
954+
))
955+
}
956+
}
957+
939958
pub fn parse_optional_table_constraint(
940959
&mut self,
941960
) -> Result<Option<TableConstraint>, ParserError> {

0 commit comments

Comments
 (0)