Skip to content

Commit 55988c3

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 55988c3

File tree

5 files changed

+393
-219
lines changed

5 files changed

+393
-219
lines changed

src/sqlast/ddl.rs

Lines changed: 101 additions & 6 deletions
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,103 @@ 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 options: Vec<ColumnOptionDef>,
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.options
99+
.iter()
100+
.map(|c| format!(" {}", c.to_string()))
101+
.collect::<Vec<_>>()
102+
.join("")
103+
)
104+
}
105+
}
106+
107+
/// An optionally-named `ColumnOption`.
108+
///
109+
/// `[ CONSTRAINT <name> ] <column-option>`
110+
#[derive(Debug, Clone, PartialEq, Hash)]
111+
pub struct ColumnOptionDef {
112+
pub name: Option<SQLIdent>,
113+
pub option: ColumnOption,
114+
}
115+
116+
impl ToString for ColumnOptionDef {
117+
fn to_string(&self) -> String {
118+
format!(
119+
"{}{}",
120+
format_constraint_name(&self.name),
121+
self.option.to_string()
122+
)
123+
}
124+
}
125+
126+
/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
127+
/// TABLE` statement.
128+
#[derive(Debug, Clone, PartialEq, Hash)]
129+
pub enum ColumnOption {
130+
/// `NULL`
131+
Null,
132+
/// `NOT NULL`
133+
NotNull,
134+
/// `DEFAULT <restricted-expr>`
135+
Default(ASTNode),
136+
/// `{ PRIMARY KEY | UNIQUE }`
137+
Unique {
138+
is_primary: bool,
139+
},
140+
/// A referential integrity constraint (`[FOREIGN KEY REFERENCES
141+
/// <foreign_table> (<referred_columns>)`).
142+
ForeignKey {
143+
foreign_table: SQLObjectName,
144+
referred_columns: Vec<SQLIdent>,
145+
},
146+
// `CHECK (<expr>)`
147+
Check(ASTNode),
148+
}
149+
150+
impl ToString for ColumnOption {
151+
fn to_string(&self) -> String {
152+
use ColumnOption::*;
153+
match self {
154+
Null => "NULL".to_string(),
155+
NotNull => "NOT NULL".to_string(),
156+
Default(expr) => format!("DEFAULT {}", expr.to_string()),
157+
Unique { is_primary } => {
158+
if *is_primary {
159+
"PRIMARY KEY".to_string()
160+
} else {
161+
"UNIQUE".to_string()
162+
}
163+
}
164+
ForeignKey {
165+
foreign_table,
166+
referred_columns,
167+
} => format!(
168+
"REFERENCES {} ({})",
169+
foreign_table.to_string(),
170+
referred_columns.join(", ")
171+
),
172+
Check(expr) => format!("CHECK ({})", expr.to_string(),),
173+
}
174+
}
175+
}
176+
177+
fn format_constraint_name(name: &Option<SQLIdent>) -> String {
178+
name.as_ref()
179+
.map(|name| format!("CONSTRAINT {} ", name))
180+
.unwrap_or_default()
181+
}

src/sqlast/mod.rs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ mod value;
2222

2323
use std::ops::Deref;
2424

25-
pub use self::ddl::{AlterTableOperation, TableConstraint};
25+
pub use self::ddl::{
26+
AlterTableOperation, ColumnOption, ColumnOptionDef, SQLColumnDef, TableConstraint,
27+
};
2628
pub use self::query::{
2729
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
2830
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableAlias, TableFactor,
@@ -580,36 +582,6 @@ impl ToString for SQLAssignment {
580582
}
581583
}
582584

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-
613585
/// SQL function
614586
#[derive(Debug, Clone, PartialEq, Hash)]
615587
pub struct SQLFunction {

src/sqlparser.rs

Lines changed: 50 additions & 39 deletions
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 options = vec![];
883+
loop {
884+
match self.peek_token() {
885+
None | Some(Token::Comma) | Some(Token::RParen) => break,
886+
_ => options.push(self.parse_column_option_def()?),
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+
options,
923895
});
924896
} else {
925897
return self.expected("column name or constraint definition", self.peek_token());
@@ -936,6 +908,45 @@ impl Parser {
936908
Ok((columns, constraints))
937909
}
938910

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

0 commit comments

Comments
 (0)