Skip to content

Commit ffa1c8f

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 ffa1c8f

File tree

5 files changed

+412
-219
lines changed

5 files changed

+412
-219
lines changed

src/sqlast/ddl.rs

+114-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,116 @@ 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`: `[ CONSTRAINT <name> ] <column-option>`.
108+
///
109+
/// Note that implementations are substantially more permissive than the ANSI
110+
/// specification on what order column options can be presented in, and whether
111+
/// they are allowed to be named. The specification distinguishes between
112+
/// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named
113+
/// and can appear in any order, and other options (DEFAULT, GENERATED), which
114+
/// cannot be named and must appear in a fixed order. PostgreSQL, however,
115+
/// allows preceding any option with `CONSTRAINT <name>`, even those that are
116+
/// not really constraints, like NULL and DEFAULT. MSSQL is less permissive,
117+
/// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or
118+
/// NOT NULL constraints (the last of which is in violation of the spec).
119+
///
120+
/// For maximum flexibility, we don't distinguish between constraint and
121+
/// non-constraint options, lumping them all together under the umbrella of
122+
/// "column options," and we allow any column option to be named.
123+
#[derive(Debug, Clone, PartialEq, Hash)]
124+
pub struct ColumnOptionDef {
125+
pub name: Option<SQLIdent>,
126+
pub option: ColumnOption,
127+
}
128+
129+
impl ToString for ColumnOptionDef {
130+
fn to_string(&self) -> String {
131+
format!(
132+
"{}{}",
133+
format_constraint_name(&self.name),
134+
self.option.to_string()
135+
)
136+
}
137+
}
138+
139+
/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
140+
/// TABLE` statement.
141+
#[derive(Debug, Clone, PartialEq, Hash)]
142+
pub enum ColumnOption {
143+
/// `NULL`
144+
Null,
145+
/// `NOT NULL`
146+
NotNull,
147+
/// `DEFAULT <restricted-expr>`
148+
Default(ASTNode),
149+
/// `{ PRIMARY KEY | UNIQUE }`
150+
Unique {
151+
is_primary: bool,
152+
},
153+
/// A referential integrity constraint (`[FOREIGN KEY REFERENCES
154+
/// <foreign_table> (<referred_columns>)`).
155+
ForeignKey {
156+
foreign_table: SQLObjectName,
157+
referred_columns: Vec<SQLIdent>,
158+
},
159+
// `CHECK (<expr>)`
160+
Check(ASTNode),
161+
}
162+
163+
impl ToString for ColumnOption {
164+
fn to_string(&self) -> String {
165+
use ColumnOption::*;
166+
match self {
167+
Null => "NULL".to_string(),
168+
NotNull => "NOT NULL".to_string(),
169+
Default(expr) => format!("DEFAULT {}", expr.to_string()),
170+
Unique { is_primary } => {
171+
if *is_primary {
172+
"PRIMARY KEY".to_string()
173+
} else {
174+
"UNIQUE".to_string()
175+
}
176+
}
177+
ForeignKey {
178+
foreign_table,
179+
referred_columns,
180+
} => format!(
181+
"REFERENCES {} ({})",
182+
foreign_table.to_string(),
183+
referred_columns.join(", ")
184+
),
185+
Check(expr) => format!("CHECK ({})", expr.to_string(),),
186+
}
187+
}
188+
}
189+
190+
fn format_constraint_name(name: &Option<SQLIdent>) -> String {
191+
name.as_ref()
192+
.map(|name| format!("CONSTRAINT {} ", name))
193+
.unwrap_or_default()
194+
}

src/sqlast/mod.rs

+3-31
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

+50-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 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)