ran cargo fmt

This commit is contained in:
Andy Grove 2018-10-06 09:39:26 -06:00
parent 9daba3ab49
commit 722ea7a91b
11 changed files with 662 additions and 615 deletions

View file

@ -32,5 +32,3 @@ pub trait Dialect {
/// Determine if a character is a valid identifier character /// Determine if a character is a valid identifier character
fn is_identifier_part(&self, ch: char) -> bool; fn is_identifier_part(&self, ch: char) -> bool;
} }

View file

@ -6,67 +6,12 @@ pub struct GenericSqlDialect {}
impl Dialect for GenericSqlDialect { impl Dialect for GenericSqlDialect {
fn keywords(&self) -> Vec<&'static str> { fn keywords(&self) -> Vec<&'static str> {
return vec![ return vec![
SELECT, SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, BY, HAVING, UNION, ALL, INSERT, INTO, UPDATE,
FROM, DELETE, IN, IS, NULL, SET, CREATE, EXTERNAL, TABLE, ASC, DESC, AND, OR, NOT, AS,
WHERE, STORED, CSV, PARQUET, LOCATION, WITH, WITHOUT, HEADER, ROW, // SQL types
LIMIT, CHAR, CHARACTER, VARYING, LARGE, OBJECT, VARCHAR, CLOB, BINARY, VARBINARY, BLOB, FLOAT,
ORDER, REAL, DOUBLE, PRECISION, INT, INTEGER, SMALLINT, BIGINT, NUMERIC, DECIMAL, DEC,
GROUP, BOOLEAN, DATE, TIME, TIMESTAMP,
BY,
HAVING,
UNION,
ALL,
INSERT,
INTO,
UPDATE,
DELETE,
IN,
IS,
NULL,
SET,
CREATE,
EXTERNAL,
TABLE,
ASC,
DESC,
AND,
OR,
NOT,
AS,
STORED,
CSV,
PARQUET,
LOCATION,
WITH,
WITHOUT,
HEADER,
ROW,
// SQL types
CHAR,
CHARACTER,
VARYING,
LARGE,
OBJECT,
VARCHAR,
CLOB,
BINARY,
VARBINARY,
BLOB,
FLOAT,
REAL,
DOUBLE,
PRECISION,
INT,
INTEGER,
SMALLINT,
BIGINT,
NUMERIC,
DECIMAL,
DEC,
BOOLEAN,
DATE,
TIME,
TIMESTAMP,
]; ];
} }
@ -82,4 +27,3 @@ impl Dialect for GenericSqlDialect {
|| ch == '_' || ch == '_'
} }
} }

View file

@ -7,83 +7,13 @@ pub struct PostgreSqlDialect {}
impl Dialect for PostgreSqlDialect { impl Dialect for PostgreSqlDialect {
fn keywords(&self) -> Vec<&'static str> { fn keywords(&self) -> Vec<&'static str> {
return vec![ return vec![
ALTER, ALTER, ONLY, SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, BY, HAVING, UNION, ALL, INSERT,
ONLY, INTO, UPDATE, DELETE, IN, IS, NULL, SET, CREATE, EXTERNAL, TABLE, ASC, DESC, AND, OR,
SELECT, NOT, AS, STORED, CSV, WITH, WITHOUT, ROW, // SQL types
FROM, CHAR, CHARACTER, VARYING, LARGE, VARCHAR, CLOB, BINARY, VARBINARY, BLOB, FLOAT, REAL,
WHERE, DOUBLE, PRECISION, INT, INTEGER, SMALLINT, BIGINT, NUMERIC, DECIMAL, DEC, BOOLEAN,
LIMIT, DATE, TIME, TIMESTAMP, VALUES, DEFAULT, ZONE, REGCLASS, TEXT, BYTEA, TRUE, FALSE, COPY,
ORDER, STDIN, PRIMARY, KEY, UNIQUE, UUID, ADD, CONSTRAINT, FOREIGN, REFERENCES,
GROUP,
BY,
HAVING,
UNION,
ALL,
INSERT,
INTO,
UPDATE,
DELETE,
IN,
IS,
NULL,
SET,
CREATE,
EXTERNAL,
TABLE,
ASC,
DESC,
AND,
OR,
NOT,
AS,
STORED,
CSV,
WITH,
WITHOUT,
ROW,
// SQL types
CHAR,
CHARACTER,
VARYING,
LARGE,
VARCHAR,
CLOB,
BINARY,
VARBINARY,
BLOB,
FLOAT,
REAL,
DOUBLE,
PRECISION,
INT,
INTEGER,
SMALLINT,
BIGINT,
NUMERIC,
DECIMAL,
DEC,
BOOLEAN,
DATE,
TIME,
TIMESTAMP,
VALUES,
DEFAULT,
ZONE,
REGCLASS,
TEXT,
BYTEA,
TRUE,
FALSE,
COPY,
STDIN,
PRIMARY,
KEY,
UNIQUE,
UUID,
ADD,
CONSTRAINT,
FOREIGN,
REFERENCES,
]; ];
} }

View file

@ -14,31 +14,22 @@
//! SQL Abstract Syntax Tree (AST) types //! SQL Abstract Syntax Tree (AST) types
//! //!
use chrono::{NaiveDate, use chrono::{
NaiveDateTime, offset::{FixedOffset, TimeZone},
NaiveTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
offset::{FixedOffset,
TimeZone,
},
DateTime,
Utc,
};
use uuid::Uuid;
pub use self::value::Value;
pub use self::sqltype::SQLType;
pub use self::table_key::{
AlterOperation,
TableKey,
Key
}; };
pub use self::sqltype::SQLType;
pub use self::table_key::{AlterOperation, Key, TableKey};
pub use self::value::Value;
use uuid::Uuid;
pub use self::sql_operator::SQLOperator; pub use self::sql_operator::SQLOperator;
mod value; mod sql_operator;
mod sqltype; mod sqltype;
mod table_key; mod table_key;
mod sql_operator; mod value;
/// SQL Abstract Syntax Tree (AST) /// SQL Abstract Syntax Tree (AST)
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -103,7 +94,7 @@ pub enum ASTNode {
/// VALUES (vector of rows to insert) /// VALUES (vector of rows to insert)
values: Vec<Vec<ASTNode>>, values: Vec<Vec<ASTNode>>,
}, },
SQLCopy{ SQLCopy {
/// TABLE /// TABLE
table_name: String, table_name: String,
/// COLUMNS /// COLUMNS
@ -139,33 +130,43 @@ pub enum ASTNode {
/// Table name /// Table name
name: String, name: String,
operation: AlterOperation, operation: AlterOperation,
} },
} }
impl ToString for ASTNode {
impl ToString for ASTNode{
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
ASTNode::SQLIdentifier(s) => s.to_string(), ASTNode::SQLIdentifier(s) => s.to_string(),
ASTNode::SQLWildcard => "*".to_string(), ASTNode::SQLWildcard => "*".to_string(),
ASTNode::SQLCompoundIdentifier(s) => s.join("."), ASTNode::SQLCompoundIdentifier(s) => s.join("."),
ASTNode::SQLAssignment(ass) => ass.to_string(), ASTNode::SQLAssignment(ass) => ass.to_string(),
ASTNode::SQLIsNull(ast) => format!("{} IS NULL",ast.as_ref().to_string()), ASTNode::SQLIsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()),
ASTNode::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()), ASTNode::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),
ASTNode::SQLBinaryExpr{left, op, right} => { ASTNode::SQLBinaryExpr { left, op, right } => format!(
format!("{} {} {}", left.as_ref().to_string(), op.to_string(), right.as_ref().to_string()) "{} {} {}",
} left.as_ref().to_string(),
ASTNode::SQLCast{expr, data_type} => { op.to_string(),
format!("CAST({} AS {})", expr.as_ref().to_string(), data_type.to_string()) right.as_ref().to_string()
} ),
ASTNode::SQLCast { expr, data_type } => format!(
"CAST({} AS {})",
expr.as_ref().to_string(),
data_type.to_string()
),
ASTNode::SQLNested(ast) => format!("({})", ast.as_ref().to_string()), ASTNode::SQLNested(ast) => format!("({})", ast.as_ref().to_string()),
ASTNode::SQLUnary {operator, rex } => { ASTNode::SQLUnary { operator, rex } => {
format!("{} {}", operator.to_string(), rex.as_ref().to_string()) format!("{} {}", operator.to_string(), rex.as_ref().to_string())
} }
ASTNode::SQLValue(v) => v.to_string(), ASTNode::SQLValue(v) => v.to_string(),
ASTNode::SQLFunction{id, args} => format!("{}({})", id, args.iter().map(|a|a.to_string()).collect::<Vec<String>>().join(", ")), ASTNode::SQLFunction { id, args } => format!(
ASTNode::SQLSelect{ "{}({})",
id,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLSelect {
projection, projection,
relation, relation,
selection, selection,
@ -174,82 +175,147 @@ impl ToString for ASTNode{
having, having,
limit, limit,
} => { } => {
let mut s = format!("SELECT {}", projection.iter().map(|p|p.to_string()).collect::<Vec<String>>().join(", ")); let mut s = format!(
if let Some(relation) = relation{ "SELECT {}",
projection
.iter()
.map(|p| p.to_string())
.collect::<Vec<String>>()
.join(", ")
);
if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string()); s += &format!(" FROM {}", relation.as_ref().to_string());
} }
if let Some(selection) = selection{ if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string()); s += &format!(" WHERE {}", selection.as_ref().to_string());
} }
if let Some(group_by) = group_by { if let Some(group_by) = group_by {
s += &format!(" GROUP BY {}", group_by.iter().map(|g|g.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" GROUP BY {}",
group_by
.iter()
.map(|g| g.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(having) = having{ if let Some(having) = having {
s += &format!(" HAVING {}", having.as_ref().to_string()); s += &format!(" HAVING {}", having.as_ref().to_string());
} }
if let Some(order_by) = order_by{ if let Some(order_by) = order_by {
s += &format!(" ORDER BY {}", order_by.iter().map(|o|o.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" ORDER BY {}",
order_by
.iter()
.map(|o| o.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(limit) = limit { if let Some(limit) = limit {
s += &format!(" LIMIT {}", limit.as_ref().to_string()); s += &format!(" LIMIT {}", limit.as_ref().to_string());
} }
s s
} }
ASTNode::SQLInsert{ table_name, columns, values } => { ASTNode::SQLInsert {
table_name,
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name); let mut s = format!("INSERT INTO {}", table_name);
if columns.len() > 0 { if columns.len() > 0 {
s += &format!(" ({})", columns.join(", ")); s += &format!(" ({})", columns.join(", "));
} }
if values.len() > 0 { if values.len() > 0 {
s += &format!(" VALUES({})", s += &format!(
values.iter() " VALUES({})",
.map(|row|row.iter().map(|c|c.to_string()) values
.collect::<Vec<String>>().join(", ") .iter()
).collect::<Vec<String>>().join(", ") .map(|row| row
); .iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "))
.collect::<Vec<String>>()
.join(", ")
);
} }
s s
} }
ASTNode::SQLCopy{table_name, columns, values} => { ASTNode::SQLCopy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name); let mut s = format!("COPY {}", table_name);
if columns.len() > 0 { if columns.len() > 0 {
s += &format!(" ({})", columns.iter().map(|c|c.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" ({})",
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
s += " FROM stdin; "; s += " FROM stdin; ";
if values.len() > 0 { if values.len() > 0 {
s += &format!("\n{}", s += &format!(
values.iter() "\n{}",
.map(|v|v.to_string()) values
.collect::<Vec<String>>().join("\t") .iter()
); .map(|v| v.to_string())
.collect::<Vec<String>>()
.join("\t")
);
} }
s += "\n\\."; s += "\n\\.";
s s
} }
ASTNode::SQLUpdate{table_name, assignments, selection} => { ASTNode::SQLUpdate {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name); let mut s = format!("UPDATE {}", table_name);
if assignments.len() > 0 { if assignments.len() > 0 {
s += &format!("{}", assignments.iter().map(|ass|ass.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
"{}",
assignments
.iter()
.map(|ass| ass.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(selection) = selection{ if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string()); s += &format!(" WHERE {}", selection.as_ref().to_string());
} }
s s
} }
ASTNode::SQLDelete{relation, selection} => { ASTNode::SQLDelete {
relation,
selection,
} => {
let mut s = String::from("DELETE"); let mut s = String::from("DELETE");
if let Some(relation) = relation{ if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string()); s += &format!(" FROM {}", relation.as_ref().to_string());
} }
if let Some(selection) = selection{ if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string()); s += &format!(" WHERE {}", selection.as_ref().to_string());
} }
s s
} }
ASTNode::SQLCreateTable{name, columns} => { ASTNode::SQLCreateTable { name, columns } => format!(
format!("CREATE TABLE {} ({})", name, columns.iter().map(|c|c.to_string()).collect::<Vec<String>>().join(", ")) "CREATE TABLE {} ({})",
} name,
ASTNode::SQLAlterTable{name, operation} => { columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name, operation.to_string()) format!("ALTER TABLE {} {}", name, operation.to_string())
} }
} }
@ -264,8 +330,7 @@ pub struct SQLAssignment {
value: Box<ASTNode>, value: Box<ASTNode>,
} }
impl ToString for SQLAssignment{ impl ToString for SQLAssignment {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!("SET {} = {}", self.id, self.value.as_ref().to_string()) format!("SET {} = {}", self.id, self.value.as_ref().to_string())
} }
@ -284,11 +349,11 @@ impl SQLOrderByExpr {
} }
} }
impl ToString for SQLOrderByExpr{ impl ToString for SQLOrderByExpr {
fn to_string(&self) -> String { fn to_string(&self) -> String {
if self.asc{ if self.asc {
format!("{} ASC", self.expr.as_ref().to_string()) format!("{} ASC", self.expr.as_ref().to_string())
}else{ } else {
format!("{} DESC", self.expr.as_ref().to_string()) format!("{} DESC", self.expr.as_ref().to_string())
} }
} }
@ -305,24 +370,21 @@ pub struct SQLColumnDef {
pub allow_null: bool, pub allow_null: bool,
} }
impl ToString for SQLColumnDef {
impl ToString for SQLColumnDef{
fn to_string(&self) -> String { fn to_string(&self) -> String {
let mut s = format!("{} {}", self.name, self.data_type.to_string()); let mut s = format!("{} {}", self.name, self.data_type.to_string());
if self.is_primary{ if self.is_primary {
s += " PRIMARY KEY"; s += " PRIMARY KEY";
} }
if self.is_unique{ if self.is_unique {
s += " UNIQUE"; s += " UNIQUE";
} }
if let Some(ref default) = self.default{ if let Some(ref default) = self.default {
s += &format!(" DEFAULT {}", default.as_ref().to_string()); s += &format!(" DEFAULT {}", default.as_ref().to_string());
} }
if !self.allow_null{ if !self.allow_null {
s += " NOT NULL"; s += " NOT NULL";
} }
s s
} }
} }

View file

@ -1,4 +1,3 @@
/// SQL Operator /// SQL Operator
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLOperator { pub enum SQLOperator {
@ -17,10 +16,9 @@ pub enum SQLOperator {
Or, Or,
} }
impl ToString for SQLOperator{ impl ToString for SQLOperator {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
SQLOperator::Plus => "+".to_string(), SQLOperator::Plus => "+".to_string(),
SQLOperator::Minus => "-".to_string(), SQLOperator::Minus => "-".to_string(),
SQLOperator::Multiply => "*".to_string(), SQLOperator::Multiply => "*".to_string(),
@ -37,4 +35,3 @@ impl ToString for SQLOperator{
} }
} }
} }

View file

@ -1,4 +1,3 @@
/// SQL datatypes for literals in SQL statements /// SQL datatypes for literals in SQL statements
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum SQLType { pub enum SQLType {
@ -50,38 +49,45 @@ pub enum SQLType {
Array(Box<SQLType>), Array(Box<SQLType>),
} }
impl ToString for SQLType{ impl ToString for SQLType {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
SQLType::Char(size) => if let Some(size) = size { SQLType::Char(size) => {
format!("char({})", size) if let Some(size) = size {
}else{ format!("char({})", size)
"char".to_string() } else {
"char".to_string()
}
} }
SQLType::Varchar(size) => if let Some(size) = size{ SQLType::Varchar(size) => {
format!("character varying({})", size) if let Some(size) = size {
}else{ format!("character varying({})", size)
"character varying".to_string() } else {
"character varying".to_string()
}
} }
SQLType::Uuid => "uuid".to_string(), SQLType::Uuid => "uuid".to_string(),
SQLType::Clob(size) => format!("clob({})", size), SQLType::Clob(size) => format!("clob({})", size),
SQLType::Binary(size) => format!("binary({})", size), SQLType::Binary(size) => format!("binary({})", size),
SQLType::Varbinary(size) => format!("varbinary({})", size), SQLType::Varbinary(size) => format!("varbinary({})", size),
SQLType::Blob(size) => format!("blob({})", size), SQLType::Blob(size) => format!("blob({})", size),
SQLType::Decimal(precision, scale) => if let Some(scale) = scale{ SQLType::Decimal(precision, scale) => {
format!("numeric({},{})", precision, scale) if let Some(scale) = scale {
}else{ format!("numeric({},{})", precision, scale)
format!("numeric({})", precision) } else {
}, format!("numeric({})", precision)
}
}
SQLType::SmallInt => "smallint".to_string(), SQLType::SmallInt => "smallint".to_string(),
SQLType::Int => "int".to_string(), SQLType::Int => "int".to_string(),
SQLType::BigInt => "bigint".to_string(), SQLType::BigInt => "bigint".to_string(),
SQLType::Float(size) => if let Some(size) = size{ SQLType::Float(size) => {
format!("float({})", size) if let Some(size) = size {
}else{ format!("float({})", size)
"float".to_string() } else {
}, "float".to_string()
}
}
SQLType::Real => "real".to_string(), SQLType::Real => "real".to_string(),
SQLType::Double => "double".to_string(), SQLType::Double => "double".to_string(),
SQLType::Boolean => "boolean".to_string(), SQLType::Boolean => "boolean".to_string(),
@ -91,7 +97,7 @@ impl ToString for SQLType{
SQLType::Regclass => "regclass".to_string(), SQLType::Regclass => "regclass".to_string(),
SQLType::Text => "text".to_string(), SQLType::Text => "text".to_string(),
SQLType::Bytea => "bytea".to_string(), SQLType::Bytea => "bytea".to_string(),
SQLType::Array(ty) => format!("{}[]",ty.to_string()), SQLType::Array(ty) => format!("{}[]", ty.to_string()),
SQLType::Custom(ty) => ty.to_string(), SQLType::Custom(ty) => ty.to_string(),
} }
} }

View file

@ -1,31 +1,28 @@
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum AlterOperation{ pub enum AlterOperation {
AddConstraint(TableKey), AddConstraint(TableKey),
RemoveConstraint{ RemoveConstraint { name: String },
name: String,
}
} }
impl ToString for AlterOperation { impl ToString for AlterOperation {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
AlterOperation::AddConstraint(table_key) => format!("ADD CONSTRAINT {}", table_key.to_string()), AlterOperation::AddConstraint(table_key) => {
AlterOperation::RemoveConstraint{name} => format!("REMOVE CONSTRAINT {}", name), format!("ADD CONSTRAINT {}", table_key.to_string())
}
AlterOperation::RemoveConstraint { name } => format!("REMOVE CONSTRAINT {}", name),
} }
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Key{ pub struct Key {
pub name: String, pub name: String,
pub columns: Vec<String>, pub columns: Vec<String>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum TableKey{ pub enum TableKey {
PrimaryKey(Key), PrimaryKey(Key),
UniqueKey(Key), UniqueKey(Key),
Key(Key), Key(Key),
@ -33,26 +30,30 @@ pub enum TableKey{
key: Key, key: Key,
foreign_table: String, foreign_table: String,
referred_columns: Vec<String>, referred_columns: Vec<String>,
} },
} }
impl ToString for TableKey {
impl ToString for TableKey{
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
TableKey::PrimaryKey(ref key) => { TableKey::PrimaryKey(ref key) => {
format!("{} PRIMARY KEY ({})", key.name, key.columns.join(", ")) format!("{} PRIMARY KEY ({})", key.name, key.columns.join(", "))
} }
TableKey::UniqueKey(ref key) => { TableKey::UniqueKey(ref key) => {
format!("{} UNIQUE KEY ({})", key.name, key.columns.join(", ")) format!("{} UNIQUE KEY ({})", key.name, key.columns.join(", "))
} }
TableKey::Key(ref key) => { TableKey::Key(ref key) => format!("{} KEY ({})", key.name, key.columns.join(", ")),
format!("{} KEY ({})", key.name, key.columns.join(", ")) TableKey::ForeignKey {
} key,
TableKey::ForeignKey{key, foreign_table, referred_columns} => { foreign_table,
format!("{} FOREIGN KEY ({}) REFERENCES {}({})", key.name, key.columns.join(", "), foreign_table, referred_columns.join(", ")) referred_columns,
} } => format!(
"{} FOREIGN KEY ({}) REFERENCES {}({})",
key.name,
key.columns.join(", "),
foreign_table,
referred_columns.join(", ")
),
} }
} }
} }

View file

