Skip to content

Commit ebb82b8

Browse files
authored
Merge pull request #65 from nickolay/pr/ddl-improvements
* Rewrite parsing of `ALTER TABLE ADD CONSTRAINT` * Support constraints in CREATE TABLE * Change `Value::Long()` to be unsigned, use u64 consistently * Allow trailing comma in CREATE TABLE
2 parents 5847a16 + d9edc25 commit ebb82b8

File tree

9 files changed

+300
-217
lines changed

9 files changed

+300
-217
lines changed

src/sqlast/ddl.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//! AST types specific to CREATE/ALTER variants of `SQLStatement`
2+
//! (commonly referred to as Data Definition Language, or DDL)
3+
use super::{ASTNode, SQLIdent, SQLObjectName};
4+
5+
/// An `ALTER TABLE` (`SQLStatement::SQLAlterTable`) operation
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub enum AlterTableOperation {
8+
/// `ADD <table_constraint>`
9+
AddConstraint(TableConstraint),
10+
/// TODO: implement `DROP CONSTRAINT <name>`
11+
DropConstraint { name: SQLIdent },
12+
}
13+
14+
impl ToString for AlterTableOperation {
15+
fn to_string(&self) -> String {
16+
match self {
17+
AlterTableOperation::AddConstraint(c) => format!("ADD {}", c.to_string()),
18+
AlterTableOperation::DropConstraint { name } => format!("DROP CONSTRAINT {}", name),
19+
}
20+
}
21+
}
22+
23+
/// A table-level constraint, specified in a `CREATE TABLE` or an
24+
/// `ALTER TABLE ADD <constraint>` statement.
25+
#[derive(Debug, Clone, PartialEq)]
26+
pub enum TableConstraint {
27+
/// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
28+
Unique {
29+
name: Option<SQLIdent>,
30+
columns: Vec<SQLIdent>,
31+
/// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
32+
is_primary: bool,
33+
},
34+
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
35+
/// REFERENCES <foreign_table> (<referred_columns>)`)
36+
ForeignKey {
37+
name: Option<SQLIdent>,
38+
columns: Vec<SQLIdent>,
39+
foreign_table: SQLObjectName,
40+
referred_columns: Vec<SQLIdent>,
41+
},
42+
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
43+
Check {
44+
name: Option<SQLIdent>,
45+
expr: Box<ASTNode>,
46+
},
47+
}
48+
49+
impl ToString for TableConstraint {
50+
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+
}
56+
match self {
57+
TableConstraint::Unique {
58+
name,
59+
columns,
60+
is_primary,
61+
} => format!(
62+
"{}{} ({})",
63+
format_constraint_name(name),
64+
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
65+
columns.join(", ")
66+
),
67+
TableConstraint::ForeignKey {
68+
name,
69+
columns,
70+
foreign_table,
71+
referred_columns,
72+
} => format!(
73+
"{}FOREIGN KEY ({}) REFERENCES {}({})",
74+
format_constraint_name(name),
75+
columns.join(", "),
76+
foreign_table.to_string(),
77+
referred_columns.join(", ")
78+
),
79+
TableConstraint::Check { name, expr } => format!(
80+
"{}CHECK ({})",
81+
format_constraint_name(name),
82+
expr.to_string()
83+
),
84+
}
85+
}
86+
}

src/sqlast/mod.rs

+25-15
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@
1414

1515
//! SQL Abstract Syntax Tree (AST) types
1616
17+
mod ddl;
1718
mod query;
1819
mod sql_operator;
1920
mod sqltype;
20-
mod table_key;
2121
mod value;
2222

23+
pub use self::ddl::{AlterTableOperation, TableConstraint};
2324
pub use self::query::{
2425
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
2526
SQLSelectItem, SQLSetExpr, SQLSetOperator, TableFactor,
2627
};
2728
pub use self::sqltype::SQLType;
28-
pub use self::table_key::{AlterOperation, Key, TableKey};
2929
pub use self::value::Value;
3030

