use std::collections::HashMap;
use super::ast::*;
use super::parser::Parser;
use super::precedence::Precedence;
use super::token::{Token, TokenType};
impl Parser {
pub fn parse_statement(&mut self) -> Option<Statement> {
while self.cur_token_is(TokenType::Comment) {
self.next_token();
}
if self.cur_token_is(TokenType::Eof) {
return None;
}
if self.cur_token_is(TokenType::Keyword) {
let keyword = self.cur_token.literal.to_uppercase();
match keyword.as_str() {
"SELECT" => self.parse_select_statement().map(Statement::Select),
"FROM" => self.parse_from_first_statement().map(Statement::Select),
"WITH" => self.parse_with_statement(),
"INSERT" => self.parse_insert_statement().map(Statement::Insert),
"UPDATE" => self.parse_update_statement().map(Statement::Update),
"DELETE" => self.parse_delete_statement().map(Statement::Delete),
"TRUNCATE" => self.parse_truncate_statement().map(Statement::Truncate),
"CREATE" => self.parse_create_statement(),
"DROP" => self.parse_drop_statement(),
"CALL" => self.parse_call_statement().map(Statement::Call),
"ALTER" => self.parse_alter_statement(),
"USE" => self.parse_use_statement().map(Statement::UseSchema),
"BEGIN" => self.parse_begin_statement().map(Statement::Begin),
"COMMIT" => self.parse_commit_statement().map(Statement::Commit),
"ROLLBACK" => self.parse_rollback_statement().map(Statement::Rollback),
"SAVEPOINT" => self.parse_savepoint_statement().map(Statement::Savepoint),
"SET" => self.parse_set_statement().map(Statement::Set),
"PRAGMA" => self.parse_pragma_statement().map(Statement::Pragma),
"SHOW" => self.parse_show_statement(),
"DESCRIBE" | "DESC" => self.parse_describe_statement().map(Statement::Describe),
"EXPLAIN" => self.parse_explain_statement().map(Statement::Explain),
"ANALYZE" => self.parse_analyze_statement().map(Statement::Analyze),
"COPY" => self.parse_copy_statement().map(Statement::Copy),
_ => {
self.parse_expression_statement().map(Statement::Expression)
}
}
} else {
self.parse_expression_statement().map(Statement::Expression)
}
}
pub fn parse_select_statement(&mut self) -> Option<SelectStatement> {
let token = self.cur_token.clone();
let mut stmt = SelectStatement {
token,
distinct: false,
columns: Vec::new(),
with: None,
table_expr: None,
where_clause: None,
group_by: GroupByClause::default(),
having: None,
window_defs: Vec::new(),
order_by: Vec::new(),
limit: None,
offset: None,
set_operations: Vec::new(),
};
if self.peek_token_is_keyword("DISTINCT") {
self.next_token();
stmt.distinct = true;
}
self.next_token();
stmt.columns = self.parse_select_columns();
if self.peek_token_is_keyword("FROM") {
self.next_token(); self.next_token(); stmt.table_expr = Some(Box::new(self.parse_table_expression()?));
}
if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
stmt.where_clause = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
if self.peek_token_is_keyword("GROUP") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.current_clause = "GROUP BY".to_string();
stmt.group_by = self.parse_group_by_clause();
}
if self.peek_token_is_keyword("HAVING") {
self.next_token(); self.current_clause = "HAVING".to_string();
self.next_token();
stmt.having = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
if self.peek_token_is_keyword("WINDOW") {
self.next_token(); self.current_clause = "WINDOW".to_string();
stmt.window_defs = self.parse_window_definitions();
}
while self.peek_token_is_keyword("UNION")
|| self.peek_token_is_keyword("INTERSECT")
|| self.peek_token_is_keyword("EXCEPT")
{
if let Some(set_op) = self.parse_set_operation() {
stmt.set_operations.push(set_op);
} else {
break;
}
}
if self.peek_token_is_keyword("ORDER") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.current_clause = "ORDER BY".to_string();
stmt.order_by = self.parse_order_by_expressions();
}
if self.peek_token_is_keyword("LIMIT") {
self.next_token(); self.current_clause = "LIMIT".to_string();
self.next_token();
stmt.limit = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
if self.peek_token_is_keyword("OFFSET") {
self.next_token(); self.current_clause = "OFFSET".to_string();
self.next_token();
stmt.offset = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("ROW") {
self.next_token();
}
}
if self.peek_token_is_keyword("FETCH") {
self.next_token();
if !self.peek_token_is_keyword("FIRST") && !self.peek_token_is_keyword("NEXT") {
self.add_error(format!(
"expected FIRST or NEXT after FETCH at {}",
self.peek_token.position
));
return None;
}
self.next_token();
self.current_clause = "FETCH".to_string();
self.next_token();
stmt.limit = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("ROW") {
self.next_token();
}
if self.peek_token_is_keyword("ONLY") {
self.next_token();
}
}
self.current_clause.clear();
Some(stmt)
}
pub fn parse_from_first_statement(&mut self) -> Option<SelectStatement> {
let token = self.cur_token.clone();
let mut stmt = SelectStatement {
token,
distinct: false,
columns: Vec::new(),
with: None,
table_expr: None,
where_clause: None,
group_by: GroupByClause::default(),
having: None,
window_defs: Vec::new(),
order_by: Vec::new(),
limit: None,
offset: None,
set_operations: Vec::new(),
};
self.next_token(); stmt.table_expr = Some(Box::new(self.parse_table_expression()?));
loop {
if !self.peek_token_is(TokenType::Keyword) {
break;
}
let keyword = self.peek_token.literal.to_uppercase();
match keyword.as_str() {
"SELECT" => {
self.next_token();
if self.peek_token_is_keyword("DISTINCT") {
self.next_token();
stmt.distinct = true;
}
self.next_token();
stmt.columns = self.parse_select_columns();
}
"WHERE" => {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
stmt.where_clause = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
"GROUP" => {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.current_clause = "GROUP BY".to_string();
stmt.group_by = self.parse_group_by_clause();
}
"HAVING" => {
self.next_token(); self.current_clause = "HAVING".to_string();
self.next_token();
stmt.having = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
"WINDOW" => {
self.next_token(); self.current_clause = "WINDOW".to_string();
stmt.window_defs = self.parse_window_definitions();
}
"ORDER" => {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.current_clause = "ORDER BY".to_string();
stmt.order_by = self.parse_order_by_expressions();
}
"LIMIT" => {
self.next_token(); self.current_clause = "LIMIT".to_string();
self.next_token();
stmt.limit = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
"OFFSET" => {
self.next_token(); self.current_clause = "OFFSET".to_string();
self.next_token();
stmt.offset = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("ROW") {
self.next_token();
}
}
"FETCH" => {
self.next_token();
if !self.peek_token_is_keyword("FIRST") && !self.peek_token_is_keyword("NEXT") {
self.add_error(format!(
"expected FIRST or NEXT after FETCH at {}",
self.peek_token.position
));
return None;
}
self.next_token();
self.current_clause = "FETCH".to_string();
self.next_token();
stmt.limit = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("ROW") {
self.next_token();
}
if self.peek_token_is_keyword("ONLY") {
self.next_token();
}
}
"UNION" | "INTERSECT" | "EXCEPT" => {
if let Some(set_op) = self.parse_set_operation() {
stmt.set_operations.push(set_op);
} else {
break;
}
}
_ => {
break;
}
}
}
if stmt.columns.is_empty() {
stmt.columns.push(Expression::Star(StarExpression {
token: Token::new(TokenType::Operator, "*", stmt.token.position),
}));
}
self.current_clause.clear();
Some(stmt)
}
fn parse_set_operation(&mut self) -> Option<SetOperation> {
self.next_token();
let keyword = self.cur_token.literal.to_uppercase();
let is_union = keyword == "UNION";
let operation = if keyword == "UNION" {
if self.peek_token_is_keyword("ALL") {
self.next_token();
SetOperationType::UnionAll
} else {
SetOperationType::Union
}
} else if keyword == "INTERSECT" {
if self.peek_token_is_keyword("ALL") {
self.next_token();
SetOperationType::IntersectAll
} else {
SetOperationType::Intersect
}
} else if keyword == "EXCEPT" {
if self.peek_token_is_keyword("ALL") {
self.next_token();
SetOperationType::ExceptAll
} else {
SetOperationType::Except
}
} else {
return None;
};
if !self.expect_keyword("SELECT") {
return None;
}
let mut right = self.parse_simple_select()?;
if is_union {
while self.peek_token_is_keyword("INTERSECT") || self.peek_token_is_keyword("EXCEPT") {
if let Some(set_op) = self.parse_set_operation() {
right.set_operations.push(set_op);
} else {
break;
}
}
}
Some(SetOperation {
operation,
right: Box::new(right),
})
}
fn parse_simple_select(&mut self) -> Option<SelectStatement> {
let token = self.cur_token.clone();
let mut stmt = SelectStatement {
token,
distinct: false,
columns: Vec::new(),
with: None,
table_expr: None,
where_clause: None,
group_by: GroupByClause::default(),
having: None,
window_defs: Vec::new(),
order_by: Vec::new(),
limit: None,
offset: None,
set_operations: Vec::new(),
};
if self.peek_token_is_keyword("DISTINCT") {
self.next_token();
stmt.distinct = true;
}
self.next_token();
stmt.columns = self.parse_select_columns();
if self.peek_token_is_keyword("FROM") {
self.next_token(); self.next_token(); stmt.table_expr = Some(Box::new(self.parse_table_expression()?));
}
if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
stmt.where_clause = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
if self.peek_token_is_keyword("GROUP") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.current_clause = "GROUP BY".to_string();
stmt.group_by = self.parse_group_by_clause();
}
if self.peek_token_is_keyword("HAVING") {
self.next_token(); self.current_clause = "HAVING".to_string();
self.next_token();
stmt.having = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
self.current_clause.clear();
Some(stmt)
}
fn parse_select_columns(&mut self) -> Vec<Expression> {
let mut columns = Vec::new();
if let Some(col) = self.parse_select_column() {
columns.push(col);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token(); if let Some(col) = self.parse_select_column() {
columns.push(col);
}
}
columns
}
fn parse_select_column(&mut self) -> Option<Expression> {
if self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "*" {
return Some(Expression::Star(StarExpression {
token: self.cur_token.clone(),
}));
}
let expr = self.parse_expression(Precedence::Lowest)?;
if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token();
return Some(Expression::Aliased(AliasedExpression {
token: self.cur_token.clone(),
expression: Box::new(expr),
alias: Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone()),
}));
}
if self.peek_token_is(TokenType::Identifier) {
let alias_candidate = self.peek_token.literal.to_uppercase();
let reserved = [
"FROM",
"WHERE",
"GROUP",
"HAVING",
"ORDER",
"LIMIT",
"OFFSET",
"UNION",
"INTERSECT",
"EXCEPT",
"INTO",
"FOR",
"WINDOW",
"FETCH",
"ON",
"USING",
"NATURAL",
"LEFT",
"RIGHT",
"INNER",
"OUTER",
"CROSS",
"FULL",
"JOIN",
];
if !reserved.contains(&alias_candidate.as_str()) {
self.next_token();
return Some(Expression::Aliased(AliasedExpression {
token: self.cur_token.clone(),
expression: Box::new(expr),
alias: Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone()),
}));
}
}
Some(expr)
}
fn parse_table_expression(&mut self) -> Option<Expression> {
let left = self.parse_simple_table_expression()?;
self.parse_join_table_expression(left)
}
fn parse_simple_table_expression(&mut self) -> Option<Expression> {
if self.cur_token_is_punctuator("(") {
self.next_token();
if self.cur_token_is_keyword("SELECT") {
let subquery = self.parse_select_statement()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after subquery at {}",
self.cur_token.position
));
return None;
}
let mut alias = None;
if self.peek_token_is_keyword("AS") {
self.next_token();
self.next_token();
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
} else if self.peek_token_is(TokenType::Identifier) {
self.next_token();
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
}
return Some(Expression::SubquerySource(SubqueryTableSource {
token: self.cur_token.clone(),
subquery: Box::new(subquery),
alias,
}));
} else if self.cur_token_is_keyword("VALUES") {
return self.parse_values_table_source();
}
}
if self.cur_token_is(TokenType::Identifier) && self.peek_token_is_punctuator("(") {
let token = self.cur_token.clone();
let function_name = Identifier::new(token.clone(), self.cur_token.literal.clone());
return self.parse_function_table_source(token, function_name);
}
if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
self.add_error(format!(
"expected table name at {}",
self.cur_token.position
));
return None;
}
let token = self.cur_token.clone();
let first_ident = Identifier::new(token.clone(), self.cur_token.literal.clone());
let table_name = if self.peek_token_is_punctuator(".") {
self.next_token(); if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token(); let second_ident =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
TableName::Qualified(QualifiedIdentifier {
token: first_ident.token.clone(),
qualifier: Box::new(first_ident),
name: Box::new(second_ident),
})
} else {
TableName::Simple(first_ident)
};
let as_of = if self.peek_token_is_keyword("AS") {
self.next_token(); if self.peek_token_is_keyword("OF") {
self.next_token(); self.next_token();
let as_of_type = self.cur_token.literal.to_uppercase();
if as_of_type != "TRANSACTION" && as_of_type != "TIMESTAMP" {
self.add_error(format!(
"expected TRANSACTION or TIMESTAMP after AS OF at {}",
self.cur_token.position
));
return None;
}
self.next_token();
let value = self.parse_expression(Precedence::Lowest)?;
Some(AsOfClause {
token: self.cur_token.clone(),
as_of_type,
value: Box::new(value),
})
} else {
None
}
} else {
None
};
let mut alias = None;
if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
return None;
}
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
} else if self.peek_token_is(TokenType::Identifier) && as_of.is_none() {
let peek_upper = self.peek_token.literal.to_uppercase();
if !matches!(
peek_upper.as_str(),
"JOIN"
| "LEFT"
| "RIGHT"
| "INNER"
| "OUTER"
| "CROSS"
| "NATURAL"
| "ON"
| "WHERE"
| "GROUP"
| "HAVING"
| "ORDER"
| "LIMIT"
| "OFFSET"
| "FETCH"
| "UNION"
| "INTERSECT"
| "EXCEPT"
) {
self.next_token();
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
}
}
Some(Expression::TableSource(SimpleTableSource {
token,
name: table_name,
alias,
as_of,
}))
}
fn parse_join_table_expression(&mut self, mut left: Expression) -> Option<Expression> {
loop {
let join_type = if self.peek_token_is_keyword("JOIN") {
self.next_token();
"INNER".to_string()
} else if self.peek_token_is_keyword("INNER") {
self.next_token();
if !self.expect_keyword("JOIN") {
return None;
}
"INNER".to_string()
} else if self.peek_token_is_keyword("LEFT") {
self.next_token();
if self.peek_token_is_keyword("OUTER") {
self.next_token();
}
if !self.expect_keyword("JOIN") {
return None;
}
"LEFT".to_string()
} else if self.peek_token_is_keyword("RIGHT") {
self.next_token();
if self.peek_token_is_keyword("OUTER") {
self.next_token();
}
if !self.expect_keyword("JOIN") {
return None;
}
"RIGHT".to_string()
} else if self.peek_token_is_keyword("FULL") {
self.next_token();
if self.peek_token_is_keyword("OUTER") {
self.next_token();
}
if !self.expect_keyword("JOIN") {
return None;
}
"FULL".to_string()
} else if self.peek_token_is_keyword("CROSS") {
self.next_token();
if !self.expect_keyword("JOIN") {
return None;
}
"CROSS".to_string()
} else if self.peek_token_is_keyword("NATURAL") {
self.next_token();
let natural_type = if self.peek_token_is_keyword("LEFT") {
self.next_token();
if self.peek_token_is_keyword("OUTER") {
self.next_token();
}
"NATURAL LEFT"
} else if self.peek_token_is_keyword("RIGHT") {
self.next_token();
if self.peek_token_is_keyword("OUTER") {
self.next_token();
}
"NATURAL RIGHT"
} else {
"NATURAL"
};
if !self.expect_keyword("JOIN") {
return None;
}
natural_type.to_string()
} else if self.peek_token_is_punctuator(",") {
self.next_token(); "CROSS".to_string()
} else {
break;
};
let token = self.cur_token.clone();
self.next_token();
let right = self.parse_simple_table_expression()?;
let mut condition = None;
let mut using_columns = Vec::new();
if !join_type.starts_with("CROSS") && !join_type.starts_with("NATURAL") {
if self.peek_token_is_keyword("ON") {
self.next_token(); self.next_token();
condition = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
} else if self.peek_token_is_keyword("USING") {
self.next_token(); if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after USING at {}",
self.cur_token.position
));
return None;
}
using_columns = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after USING columns at {}",
self.cur_token.position
));
return None;
}
}
}
left = Expression::JoinSource(Box::new(JoinTableSource {
token,
left: Box::new(left),
join_type,
right: Box::new(right),
condition,
using_columns,
}));
}
Some(left)
}
fn parse_with_statement(&mut self) -> Option<Statement> {
let with_clause = self.parse_with_clause()?;
self.next_token();
if self.cur_token_is_keyword("SELECT") {
let mut select = self.parse_select_statement()?;
select.with = Some(with_clause);
Some(Statement::Select(select))
} else if self.cur_token_is_keyword("INSERT") {
let mut insert = self.parse_insert_statement()?;
if let Some(ref mut select) = insert.select {
select.with = Some(with_clause);
} else {
self.add_error(
"WITH clause requires INSERT ... SELECT, not INSERT ... VALUES".to_string(),
);
return None;
}
Some(Statement::Insert(insert))
} else {
self.add_error(format!(
"expected SELECT or INSERT after WITH clause at {}",
self.cur_token.position
));
None
}
}
fn parse_with_clause(&mut self) -> Option<WithClause> {
let token = self.cur_token.clone();
let mut is_recursive = false;
if self.peek_token_is_keyword("RECURSIVE") {
self.next_token();
is_recursive = true;
}
let mut ctes = Vec::new();
self.next_token();
if let Some(cte) = self.parse_common_table_expression(is_recursive) {
ctes.push(cte);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token(); if let Some(cte) = self.parse_common_table_expression(is_recursive) {
ctes.push(cte);
}
}
Some(WithClause {
token,
ctes,
is_recursive,
})
}
fn parse_common_table_expression(
&mut self,
is_recursive: bool,
) -> Option<CommonTableExpression> {
if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
self.add_error(format!("expected CTE name at {}", self.cur_token.position));
return None;
}
let token = self.cur_token.clone();
let name = Identifier::new(token.clone(), self.cur_token.literal.clone());
let mut column_names = Vec::new();
if self.peek_token_is_punctuator("(") {
self.next_token(); column_names = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after column list at {}",
self.cur_token.position
));
return None;
}
}
if !self.expect_keyword("AS") {
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after AS at {}",
self.cur_token.position
));
return None;
}
self.next_token();
if !self.cur_token_is_keyword("SELECT") {
self.add_error(format!(
"expected SELECT in CTE at {}",
self.cur_token.position
));
return None;
}
let query = self.parse_select_statement()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after CTE query at {}",
self.cur_token.position
));
return None;
}
Some(CommonTableExpression {
token,
name,
column_names,
query: Box::new(query),
is_recursive,
})
}
fn parse_insert_statement(&mut self) -> Option<InsertStatement> {
let token = self.cur_token.clone();
if !self.expect_keyword("INTO") {
return None;
}
let table_name = self.parse_table_name()?;
let mut columns = Vec::new();
if self.peek_token_is_punctuator("(") {
self.next_token(); columns = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
}
if self.peek_token_is_keyword("SELECT") {
self.next_token(); let select_stmt = self.parse_select_statement()?;
let returning = self.parse_returning_clause();
return Some(InsertStatement {
token,
table_name,
columns,
values: Vec::new(),
select: Some(Box::new(select_stmt)),
on_duplicate: false,
update_columns: Vec::new(),
update_expressions: Vec::new(),
returning,
});
}
if self.peek_token_is_keyword("WITH") {
self.next_token(); let with_clause = self.parse_with_clause()?;
if !self.expect_keyword("SELECT") {
return None;
}
let mut select_stmt = self.parse_select_statement()?;
select_stmt.with = Some(with_clause);
let returning = self.parse_returning_clause();
return Some(InsertStatement {
token,
table_name,
columns,
values: Vec::new(),
select: Some(Box::new(select_stmt)),
on_duplicate: false,
update_columns: Vec::new(),
update_expressions: Vec::new(),
returning,
});
}
if !self.expect_keyword("VALUES") {
return None;
}
let values = self.parse_value_lists()?;
let mut on_duplicate = false;
let mut update_columns = Vec::new();
let mut update_expressions = Vec::new();
if self.peek_token_is_keyword("ON") {
self.next_token(); if !self.expect_keyword("DUPLICATE") {
return None;
}
if !self.expect_keyword("KEY") {
return None;
}
if !self.expect_keyword("UPDATE") {
return None;
}
on_duplicate = true;
loop {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let col = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
update_columns.push(col);
if !self.expect_peek(TokenType::Operator) || self.cur_token.literal != "=" {
self.add_error(format!("expected '=' at {}", self.cur_token.position));
return None;
}
self.next_token();
let expr = self.parse_expression(Precedence::Lowest)?;
update_expressions.push(expr);
if !self.peek_token_is_punctuator(",") {
break;
}
self.next_token(); }
}
let returning = self.parse_returning_clause();
Some(InsertStatement {
token,
table_name,
columns,
values,
select: None,
on_duplicate,
update_columns,
update_expressions,
returning,
})
}
fn parse_returning_clause(&mut self) -> Vec<Expression> {
if !self.peek_token_is_keyword("RETURNING") {
return Vec::new();
}
self.next_token();
self.parse_expression_list()
}
fn parse_value_lists(&mut self) -> Option<Vec<Vec<Expression>>> {
let mut value_lists = Vec::new();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let values = self.parse_expression_list();
value_lists.push(values);
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
while self.peek_token_is_punctuator(",") {
self.next_token();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let values = self.parse_expression_list();
value_lists.push(values);
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
}
Some(value_lists)
}
fn parse_function_table_source(
&mut self,
token: Token,
name: Identifier,
) -> Option<Expression> {
self.next_token();
let mut arguments = Vec::new();
if !self.peek_token_is_punctuator(")") {
arguments = self.parse_expression_list(); } else {
self.next_token(); }
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after function arguments, got {:?} at {}",
self.cur_token.token_type, self.cur_token.position
));
return None;
}
if !self.cur_token_is(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after function arguments, got {:?} at {}",
self.cur_token.token_type, self.cur_token.position
));
return None;
}
let mut alias = None;
let mut column_aliases = Vec::new();
if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
return None;
}
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
} else if self.peek_token_is(TokenType::Identifier) {
self.next_token(); alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
}
if alias.is_some() && self.peek_token_is_punctuator("(") {
self.next_token(); self.next_token();
while !self.cur_token_is(TokenType::Punctuator) || self.cur_token.literal != ")" {
if !self.cur_token_is(TokenType::Identifier) {
self.add_error(format!(
"expected identifier for column alias, got {:?} at {}",
self.cur_token.token_type, self.cur_token.position
));
return None;
}
column_aliases.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
self.next_token();
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == "," {
self.next_token();
}
}
}
Some(Expression::FunctionTableSource(FunctionTableSource {
token,
function: name,
arguments,
alias,
column_aliases,
}))
}
fn parse_values_table_source(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
let rows = self.parse_value_lists()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after VALUES at {}",
self.cur_token.position
));
return None;
}
let mut alias = None;
let mut column_aliases = Vec::new();
if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
self.add_error(format!(
"expected alias after AS at {}",
self.cur_token.position
));
return None;
}
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
if self.peek_token_is_punctuator("(") {
self.next_token(); column_aliases = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after column aliases at {}",
self.cur_token.position
));
return None;
}
}
} else if self.peek_token_is(TokenType::Identifier) {
self.next_token();
alias = Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
if self.peek_token_is_punctuator("(") {
self.next_token(); column_aliases = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after column aliases at {}",
self.cur_token.position
));
return None;
}
}
}
Some(Expression::ValuesSource(ValuesTableSource {
token,
rows,
alias,
column_aliases,
}))
}
fn parse_update_statement(&mut self) -> Option<UpdateStatement> {
let token = self.cur_token.clone();
let table_name = self.parse_table_name()?;
if !self.expect_keyword("SET") {
return None;
}
let mut updates = HashMap::new();
loop {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let column_name = self.cur_token.literal.clone();
if !self.expect_peek(TokenType::Operator) || self.cur_token.literal != "=" {
self.add_error(format!("expected '=' at {}", self.cur_token.position));
return None;
}
self.next_token();
let value_expr = self.parse_expression(Precedence::Lowest)?;
updates.insert(column_name, value_expr);
if !self.peek_token_is_punctuator(",") {
break;
}
self.next_token(); }
let where_clause = if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
Some(Box::new(self.parse_expression(Precedence::Lowest)?))
} else {
None
};
self.current_clause.clear();
let returning = self.parse_returning_clause();
Some(UpdateStatement {
token,
table_name,
updates,
where_clause,
returning,
})
}
fn parse_delete_statement(&mut self) -> Option<DeleteStatement> {
let token = self.cur_token.clone();
if !self.expect_keyword("FROM") {
return None;
}
let table_name = self.parse_table_name()?;
let alias = if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
return None;
}
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else if self.peek_token_is(TokenType::Identifier)
&& !self.peek_token_is_keyword("WHERE")
&& !self.peek_token_is_keyword("RETURNING")
{
self.next_token();
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else {
None
};
let where_clause = if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
Some(Box::new(self.parse_expression(Precedence::Lowest)?))
} else {
None
};
self.current_clause.clear();
let returning = self.parse_returning_clause();
Some(DeleteStatement {
token,
table_name,
alias,
where_clause,
returning,
})
}
fn parse_truncate_statement(&mut self) -> Option<TruncateStatement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TABLE") {
self.next_token(); }
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let table_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(TruncateStatement { token, table_name })
}
fn parse_create_statement(&mut self) -> Option<Statement> {
let or_replace = if self.peek_token_is_keyword("OR") {
self.next_token();
if self.expect_keyword("REPLACE") {
true
} else {
return None;
}
} else {
false
};
if self.peek_token_is_keyword("PROCEDURE") {
self.next_token();
return self
.parse_create_procedure_statement(or_replace)
.map(Statement::CreateProcedure);
} else if or_replace {
self.add_error(format!(
"OR REPLACE is only supported for PROCEDURE at {}",
self.cur_token.position
));
return None;
}
if self.peek_token_is_keyword("TRIGGER") {
self.next_token();
self.parse_create_trigger_statement()
.map(Statement::CreateTrigger)
} else if self.peek_token_is_keyword("TABLE") {
self.next_token();
self.parse_create_table_statement()
.map(Statement::CreateTable)
} else if self.peek_token_is_keyword("SEQUENCE") {
self.next_token();
self.parse_create_sequence_statement()
.map(Statement::CreateSequence)
} else if self.peek_token_is_keyword("SEQUENCE") {
self.next_token();
self.parse_drop_sequence_statement()
.map(Statement::DropSequence)
} else if self.peek_token_is_keyword("SCHEMA") {
self.next_token();
self.parse_create_schema_statement()
.map(Statement::CreateSchema)
} else if self.peek_token_is_keyword("UNIQUE") {
self.next_token();
if !self.expect_keyword("INDEX") {
return None;
}
self.parse_create_index_statement(true)
.map(Statement::CreateIndex)
} else if self.peek_token_is_keyword("INDEX") {
self.next_token();
self.parse_create_index_statement(false)
.map(Statement::CreateIndex)
} else if self.peek_token_is_keyword("COLUMNAR") {
self.next_token();
if !self.expect_keyword("INDEX") {
return None;
}
self.parse_create_columnar_index_statement()
.map(Statement::CreateColumnarIndex)
} else if self.peek_token_is_keyword("VIEW") {
self.next_token();
self.parse_create_view_statement()
.map(Statement::CreateView)
} else if self.peek_token_is_keyword("FUNCTION") {
self.next_token();
self.parse_create_function_statement()
.map(Statement::CreateFunction)
} else if self.peek_token_is_keyword("TRIGGER") {
self.next_token();
self.parse_create_trigger_statement()
.map(Statement::CreateTrigger)
} else if self.peek_token.literal.eq_ignore_ascii_case("SCHEDULE") {
self.next_token();
self.parse_create_schedule_statement()
.map(Statement::CreateSchedule)
} else {
self.add_error(format!(
"expected TABLE, SCHEMA, INDEX, COLUMNAR INDEX, VIEW, FUNCTION, PROCEDURE, TRIGGER, or SCHEDULE after CREATE at {}",
self.cur_token.position
));
None
}
}
fn parse_table_name(&mut self) -> Option<TableName> {
if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword) {
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token();
let first_ident = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if self.peek_token_is_punctuator(".") {
self.next_token(); if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token(); let second_ident =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(TableName::Qualified(QualifiedIdentifier {
token: first_ident.token.clone(),
qualifier: Box::new(first_ident),
name: Box::new(second_ident),
}))
} else {
Some(TableName::Simple(first_ident))
}
}
fn parse_create_table_statement(&mut self) -> Option<CreateTableStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let table_name = self.parse_table_name()?;
if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_keyword("SELECT") {
return None;
}
let select_stmt = self.parse_select_statement()?;
return Some(CreateTableStatement {
token,
table_name,
if_not_exists,
columns: Vec::new(),
table_constraints: Vec::new(),
as_select: Some(Box::new(select_stmt)),
});
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let (columns, table_constraints) = self.parse_column_definitions_and_constraints();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
Some(CreateTableStatement {
token,
table_name,
if_not_exists,
columns,
table_constraints,
as_select: None,
})
}
fn parse_column_definitions_and_constraints(
&mut self,
) -> (Vec<ColumnDefinition>, Vec<TableConstraint>) {
let mut columns = Vec::new();
let mut table_constraints = Vec::new();
self.next_token();
if let Some(item) = self.parse_column_or_constraint() {
match item {
ColumnOrConstraint::Column(col) => columns.push(col),
ColumnOrConstraint::Constraint(tc) => table_constraints.push(tc),
}
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(item) = self.parse_column_or_constraint() {
match item {
ColumnOrConstraint::Column(col) => columns.push(col),
ColumnOrConstraint::Constraint(tc) => table_constraints.push(tc),
}
}
}
(columns, table_constraints)
}
fn parse_column_or_constraint(&mut self) -> Option<ColumnOrConstraint> {
let mut constraint_name = None;
if self.cur_token_is_keyword("CONSTRAINT") {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
constraint_name = Some(self.cur_token.literal.clone());
self.next_token();
}
if self.cur_token_is_keyword("UNIQUE") {
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
let columns = self.parse_constraint_column_list()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
return Some(ColumnOrConstraint::Constraint(TableConstraint::Unique(
columns,
)));
}
if self.cur_token_is_keyword("CHECK") {
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
self.next_token();
let expr = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
return Some(ColumnOrConstraint::Constraint(TableConstraint::Check(
Box::new(expr),
)));
}
if self.cur_token_is_keyword("PRIMARY") {
if !self.expect_keyword("KEY") {
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
let columns = self.parse_constraint_column_list()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
return Some(ColumnOrConstraint::Constraint(TableConstraint::PrimaryKey(
columns,
)));
}
if self.cur_token_is_keyword("FOREIGN") {
if !self.expect_keyword("KEY") {
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
self.next_token();
if !self.cur_token_is_identifier_like() {
self.add_error(format!(
"expected column name at {}",
self.cur_token.position
));
return None;
}
let column = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
if !self.expect_keyword("REFERENCES") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let foreign_table =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
self.next_token();
if !self.cur_token_is_identifier_like() {
self.add_error(format!(
"expected foreign column name at {}",
self.cur_token.position
));
return None;
}
let foreign_column =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
let mut on_delete = ReferentialAction::NoAction;
let mut on_update = ReferentialAction::NoAction;
while self.peek_token_is_keyword("ON") {
self.next_token();
if self.peek_token_is_keyword("DELETE") {
self.next_token();
on_delete = self.parse_referential_action()?;
} else if self.peek_token_is_keyword("UPDATE") {
self.next_token();
on_update = self.parse_referential_action()?;
} else {
self.add_error(format!(
"expected DELETE or UPDATE after ON at {}",
self.peek_token.position
));
return None;
}
}
return Some(ColumnOrConstraint::Constraint(
TableConstraint::ForeignKey {
name: constraint_name,
column,
foreign_table,
foreign_column,
on_delete,
on_update,
},
));
}
self.parse_column_definition()
.map(ColumnOrConstraint::Column)
}
fn parse_constraint_column_list(&mut self) -> Option<Vec<Identifier>> {
let mut identifiers = Vec::new();
self.next_token();
if !self.cur_token_is_identifier_like() {
self.add_error(format!(
"expected column name at {}",
self.cur_token.position
));
return None;
}
identifiers.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if !self.cur_token_is_identifier_like() {
self.add_error(format!(
"expected column name at {}",
self.cur_token.position
));
return None;
}
identifiers.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
}
Some(identifiers)
}
fn parse_referential_action(&mut self) -> Option<ReferentialAction> {
if self.peek_token_is_keyword("CASCADE") {
self.next_token();
return Some(ReferentialAction::Cascade);
} else if self.peek_token_is_keyword("RESTRICT") {
self.next_token();
return Some(ReferentialAction::Restrict);
} else if self.peek_token_is_keyword("SET") {
self.next_token();
if self.expect_keyword("NULL") {
return Some(ReferentialAction::SetNull);
}
return None;
} else if self.peek_token_is_keyword("NO") {
self.next_token();
if self.expect_keyword("ACTION") {
return Some(ReferentialAction::NoAction);
}
return None;
}
self.add_error(format!(
"expected referential action (CASCADE, RESTRICT, SET NULL, NO ACTION) at {}",
self.peek_token.position
));
None
}
fn parse_column_definition(&mut self) -> Option<ColumnDefinition> {
if !self.cur_token_is_identifier_like() {
if self.cur_token.token_type == TokenType::Keyword
&& Self::is_reserved_keyword(&self.cur_token.literal)
{
self.add_error(format!(
"'{}' is a reserved keyword and cannot be used as a column name. Use double quotes to escape it: \"{}\"",
self.cur_token.literal.to_uppercase(),
self.cur_token.literal
));
} else {
self.add_error(format!(
"expected column name at {}",
self.cur_token.position
));
}
return None;
}
let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Keyword) {
return None;
}
let data_type = self.cur_token.literal.to_uppercase();
if (data_type == "DECIMAL" || data_type == "NUMERIC") && self.peek_token_is_punctuator("(")
{
self.next_token(); if self.peek_token.token_type == TokenType::Integer {
self.next_token();
}
if self.peek_token_is_punctuator(",") {
self.next_token(); if self.peek_token.token_type == TokenType::Integer {
self.next_token(); }
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error("expected ) after DECIMAL precision/scale".to_string());
return None;
}
}
let mut constraints = Vec::new();
while self.peek_token_is(TokenType::Keyword) {
let constraint_keyword = self.peek_token.literal.to_uppercase();
match constraint_keyword.as_str() {
"PRIMARY" => {
self.next_token(); if !self.expect_keyword("KEY") {
return None;
}
constraints.push(ColumnConstraint::PrimaryKey);
}
"NOT" => {
self.next_token(); if !self.expect_keyword("NULL") {
return None;
}
constraints.push(ColumnConstraint::NotNull);
}
"UNIQUE" => {
self.next_token();
constraints.push(ColumnConstraint::Unique);
}
"DEFAULT" => {
self.next_token(); self.next_token();
let expr = self.parse_expression(Precedence::Lowest)?;
constraints.push(ColumnConstraint::Default(expr));
}
"CHECK" => {
self.next_token(); if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
return None;
}
self.next_token();
let expr = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
constraints.push(ColumnConstraint::Check(expr));
}
"REFERENCES" => {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
return None;
}
let ref_table =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
let ref_column = if self.peek_token_is_punctuator("(") {
self.next_token();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let col =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")"
{
return None;
}
Some(col)
} else {
None
};
constraints.push(ColumnConstraint::References(ref_table, ref_column));
}
"AUTO_INCREMENT" | "AUTOINCREMENT" => {
constraints.push(ColumnConstraint::AutoIncrement);
self.next_token();
}
_ => break,
}
}
Some(ColumnDefinition {
name,
data_type,
constraints,
})
}
fn parse_create_index_statement(&mut self, is_unique: bool) -> Option<CreateIndexStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let index_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_keyword("ON") {
return None;
}
let table_name = self.parse_table_name()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let columns = self.parse_identifier_list();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
let index_method = if self.peek_token_is_keyword("USING") {
self.next_token(); self.next_token();
let method_name = self.cur_token.literal.to_uppercase();
match method_name.as_str() {
"BTREE" | "B_TREE" => Some(IndexMethod::BTree),
"HASH" => Some(IndexMethod::Hash),
"BITMAP" => Some(IndexMethod::Bitmap),
_ => {
self.add_error(format!(
"unknown index method '{}'. Supported methods: BTREE, HASH, BITMAP",
self.cur_token.literal
));
return None;
}
}
} else {
None
};
Some(CreateIndexStatement {
token,
index_name,
table_name,
columns,
is_unique,
if_not_exists,
index_method,
})
}
fn parse_create_columnar_index_statement(&mut self) -> Option<CreateColumnarIndexStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_keyword("ON") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let table_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let column_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
Some(CreateColumnarIndexStatement {
token,
table_name,
column_name,
if_not_exists,
is_unique: false,
})
}
fn parse_create_view_statement(&mut self) -> Option<CreateViewStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let view_name = self.parse_table_name()?;
if !self.expect_keyword("AS") {
return None;
}
self.next_token();
let query = if self.cur_token_is_keyword("SELECT") {
self.parse_select_statement()?
} else if self.cur_token_is_keyword("WITH") {
let with_clause = self.parse_with_clause()?;
self.next_token();
if !self.cur_token_is_keyword("SELECT") {
self.add_error(format!(
"expected SELECT after WITH clause in CREATE VIEW at {}",
self.cur_token.position
));
return None;
}
let mut select = self.parse_select_statement()?;
select.with = Some(with_clause);
select
} else {
self.add_error(format!(
"expected SELECT or WITH after AS in CREATE VIEW at {}",
self.cur_token.position
));
return None;
};
Some(CreateViewStatement {
token,
view_name,
query: Box::new(query),
if_not_exists,
})
}
fn parse_data_type(&mut self) -> Option<String> {
if !self.expect_peek(TokenType::Keyword) {
return None;
}
let data_type = self.cur_token.literal.to_uppercase();
if (data_type == "DECIMAL" || data_type == "NUMERIC") && self.peek_token_is_punctuator("(")
{
self.next_token(); if self.peek_token.token_type == TokenType::Integer {
self.next_token();
}
if self.peek_token_is_punctuator(",") {
self.next_token(); if self.peek_token.token_type == TokenType::Integer {
self.next_token();
}
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
Some(data_type)
} else {
Some(data_type)
}
}
fn parse_create_function_statement(&mut self) -> Option<CreateFunctionStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let function_name = self.parse_function_name()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let mut parameters = Vec::new();
if !self.peek_token_is_punctuator(")") {
loop {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let param_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
let param_type = self.parse_data_type()?;
parameters.push(FunctionParameter {
name: param_name,
data_type: param_type,
});
if !self.peek_token_is_punctuator(",") {
break;
}
self.next_token(); }
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
if !self.expect_keyword("RETURNS") {
return None;
}
let return_type = self.parse_data_type()?;
if !self.expect_keyword("LANGUAGE") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let language = self.cur_token.literal.clone();
if !self.expect_keyword("AS") {
return None;
}
if !self.peek_token_is(TokenType::String) && !self.peek_token_is(TokenType::RawString) {
self.add_error(format!(
"expected string literal for function body at {}",
self.peek_token.position
));
return None;
}
self.next_token();
let body = if self.cur_token.token_type == TokenType::RawString {
self.cur_token.literal.clone()
} else {
self.cur_token.literal.trim_matches('\'').to_string()
};
Some(CreateFunctionStatement {
token,
function_name,
parameters,
return_type,
language,
body,
if_not_exists,
})
}
fn parse_create_procedure_statement(
&mut self,
or_replace: bool,
) -> Option<CreateProcedureStatement> {
let token = self.cur_token.clone();
let procedure_name = self.parse_function_name()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let mut parameters = Vec::new();
if !self.peek_token_is_punctuator(")") {
loop {
let mut mode = ParameterMode::In;
if self.peek_token_is_keyword("INOUT") {
self.next_token();
mode = ParameterMode::InOut;
} else if self.peek_token_is_keyword("OUT") {
self.next_token();
mode = ParameterMode::Out;
} else if self.peek_token_is_keyword("IN") {
self.next_token();
mode = ParameterMode::In;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let param_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
let param_type = self.parse_data_type()?;
parameters.push(ProcedureParameter {
mode,
name: param_name,
data_type: param_type,
});
if self.peek_token_is_punctuator(",") {
self.next_token();
} else {
break;
}
}
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
if !self.expect_keyword("LANGUAGE") {
return None;
}
if !self.expect_peek(TokenType::Identifier) && !self.expect_peek(TokenType::Keyword) {
self.add_error(format!(
"expected language name at {}",
self.cur_token.position
));
return None;
}
let language = self.cur_token.literal.clone();
if !self.expect_keyword("AS") {
return None;
}
if !self.peek_token_is(TokenType::String) && !self.peek_token_is(TokenType::RawString) {
self.add_error(format!(
"expected procedure body string at {}",
self.peek_token.position
));
return None;
}
self.next_token();
let body = if self.cur_token.token_type == TokenType::RawString {
self.cur_token.literal.clone()
} else {
self.cur_token.literal.trim_matches('\'').to_string()
};
Some(CreateProcedureStatement {
token,
procedure_name,
parameters,
language,
body,
or_replace,
})
}
fn parse_call_statement(&mut self) -> Option<CallStatement> {
let token = self.cur_token.clone();
let procedure_name = self.parse_function_name()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let mut arguments = Vec::new();
if !self.peek_token_is_punctuator(")") {
loop {
self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
arguments.push(expr);
} else {
return None;
}
if self.peek_token_is_punctuator(",") {
self.next_token();
} else {
break;
}
}
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
return None;
}
Some(CallStatement {
token,
procedure_name,
arguments,
})
}
fn parse_drop_statement(&mut self) -> Option<Statement> {
if self.peek_token_is_keyword("TRIGGER") {
self.next_token();
self.parse_drop_trigger_statement()
.map(Statement::DropTrigger)
} else if self.peek_token_is_keyword("TABLE") {
self.next_token();
self.parse_drop_table_statement().map(Statement::DropTable)
} else if self.peek_token_is_keyword("SEQUENCE") {
self.next_token();
self.parse_drop_sequence_statement()
.map(Statement::DropSequence)
} else if self.peek_token_is_keyword("SCHEMA") {
self.next_token();
self.parse_drop_schema_statement()
.map(Statement::DropSchema)
} else if self.peek_token.literal.eq_ignore_ascii_case("SCHEDULE") {
self.next_token();
self.parse_drop_schedule_statement()
.map(Statement::DropSchedule)
} else if self.peek_token_is_keyword("INDEX") {
self.next_token();
self.parse_drop_index_statement().map(Statement::DropIndex)
} else if self.peek_token_is_keyword("COLUMNAR") {
self.next_token();
if !self.expect_keyword("INDEX") {
return None;
}
self.parse_drop_columnar_index_statement()
.map(Statement::DropColumnarIndex)
} else if self.peek_token_is_keyword("VIEW") {
self.next_token();
self.parse_drop_view_statement().map(Statement::DropView)
} else if self.peek_token_is_keyword("FUNCTION") {
self.next_token();
self.parse_drop_function_statement()
.map(Statement::DropFunction)
} else if self.peek_token_is_keyword("PROCEDURE") {
self.next_token();
self.parse_drop_procedure_statement()
.map(Statement::DropProcedure)
} else {
self.add_error(format!(
"expected TABLE, SCHEMA, INDEX, COLUMNAR INDEX, VIEW, FUNCTION, or PROCEDURE after DROP at {}",
self.cur_token.position
));
None
}
}
fn parse_drop_table_statement(&mut self) -> Option<DropTableStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let table_name = self.parse_table_name()?;
Some(DropTableStatement {
token,
table_name,
if_exists,
})
}
fn parse_function_name(&mut self) -> Option<FunctionName> {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let first_ident = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if self.peek_token_is_punctuator(".") {
self.next_token(); if !self.expect_peek(TokenType::Identifier) {
return None;
}
let second_ident =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(FunctionName::Qualified(QualifiedIdentifier {
token: first_ident.token.clone(),
qualifier: Box::new(first_ident),
name: Box::new(second_ident),
}))
} else {
Some(FunctionName::Simple(first_ident))
}
}
fn parse_drop_function_statement(&mut self) -> Option<DropFunctionStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let function_name = self.parse_function_name()?;
Some(DropFunctionStatement {
token,
function_name,
if_exists,
})
}
fn parse_drop_procedure_statement(&mut self) -> Option<DropProcedureStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
let procedure_name = self.parse_function_name()?;
Some(DropProcedureStatement {
token,
procedure_name,
if_exists,
})
}
fn parse_drop_schedule_statement(&mut self) -> Option<DropScheduleStatement> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
Some(DropScheduleStatement {
token,
name: self.cur_token.literal.clone(),
})
}
fn parse_drop_index_statement(&mut self) -> Option<DropIndexStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let index_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
let table_name = if self.peek_token_is_keyword("ON") {
self.next_token();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else {
None
};
Some(DropIndexStatement {
token,
index_name,
table_name,
if_exists,
})
}
fn parse_drop_columnar_index_statement(&mut self) -> Option<DropColumnarIndexStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_keyword("ON") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let table_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let column_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
Some(DropColumnarIndexStatement {
token,
table_name,
column_name,
if_exists,
})
}
fn parse_drop_view_statement(&mut self) -> Option<DropViewStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let view_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(DropViewStatement {
token,
view_name,
if_exists,
})
}
fn parse_create_schema_statement(&mut self) -> Option<CreateSchemaStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let schema_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(CreateSchemaStatement {
token,
schema_name,
if_not_exists,
})
}
fn parse_drop_schema_statement(&mut self) -> Option<DropSchemaStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let schema_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(DropSchemaStatement {
token,
schema_name,
if_exists,
})
}
fn parse_use_statement(&mut self) -> Option<UseSchemaStatement> {
let token = self.cur_token.clone();
if !self.expect_keyword("SCHEMA") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let schema_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(UseSchemaStatement { token, schema_name })
}
fn parse_alter_statement(&mut self) -> Option<Statement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("SEQUENCE") {
self.next_token();
return self
.parse_alter_sequence_statement()
.map(Statement::AlterSequence);
} else if self.peek_token.literal.eq_ignore_ascii_case("SCHEDULE") {
self.next_token();
return self
.parse_alter_schedule_statement()
.map(Statement::AlterSchedule);
}
if !self.expect_keyword("TABLE") {
return None;
}
self.parse_alter_table_statement(token)
.map(Statement::AlterTable)
}
fn parse_alter_schedule_statement(&mut self) -> Option<AlterScheduleStatement> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let name = self.cur_token.literal.clone();
self.next_token();
if !self.cur_token.literal.eq_ignore_ascii_case("ACTIVE") {
self.add_error(format!(
"expected ACTIVE, got {} at {}",
self.cur_token.literal, self.cur_token.position
));
return None;
}
self.next_token();
let active = if self.cur_token.is_keyword("TRUE") {
true
} else if self.cur_token.is_keyword("FALSE") {
false
} else {
self.add_error(format!(
"expected TRUE or FALSE after ACTIVE at {}",
self.cur_token.position
));
return None;
};
Some(AlterScheduleStatement {
token,
name,
active,
})
}
fn parse_alter_table_statement(
&mut self,
token: super::token::Token,
) -> Option<AlterTableStatement> {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let table_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_peek(TokenType::Keyword) {
return None;
}
let operation_keyword = self.cur_token.literal.to_uppercase();
let mut constraint = None;
let (operation, column_def, column_name, new_column_name, new_table_name) =
match operation_keyword.as_str() {
"ADD" => {
if self.peek_token_is_keyword("CONSTRAINT")
|| self.peek_token_is_keyword("FOREIGN")
|| self.peek_token_is_keyword("UNIQUE")
|| self.peek_token_is_keyword("PRIMARY")
|| self.peek_token_is_keyword("CHECK")
{
self.next_token();
if let Some(ColumnOrConstraint::Constraint(tc)) =
self.parse_column_or_constraint()
{
constraint = Some(tc);
(AlterTableOperation::AddConstraint, None, None, None, None)
} else {
return None;
}
} else {
if self.peek_token_is_keyword("COLUMN") {
self.next_token();
}
self.next_token();
let col_def = self.parse_column_definition()?;
(
AlterTableOperation::AddColumn,
Some(col_def),
None,
None,
None,
)
}
}
"DROP" => {
if self.peek_token_is_keyword("COLUMN") {
self.next_token();
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let col_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
(
AlterTableOperation::DropColumn,
None,
Some(col_name),
None,
None,
)
}
"RENAME" => {
if !self.expect_peek(TokenType::Keyword) {
return None;
}
let rename_keyword = self.cur_token.literal.to_uppercase();
if rename_keyword == "COLUMN" {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let col_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_keyword("TO") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let new_col_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
(
AlterTableOperation::RenameColumn,
None,
Some(col_name),
Some(new_col_name),
None,
)
} else if rename_keyword == "TO" {
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let new_tbl_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
(
AlterTableOperation::RenameTable,
None,
None,
None,
Some(new_tbl_name),
)
} else {
self.add_error(format!(
"expected COLUMN or TO after RENAME at {}",
self.cur_token.position
));
return None;
}
}
"MODIFY" => {
if self.peek_token_is_keyword("COLUMN") {
self.next_token();
}
self.next_token();
let col_def = self.parse_column_definition()?;
(
AlterTableOperation::ModifyColumn,
Some(col_def),
None,
None,
None,
)
}
_ => {
self.add_error(format!(
"expected ADD, DROP, RENAME, or MODIFY at {}",
self.cur_token.position
));
return None;
}
};
Some(AlterTableStatement {
token,
table_name,
operation,
column_def,
column_name,
new_column_name,
new_table_name,
constraint,
})
}
fn parse_begin_statement(&mut self) -> Option<BeginStatement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TRANSACTION") {
self.next_token();
}
let isolation_level = if self.peek_token_is_keyword("ISOLATION") {
self.next_token();
if !self.expect_keyword("LEVEL") {
return None;
}
self.next_token();
let level = self.cur_token.literal.to_uppercase();
let isolation = match level.as_str() {
"SNAPSHOT" | "SERIALIZABLE" => level,
"REPEATABLE" => {
if self.peek_token_is_keyword("READ") {
self.next_token();
}
"REPEATABLE READ".to_string()
}
"READ" => {
if self.peek_token_is_keyword("UNCOMMITTED") {
self.next_token();
"READ UNCOMMITTED".to_string()
} else if self.peek_token_is_keyword("COMMITTED") {
self.next_token();
"READ COMMITTED".to_string()
} else {
self.add_error(format!(
"expected UNCOMMITTED or COMMITTED after READ at {}",
self.cur_token.position
));
return None;
}
}
_ => {
self.add_error(format!(
"invalid isolation level: {} at {}",
level, self.cur_token.position
));
return None;
}
};
Some(isolation)
} else {
None
};
Some(BeginStatement {
token,
isolation_level,
})
}
fn parse_commit_statement(&mut self) -> Option<CommitStatement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TRANSACTION") {
self.next_token();
}
Some(CommitStatement { token })
}
fn parse_rollback_statement(&mut self) -> Option<RollbackStatement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TRANSACTION") {
self.next_token();
}
let savepoint_name = if self.peek_token_is_keyword("TO") {
self.next_token();
if self.peek_token_is_keyword("SAVEPOINT") {
self.next_token();
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else {
None
};
Some(RollbackStatement {
token,
savepoint_name,
})
}
fn parse_savepoint_statement(&mut self) -> Option<SavepointStatement> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let savepoint_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(SavepointStatement {
token,
savepoint_name,
})
}
fn parse_set_statement(&mut self) -> Option<SetStatement> {
let token = self.cur_token.clone();
self.next_token();
if !self.cur_token_is(TokenType::Identifier) {
self.add_error(format!(
"expected variable name at {}",
self.cur_token.position
));
return None;
}
let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
self.next_token();
let is_equals = self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=";
if !is_equals && !self.cur_token_is_keyword("TO") {
self.add_error(format!(
"expected '=' or 'TO' after variable name at {}",
self.cur_token.position
));
return None;
}
self.next_token();
let value = self.parse_expression(Precedence::Lowest)?;
Some(SetStatement { token, name, value })
}
fn parse_pragma_statement(&mut self) -> Option<PragmaStatement> {
let token = self.cur_token.clone();
self.next_token();
if !self.cur_token_is(TokenType::Identifier) {
self.add_error(format!(
"expected pragma name at {}",
self.cur_token.position
));
return None;
}
let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
self.next_token();
let value = if self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=" {
self.next_token();
Some(self.parse_expression(Precedence::Lowest)?)
} else {
None
};
Some(PragmaStatement { token, name, value })
}
fn parse_show_statement(&mut self) -> Option<Statement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TABLES") {
self.next_token();
Some(Statement::ShowTables(ShowTablesStatement { token }))
} else if self.peek_token_is_keyword("VIEWS") {
self.next_token();
Some(Statement::ShowViews(ShowViewsStatement { token }))
} else if self.peek_token_is_keyword("CREATE") {
self.next_token();
if self.peek_token_is_keyword("TABLE") {
self.next_token();
let table_name = self.parse_table_name()?;
Some(Statement::ShowCreateTable(ShowCreateTableStatement {
token,
table_name,
}))
} else if self.peek_token_is_keyword("VIEW") {
self.next_token();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let view_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(Statement::ShowCreateView(ShowCreateViewStatement {
token,
view_name,
}))
} else {
self.add_error(format!(
"expected TABLE or VIEW after SHOW CREATE at {}",
self.cur_token.position
));
None
}
} else if self.peek_token_is_keyword("INDEXES") || self.peek_token_is_keyword("INDEX") {
self.next_token();
if !self.expect_keyword("FROM") {
return None;
}
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let table_name =
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(Statement::ShowIndexes(ShowIndexesStatement {
token,
table_name,
}))
} else if self.peek_token_is_keyword("FUNCTIONS") || self.peek_token_is_keyword("FUNCTION")
{
let plural = self.peek_token_is_keyword("FUNCTIONS");
self.next_token();
Some(Statement::ShowFunctions(ShowFunctionsStatement {
token,
plural,
}))
} else {
self.add_error(format!(
"unsupported SHOW statement at {}",
self.cur_token.position
));
None
}
}
fn parse_describe_statement(&mut self) -> Option<DescribeStatement> {
let token = self.cur_token.clone();
self.next_token();
if self.cur_token_is(TokenType::Keyword) && self.cur_token.literal.to_uppercase() == "TABLE"
{
self.next_token();
}
if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
self.add_error(format!(
"expected table name after DESCRIBE at {}",
self.cur_token.position
));
return None;
}
let table_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
Some(DescribeStatement { token, table_name })
}
fn parse_explain_statement(&mut self) -> Option<ExplainStatement> {
let token = self.cur_token.clone();
let analyze = if self.peek_token_is_keyword("ANALYZE") {
self.next_token();
true
} else {
false
};
self.next_token();
let statement = self.parse_statement()?;
Some(ExplainStatement {
token,
statement: Box::new(statement),
analyze,
})
}
fn parse_analyze_statement(&mut self) -> Option<AnalyzeStatement> {
let token = self.cur_token.clone();
self.next_token();
let table_name = if self.cur_token_is(TokenType::Identifier)
|| (self.cur_token_is(TokenType::Keyword)
&& !self.cur_token.literal.eq_ignore_ascii_case("TABLE"))
{
let name = self.cur_token.literal.clone();
Some(name)
} else if self.cur_token_is(TokenType::Keyword)
&& self.cur_token.literal.eq_ignore_ascii_case("TABLE")
{
self.next_token();
if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
let name = self.cur_token.literal.clone();
Some(name)
} else {
None
}
} else {
None
};
Some(AnalyzeStatement { token, table_name })
}
fn parse_expression_statement(&mut self) -> Option<ExpressionStatement> {
let token = self.cur_token.clone();
let expression = self.parse_expression(Precedence::Lowest)?;
Some(ExpressionStatement { token, expression })
}
fn parse_create_schedule_statement(&mut self) -> Option<CreateScheduleStatement> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let name = self.cur_token.literal.clone();
self.next_token();
if !self.cur_token.literal.eq_ignore_ascii_case("CRON") {
self.add_error(format!(
"expected CRON, got {} at {}",
self.cur_token.literal, self.cur_token.position
));
return None;
}
if !self.expect_peek(TokenType::String) {
return None;
}
let raw_cron = &self.cur_token.literal;
let cron_expr =
if raw_cron.len() >= 2 && raw_cron.starts_with('\'') && raw_cron.ends_with('\'') {
raw_cron[1..raw_cron.len() - 1].to_string()
} else {
raw_cron.clone()
};
if !self.expect_keyword("AS") {
return None;
}
if !self.expect_peek(TokenType::String) {
return None;
}
let raw_cmd = &self.cur_token.literal;
let command = if raw_cmd.len() >= 2 && raw_cmd.starts_with('\'') && raw_cmd.ends_with('\'')
{
raw_cmd[1..raw_cmd.len() - 1].to_string()
} else {
raw_cmd.clone()
};
Some(CreateScheduleStatement {
token,
name,
cron_expr,
command,
})
}
fn parse_create_trigger_statement(&mut self) -> Option<CreateTriggerStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("NOT") {
return None;
}
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let trigger_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
self.next_token();
let timing_str = self.cur_token.literal.to_uppercase();
let timing = if timing_str == "BEFORE" {
TriggerTiming::Before
} else if timing_str == "AFTER" {
TriggerTiming::After
} else {
self.add_error(format!(
"expected BEFORE or AFTER at {}",
self.cur_token.position
));
return None;
};
self.next_token();
let event_str = self.cur_token.literal.to_uppercase();
let event = if event_str == "INSERT" {
TriggerEvent::Insert
} else if event_str == "UPDATE" {
TriggerEvent::Update
} else if event_str == "DELETE" {
TriggerEvent::Delete
} else {
self.add_error(format!(
"expected INSERT, UPDATE, or DELETE at {}",
self.cur_token.position
));
return None;
};
if !self.expect_keyword("ON") {
return None;
}
let table_name = self.parse_table_name()?;
let mut for_each_row = false;
if self.peek_token.literal.to_uppercase() == "FOR" {
self.next_token();
if self.peek_token.literal.to_uppercase() != "EACH" {
return None;
}
self.next_token();
if self.peek_token.literal.to_uppercase() != "ROW" {
return None;
}
self.next_token();
for_each_row = true;
}
if !self.expect_keyword("LANGUAGE") {
return None;
}
if !self.expect_peek(TokenType::Identifier) && !self.expect_peek(TokenType::Keyword) {
self.add_error(format!(
"expected language name at {}",
self.cur_token.position
));
return None;
}
let language = self.cur_token.literal.clone();
if !self.expect_keyword("AS") {
return None;
}
if !self.expect_peek(TokenType::String) {
self.add_error(format!(
"expected procedure body string at {}",
self.cur_token.position
));
return None;
}
let body = self.cur_token.literal.trim_matches('\'').to_string();
Some(CreateTriggerStatement {
token,
trigger_name,
if_not_exists,
timing,
event,
table_name,
for_each_row,
language,
body,
})
}
fn parse_drop_trigger_statement(&mut self) -> Option<DropTriggerStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token();
if !self.expect_keyword("EXISTS") {
return None;
}
true
} else {
false
};
if !self.expect_peek(TokenType::Identifier) {
return None;
}
let trigger_name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !self.expect_keyword("ON") {
return None;
}
let table_name = self.parse_table_name()?;
Some(DropTriggerStatement {
token,
trigger_name,
table_name,
if_exists,
})
}
pub fn parse_identifier_list(&mut self) -> Vec<Identifier> {
let mut list = Vec::new();
self.next_token();
if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
list.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token(); if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
list.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
} else {
self.add_error(format!(
"expected Identifier, got {:?} at {}",
self.cur_token.token_type, self.cur_token.position
));
return list;
}
}
list
}
fn parse_create_sequence_statement(&mut self) -> Option<CreateSequenceStatement> {
let token = self.cur_token.clone();
let if_not_exists = if self.peek_token_is_keyword("IF") {
self.next_token(); if self.expect_keyword("NOT") && self.expect_keyword("EXISTS") {
true
} else {
return None;
}
} else {
false
};
let name = self.parse_table_name()?;
let mut start_with = None;
let mut increment_by = None;
let mut min_value = None;
let mut max_value = None;
let mut cycle = false;
while !(self.cur_token_is(TokenType::Eof)
|| self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ";")
{
if self.peek_token_is_keyword("START") {
self.next_token(); if self.peek_token_is_keyword("WITH") {
self.next_token(); }
if !self.expect_peek(TokenType::Integer) {
return None;
}
start_with = Some(self.cur_token.literal.parse::<i64>().unwrap_or(1));
} else if self.peek_token_is_keyword("INCREMENT") {
self.next_token(); if self.peek_token_is_keyword("BY") {
self.next_token(); }
let is_negative =
if self.peek_token_is(TokenType::Operator) && self.peek_token.literal == "-" {
self.next_token();
true
} else {
false
};
if !self.expect_peek(TokenType::Integer) {
return None;
}
let val = self.cur_token.literal.parse::<i64>().unwrap_or(1);
increment_by = Some(if is_negative { -val } else { val });
} else if self.peek_token_is_keyword("MINVALUE") {
self.next_token(); let is_negative =
if self.peek_token_is(TokenType::Operator) && self.peek_token.literal == "-" {
self.next_token();
true
} else {
false
};
if !self.expect_peek(TokenType::Integer) {
return None;
}
let val = self.cur_token.literal.parse::<i64>().unwrap_or(1);
min_value = Some(if is_negative { -val } else { val });
} else if self.peek_token_is_keyword("NO") {
self.next_token(); if self.peek_token_is_keyword("MINVALUE") {
self.next_token();
min_value = None;
} else if self.peek_token_is_keyword("MAXVALUE") {
self.next_token();
max_value = None;
} else if self.peek_token_is_keyword("CYCLE") {
self.next_token();
cycle = false;
} else {
self.add_error(format!(
"Unexpected token after NO: {}",
self.peek_token.literal
));
return None;
}
} else if self.peek_token_is_keyword("MAXVALUE") {
self.next_token(); if !self.expect_peek(TokenType::Integer) {
return None;
}
max_value = Some(self.cur_token.literal.parse::<i64>().unwrap_or(i64::MAX));
} else if self.peek_token_is_keyword("CYCLE") {
self.next_token(); cycle = true;
} else {
break;
}
}
Some(CreateSequenceStatement {
token,
name,
if_not_exists,
start_with,
increment_by,
min_value,
max_value,
cycle,
})
}
fn parse_alter_sequence_statement(&mut self) -> Option<AlterSequenceStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token(); if self.expect_keyword("EXISTS") {
true
} else {
return None;
}
} else {
false
};
let name = self.parse_table_name()?;
let mut restart_with = None;
let mut increment_by = None;
let mut min_value = None;
let mut max_value = None;
let mut cycle = None;
while !(self.cur_token_is(TokenType::Eof)
|| self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ";")
{
if self.peek_token_is_keyword("RESTART") {
self.next_token(); if self.peek_token_is_keyword("WITH") {
self.next_token(); }
if !self.expect_peek(TokenType::Integer) {
return None;
}
restart_with = Some(self.cur_token.literal.parse::<i64>().unwrap_or(1));
} else if self.peek_token_is_keyword("INCREMENT") {
self.next_token(); if self.peek_token_is_keyword("BY") {
self.next_token(); }
let is_negative =
if self.peek_token_is(TokenType::Operator) && self.peek_token.literal == "-" {
self.next_token();
true
} else {
false
};
if !self.expect_peek(TokenType::Integer) {
return None;
}
let val = self.cur_token.literal.parse::<i64>().unwrap_or(1);
increment_by = Some(if is_negative { -val } else { val });
} else if self.peek_token_is_keyword("MINVALUE") {
self.next_token(); let is_negative =
if self.peek_token_is(TokenType::Operator) && self.peek_token.literal == "-" {
self.next_token();
true
} else {
false
};
if !self.expect_peek(TokenType::Integer) {
return None;
}
let val = self.cur_token.literal.parse::<i64>().unwrap_or(1);
min_value = Some(if is_negative { -val } else { val });
} else if self.peek_token_is_keyword("NO") {
self.next_token(); if self.peek_token_is_keyword("MINVALUE") || self.peek_token_is_keyword("MAXVALUE")
{
self.next_token();
} else if self.peek_token_is_keyword("CYCLE") {
self.next_token();
cycle = Some(false);
} else {
self.add_error(format!(
"Unexpected token after NO: {}",
self.peek_token.literal
));
return None;
}
} else if self.peek_token_is_keyword("MAXVALUE") {
self.next_token(); if !self.expect_peek(TokenType::Integer) {
return None;
}
max_value = Some(self.cur_token.literal.parse::<i64>().unwrap_or(i64::MAX));
} else if self.peek_token_is_keyword("CYCLE") {
self.next_token(); cycle = Some(true);
} else {
break;
}
}
Some(AlterSequenceStatement {
token,
name,
if_exists,
restart_with,
increment_by,
min_value,
max_value,
cycle,
})
}
fn parse_drop_sequence_statement(&mut self) -> Option<DropSequenceStatement> {
let token = self.cur_token.clone();
let if_exists = if self.peek_token_is_keyword("IF") {
self.next_token(); if self.expect_keyword("EXISTS") {
true
} else {
return None;
}
} else {
false
};
let name = self.parse_table_name()?;
Some(DropSequenceStatement {
token,
name,
if_exists,
})
}
pub fn parse_copy_statement(&mut self) -> Option<CopyStatement> {
let token = self.cur_token.clone();
let table_name = self.parse_table_name()?;
self.next_token();
let mut columns = Vec::new();
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == "(" {
self.next_token(); while !(self.cur_token_is(TokenType::Eof)
|| (self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")"))
{
if !self.cur_token_is(TokenType::Identifier)
&& !self.cur_token_is(TokenType::Keyword)
{
self.add_error(format!(
"Expected column name, got {}",
self.cur_token.token_type
));
return None;
}
columns.push(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
self.next_token();
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == "," {
self.next_token(); }
}
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")" {
self.next_token();
} else {
self.add_error("Expected ')'".to_string());
return None;
}
}
if self.cur_token_is(TokenType::Keyword)
&& self.cur_token.literal.eq_ignore_ascii_case("FROM")
{
self.next_token();
} else {
self.add_error("Expected FROM".to_string());
return None;
}
if !self.cur_token_is(TokenType::String) {
self.add_error(format!(
"Expected string literal for file path, got {}",
self.cur_token.token_type
));
return None;
}
let mut file_path = self.cur_token.literal.clone();
if file_path.len() >= 2 && file_path.starts_with('\'') && file_path.ends_with('\'') {
file_path = file_path[1..file_path.len() - 1].to_string();
}
self.next_token();
let mut format = CopyFormat::Csv;
let mut header = true;
let mut delimiter = b',';
let mut null_string = None;
if self.cur_token_is(TokenType::Keyword)
&& self.cur_token.literal.eq_ignore_ascii_case("WITH")
{
self.next_token();
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == "(" {
self.next_token();
} else {
self.add_error("Expected '(' after WITH".to_string());
return None;
}
while !(self.cur_token_is(TokenType::Eof)
|| (self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")"))
{
if !self.cur_token_is(TokenType::Keyword) {
self.add_error(format!(
"Expected COPY option keyword, got {}",
self.cur_token.literal
));
return None;
}
let option_name = self.cur_token.literal.to_uppercase();
self.next_token();
match option_name.as_str() {
"FORMAT" => {
if !self.cur_token_is(TokenType::Keyword)
&& !self.cur_token_is(TokenType::Identifier)
{
self.add_error("Expected format name (CSV or JSON)".to_string());
return None;
}
let format_name = self.cur_token.literal.to_uppercase();
if format_name == "CSV" {
format = CopyFormat::Csv;
} else if format_name == "JSON" {
format = CopyFormat::Json;
} else {
self.add_error(format!("Unsupported copy format: {}", format_name));
return None;
}
self.next_token();
}
"HEADER" => {
if (self.cur_token_is(TokenType::Keyword)
|| self.cur_token_is(TokenType::Identifier))
&& (self.cur_token.literal.eq_ignore_ascii_case("TRUE")
|| self.cur_token.literal.eq_ignore_ascii_case("FALSE"))
{
header = self.cur_token.literal.eq_ignore_ascii_case("TRUE");
self.next_token();
} else {
header = true;
}
}
"DELIMITER" => {
if !self.cur_token_is(TokenType::String) {
self.add_error("Expected string literal for delimiter".to_string());
return None;
}
let mut delim_str = self.cur_token.literal.clone();
if delim_str.len() >= 2
&& delim_str.starts_with('\'')
&& delim_str.ends_with('\'')
{
delim_str = delim_str[1..delim_str.len() - 1].to_string();
}
if delim_str.len() != 1 {
self.add_error("Delimiter must be a single character".to_string());
return None;
}
delimiter = delim_str.as_bytes()[0];
self.next_token();
}
"NULL" => {
if !self.cur_token_is(TokenType::String) {
self.add_error(
"Expected string literal for null representation".to_string(),
);
return None;
}
let mut null_str = self.cur_token.literal.clone();
if null_str.len() >= 2
&& null_str.starts_with('\'')
&& null_str.ends_with('\'')
{
null_str = null_str[1..null_str.len() - 1].to_string();
}
null_string = Some(null_str);
self.next_token();
}
_ => {
self.add_error(format!("Unknown COPY option: {}", option_name));
return None;
}
}
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == "," {
self.next_token(); }
}
if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")" {
self.next_token();
} else {
self.add_error("Expected ')'".to_string());
return None;
}
}
Some(CopyStatement {
token,
table_name,
columns,
file_path,
format,
header,
delimiter,
null_string,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_stmt(input: &str) -> Option<Statement> {
let mut parser = Parser::new(input);
let stmt = parser.parse_statement();
if stmt.is_none() {
println!("Parser errors: {:?}", parser.errors());
}
stmt
}
#[test]
fn test_parse_simple_select() {
let stmt = parse_stmt("SELECT * FROM users").unwrap();
match stmt {
Statement::Select(select) => {
assert_eq!(select.columns.len(), 1);
assert!(matches!(select.columns[0], Expression::Star(_)));
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_select_with_where() {
let stmt = parse_stmt("SELECT id, name FROM users WHERE id = 1").unwrap();
match stmt {
Statement::Select(select) => {
assert_eq!(select.columns.len(), 2);
assert!(select.where_clause.is_some());
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_count_star_with_filter_in_select() {
let stmt = parse_stmt("SELECT COUNT(*) FILTER (WHERE category = 'Z') FROM data").unwrap();
match stmt {
Statement::Select(select) => {
assert_eq!(select.columns.len(), 1);
match &select.columns[0] {
Expression::FunctionCall(fc) => {
assert_eq!(fc.function.to_uppercase(), "COUNT");
assert!(
fc.filter.is_some(),
"FILTER clause should be parsed for COUNT(*) in SELECT"
);
}
_ => panic!("expected FunctionCall for COUNT(*)"),
}
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_select_with_join() {
let stmt =
parse_stmt("SELECT u.id FROM users u LEFT JOIN orders o ON u.id = o.user_id").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.table_expr.is_some());
match select.table_expr.as_ref().unwrap().as_ref() {
Expression::JoinSource(_) => {}
_ => panic!("expected JoinSource"),
}
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_insert() {
let stmt = parse_stmt("INSERT INTO users (id, name) VALUES (1, 'Alice')").unwrap();
match stmt {
Statement::Insert(insert) => {
assert_eq!(insert.table_name.value(), "users");
assert_eq!(insert.columns.len(), 2);
assert_eq!(insert.values.len(), 1);
}
_ => panic!("expected InsertStatement"),
}
}
#[test]
fn test_parse_update() {
let stmt = parse_stmt("UPDATE users SET name = 'Bob' WHERE id = 1").unwrap();
match stmt {
Statement::Update(update) => {
assert_eq!(update.table_name.value(), "users");
assert_eq!(update.updates.len(), 1);
assert!(update.where_clause.is_some());
}
_ => panic!("expected UpdateStatement"),
}
}
#[test]
fn test_parse_delete() {
let stmt = parse_stmt("DELETE FROM users WHERE id = 1").unwrap();
match stmt {
Statement::Delete(delete) => {
assert_eq!(delete.table_name.value(), "users");
assert!(delete.where_clause.is_some());
}
_ => panic!("expected DeleteStatement"),
}
}
#[test]
fn test_parse_create_table_with_foreign_key() {
let input = "CREATE TABLE orders (id INTEGER, user_id INTEGER, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE SET NULL)";
let mut parser = Parser::new(input);
let program = parser.parse_program().unwrap();
assert_eq!(program.statements.len(), 1);
let stmt = match &program.statements[0] {
Statement::CreateTable(s) => s,
_ => panic!("Expected CreateTableStatement"),
};
assert_eq!(stmt.table_constraints.len(), 1);
match &stmt.table_constraints[0] {
TableConstraint::ForeignKey {
column,
foreign_table,
foreign_column,
on_delete,
on_update,
..
} => {
assert_eq!(column.value, "user_id");
assert_eq!(foreign_table.value, "users");
assert_eq!(foreign_column.value, "id");
assert_eq!(*on_delete, ReferentialAction::Cascade);
assert_eq!(*on_update, ReferentialAction::SetNull);
}
_ => panic!("Expected ForeignKey constraint"),
}
}
#[test]
fn test_parse_alter_table_add_foreign_key() {
let input =
"ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)";
let mut parser = Parser::new(input);
let program = parser.parse_program().unwrap();
assert_eq!(program.statements.len(), 1);
let stmt = match &program.statements[0] {
Statement::AlterTable(s) => s,
_ => panic!("Expected AlterTableStatement"),
};
assert_eq!(stmt.operation, AlterTableOperation::AddConstraint);
match stmt.constraint.as_ref().unwrap() {
TableConstraint::ForeignKey {
name,
column,
foreign_table,
foreign_column,
on_delete,
on_update,
} => {
assert_eq!(name.as_deref(), Some("fk_user"));
assert_eq!(column.value, "user_id");
assert_eq!(foreign_table.value, "users");
assert_eq!(foreign_column.value, "id");
assert_eq!(*on_delete, ReferentialAction::NoAction);
assert_eq!(*on_update, ReferentialAction::NoAction);
}
_ => panic!("Expected ForeignKey constraint"),
}
}
#[test]
fn test_parse_create_table() {
let stmt =
parse_stmt("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)").unwrap();
match stmt {
Statement::CreateTable(create) => {
assert_eq!(create.table_name.value(), "users");
assert_eq!(create.columns.len(), 2);
}
_ => panic!("expected CreateTableStatement"),
}
}
#[test]
fn test_parse_drop_table() {
let stmt = parse_stmt("DROP TABLE IF EXISTS users").unwrap();
match stmt {
Statement::DropTable(drop) => {
assert_eq!(drop.table_name.value(), "users");
assert!(drop.if_exists);
}
_ => panic!("expected DropTableStatement"),
}
}
#[test]
fn test_parse_create_schema() {
let stmt = parse_stmt("CREATE SCHEMA IF NOT EXISTS myschema").unwrap();
match stmt {
Statement::CreateSchema(create) => {
assert_eq!(create.schema_name.value(), "myschema");
assert!(create.if_not_exists);
}
_ => panic!("expected CreateSchemaStatement"),
}
}
#[test]
fn test_parse_drop_schema() {
let stmt = parse_stmt("DROP SCHEMA IF EXISTS myschema").unwrap();
match stmt {
Statement::DropSchema(drop) => {
assert_eq!(drop.schema_name.value(), "myschema");
assert!(drop.if_exists);
}
_ => panic!("expected DropSchemaStatement"),
}
}
#[test]
fn test_parse_use_schema() {
let stmt = parse_stmt("USE SCHEMA myschema").unwrap();
match stmt {
Statement::UseSchema(use_stmt) => {
assert_eq!(use_stmt.schema_name.value(), "myschema");
}
_ => panic!("expected UseSchemaStatement"),
}
}
#[test]
fn test_parse_begin_commit() {
let stmt = parse_stmt("BEGIN TRANSACTION").unwrap();
match stmt {
Statement::Begin(_) => {}
_ => panic!("expected BeginStatement"),
}
let stmt = parse_stmt("COMMIT").unwrap();
match stmt {
Statement::Commit(_) => {}
_ => panic!("expected CommitStatement"),
}
}
#[test]
fn test_parse_with_cte() {
let stmt = parse_stmt("WITH temp AS (SELECT * FROM users) SELECT * FROM temp").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.with.is_some());
let with = select.with.as_ref().unwrap();
assert_eq!(with.ctes.len(), 1);
assert_eq!(with.ctes[0].name.value(), "temp");
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_fetch_first() {
let stmt = parse_stmt("SELECT * FROM users FETCH FIRST 10 ROWS ONLY").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.limit.is_some());
}
_ => panic!("expected SelectStatement"),
}
let stmt = parse_stmt("SELECT * FROM users FETCH FIRST 1 ROW ONLY").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.limit.is_some());
}
_ => panic!("expected SelectStatement"),
}
let stmt = parse_stmt("SELECT * FROM users FETCH NEXT 5 ROWS ONLY").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.limit.is_some());
}
_ => panic!("expected SelectStatement"),
}
let stmt =
parse_stmt("SELECT * FROM users OFFSET 10 ROWS FETCH FIRST 5 ROWS ONLY").unwrap();
match stmt {
Statement::Select(select) => {
assert!(select.limit.is_some());
assert!(select.offset.is_some());
}
_ => panic!("expected SelectStatement"),
}
}
#[test]
fn test_parse_create_sequence() {
let input = "CREATE SEQUENCE seq1 START WITH 100 INCREMENT BY -10 MINVALUE -1000 MAXVALUE 1000 CYCLE";
let stmt = parse_stmt(input).expect("Failed to parse create sequence");
if let Statement::CreateSequence(s) = stmt {
assert_eq!(s.name.to_string(), "seq1");
assert_eq!(s.start_with, Some(100));
assert_eq!(s.increment_by, Some(-10));
assert_eq!(s.min_value, Some(-1000));
assert_eq!(s.max_value, Some(1000));
assert!(s.cycle);
} else {
panic!("Expected CreateSequence");
}
}
#[test]
fn test_parse_alter_sequence() {
let input = "ALTER SEQUENCE seq1 RESTART WITH 50 INCREMENT BY 5 NO MINVALUE CYCLE";
let stmt = parse_stmt(input).unwrap();
if let Statement::AlterSequence(s) = stmt {
assert_eq!(s.name.to_string(), "seq1");
assert_eq!(s.restart_with, Some(50));
assert_eq!(s.increment_by, Some(5));
assert_eq!(s.cycle, Some(true));
} else {
panic!("Expected AlterSequence");
}
}
#[test]
fn test_parse_drop_sequence() {
let input = "DROP SEQUENCE IF EXISTS seq1";
let stmt = parse_stmt(input).unwrap();
if let Statement::DropSequence(s) = stmt {
assert_eq!(s.name.to_string(), "seq1");
assert!(s.if_exists);
} else {
panic!("Expected DropSequence");
}
}
#[test]
fn test_parse_copy_statement() {
let input = "COPY my_table (id, name) FROM 'data.csv' WITH (FORMAT CSV, HEADER TRUE, DELIMITER '|', NULL 'N/A')";
let stmt = parse_stmt(input).unwrap();
if let Statement::Copy(s) = stmt {
assert_eq!(s.table_name.table(), "my_table");
assert!(s.table_name.schema().is_none());
assert_eq!(s.columns.len(), 2);
assert_eq!(s.columns[0].to_string(), "id");
assert_eq!(s.columns[1].to_string(), "name");
assert_eq!(s.file_path, "data.csv");
assert_eq!(s.format, CopyFormat::Csv);
assert!(s.header);
assert_eq!(s.delimiter, b'|');
assert_eq!(s.null_string, Some("N/A".to_string()));
} else {
panic!("Expected CopyStatement");
}
let input_json = "COPY events FROM 'data.json' WITH (FORMAT JSON)";
let stmt_json = parse_stmt(input_json).unwrap();
if let Statement::Copy(s) = stmt_json {
assert_eq!(s.table_name.table(), "events");
assert!(s.table_name.schema().is_none());
assert!(s.columns.is_empty());
assert_eq!(s.file_path, "data.json");
assert_eq!(s.format, CopyFormat::Json);
assert!(s.header); assert_eq!(s.delimiter, b',');
assert_eq!(s.null_string, None);
} else {
panic!("Expected CopyStatement");
}
let input_schema = "COPY cdm.concept FROM 'data.csv'";
let stmt_schema = parse_stmt(input_schema).unwrap();
if let Statement::Copy(s) = stmt_schema {
assert_eq!(s.table_name.table(), "concept");
assert_eq!(s.table_name.schema().unwrap(), "cdm");
assert!(s.columns.is_empty());
assert_eq!(s.file_path, "data.csv");
assert_eq!(s.format, CopyFormat::Csv);
assert!(s.header);
} else {
panic!("Expected CopyStatement");
}
}
#[test]
fn test_parse_from_first_with_select() {
let stmt1 = parse_stmt("FROM tbl SELECT a, b").unwrap();
let stmt2 = parse_stmt("SELECT a, b FROM tbl").unwrap();
assert_eq!(stmt1.to_string(), stmt2.to_string());
}
#[test]
fn test_parse_from_first_complex_source() {
let stmt1 = parse_stmt("FROM (VALUES (1)) t(a) SELECT a").unwrap();
let stmt2 = parse_stmt("SELECT a FROM (VALUES (1)) t(a)").unwrap();
assert_eq!(stmt1.to_string(), stmt2.to_string());
}
#[test]
fn test_parse_from_first_any_order() {
let stmt1 = parse_stmt("FROM tbl WHERE a > 1 SELECT a, b ORDER BY a LIMIT 10").unwrap();
let stmt2 = parse_stmt("SELECT a, b FROM tbl WHERE a > 1 ORDER BY a LIMIT 10").unwrap();
assert_eq!(stmt1.to_string(), stmt2.to_string());
let stmt3 = parse_stmt("FROM tbl ORDER BY a SELECT a, b LIMIT 10 WHERE a > 1").unwrap();
assert_eq!(stmt3.to_string(), stmt2.to_string());
}
#[test]
fn test_parse_from_first_without_select() {
let stmt1 = parse_stmt("FROM tbl").unwrap();
let stmt2 = parse_stmt("SELECT * FROM tbl").unwrap();
assert_eq!(stmt1.to_string(), stmt2.to_string());
}
#[test]
fn test_parse_from_first_without_select_where() {
let stmt1 = parse_stmt("FROM tbl WHERE x > 1").unwrap();
let stmt2 = parse_stmt("SELECT * FROM tbl WHERE x > 1").unwrap();
assert_eq!(stmt1.to_string(), stmt2.to_string());
}
}