Skip to content

Commit c42250d

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 2308c1c commit c42250d

File tree

5 files changed

+306
-213
lines changed

5 files changed

+306
-213
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)]
@@ -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)]
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)]
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
@@ -20,7 +20,7 @@ mod sql_operator;
2020
mod sqltype;
2121
mod value;
2222

23-
pub use self::ddl::{AlterTableOperation, TableConstraint};
23+
pub use self::ddl::{AlterTableOperation, ColumnConstraint, SQLColumnDef, TableConstraint};
2424
pub use self::query::{
2525
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
2626
SQLSelectItem, SQLSetExpr, SQLSetOperator, TableFactor,
@@ -570,36 +570,6 @@ impl ToString for SQLAssignment {
570570
}
571571
}
572572

573-
/// SQL column definition
574-
#[derive(Debug, Clone, PartialEq)]
575-
pub struct SQLColumnDef {
576-
pub name: SQLIdent,
577-
pub data_type: SQLType,
578-
pub is_primary: bool,
579-
pub is_unique: bool,
580-
pub default: Option<ASTNode>,
581-
pub allow_null: bool,
582-
}
583-
584-
impl ToString for SQLColumnDef {
585-
fn to_string(&self) -> String {
586-
let mut s = format!("{} {}", self.name, self.data_type.to_string());
587-
if self.is_primary {
588-
s += " PRIMARY KEY";
589-
}
590-
if self.is_unique {
591-
s += " UNIQUE";
592-
}
593-
if let Some(ref default) = self.default {
594-
s += &format!(" DEFAULT {}", default.to_string());
595-
}
596-
if !self.allow_null {
597-
s += " NOT NULL";
598-
}
599-
s
600-
}
601-
}
602-
603573
/// External table's available file format
604574
#[derive(Debug, Clone, PartialEq)]
605575
pub enum FileFormat {

src/sqlparser.rs

+60-42
Original file line numberDiff line numberDiff line change
@@ -155,29 +155,6 @@ impl Parser {
155155
Ok(expr)
156156
}
157157

158-
/// Parse expression for DEFAULT clause in CREATE TABLE
159-
pub fn parse_default_expr(&mut self, precedence: u8) -> Result<ASTNode, ParserError> {
160-
debug!("parsing expr");
161-
let mut expr = self.parse_prefix()?;
162-
debug!("prefix: {:?}", expr);
163-
loop {
164-
// stop parsing on `NULL` | `NOT NULL`
165-
match self.peek_token() {
166-
Some(Token::SQLWord(ref k)) if k.keyword == "NOT" || k.keyword == "NULL" => break,
167-
_ => {}
168-
}
169-
170-
let next_precedence = self.get_next_precedence()?;
171-
debug!("next precedence: {:?}", next_precedence);
172-
if precedence >= next_precedence {
173-
break;
174-
}
175-
176-
expr = self.parse_infix(expr, next_precedence)?;
177-
}
178-
Ok(expr)
179-
}
180-
181158
/// Parse an expression prefix
182159
pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
183160
let tok = self
@@ -843,29 +820,18 @@ impl Parser {
843820
} else if let Some(Token::SQLWord(column_name)) = self.peek_token() {
844821
self.next_token();
845822
let data_type = self.parse_data_type()?;
846-
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
847-
let is_unique = self.parse_keyword("UNIQUE");
848-
let default = if self.parse_keyword("DEFAULT") {
849-
let expr = self.parse_default_expr(0)?;
850-
Some(expr)
851-
} else {
852-
None
853-
};
854-
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
855-
false
856-
} else {
857-
let _ = self.parse_keyword("NULL");
858-
true
859-
};
860-
debug!("default: {:?}", default);
823+
let mut constraints = vec![];
824+
loop {
825+
match self.peek_token() {
826+
None | Some(Token::Comma) | Some(Token::RParen) => break,
827+
_ => constraints.push(self.parse_column_constraint()?),
828+
}
829+
}
861830

862831
columns.push(SQLColumnDef {
863832
name: column_name.as_sql_ident(),
864833
data_type,
865-
allow_null,
866-
is_primary,
867-
is_unique,
868-
default,
834+
constraints,
869835
});
870836
} else {
871837
return self.expected("column name or constraint definition", self.peek_token());
@@ -882,6 +848,58 @@ impl Parser {
882848
Ok((columns, constraints))
883849
}
884850

851+
pub fn parse_column_constraint(&mut self) -> Result<ColumnConstraint, ParserError> {
852+
if self.parse_keywords(vec!["NOT", "NULL"]) {
853+
return Ok(ColumnConstraint::NotNull);
854+
} else if self.parse_keyword("NULL") {
855+
return Ok(ColumnConstraint::Null);
856+
} else if self.parse_keyword("DEFAULT") {
857+
// We want to allow as many DEFAULT expressions as possible, but
858+
// calling self.parse_expr() will choke on expressions like "DEFAULT
859+
// 1 NOT NULL". Typically this would be a syntax error, as "1 NOT
860+
// NULL" is not a valid SQL expression, but in this case we know
861+
// that NOT NULL is part of the next column constraint. As it turns
862+
// out, the only expressions that cause trouble all have precedence
863+
// less than or equal to BETWEEN, so we pass BETWEEN_PREC to stop
864+
// parsing on tokens with precedence less than or equal to BETWEEN.
865+
// The same trick is employed by PostgreSQL [0].
866+
// https://github.com/postgres/postgres/blob/56b78626c/src/backend/parser/gram.y#L13366-L13370
867+
let expr = self.parse_subexpr(Self::BETWEEN_PREC)?;
868+
return Ok(ColumnConstraint::Default(expr));
869+
}
870+
871+
let name = if self.parse_keyword("CONSTRAINT") {
872+
Some(self.parse_identifier()?)
873+
} else {
874+
None
875+
};
876+
877+
if self.parse_keywords(vec!["PRIMARY", "KEY"]) {
878+
Ok(ColumnConstraint::Unique {
879+
name,
880+
is_primary: true,
881+
})
882+
} else if self.parse_keyword("UNIQUE") {
883+
Ok(ColumnConstraint::Unique {
884+
name,
885+
is_primary: false,
886+
})
887+
} else if self.parse_keyword("REFERENCES") {
888+
let foreign_table = self.parse_object_name()?;
889+
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
890+
Ok(ColumnConstraint::ForeignKey {
891+
name,
892+
foreign_table,
893+
referred_columns,
894+
})
895+
} else {
896+
parser_err!(format!(
897+
"Unexpected token in column definition: {:?}",
898+
self.peek_token()
899+
))
900+
}
901+
}
902+
885903
pub fn parse_optional_table_constraint(
886904
&mut self,
887905
) -> Result<Option<TableConstraint>, ParserError> {

0 commit comments

Comments
 (0)