3131
pub use self::sql_operator::SQLOperator;
@@ -396,6 +396,7 @@ pub enum SQLStatement {
396396
name: SQLObjectName,
397397
/// Optional schema
398398
columns: Vec<SQLColumnDef>,
399+
constraints: Vec<TableConstraint>,
399400
external: bool,
400401
file_format: Option<FileFormat>,
401402
location: Option<String>,
@@ -404,7 +405,7 @@ pub enum SQLStatement {
404405
SQLAlterTable {
405406
/// Table name
406407
name: SQLObjectName,
407-
operation: AlterOperation,
408+
operation: AlterTableOperation,
408409
},
409410
/// DROP TABLE
410411
SQLDrop {
@@ -503,21 +504,30 @@ impl ToString for SQLStatement {
503504
SQLStatement::SQLCreateTable {
504505
name,
505506
columns,
507+
constraints,
506508
external,
507509
file_format,
508510
location,
509-
} if *external => format!(
510-
"CREATE EXTERNAL TABLE {} ({}) STORED AS {} LOCATION '{}'",
511-
name.to_string(),
512-
comma_separated_string(columns),
513-
file_format.as_ref().unwrap().to_string(),
514-
location.as_ref().unwrap()
515-
),
516-
SQLStatement::SQLCreateTable { name, columns, .. } => format!(
517-
"CREATE TABLE {} ({})",
518-
name.to_string(),
519-
comma_separated_string(columns)
520-
),
511+
} => {
512+
let mut s = format!(
513+
"CREATE {}TABLE {} ({}",
514+
if *external { "EXTERNAL " } else { "" },
515+
name.to_string(),
516+
comma_separated_string(columns)
517+
);
518+
if !constraints.is_empty() {
519+
s += &format!(", {}", comma_separated_string(constraints));
520+
}
521+
s += ")";
522+
if *external {
523+
s += &format!(
524+
" STORED AS {} LOCATION '{}'",
525+
file_format.as_ref().unwrap().to_string(),
526+
location.as_ref().unwrap()
527+
);
528+
}
529+
s
530+
}
521531
SQLStatement::SQLAlterTable { name, operation } => {
522532
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
523533
}

src/sqlast/sqltype.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
use super::SQLObjectName;
22

3-
/// SQL datatypes for literals in SQL statements
3+
/// SQL data types
44
#[derive(Debug, Clone, PartialEq)]
55
pub enum SQLType {
66
/// Fixed-length character type e.g. CHAR(10)
7-
Char(Option<usize>),
7+
Char(Option<u64>),
88
/// Variable-length character type e.g. VARCHAR(10)
9-
Varchar(Option<usize>),
9+
Varchar(Option<u64>),
1010
/// Uuid type
1111
Uuid,
1212
/// Large character object e.g. CLOB(1000)
13-
Clob(usize),
13+
Clob(u64),
1414
/// Fixed-length binary type e.g. BINARY(10)
15-
Binary(usize),
15+
Binary(u64),
1616
/// Variable-length binary type e.g. VARBINARY(10)
17-
Varbinary(usize),
17+
Varbinary(u64),
1818
/// Large binary object e.g. BLOB(1000)
19-
Blob(usize),
19+
Blob(u64),
2020
/// Decimal type with optional precision and scale e.g. DECIMAL(10,2)
21-
Decimal(Option<usize>, Option<usize>),
21+
Decimal(Option<u64>, Option<u64>),
2222
/// Floating point with optional precision e.g. FLOAT(8)
23-
Float(Option<usize>),
23+
Float(Option<u64>),
2424
/// Small integer
2525
SmallInt,
2626
/// Integer
@@ -87,7 +87,7 @@ impl ToString for SQLType {
8787
}
8888
}
8989

90-
fn format_type_with_optional_length(sql_type: &str, len: &Option<usize>) -> String {
90+
fn format_type_with_optional_length(sql_type: &str, len: &Option<u64>) -> String {
9191
let mut s = sql_type.to_string();
9292
if let Some(len) = len {
9393
s += &format!("({})", len);

src/sqlast/table_key.rs

-61
This file was deleted.

src/sqlast/value.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
/// SQL values such as int, double, string, timestamp
1+
/// Primitive SQL values such as number and string
22
#[derive(Debug, Clone, PartialEq)]
33
pub enum Value {
4-
/// Literal signed long
5-
Long(i64),
6-
/// Literal floating point value
4+
/// Unsigned integer value
5+
Long(u64),
6+
/// Unsigned floating point value
77
Double(f64),
88
/// 'string value'
99
SingleQuotedString(String),
1010
/// N'string value'
1111
NationalStringLiteral(String),
12-
/// Boolean value true or false,
12+
/// Boolean value true or false
1313
Boolean(bool),
1414
/// NULL value in insert statements,
1515
Null,

0 commit comments

Comments
 (0)