@ -1,19 +1,13 @@
use chrono::{
use chrono::{NaiveDate, offset::{FixedOffset, TimeZone},
NaiveDateTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
NaiveTime, };
offset::{FixedOffset,
TimeZone,
},
DateTime,
Utc,
};
use uuid::Uuid; use uuid::Uuid;
/// SQL values such as int, double, string timestamp /// SQL values such as int, double, string timestamp
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Value{ pub enum Value {
/// Literal signed long /// Literal signed long
Long(i64), Long(i64),
/// Literal floating point value /// Literal floating point value
@ -40,11 +34,9 @@ pub enum Value{
Null, Null,
} }
impl ToString for Value {
impl ToString for Value{
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
Value::Long(v) => v.to_string(), Value::Long(v) => v.to_string(),
Value::Double(v) => v.to_string(), Value::Double(v) => v.to_string(),
Value::String(v) => v.to_string(), Value::String(v) => v.to_string(),

View file

@ -17,15 +17,10 @@
use super::dialect::Dialect; use super::dialect::Dialect;
use super::sqlast::*; use super::sqlast::*;
use super::sqltokenizer::*; use super::sqltokenizer::*;
use chrono::{NaiveDate, use chrono::{
NaiveDateTime, offset::{FixedOffset, TimeZone},
NaiveTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
offset::{FixedOffset, };
TimeZone,
},
DateTime,
Utc,
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ParserError { pub enum ParserError {
@ -95,100 +90,102 @@ impl Parser {
/// Parse an expression prefix /// Parse an expression prefix
pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
match self.next_token() { match self.next_token() {
Some(t) => { Some(t) => match t {
match t { Token::Keyword(k) => match k.to_uppercase().as_ref() {
Token::Keyword(k) => match k.to_uppercase().as_ref() { "SELECT" => Ok(self.parse_select()?),
"SELECT" => Ok(self.parse_select()?), "CREATE" => Ok(self.parse_create()?),
"CREATE" => Ok(self.parse_create()?), "DELETE" => Ok(self.parse_delete()?),
"DELETE" => Ok(self.parse_delete()?), "INSERT" => Ok(self.parse_insert()?),
"INSERT" => Ok(self.parse_insert()?), "ALTER" => Ok(self.parse_alter()?),
"ALTER" => Ok(self.parse_alter()?), "COPY" => Ok(self.parse_copy()?),
"COPY" => Ok(self.parse_copy()?), "TRUE" => {
"TRUE" => { self.prev_token();
self.prev_token(); self.parse_sql_value()
self.parse_sql_value() }
} "FALSE" => {
"FALSE" => { self.prev_token();
self.prev_token(); self.parse_sql_value()
self.parse_sql_value() }
} "NULL" => {
"NULL" => { self.prev_token();
self.prev_token(); self.parse_sql_value()
self.parse_sql_value() }
} _ => return parser_err!(format!("No prefix parser for keyword {}", k)),
_ => return parser_err!(format!("No prefix parser for keyword {}", k)), },
}, Token::Mult => Ok(ASTNode::SQLWildcard),
Token::Mult => Ok(ASTNode::SQLWildcard), Token::Identifier(id) => {
Token::Identifier(id) => { if "CAST" == id.to_uppercase() {
if "CAST" == id.to_uppercase(){ self.parse_cast_expression()
self.parse_cast_expression() } else {
}else{ match self.peek_token() {
match self.peek_token() { Some(Token::LParen) => self.parse_function_or_pg_cast(&id),
Some(Token::LParen) => { Some(Token::Period) => {
self.parse_function_or_pg_cast(&id) let mut id_parts: Vec<String> = vec![id];
} while self.peek_token() == Some(Token::Period) {
Some(Token::Period) => { self.consume_token(&Token::Period)?;
let mut id_parts: Vec<String> = vec![id]; match self.next_token() {
while self.peek_token() == Some(Token::Period) { Some(Token::Identifier(id)) => id_parts.push(id),
self.consume_token(&Token::Period)?; _ => {
match self.next_token() { return parser_err!(format!(
Some(Token::Identifier(id)) => id_parts.push(id), "Error parsing compound identifier"
_ => { ))
return parser_err!(format!(
"Error parsing compound identifier"
))
}
} }
} }
Ok(ASTNode::SQLCompoundIdentifier(id_parts))
} }
_ => Ok(ASTNode::SQLIdentifier(id)), Ok(ASTNode::SQLCompoundIdentifier(id_parts))
} }
_ => Ok(ASTNode::SQLIdentifier(id)),
} }
} }
Token::Number(ref n) => {
self.prev_token();
self.parse_sql_value()
},
Token::String(ref s) => {
self.prev_token();
self.parse_sql_value()
}
Token::SingleQuotedString(ref s) => {
self.prev_token();
self.parse_sql_value()
}
Token::DoubleQuotedString(ref s) => {
self.prev_token();
self.parse_sql_value()
}
_ => parser_err!(format!(
"Prefix parser expected a keyword but found {:?}",
t
)),
} }
} Token::Number(ref n) => {
self.prev_token();
self.parse_sql_value()
}
Token::String(ref s) => {
self.prev_token();
self.parse_sql_value()
}
Token::SingleQuotedString(ref s) => {
self.prev_token();
self.parse_sql_value()
}
Token::DoubleQuotedString(ref s) => {
self.prev_token();
self.parse_sql_value()
}
_ => parser_err!(format!(
"Prefix parser expected a keyword but found {:?}",
t
)),
},
None => parser_err!(format!("Prefix parser expected a keyword but hit EOF")), None => parser_err!(format!("Prefix parser expected a keyword but hit EOF")),
} }
} }
pub fn parse_function_or_pg_cast(&mut self, id: &str) -> Result<ASTNode, ParserError> { pub fn parse_function_or_pg_cast(&mut self, id: &str) -> Result<ASTNode, ParserError> {
let func = self.parse_function(&id)?; let func = self.parse_function(&id)?;
if let Some(Token::DoubleColon) = self.peek_token(){ if let Some(Token::DoubleColon) = self.peek_token() {
self.parse_pg_cast(func) self.parse_pg_cast(func)
}else{ } else {
Ok(func) Ok(func)
} }
} }
pub fn parse_function(&mut self, id: &str) -> Result<ASTNode, ParserError> { pub fn parse_function(&mut self, id: &str) -> Result<ASTNode, ParserError> {
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
if let Ok(true) = self.consume_token(&Token::RParen){ if let Ok(true) = self.consume_token(&Token::RParen) {
Ok(ASTNode::SQLFunction { id: id.to_string(), args: vec![] }) Ok(ASTNode::SQLFunction {
}else{ id: id.to_string(),
args: vec![],
})
} else {
let args = self.parse_expr_list()?; let args = self.parse_expr_list()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
Ok(ASTNode::SQLFunction { id: id.to_string(), args }) Ok(ASTNode::SQLFunction {
id: id.to_string(),
args,
})
} }
} }
@ -205,24 +202,23 @@ impl Parser {
}) })
} }
/// Parse a postgresql casting style which is in the form or expr::datatype /// Parse a postgresql casting style which is in the form or expr::datatype
pub fn parse_pg_cast(&mut self, expr: ASTNode) -> Result<ASTNode, ParserError> { pub fn parse_pg_cast(&mut self, expr: ASTNode) -> Result<ASTNode, ParserError> {
let ast = self.consume_token(&Token::DoubleColon)?; let ast = self.consume_token(&Token::DoubleColon)?;
let datatype = if let Ok(data_type) = self.parse_data_type(){ let datatype = if let Ok(data_type) = self.parse_data_type() {
Ok(data_type) Ok(data_type)
}else if let Ok(table_name) = self.parse_tablename(){ } else if let Ok(table_name) = self.parse_tablename() {
Ok(SQLType::Custom(table_name)) Ok(SQLType::Custom(table_name))
}else{ } else {
parser_err!("Expecting datatype or identifier") parser_err!("Expecting datatype or identifier")
}; };
let pg_cast = ASTNode::SQLCast{ let pg_cast = ASTNode::SQLCast {
expr: Box::new(expr), expr: Box::new(expr),
data_type: datatype?, data_type: datatype?,
}; };
if let Some(Token::DoubleColon) = self.peek_token(){ if let Some(Token::DoubleColon) = self.peek_token() {
self.parse_pg_cast(pg_cast) self.parse_pg_cast(pg_cast)
}else{ } else {
Ok(pg_cast) Ok(pg_cast)
} }
} }
@ -236,21 +232,23 @@ impl Parser {
debug!("parsing infix"); debug!("parsing infix");
match self.next_token() { match self.next_token() {
Some(tok) => match tok { Some(tok) => match tok {
Token::Keyword(ref k) => if k == "IS" { Token::Keyword(ref k) => {
if self.parse_keywords(vec!["NULL"]) { if k == "IS" {
Ok(Some(ASTNode::SQLIsNull(Box::new(expr)))) if self.parse_keywords(vec!["NULL"]) {
} else if self.parse_keywords(vec!["NOT", "NULL"]) { Ok(Some(ASTNode::SQLIsNull(Box::new(expr))))
Ok(Some(ASTNode::SQLIsNotNull(Box::new(expr)))) } else if self.parse_keywords(vec!["NOT", "NULL"]) {
Ok(Some(ASTNode::SQLIsNotNull(Box::new(expr))))
} else {
parser_err!("Invalid tokens after IS")
}
} else { } else {
parser_err!("Invalid tokens after IS") Ok(Some(ASTNode::SQLBinaryExpr {
left: Box::new(expr),
op: self.to_sql_operator(&tok)?,
right: Box::new(self.parse_expr(precedence)?),
}))
} }
} else { }
Ok(Some(ASTNode::SQLBinaryExpr {
left: Box::new(expr),
op: self.to_sql_operator(&tok)?,
right: Box::new(self.parse_expr(precedence)?),
}))
},
Token::Eq Token::Eq
| Token::Neq | Token::Neq
| Token::Gt | Token::Gt
@ -266,10 +264,10 @@ impl Parser {
op: self.to_sql_operator(&tok)?, op: self.to_sql_operator(&tok)?,
right: Box::new(self.parse_expr(precedence)?), right: Box::new(self.parse_expr(precedence)?),
})), })),
| Token::DoubleColon => { Token::DoubleColon => {
let pg_cast = self.parse_pg_cast(expr)?; let pg_cast = self.parse_pg_cast(expr)?;
Ok(Some(pg_cast)) Ok(Some(pg_cast))
}, }
_ => parser_err!(format!("No infix parser for token {:?}", tok)), _ => parser_err!(format!("No infix parser for token {:?}", tok)),
}, },
None => Ok(None), None => Ok(None),
@ -327,10 +325,9 @@ impl Parser {
self.peek_token_skip_whitespace() self.peek_token_skip_whitespace()
} }
pub fn skip_whitespace(&mut self) -> Option<Token> { pub fn skip_whitespace(&mut self) -> Option<Token> {
loop { loop {
match self.next_token_no_skip(){ match self.next_token_no_skip() {
Some(Token::Whitespace(ws)) => { Some(Token::Whitespace(ws)) => {
continue; continue;
} }
@ -345,7 +342,7 @@ impl Parser {
fn til_non_whitespace(&self) -> Option<usize> { fn til_non_whitespace(&self) -> Option<usize> {
let mut index = self.index; let mut index = self.index;
loop { loop {
match self.token_at(index){ match self.token_at(index) {
Some(Token::Whitespace(ws)) => { Some(Token::Whitespace(ws)) => {
index = index + 1; index = index + 1;
} }
@ -360,23 +357,22 @@ impl Parser {
} }
/// see the token at this index /// see the token at this index
fn token_at(&self, n: usize) -> Option<Token>{ fn token_at(&self, n: usize) -> Option<Token> {
if let Some(token) = self.tokens.get(n){ if let Some(token) = self.tokens.get(n) {
Some(token.clone()) Some(token.clone())
}else{ } else {
None None
} }
} }
pub fn peek_token_skip_whitespace(&self) -> Option<Token> { pub fn peek_token_skip_whitespace(&self) -> Option<Token> {
if let Some(n) = self.til_non_whitespace(){ if let Some(n) = self.til_non_whitespace() {
self.token_at(n) self.token_at(n)
}else{ } else {
None None
} }
} }
/// Get the next token skipping whitespace and increment the token index /// Get the next token skipping whitespace and increment the token index
pub fn next_token(&mut self) -> Option<Token> { pub fn next_token(&mut self) -> Option<Token> {
self.skip_whitespace() self.skip_whitespace()
@ -393,15 +389,15 @@ impl Parser {
/// if prev token is whitespace skip it /// if prev token is whitespace skip it
/// if prev token is not whitespace skipt it as well /// if prev token is not whitespace skipt it as well
pub fn prev_token_skip_whitespace(&mut self) -> Option<Token>{ pub fn prev_token_skip_whitespace(&mut self) -> Option<Token> {
loop{ loop {
match self.prev_token_no_skip(){ match self.prev_token_no_skip() {
Some(Token::Whitespace(ws)) => { Some(Token::Whitespace(ws)) => {
continue; continue;
} }
token => { token => {
return token; return token;
}, }
} }
} }
} }
@ -454,21 +450,18 @@ impl Parser {
/// Consume the next token if it matches the expected token, otherwise return an error /// Consume the next token if it matches the expected token, otherwise return an error
pub fn consume_token(&mut self, expected: &Token) -> Result<bool, ParserError> { pub fn consume_token(&mut self, expected: &Token) -> Result<bool, ParserError> {
match self.peek_token() { match self.peek_token() {
Some(ref t) => if *t == *expected { Some(ref t) => {
self.next_token(); if *t == *expected {
Ok(true) self.next_token();
} else { Ok(true)
Ok(false) } else {
}, Ok(false)
other => parser_err!(format!( }
"expected token {:?} but was {:?}", }
expected, other => parser_err!(format!("expected token {:?} but was {:?}", expected, other,)),
other,
)),
} }
} }
/// Parse a SQL CREATE statement /// Parse a SQL CREATE statement
pub fn parse_create(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_create(&mut self) -> Result<ASTNode, ParserError> {
if self.parse_keywords(vec!["TABLE"]) { if self.parse_keywords(vec!["TABLE"]) {
@ -481,10 +474,10 @@ impl Parser {
if let Ok(data_type) = self.parse_data_type() { if let Ok(data_type) = self.parse_data_type() {
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]); let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
let is_unique = self.parse_keyword("UNIQUE"); let is_unique = self.parse_keyword("UNIQUE");
let default = if self.parse_keyword("DEFAULT"){ let default = if self.parse_keyword("DEFAULT") {
let expr = self.parse_expr(0)?; let expr = self.parse_expr(0)?;
Some(Box::new(expr)) Some(Box::new(expr))
}else{ } else {
None None
}; };
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) { let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
@ -527,16 +520,20 @@ impl Parser {
} }
} }
} else { } else {
return parser_err!( return parser_err!(format!(
format!("Error parsing data type in column definition near: {:?}", self.peek_token()) "Error parsing data type in column definition near: {:?}",
); self.peek_token()
));
} }
} else { } else {
return parser_err!("Error parsing column name"); return parser_err!("Error parsing column name");
} }
} }
} }
Ok(ASTNode::SQLCreateTable { name: table_name, columns }) Ok(ASTNode::SQLCreateTable {
name: table_name,
columns,
})
} else { } else {
parser_err!(format!( parser_err!(format!(
"Unexpected token after CREATE: {:?}", "Unexpected token after CREATE: {:?}",
@ -550,34 +547,35 @@ impl Parser {
let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]); let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]);
let is_foreign_key = self.parse_keywords(vec!["FOREIGN", "KEY"]); let is_foreign_key = self.parse_keywords(vec!["FOREIGN", "KEY"]);
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
let column_names= self.parse_column_names()?; let column_names = self.parse_column_names()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
let key = Key{ let key = Key {
name: constraint_name.to_string(), name: constraint_name.to_string(),
columns: column_names columns: column_names,
}; };
if is_primary_key{ if is_primary_key {
Ok(TableKey::PrimaryKey(key)) Ok(TableKey::PrimaryKey(key))
} } else if is_unique_key {
else if is_unique_key{
Ok(TableKey::UniqueKey(key)) Ok(TableKey::UniqueKey(key))
} } else if is_foreign_key {
else if is_foreign_key{ if self.parse_keyword("REFERENCES") {
if self.parse_keyword("REFERENCES"){
let foreign_table = self.parse_tablename()?; let foreign_table = self.parse_tablename()?;
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
let referred_columns = self.parse_column_names()?; let referred_columns = self.parse_column_names()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
Ok(TableKey::ForeignKey{ Ok(TableKey::ForeignKey {
key, key,
foreign_table, foreign_table,
referred_columns, referred_columns,
}) })
}else{ } else {
parser_err!("Expecting references") parser_err!("Expecting references")
} }
}else{ } else {
parser_err!(format!("Expecting primary key, unique key, or foreign key, found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting primary key, unique key, or foreign key, found: {:?}",
self.peek_token()
))
} }
} }
@ -585,52 +583,64 @@ impl Parser {
if self.parse_keyword("TABLE") { if self.parse_keyword("TABLE") {
let is_only = self.parse_keyword("ONLY"); let is_only = self.parse_keyword("ONLY");
let table_name = self.parse_tablename()?; let table_name = self.parse_tablename()?;
let operation:Result<AlterOperation,ParserError> = if self.parse_keywords(vec!["ADD", "CONSTRAINT"]){ let operation: Result<AlterOperation, ParserError> =
match self.next_token(){ if self.parse_keywords(vec!["ADD", "CONSTRAINT"]) {
Some(Token::Identifier(ref id)) => { match self.next_token() {
let table_key = self.parse_table_key(id)?; Some(Token::Identifier(ref id)) => {
Ok(AlterOperation::AddConstraint(table_key)) let table_key = self.parse_table_key(id)?;
Ok(AlterOperation::AddConstraint(table_key))
}
_ => {
return parser_err!(format!(
"Expecting identifier, found : {:?}",
self.peek_token()
));
}
} }
_ => { } else {
return parser_err!(format!("Expecting identifier, found : {:?}", self.peek_token())); return parser_err!(format!(
} "Expecting ADD CONSTRAINT, found :{:?}",
} self.peek_token()
}else{ ));
return parser_err!(format!("Expecting ADD CONSTRAINT, found :{:?}", self.peek_token())); };
}; Ok(ASTNode::SQLAlterTable {
Ok(ASTNode::SQLAlterTable{
name: table_name, name: table_name,
operation: operation?, operation: operation?,
}) })
} else { } else {
parser_err!(format!("Expecting TABLE after ALTER, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting TABLE after ALTER, found {:?}",
self.peek_token()
))
} }
} }
/// Parse a copy statement /// Parse a copy statement
pub fn parse_copy(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_copy(&mut self) -> Result<ASTNode, ParserError> {
let table_name = self.parse_tablename()?; let table_name = self.parse_tablename()?;
let columns = if self.consume_token(&Token::LParen)?{ let columns = if self.consume_token(&Token::LParen)? {
let column_names = self.parse_column_names()?; let column_names = self.parse_column_names()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
column_names column_names
}else{ } else {
vec![] vec![]
}; };
self.parse_keyword("FROM"); self.parse_keyword("FROM");
self.parse_keyword("STDIN"); self.parse_keyword("STDIN");
self.consume_token(&Token::SemiColon); self.consume_token(&Token::SemiColon);
let values = self.parse_tsv()?; let values = self.parse_tsv()?;
Ok(ASTNode::SQLCopy{table_name, columns, values}) Ok(ASTNode::SQLCopy {
table_name,
columns,
values,
})
} }
/// Parse a tab separated values in /// Parse a tab separated values in
/// COPY payload /// COPY payload
fn parse_tsv(&mut self) -> Result<Vec<Value>, ParserError>{ fn parse_tsv(&mut self) -> Result<Vec<Value>, ParserError> {
let values = self.parse_tab_value()?; let values = self.parse_tab_value()?;
Ok(values) Ok(values)
} }
fn parse_sql_value(&mut self) -> Result<ASTNode, ParserError> { fn parse_sql_value(&mut self) -> Result<ASTNode, ParserError> {
@ -640,8 +650,8 @@ impl Parser {
fn parse_tab_value(&mut self) -> Result<Vec<Value>, ParserError> { fn parse_tab_value(&mut self) -> Result<Vec<Value>, ParserError> {
let mut values = vec![]; let mut values = vec![];
let mut content = String::from(""); let mut content = String::from("");
while let Some(t) = self.next_token_no_skip(){ while let Some(t) = self.next_token_no_skip() {
match t{ match t {
Token::Whitespace(Whitespace::Tab) => { Token::Whitespace(Whitespace::Tab) => {
values.push(Value::String(content.to_string())); values.push(Value::String(content.to_string()));
content.clear(); content.clear();
@ -653,11 +663,12 @@ impl Parser {
Token::Backslash => { Token::Backslash => {
if let Ok(true) = self.consume_token(&Token::Period) { if let Ok(true) = self.consume_token(&Token::Period) {
return Ok(values); return Ok(values);
}if let Some(token) = self.next_token(){ }
if token == Token::Identifier("N".to_string()){ if let Some(token) = self.next_token() {
if token == Token::Identifier("N".to_string()) {
values.push(Value::Null); values.push(Value::Null);
} }
}else{ } else {
continue; continue;
} }
} }
@ -685,10 +696,10 @@ impl Parser {
Err(e) => { Err(e) => {
let index = self.index; let index = self.index;
self.prev_token(); self.prev_token();
if let Ok(timestamp) = self.parse_timestamp_value(){ if let Ok(timestamp) = self.parse_timestamp_value() {
println!("timstamp: {:?}", timestamp); println!("timstamp: {:?}", timestamp);
Ok(timestamp) Ok(timestamp)
}else{ } else {
self.index = index; self.index = index;
parser_err!(format!("Could not parse '{}' as i64: {}", n, e)) parser_err!(format!("Could not parse '{}' as i64: {}", n, e))
} }
@ -696,10 +707,10 @@ impl Parser {
}, },
Token::Number(ref n) => match n.parse::<i64>() { Token::Number(ref n) => match n.parse::<i64>() {
Ok(n) => { Ok(n) => {
if let Some(Token::Minus) = self.peek_token(){ if let Some(Token::Minus) = self.peek_token() {
self.prev_token(); self.prev_token();
self.parse_timestamp_value() self.parse_timestamp_value()
}else{ } else {
Ok(Value::Long(n)) Ok(Value::Long(n))
} }
} }
@ -707,8 +718,12 @@ impl Parser {
}, },
Token::Identifier(id) => Ok(Value::String(id.to_string())), Token::Identifier(id) => Ok(Value::String(id.to_string())),
Token::String(ref s) => Ok(Value::String(s.to_string())), Token::String(ref s) => Ok(Value::String(s.to_string())),
Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())), Token::SingleQuotedString(ref s) => {
Token::DoubleQuotedString(ref s) => Ok(Value::DoubleQuotedString(s.to_string())), Ok(Value::SingleQuotedString(s.to_string()))
}
Token::DoubleQuotedString(ref s) => {
Ok(Value::DoubleQuotedString(s.to_string()))
}
other => parser_err!(format!("Unsupported value: {:?}", self.peek_token())), other => parser_err!(format!("Unsupported value: {:?}", self.peek_token())),
} }
} }
@ -744,9 +759,8 @@ impl Parser {
} }
} }
pub fn parse_timezone_offset(&mut self) -> Result<i8, ParserError> { pub fn parse_timezone_offset(&mut self) -> Result<i8, ParserError> {
match self.next_token(){ match self.next_token() {
Some(Token::Plus) => { Some(Token::Plus) => {
let n = self.parse_literal_int()?; let n = self.parse_literal_int()?;
Ok(n as i8) Ok(n as i8)
@ -755,46 +769,56 @@ impl Parser {
let n = self.parse_literal_int()?; let n = self.parse_literal_int()?;
Ok(-n as i8) Ok(-n as i8)
} }
other => parser_err!(format!("Expecting `+` or `-` in timezone, but found {:?}", other)), other => parser_err!(format!(
"Expecting `+` or `-` in timezone, but found {:?}",
other
)),
} }
} }
pub fn parse_timestamp_value(&mut self) -> Result<Value, ParserError> { pub fn parse_timestamp_value(&mut self) -> Result<Value, ParserError> {
let year = self.parse_literal_int()?; let year = self.parse_literal_int()?;
let date = self.parse_date(year)?; let date = self.parse_date(year)?;
if let Ok(time) = self.parse_time(){ if let Ok(time) = self.parse_time() {
let date_time = NaiveDateTime::new(date, time); let date_time = NaiveDateTime::new(date, time);
match self.peek_token(){ match self.peek_token() {
Some(token) => match token{ Some(token) => match token {
Token::Plus | Token::Minus => { Token::Plus | Token::Minus => {
let tz = self.parse_timezone_offset()?; let tz = self.parse_timezone_offset()?;
let offset = FixedOffset::east(tz as i32 * 3600); let offset = FixedOffset::east(tz as i32 * 3600);
Ok(Value::Timestamp(DateTime::from_utc(date_time, offset))) Ok(Value::Timestamp(DateTime::from_utc(date_time, offset)))
} }
_ => Ok(Value::DateTime(date_time)), _ => Ok(Value::DateTime(date_time)),
}, },
_ => Ok(Value::DateTime(date_time)), _ => Ok(Value::DateTime(date_time)),
} }
}else{ } else {
parser_err!(format!("Expecting time after date, but found {:?}", self.peek_token())) parser_err!(format!(
"Expecting time after date, but found {:?}",
self.peek_token()
))
} }
} }
pub fn parse_date(&mut self, year: i64) -> Result<NaiveDate, ParserError> { pub fn parse_date(&mut self, year: i64) -> Result<NaiveDate, ParserError> {
if let Ok(true) = self.consume_token(&Token::Minus){ if let Ok(true) = self.consume_token(&Token::Minus) {
let month = self.parse_literal_int()?; let month = self.parse_literal_int()?;
if let Ok(true) = self.consume_token(&Token::Minus){ if let Ok(true) = self.consume_token(&Token::Minus) {
let day = self.parse_literal_int()?; let day = self.parse_literal_int()?;
let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32); let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32);
Ok(date) Ok(date)
}else{ } else {
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting `-` for date separator, found {:?}",
self.peek_token()
))
} }
}else{ } else {
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting `-` for date separator, found {:?}",
self.peek_token()
))
} }
} }
pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> { pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> {
@ -804,10 +828,15 @@ impl Parser {
self.consume_token(&Token::Colon)?; self.consume_token(&Token::Colon)?;
let sec = self.parse_literal_double()?; let sec = self.parse_literal_double()?;
let ms = (sec.fract() * 1000.0).round(); let ms = (sec.fract() * 1000.0).round();
if let Ok(true) = self.consume_token(&Token::Period){ if let Ok(true) = self.consume_token(&Token::Period) {
let ms = self.parse_literal_int()?; let ms = self.parse_literal_int()?;
Ok(NaiveTime::from_hms_milli(hour as u32, min as u32, sec as u32, ms as u32)) Ok(NaiveTime::from_hms_milli(
}else{ hour as u32,
min as u32,
sec as u32,
ms as u32,
))
} else {
Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32)) Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32))
} }
} }
@ -819,60 +848,78 @@ impl Parser {
"BOOLEAN" => Ok(SQLType::Boolean), "BOOLEAN" => Ok(SQLType::Boolean),
"FLOAT" => Ok(SQLType::Float(self.parse_optional_precision()?)), "FLOAT" => Ok(SQLType::Float(self.parse_optional_precision()?)),
"REAL" => Ok(SQLType::Real), "REAL" => Ok(SQLType::Real),
"DOUBLE" => if self.parse_keyword("PRECISION"){ "DOUBLE" => {
Ok(SQLType::Double) if self.parse_keyword("PRECISION") {
}else{ Ok(SQLType::Double)
Ok(SQLType::Double) } else {
Ok(SQLType::Double)
}
} }
"SMALLINT" => Ok(SQLType::SmallInt), "SMALLINT" => Ok(SQLType::SmallInt),
"INT" | "INTEGER" => Ok(SQLType::Int), "INT" | "INTEGER" => Ok(SQLType::Int),
"BIGINT" => Ok(SQLType::BigInt), "BIGINT" => Ok(SQLType::BigInt),
"VARCHAR" => Ok(SQLType::Varchar(self.parse_optional_precision()?)), "VARCHAR" => Ok(SQLType::Varchar(self.parse_optional_precision()?)),
"CHARACTER" => { "CHARACTER" => {
if self.parse_keyword("VARYING"){ if self.parse_keyword("VARYING") {
Ok(SQLType::Varchar(self.parse_optional_precision()?)) Ok(SQLType::Varchar(self.parse_optional_precision()?))
}else{ } else {
Ok(SQLType::Char(self.parse_optional_precision()?)) Ok(SQLType::Char(self.parse_optional_precision()?))
} }
} }
"UUID" => Ok(SQLType::Uuid), "UUID" => Ok(SQLType::Uuid),
"DATE" => Ok(SQLType::Date), "DATE" => Ok(SQLType::Date),
"TIMESTAMP" => if self.parse_keyword("WITH"){ "TIMESTAMP" => {
if self.parse_keywords(vec!["TIME","ZONE"]){ if self.parse_keyword("WITH") {
if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Timestamp)
} else {
parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
}
} else if self.parse_keyword("WITHOUT") {
if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Timestamp)
} else {
parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
}
} else {
Ok(SQLType::Timestamp) Ok(SQLType::Timestamp)
}else{
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token()))
} }
}else if self.parse_keyword("WITHOUT"){
if self.parse_keywords(vec!["TIME","ZONE"]){
Ok(SQLType::Timestamp)
}else{
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token()))
}
}else{
Ok(SQLType::Timestamp)
} }
"TIME" => if self.parse_keyword("WITH"){ "TIME" => {
if self.parse_keywords(vec!["TIME","ZONE"]){ if self.parse_keyword("WITH") {
Ok(SQLType::Time) if self.parse_keywords(vec!["TIME", "ZONE"]) {
}else{ Ok(SQLType::Time)
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) } else {
parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
}
} else if self.parse_keyword("WITHOUT") {
if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Time)
} else {
parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
}
} else {
Ok(SQLType::Timestamp)
} }
}else if self.parse_keyword("WITHOUT"){
if self.parse_keywords(vec!["TIME","ZONE"]){
Ok(SQLType::Time)
}else{
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token()))
}
}else{
Ok(SQLType::Timestamp)
} }
"REGCLASS" => Ok(SQLType::Regclass), "REGCLASS" => Ok(SQLType::Regclass),
"TEXT" => { "TEXT" => {
if let Ok(true) = self.consume_token(&Token::LBracket){ if let Ok(true) = self.consume_token(&Token::LBracket) {
self.consume_token(&Token::RBracket)?; self.consume_token(&Token::RBracket)?;
Ok(SQLType::Array(Box::new(SQLType::Text))) Ok(SQLType::Array(Box::new(SQLType::Text)))
}else{ } else {
Ok(SQLType::Text) Ok(SQLType::Text)
} }
} }
@ -886,8 +933,8 @@ impl Parser {
Some(Token::Identifier(id)) => { Some(Token::Identifier(id)) => {
if let Ok(true) = self.consume_token(&Token::Period) { if let Ok(true) = self.consume_token(&Token::Period) {
let ids = self.parse_tablename()?; let ids = self.parse_tablename()?;
Ok(SQLType::Custom(format!("{}.{}",id,ids))) Ok(SQLType::Custom(format!("{}.{}", id, ids)))
}else{ } else {
Ok(SQLType::Custom(id)) Ok(SQLType::Custom(id))
} }
} }
@ -895,25 +942,26 @@ impl Parser {
} }
} }
pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> { pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> {
let mut idents = vec![]; let mut idents = vec![];
let mut expect_identifier = true; let mut expect_identifier = true;
loop { loop {
let token = &self.next_token(); let token = &self.next_token();
match token{ match token {
Some(token) => match token{ Some(token) => match token {
Token::Identifier(s) => if expect_identifier{ Token::Identifier(s) => {
expect_identifier = false; if expect_identifier {
idents.push(s.to_string()); expect_identifier = false;
}else{ idents.push(s.to_string());
self.prev_token(); } else {
break; self.prev_token();
break;
}
} }
token if token == separator => { token if token == separator => {
if expect_identifier{ if expect_identifier {
return parser_err!(format!("Expecting identifier, found {:?}", token)); return parser_err!(format!("Expecting identifier, found {:?}", token));
}else{ } else {
expect_identifier = true; expect_identifier = true;
continue; continue;
} }
@ -922,7 +970,7 @@ impl Parser {
self.prev_token(); self.prev_token();
break; break;
} }
} },
None => { None => {
self.prev_token(); self.prev_token();
break; break;
@ -934,7 +982,7 @@ impl Parser {
pub fn parse_tablename(&mut self) -> Result<String, ParserError> { pub fn parse_tablename(&mut self) -> Result<String, ParserError> {
let identifier = self.parse_compound_identifier(&Token::Period)?; let identifier = self.parse_compound_identifier(&Token::Period)?;
match identifier{ match identifier {
ASTNode::SQLCompoundIdentifier(idents) => Ok(idents.join(".")), ASTNode::SQLCompoundIdentifier(idents) => Ok(idents.join(".")),
other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)), other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
} }
@ -942,7 +990,7 @@ impl Parser {
pub fn parse_column_names(&mut self) -> Result<Vec<String>, ParserError> { pub fn parse_column_names(&mut self) -> Result<Vec<String>, ParserError> {
let identifier = self.parse_compound_identifier(&Token::Comma)?; let identifier = self.parse_compound_identifier(&Token::Comma)?;
match identifier{ match identifier {
ASTNode::SQLCompoundIdentifier(idents) => Ok(idents), ASTNode::SQLCompoundIdentifier(idents) => Ok(idents),
other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)), other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
} }
@ -964,12 +1012,14 @@ impl Parser {
} }
} }
pub fn parse_optional_precision_scale(&mut self) -> Result<(usize, Option<usize>), ParserError> { pub fn parse_optional_precision_scale(
&mut self,
) -> Result<(usize, Option<usize>), ParserError> {
if self.consume_token(&Token::LParen)? { if self.consume_token(&Token::LParen)? {
let n = self.parse_literal_int()?; let n = self.parse_literal_int()?;
let scale = if let Ok(true) = self.consume_token(&Token::Comma){ let scale = if let Ok(true) = self.consume_token(&Token::Comma) {
Some(self.parse_literal_int()? as usize) Some(self.parse_literal_int()? as usize)
}else{ } else {
None None
}; };
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
@ -1070,18 +1120,22 @@ impl Parser {
pub fn parse_insert(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_insert(&mut self) -> Result<ASTNode, ParserError> {
self.parse_keyword("INTO"); self.parse_keyword("INTO");
let table_name = self.parse_tablename()?; let table_name = self.parse_tablename()?;
let columns = if self.consume_token(&Token::LParen)?{ let columns = if self.consume_token(&Token::LParen)? {
let column_names = self.parse_column_names()?; let column_names = self.parse_column_names()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
column_names column_names
}else{ } else {
vec![] vec![]
}; };
self.parse_keyword("VALUES"); self.parse_keyword("VALUES");
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
let values = self.parse_expr_list()?; let values = self.parse_expr_list()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
Ok(ASTNode::SQLInsert{table_name, columns, values: vec![values]}) Ok(ASTNode::SQLInsert {
table_name,
columns,
values: vec![values],
})
} }
/// Parse a comma-delimited list of SQL expressions /// Parse a comma-delimited list of SQL expressions
@ -1157,4 +1211,3 @@ impl Parser {
} }
} }
} }

View file

@ -71,7 +71,7 @@ pub enum Token {
RParen, RParen,
/// Period (used for compound identifiers or projections into nested types) /// Period (used for compound identifiers or projections into nested types)
Period, Period,
/// Colon `:` /// Colon `:`
Colon, Colon,
/// DoubleColon `::` (used for casting in postgresql) /// DoubleColon `::` (used for casting in postgresql)
DoubleColon, DoubleColon,
@ -91,16 +91,16 @@ pub enum Token {
RBrace, RBrace,
} }
impl ToString for Token{ impl ToString for Token {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
Token::Identifier(ref id) => id.to_string(), Token::Identifier(ref id) => id.to_string(),
Token::Keyword(ref k) =>k.to_string(), Token::Keyword(ref k) => k.to_string(),
Token::Number(ref n) => n.to_string(), Token::Number(ref n) => n.to_string(),
Token::String(ref s) => s.to_string(), Token::String(ref s) => s.to_string(),
Token::Char(ref c) => c.to_string(), Token::Char(ref c) => c.to_string(),
Token::SingleQuotedString(ref s) => format!("'{}'",s), Token::SingleQuotedString(ref s) => format!("'{}'", s),
Token::DoubleQuotedString(ref s) => format!("\"{}\"",s), Token::DoubleQuotedString(ref s) => format!("\"{}\"", s),
Token::Comma => ",".to_string(), Token::Comma => ",".to_string(),
Token::Whitespace(ws) => ws.to_string(), Token::Whitespace(ws) => ws.to_string(),
Token::Eq => "=".to_string(), Token::Eq => "=".to_string(),
@ -131,15 +131,15 @@ impl ToString for Token{
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Whitespace{ pub enum Whitespace {
Space, Space,
Newline, Newline,
Tab Tab,
} }
impl ToString for Whitespace{ impl ToString for Whitespace {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self{ match self {
Whitespace::Space => " ".to_string(), Whitespace::Space => " ".to_string(),
Whitespace::Newline => "\n".to_string(), Whitespace::Newline => "\n".to_string(),
Whitespace::Tab => "\t".to_string(), Whitespace::Tab => "\t".to_string(),
@ -349,10 +349,8 @@ impl<'a> Tokenizer<'a> {
match chars.peek() { match chars.peek() {
Some(&ch) => match ch { Some(&ch) => match ch {
// double colon // double colon
':' => { ':' => self.consume_and_return(chars, Token::DoubleColon),
self.consume_and_return(chars, Token::DoubleColon) _ => Ok(Some(Token::Colon)),
}
_ => Ok(Some(Token::Colon)),
}, },
None => Ok(Some(Token::Colon)), None => Ok(Some(Token::Colon)),
} }
@ -365,13 +363,17 @@ impl<'a> Tokenizer<'a> {
'&' => self.consume_and_return(chars, Token::Ampersand), '&' => self.consume_and_return(chars, Token::Ampersand),
'{' => self.consume_and_return(chars, Token::LBrace), '{' => self.consume_and_return(chars, Token::LBrace),
'}' => self.consume_and_return(chars, Token::RBrace), '}' => self.consume_and_return(chars, Token::RBrace),
other => self.consume_and_return(chars, Token::Char(other)) other => self.consume_and_return(chars, Token::Char(other)),
}, },
None => Ok(None), None => Ok(None),
} }
} }
fn consume_and_return(&self, chars: &mut Peekable<Chars>, t: Token) -> Result<Option<Token>, TokenizerError> { fn consume_and_return(
&self,
chars: &mut Peekable<Chars>,
t: Token,
) -> Result<Option<Token>, TokenizerError> {
chars.next(); chars.next();
Ok(Some(t)) Ok(Some(t))
} }
@ -486,16 +488,15 @@ mod tests {
let tokens = tokenizer.tokenize().unwrap(); let tokens = tokenizer.tokenize().unwrap();
println!("tokens: {:#?}", tokens); println!("tokens: {:#?}", tokens);
let expected = vec![ let expected = vec![
Token::Whitespace(Whitespace::Newline), Token::Whitespace(Whitespace::Newline),
Token::Char('م'), Token::Char('م'),
Token::Char('ص'), Token::Char('ص'),
Token::Char('ط'), Token::Char('ط'),
Token::Char('ف'), Token::Char('ف'),
Token::Char('ى'), Token::Char('ى'),
Token::Identifier("h".to_string()) Token::Identifier("h".to_string()),
]; ];
compare(expected, tokens); compare(expected, tokens);
} }
#[test] #[test]

View file

@ -1,22 +1,31 @@
extern crate log; extern crate log;
extern crate sqlparser; extern crate sqlparser;
use sqlparser::sqlast::*;
use sqlparser::sqltokenizer::*;
use sqlparser::sqlparser::*;
use sqlparser::dialect::PostgreSqlDialect; use sqlparser::dialect::PostgreSqlDialect;
use sqlparser::sqlast::*;
use sqlparser::sqlparser::*;
use sqlparser::sqltokenizer::*;
use log::*; use log::*;
#[test] #[test]
fn test_prev_index(){ fn test_prev_index() {
let sql: &str = "SELECT version()"; let sql: &str = "SELECT version()";
let mut parser = parser(sql); let mut parser = parser(sql);
assert_eq!(parser.prev_token(), None); assert_eq!(parser.prev_token(), None);
assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into()))); assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into())));
assert_eq!(parser.next_token(), Some(Token::Identifier("version".into()))); assert_eq!(
assert_eq!(parser.prev_token(), Some(Token::Identifier("version".into()))); parser.next_token(),
assert_eq!(parser.peek_token(), Some(Token::Identifier("version".into()))); Some(Token::Identifier("version".into()))
);
assert_eq!(
parser.prev_token(),
Some(Token::Identifier("version".into()))
);
assert_eq!(
parser.peek_token(),
Some(Token::Identifier("version".into()))
);
assert_eq!(parser.prev_token(), Some(Token::Keyword("SELECT".into()))); assert_eq!(parser.prev_token(), Some(Token::Keyword("SELECT".into())));
assert_eq!(parser.prev_token(), None); assert_eq!(parser.prev_token(), None);
} }
@ -30,7 +39,9 @@ fn parse_delete_statement() {
match parse_sql(&sql) { match parse_sql(&sql) {
ASTNode::SQLDelete { relation, .. } => { ASTNode::SQLDelete { relation, .. } => {
assert_eq!( assert_eq!(
Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
"table".to_string()
)))),
relation relation
); );
} }
@ -56,7 +67,9 @@ fn parse_where_delete_statement() {
.. ..
} => { } => {
assert_eq!( assert_eq!(
Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
"table".to_string()
)))),
relation relation
); );
@ -97,11 +110,21 @@ fn parse_simple_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "customer"); assert_eq!(table_name, "customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -114,11 +137,21 @@ fn parse_common_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "public.customer"); assert_eq!(table_name, "public.customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -131,11 +164,21 @@ fn parse_complex_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "db.public.customer"); assert_eq!(table_name, "db.public.customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -155,11 +198,24 @@ fn parse_insert_with_columns() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "public.customer"); assert_eq!(table_name, "public.customer");
assert_eq!(columns, vec!["id".to_string(), "name".to_string(), "active".to_string()]); assert_eq!(
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); columns,
vec!["id".to_string(), "name".to_string(), "active".to_string()]
);
assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -412,7 +468,8 @@ fn parse_create_table_with_defaults() {
activebool boolean DEFAULT true NOT NULL, activebool boolean DEFAULT true NOT NULL,
create_date date DEFAULT now()::text NOT NULL, create_date date DEFAULT now()::text NOT NULL,
last_update timestamp without time zone DEFAULT now() NOT NULL, last_update timestamp without time zone DEFAULT now() NOT NULL,
active integer NOT NULL)"); active integer NOT NULL)",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
match ast { match ast {
ASTNode::SQLCreateTable { name, columns } => { ASTNode::SQLCreateTable { name, columns } => {
@ -481,13 +538,15 @@ fn parse_create_table_from_pg_dump() {
#[test] #[test]
fn parse_create_table_with_inherit() { fn parse_create_table_with_inherit() {
let sql = String::from("\ let sql = String::from(
CREATE TABLE bazaar.settings (\ "\
settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \ CREATE TABLE bazaar.settings (\
user_id uuid UNIQUE, \ settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \
value text[], \ user_id uuid UNIQUE, \
use_metric boolean DEFAULT true\ value text[], \
)"); use_metric boolean DEFAULT true\
)",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
@ -513,15 +572,17 @@ fn parse_create_table_with_inherit() {
} }
#[test] #[test]
fn parse_alter_table_constraint_primary_key(){ fn parse_alter_table_constraint_primary_key() {
let sql = String::from("\ let sql = String::from(
ALTER TABLE bazaar.address \ "\
ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)"); ALTER TABLE bazaar.address \
ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
println!("ast: {:?}", ast); println!("ast: {:?}", ast);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLAlterTable{ name, operation } => { ASTNode::SQLAlterTable { name, operation } => {
assert_eq!(name, "bazaar.address"); assert_eq!(name, "bazaar.address");
} }
_ => assert!(false), _ => assert!(false),
@ -529,7 +590,7 @@ fn parse_alter_table_constraint_primary_key(){
} }
#[test] #[test]
fn parse_alter_table_constraint_foreign_key(){ fn parse_alter_table_constraint_foreign_key() {
let sql = String::from("\ let sql = String::from("\
ALTER TABLE public.customer \ ALTER TABLE public.customer \
ADD CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)"); ADD CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)");
@ -537,7 +598,7 @@ fn parse_alter_table_constraint_foreign_key(){
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
println!("ast: {:?}", ast); println!("ast: {:?}", ast);
match ast { match ast {
ASTNode::SQLAlterTable{ name, operation } => { ASTNode::SQLAlterTable { name, operation } => {
assert_eq!(name, "public.customer"); assert_eq!(name, "public.customer");
} }
_ => assert!(false), _ => assert!(false),
@ -545,7 +606,7 @@ fn parse_alter_table_constraint_foreign_key(){
} }
#[test] #[test]
fn parse_copy_example(){ fn parse_copy_example() {
let sql = String::from(r#"COPY public.actor (actor_id, first_name, last_name, last_update, value) FROM stdin; let sql = String::from(r#"COPY public.actor (actor_id, first_name, last_name, last_update, value) FROM stdin;
1 PENELOPE GUINESS 2006-02-15 09:34:33 0.11111 1 PENELOPE GUINESS 2006-02-15 09:34:33 0.11111
2 NICK WAHLBERG 2006-02-15 09:34:33 0.22222 2 NICK WAHLBERG 2006-02-15 09:34:33 0.22222
@ -572,20 +633,20 @@ PHP ₱ USD $
} }
#[test] #[test]
fn parse_timestamps_example(){ fn parse_timestamps_example() {
let sql = "2016-02-15 09:43:33"; let sql = "2016-02-15 09:43:33";
let ast = parse_sql(sql); let ast = parse_sql(sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
} }
#[test] #[test]
fn parse_timestamps_with_millis_example(){ fn parse_timestamps_with_millis_example() {
let sql = "2017-11-02 19:15:42.308637"; let sql = "2017-11-02 19:15:42.308637";
let ast = parse_sql(sql); let ast = parse_sql(sql);
} }
#[test] #[test]
fn parse_example_value(){ fn parse_example_value() {
let sql = "SARAH.LEWIS@sakilacustomer.org"; let sql = "SARAH.LEWIS@sakilacustomer.org";
let ast = parse_sql(sql); let ast = parse_sql(sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
@ -623,7 +684,10 @@ fn parse_literal_string() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLSelect { ref projection, .. } => { ASTNode::SQLSelect { ref projection, .. } => {
assert_eq!(projection[0], ASTNode::SQLValue(Value::SingleQuotedString("one".to_string()))); assert_eq!(
projection[0],
ASTNode::SQLValue(Value::SingleQuotedString("one".to_string()))
);
} }
_ => panic!(), _ => panic!(),
} }
@ -634,7 +698,7 @@ fn parse_select_version() {
let sql = "SELECT @@version"; let sql = "SELECT @@version";
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast{ match ast {
ASTNode::SQLSelect { ref projection, .. } => { ASTNode::SQLSelect { ref projection, .. } => {
assert_eq!( assert_eq!(
projection[0], projection[0],
@ -646,7 +710,7 @@ fn parse_select_version() {
} }
#[test] #[test]
fn parse_function_now(){ fn parse_function_now() {
let sql = "now()"; let sql = "now()";
let ast = parse_sql(sql); let ast = parse_sql(sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
@ -666,4 +730,3 @@ fn parser(sql: &str) -> Parser {
debug!("tokens: {:#?}", tokens); debug!("tokens: {:#?}", tokens);
Parser::new(tokens) Parser::new(tokens)
} }