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
fn is_identifier_part(&self, ch: char) -> bool;
}

View file

@ -6,67 +6,12 @@ pub struct GenericSqlDialect {}
impl Dialect for GenericSqlDialect {
fn keywords(&self) -> Vec<&'static str> {
return vec![
SELECT,
FROM,
WHERE,
LIMIT,
ORDER,
GROUP,
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,
SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, 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 == '_'
}
}

View file

@ -7,83 +7,13 @@ pub struct PostgreSqlDialect {}
impl Dialect for PostgreSqlDialect {
fn keywords(&self) -> Vec<&'static str> {
return vec![
ALTER,
ONLY,
SELECT,
FROM,
WHERE,
LIMIT,
ORDER,
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,
ALTER, ONLY, SELECT, FROM, WHERE, LIMIT, ORDER, 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
//!
use chrono::{NaiveDate,
NaiveDateTime,
NaiveTime,
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
use chrono::{
offset::{FixedOffset, TimeZone},
DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
};
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;
mod value;
mod sql_operator;
mod sqltype;
mod table_key;
mod sql_operator;
mod value;
/// SQL Abstract Syntax Tree (AST)
#[derive(Debug, Clone, PartialEq)]
@ -103,7 +94,7 @@ pub enum ASTNode {
/// VALUES (vector of rows to insert)
values: Vec<Vec<ASTNode>>,
},
SQLCopy{
SQLCopy {
/// TABLE
table_name: String,
/// COLUMNS
@ -139,33 +130,43 @@ pub enum ASTNode {
/// Table name
name: String,
operation: AlterOperation,
}
},
}
impl ToString for ASTNode{
impl ToString for ASTNode {
fn to_string(&self) -> String {
match self{
match self {
ASTNode::SQLIdentifier(s) => s.to_string(),
ASTNode::SQLWildcard => "*".to_string(),
ASTNode::SQLCompoundIdentifier(s) => s.join("."),
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::SQLBinaryExpr{left, op, right} => {
format!("{} {} {}", left.as_ref().to_string(), op.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::SQLBinaryExpr { left, op, right } => format!(
"{} {} {}",
left.as_ref().to_string(),
op.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::SQLUnary {operator, rex } => {
ASTNode::SQLUnary { operator, rex } => {
format!("{} {}", operator.to_string(), rex.as_ref().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::SQLSelect{
ASTNode::SQLFunction { id, args } => format!(
"{}({})",
id,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLSelect {
projection,
relation,
selection,
@ -174,82 +175,147 @@ impl ToString for ASTNode{
having,
limit,
} => {
let mut s = format!("SELECT {}", projection.iter().map(|p|p.to_string()).collect::<Vec<String>>().join(", "));
if let Some(relation) = relation{
let mut s = format!(
"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());
}
if let Some(selection) = selection{
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string());
}
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());
}
if let Some(order_by) = order_by{
s += &format!(" ORDER BY {}", order_by.iter().map(|o|o.to_string()).collect::<Vec<String>>().join(", "));
if let Some(order_by) = order_by {
s += &format!(
" ORDER BY {}",
order_by
.iter()
.map(|o| o.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
if let Some(limit) = limit {
s += &format!(" LIMIT {}", limit.as_ref().to_string());
}
s
}
ASTNode::SQLInsert{ table_name, columns, values } => {
ASTNode::SQLInsert {
table_name,
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name);
if columns.len() > 0 {
s += &format!(" ({})", columns.join(", "));
}
if values.len() > 0 {
s += &format!(" VALUES({})",
values.iter()
.map(|row|row.iter().map(|c|c.to_string())
.collect::<Vec<String>>().join(", ")
).collect::<Vec<String>>().join(", ")
);
s += &format!(
" VALUES({})",
values
.iter()
.map(|row| row
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "))
.collect::<Vec<String>>()
.join(", ")
);
}
s
}
ASTNode::SQLCopy{table_name, columns, values} => {
ASTNode::SQLCopy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name);
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; ";
if values.len() > 0 {
s += &format!("\n{}",
values.iter()
.map(|v|v.to_string())
.collect::<Vec<String>>().join("\t")
);
s += &format!(
"\n{}",
values
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join("\t")
);
}
s += "\n\\.";
s
}
ASTNode::SQLUpdate{table_name, assignments, selection} => {
ASTNode::SQLUpdate {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name);
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
}
ASTNode::SQLDelete{relation, selection} => {
ASTNode::SQLDelete {
relation,
selection,
} => {
let mut s = String::from("DELETE");
if let Some(relation) = relation{
if let Some(relation) = relation {
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
}
ASTNode::SQLCreateTable{name, columns} => {
format!("CREATE TABLE {} ({})", name, columns.iter().map(|c|c.to_string()).collect::<Vec<String>>().join(", "))
}
ASTNode::SQLAlterTable{name, operation} => {
ASTNode::SQLCreateTable { name, columns } => format!(
"CREATE TABLE {} ({})",
name,
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name, operation.to_string())
}
}
@ -264,8 +330,7 @@ pub struct SQLAssignment {
value: Box<ASTNode>,
}
impl ToString for SQLAssignment{
impl ToString for SQLAssignment {
fn to_string(&self) -> 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 {
if self.asc{
if self.asc {
format!("{} ASC", self.expr.as_ref().to_string())
}else{
} else {
format!("{} DESC", self.expr.as_ref().to_string())
}
}
@ -305,24 +370,21 @@ pub struct SQLColumnDef {
pub allow_null: bool,
}
impl ToString for SQLColumnDef{
impl ToString for SQLColumnDef {
fn to_string(&self) -> String {
let mut s = format!("{} {}", self.name, self.data_type.to_string());
if self.is_primary{
if self.is_primary {
s += " PRIMARY KEY";
}
if self.is_unique{
if self.is_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());
}
if !self.allow_null{
if !self.allow_null {
s += " NOT NULL";
}
s
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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