mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-10 01:08:02 +00:00
ran cargo fmt
This commit is contained in:
parent
9daba3ab49
commit
722ea7a91b
11 changed files with 662 additions and 615 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 == '_'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
220
src/sqlast.rs
220
src/sqlast.rs
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(", ")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
581
src/sqlparser.rs
581
src/sqlparser.rs
|
@ -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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue