Skip to content

Commit bf7d50b

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 1f87083 commit bf7d50b

File tree

5 files changed

+306
-221
lines changed

5 files changed

+306
-221
lines changed

src/sqlast/ddl.rs

+89-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,91 @@ 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 constraints: Vec<ColumnConstraint>,
89+
}
90+
91+
impl ToString for SQLColumnDef {
92+
fn to_string(&self) -> String {
93+
format!(
94+
"{} {}{}",
95+
self.name,
96+
self.data_type.to_string(),
97+
self.constraints
98+
.iter()
99+
.map(|c| format!(" {}", c.to_string()))
100+
.collect::<Vec<_>>()
101+
.join("")
102+
)
103+
}
104+
}
105+
106+
#[derive(Debug, Clone, PartialEq, Hash)]
107+
pub enum ColumnConstraint {
108+
/// `NULL`
109+
Null,
110+
/// `NOT NULL`
111+
NotNull,
112+
/// `DEFAULT <restricted-expr>`
113+
Default(ASTNode),
114+
/// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE }`
115+
Unique {
116+
name: Option<SQLIdent>,
117+
/// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
118+
is_primary: bool,
119+
},
120+
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY
121+
/// REFERENCES <foreign_table> (<referred_columns>)`)
122+
ForeignKey {
123+
name: Option<SQLIdent>,
124+
foreign_table: SQLObjectName,
125+
referred_columns: Vec<SQLIdent>,
126+
},
127+
// `[ CONSTRAINT <name> ] CHECK (<expr>)`
128+
Check {
129+
name: Option<SQLIdent>,
130+
expr: Box<ASTNode>,
131+
},
132+
}
133+
134+
impl ToString for ColumnConstraint {
135+
fn to_string(&self) -> String {
136+
use ColumnConstraint::*;
137+
match self {
138+
Null => "NULL".to_string(),
139+
NotNull => "NOT NULL".to_string(),
140+
Default(expr) => format!("DEFAULT {}", expr.to_string()),
141+
Unique { name, is_primary } => format!(
142+
"{}{}",
143+
format_constraint_name(name),
144+
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
145+
),
146+
ForeignKey {
147+
name,
148+
foreign_table,
149+
referred_columns,
150+
} => format!(
151+
"{}REFERENCES {} ({})",
152+
format_constraint_name(name),
153+
foreign_table.to_string(),
154+
referred_columns.join(", ")
155+
),
156+
Check { name, expr } => format!(
157+
"{}CHECK ({})",
158+
format_constraint_name(name),
159+
expr.to_string()
160+
),
161+
}
162+
}
163+
}
164+
165+
fn format_constraint_name(name: &Option<SQLIdent>) -> String {
166+
name.as_ref()
167+
.map(|name| format!("CONSTRAINT {} ", name))
168+
.unwrap_or_default()
169+
}

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

+60-42
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
@@ -896,29 +873,18 @@ impl Parser {
896873
} else if let Some(Token::SQLWord(column_name)) = self.peek_token() {
897874
self.next_token();
898875
let data_type = self.parse_data_type()?;
899-
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
900-
let is_unique = self.parse_keyword("UNIQUE");
901-
let default = if self.parse_keyword("DEFAULT") {
902-
let expr = self.parse_default_expr(0)?;
903-
Some(expr)
904-
} else {
905-
None
906-
};
907-
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
908-
false
909-
} else {
910-
let _ = self.parse_keyword("NULL");
911-
true
912-
};
913-
debug!("default: {:?}", default);
876+
let mut constraints = vec![];
877+
loop {
878+
match self.peek_token() {
879+
None | Some(Token::Comma) | Some(Token::RParen) => break,
880+
_ => constraints.push(self.parse_column_constraint()?),
881+
}
882+
}
914883

915884
columns.push(SQLColumnDef {
916885
name: column_name.as_sql_ident(),
917886
data_type,
918-
allow_null,
919-
is_primary,
920-
is_unique,
921-
default,
887+
constraints,
922888
});
923889
} else {
924890
return self.expected("column name or constraint definition", self.peek_token());
@@ -935,6 +901,58 @@ impl Parser {
935901
Ok((columns, constraints))
936902
}
937903

904+
pub fn parse_column_constraint(&mut self) -> Result<ColumnConstraint, ParserError> {
905+
if self.parse_keywords(vec!["NOT", "NULL"]) {
906+
return Ok(ColumnConstraint::NotNull);
907+
} else if self.parse_keyword("NULL") {
908+
return Ok(ColumnConstraint::Null);
909+
} else if self.parse_keyword("DEFAULT") {
910+
// We want to allow as many DEFAULT expressions as possible, but
911+
// calling self.parse_expr() will choke on expressions like "DEFAULT
912+
// 1 NOT NULL". Typically this would be a syntax error, as "1 NOT
913+
// NULL" is not a valid SQL expression, but in this case we know
914+
// that NOT NULL is part of the next column constraint. As it turns
915+
// out, the only expressions that cause trouble all have precedence
916+
// less than or equal to BETWEEN, so we pass BETWEEN_PREC to stop
917+
// parsing on tokens with precedence less than or equal to BETWEEN.
918+
// The same trick is employed by PostgreSQL [0].
919+
// https://github.com/postgres/postgres/blob/56b78626c/src/backend/parser/gram.y#L13366-L13370
920+
let expr = self.parse_subexpr(Self::BETWEEN_PREC)?;
921+
return Ok(ColumnConstraint::Default(expr));
922+
}
923+
924+
let name = if self.parse_keyword("CONSTRAINT") {
925+
Some(self.parse_identifier()?)
926+
} else {
927+
None
928+
};
929+
930+
if self.parse_keywords(vec!["PRIMARY", "KEY"]) {
931+
Ok(ColumnConstraint::Unique {
932+
name,
933+
is_primary: true,
934+
})
935+
} else if self.parse_keyword("UNIQUE") {
936+
Ok(ColumnConstraint::Unique {
937+
name,
938+
is_primary: false,
939+
})
940+
} else if self.parse_keyword("REFERENCES") {
941+
let foreign_table = self.parse_object_name()?;
942+
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
943+
Ok(ColumnConstraint::ForeignKey {
944+
name,
945+
foreign_table,
946+
referred_columns,
947+
})
948+
} else {
949+
parser_err!(format!(
950+
"Unexpected token in column definition: {:?}",
951+
self.peek_token()
952+
))
953+
}
954+
}
955+
938956
pub fn parse_optional_table_constraint(
939957
&mut self,
940958
) -> Result<Option<TableConstraint>, ParserError> {

0 commit comments

Comments
 (0)