use crate::error::{DdbError, Result};
use crate::lexer::{Token, TokenType};
use super::ast::*;
pub struct Parser {
tokens: Vec<Token>,
position: usize,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Parser { tokens, position: 0 }
}
pub fn parse(&mut self) -> Result<Statement> {
let token = self.peek()?;
match &token.token_type {
TokenType::Select => self.parse_select(),
TokenType::Insert => self.parse_insert(),
TokenType::Update => self.parse_update(),
TokenType::Delete => self.parse_delete(),
TokenType::Upsert => self.parse_upsert(),
TokenType::Create => self.parse_create(),
TokenType::Drop => self.parse_drop(),
TokenType::Use => self.parse_use(),
TokenType::Show => self.parse_show(),
TokenType::Set => self.parse_set(),
TokenType::Begin => {
self.advance()?;
Ok(Statement::Begin)
}
TokenType::Commit => {
self.advance()?;
Ok(Statement::Commit)
}
TokenType::Rollback => {
self.advance()?;
Ok(Statement::Rollback)
}
_ => Err(DdbError::ParseError(format!(
"Unexpected token: {:?}",
token.token_type
))),
}
}
fn parse_select(&mut self) -> Result<Statement> {
self.expect(TokenType::Select)?;
let distinct = self.match_token(TokenType::Distinct);
let columns = self.parse_select_columns()?;
let from = if self.match_token(TokenType::From) {
Some(self.expect_identifier()?)
} else {
None
};
let joins = self.parse_joins()?;
let where_clause = if self.match_token(TokenType::Where) {
Some(self.parse_expression()?)
} else {
None
};
let group_by = if self.match_token(TokenType::Group) {
self.expect(TokenType::By)?;
self.parse_group_by()?
} else if self.match_token(TokenType::GroupBy) {
self.parse_group_by()?
} else {
Vec::new()
};
let having = if self.match_token(TokenType::Having) {
Some(self.parse_expression()?)
} else {
None
};
let order_by = if self.match_token(TokenType::OrderBy) {
self.parse_order_by()?
} else if self.match_token(TokenType::Order) {
self.expect(TokenType::By)?;
self.parse_order_by()?
} else {
Vec::new()
};
let limit = if self.match_token(TokenType::Limit) {
Some(self.parse_limit()?)
} else {
None
};
Ok(Statement::Select(SelectStatement {
distinct,
columns,
from,
joins,
where_clause,
group_by,
having,
order_by,
limit,
}))
}
fn parse_select_columns(&mut self) -> Result<Vec<SelectColumn>> {
let mut columns = Vec::new();
loop {
if self.match_token(TokenType::Star) {
columns.push(SelectColumn::Wildcard);
} else if matches!(self.peek()?.token_type, TokenType::Identifier(_)) {
let name = self.expect_identifier()?;
if self.match_token(TokenType::LeftParen) {
let args = self.parse_function_args()?;
self.expect(TokenType::RightParen)?;
let alias = if self.match_token(TokenType::As) {
Some(self.expect_identifier()?)
} else {
None
};
columns.push(SelectColumn::Function { name, args, alias });
} else {
let alias = if self.match_token(TokenType::As) {
Some(self.expect_identifier()?)
} else {
None
};
columns.push(SelectColumn::Column { name, alias });
}
} else {
return Err(DdbError::ParseError(
"Expected column name or *".to_string()
));
}
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(columns)
}
fn parse_order_by(&mut self) -> Result<Vec<OrderByColumn>> {
let mut order_by = Vec::new();
loop {
let column = self.expect_identifier()?;
let direction = if self.match_token(TokenType::Desc) {
OrderDirection::Desc
} else {
self.match_token(TokenType::Asc); OrderDirection::Asc
};
order_by.push(OrderByColumn { column, direction });
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(order_by)
}
fn parse_limit(&mut self) -> Result<Limit> {
let first = self.expect_integer()?;
if self.match_token(TokenType::Comma) {
let count = self.expect_integer()?;
Ok(Limit {
offset: Some(first as usize),
count: count as usize,
})
} else if self.match_token(TokenType::Offset) {
let offset = self.expect_integer()?;
Ok(Limit {
offset: Some(offset as usize),
count: first as usize,
})
} else {
Ok(Limit {
offset: None,
count: first as usize,
})
}
}
fn parse_joins(&mut self) -> Result<Vec<JoinClause>> {
let mut joins = Vec::new();
loop {
let join_type = if self.match_token(TokenType::Inner) {
self.expect(TokenType::Join)?;
JoinType::Inner
} else if self.match_token(TokenType::Left) {
self.match_token(TokenType::Outer); self.expect(TokenType::Join)?;
JoinType::Left
} else if self.match_token(TokenType::Right) {
self.match_token(TokenType::Outer); self.expect(TokenType::Join)?;
JoinType::Right
} else if self.match_token(TokenType::Full) {
self.match_token(TokenType::Outer); self.expect(TokenType::Join)?;
JoinType::Full
} else if self.match_token(TokenType::Join) {
JoinType::Inner
} else {
break;
};
let table = self.expect_identifier()?;
self.expect(TokenType::On)?;
let on_condition = self.parse_expression()?;
joins.push(JoinClause {
join_type,
table,
on_condition,
});
}
Ok(joins)
}
fn parse_group_by(&mut self) -> Result<Vec<String>> {
let mut group_by = Vec::new();
loop {
group_by.push(self.expect_identifier()?);
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(group_by)
}
fn parse_insert(&mut self) -> Result<Statement> {
self.expect(TokenType::Insert)?;
self.expect(TokenType::Into)?;
let table = self.expect_identifier()?;
let columns = if self.match_token(TokenType::LeftParen) {
let cols = self.parse_identifier_list()?;
self.expect(TokenType::RightParen)?;
cols
} else {
Vec::new()
};
self.expect(TokenType::Values)?;
let mut values = Vec::new();
loop {
self.expect(TokenType::LeftParen)?;
let value_row = self.parse_expression_list()?;
self.expect(TokenType::RightParen)?;
values.push(value_row);
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(Statement::Insert(InsertStatement {
table,
columns,
values,
}))
}
fn parse_upsert(&mut self) -> Result<Statement> {
self.expect(TokenType::Upsert)?;
self.expect(TokenType::Into)?;
let table = self.expect_identifier()?;
self.expect(TokenType::LeftParen)?;
let columns = self.parse_identifier_list()?;
self.expect(TokenType::RightParen)?;
self.expect(TokenType::Values)?;
let mut values = Vec::new();
loop {
self.expect(TokenType::LeftParen)?;
let value_row = self.parse_expression_list()?;
self.expect(TokenType::RightParen)?;
values.push(value_row);
if !self.match_token(TokenType::Comma) {
break;
}
}
self.expect(TokenType::On)?;
let key_column = self.expect_identifier()?;
Ok(Statement::Upsert(UpsertStatement {
table,
columns,
values,
key_column,
}))
}
fn parse_update(&mut self) -> Result<Statement> {
self.expect(TokenType::Update)?;
let table = self.expect_identifier()?;
self.expect(TokenType::Set)?;
let mut assignments = Vec::new();
loop {
let column = self.expect_identifier()?;
self.expect(TokenType::Equal)?;
let value = self.parse_expression()?;
assignments.push((column, value));
if !self.match_token(TokenType::Comma) {
break;
}
}
let where_clause = if self.match_token(TokenType::Where) {
Some(self.parse_expression()?)
} else {
None
};
Ok(Statement::Update(UpdateStatement {
table,
assignments,
where_clause,
}))
}
fn parse_delete(&mut self) -> Result<Statement> {
self.expect(TokenType::Delete)?;
self.expect(TokenType::From)?;
let table = self.expect_identifier()?;
let where_clause = if self.match_token(TokenType::Where) {
Some(self.parse_expression()?)
} else {
None
};
Ok(Statement::Delete(DeleteStatement {
table,
where_clause,
}))
}
fn parse_create(&mut self) -> Result<Statement> {
self.expect(TokenType::Create)?;
self.expect(TokenType::Table)?;
let if_not_exists = if self.match_token(TokenType::If) {
self.expect(TokenType::Not)?;
self.expect(TokenType::Exists)?;
true
} else {
false
};
let name = self.expect_identifier()?;
self.expect(TokenType::LeftParen)?;
let columns = self.parse_column_definitions()?;
self.expect(TokenType::RightParen)?;
let mut file_path = String::new();
let mut delimiter = None;
let mut data_starts_on = None;
let mut comment_char = None;
let mut quote_char = None;
if self.match_token(TokenType::File) {
file_path = self.expect_string()?;
}
if self.match_token(TokenType::Delimiter) {
let delim_str = self.expect_string()?;
delimiter = delim_str.chars().next();
}
while let Ok(token) = self.peek() {
if let TokenType::Identifier(ref id) = token.token_type {
match id.to_uppercase().as_str() {
"DATA_STARTS_ON" => {
self.advance()?;
data_starts_on = Some(self.expect_integer()? as usize);
}
"COMMENT_CHAR" => {
self.advance()?;
let comment_str = self.expect_string()?;
comment_char = comment_str.chars().next();
}
"QUOTE_CHAR" => {
self.advance()?;
let quote_str = self.expect_string()?;
quote_char = quote_str.chars().next();
}
_ => break,
}
} else {
break;
}
}
Ok(Statement::CreateTable(CreateTableStatement {
name,
columns,
file_path,
delimiter,
data_starts_on,
comment_char,
quote_char,
if_not_exists,
}))
}
fn parse_column_definitions(&mut self) -> Result<Vec<ColumnDefinition>> {
let mut columns = Vec::new();
loop {
let col_name = self.expect_identifier()?;
let type_str = self.expect_identifier()?;
let data_type = self.parse_column_type(&type_str)?;
let nullable = if self.match_token(TokenType::Not) {
self.expect(TokenType::Null)?;
false
} else {
self.match_token(TokenType::Null); true
};
columns.push(ColumnDefinition {
name: col_name,
data_type,
nullable,
});
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(columns)
}
fn parse_column_type(&self, type_str: &str) -> Result<ColumnType> {
match type_str.to_uppercase().as_str() {
"INTEGER" | "INT" => Ok(ColumnType::Integer),
"FLOAT" | "DOUBLE" | "REAL" => Ok(ColumnType::Float),
"STRING" | "VARCHAR" | "TEXT" | "CHAR" => Ok(ColumnType::String),
"BOOLEAN" | "BOOL" => Ok(ColumnType::Boolean),
"DATE" => Ok(ColumnType::Date),
"DATETIME" | "TIMESTAMP" => Ok(ColumnType::DateTime),
"TIME" => Ok(ColumnType::Time),
_ => Err(DdbError::ParseError(format!(
"Unknown column type: {}",
type_str
))),
}
}
fn parse_drop(&mut self) -> Result<Statement> {
self.expect(TokenType::Drop)?;
self.expect(TokenType::Table)?;
let if_exists = if self.match_token(TokenType::If) {
self.expect(TokenType::Exists)?;
true
} else {
false
};
let name = self.expect_identifier()?;
Ok(Statement::DropTable(DropTableStatement { name, if_exists }))
}
fn parse_use(&mut self) -> Result<Statement> {
self.expect(TokenType::Use)?;
let database = self.expect_identifier()?;
Ok(Statement::Use(UseStatement { database }))
}
fn parse_show(&mut self) -> Result<Statement> {
self.expect(TokenType::Show)?;
let token = self.peek()?;
let show_stmt = match &token.token_type {
TokenType::Tables => {
self.advance()?;
ShowStatement::Tables
}
TokenType::Columns => {
self.advance()?;
self.expect(TokenType::From)?;
let table = self.expect_identifier()?;
ShowStatement::Columns(table)
}
TokenType::Variables => {
self.advance()?;
ShowStatement::Variables
}
_ => {
return Err(DdbError::ParseError(format!(
"Expected TABLES, COLUMNS, or VARIABLES after SHOW, got {:?}",
token.token_type
)))
}
};
Ok(Statement::Show(show_stmt))
}
fn parse_set(&mut self) -> Result<Statement> {
self.expect(TokenType::Set)?;
let variable = self.expect_identifier()?;
self.expect(TokenType::Equal)?;
let value = self.parse_expression()?;
Ok(Statement::Set(SetStatement { variable, value }))
}
fn parse_expression(&mut self) -> Result<Expression> {
self.parse_or_expression()
}
fn parse_or_expression(&mut self) -> Result<Expression> {
let mut left = self.parse_and_expression()?;
while self.match_token(TokenType::Or) {
let right = self.parse_and_expression()?;
left = Expression::BinaryOp {
left: Box::new(left),
op: BinaryOperator::Or,
right: Box::new(right),
};
}
Ok(left)
}
fn parse_and_expression(&mut self) -> Result<Expression> {
let mut left = self.parse_comparison_expression()?;
while self.match_token(TokenType::And) {
let right = self.parse_comparison_expression()?;
left = Expression::BinaryOp {
left: Box::new(left),
op: BinaryOperator::And,
right: Box::new(right),
};
}
Ok(left)
}
fn parse_comparison_expression(&mut self) -> Result<Expression> {
let mut left = self.parse_primary_expression()?;
while let Some(op) = self.match_comparison_op() {
let right = self.parse_primary_expression()?;
left = Expression::BinaryOp {
left: Box::new(left),
op,
right: Box::new(right),
};
}
Ok(left)
}
fn match_comparison_op(&mut self) -> Option<BinaryOperator> {
let token = self.peek().ok()?;
let op = match &token.token_type {
TokenType::Equal => Some(BinaryOperator::Equal),
TokenType::NotEqual => Some(BinaryOperator::NotEqual),
TokenType::GreaterThan => Some(BinaryOperator::GreaterThan),
TokenType::GreaterEqual => Some(BinaryOperator::GreaterEqual),
TokenType::LessThan => Some(BinaryOperator::LessThan),
TokenType::LessEqual => Some(BinaryOperator::LessEqual),
TokenType::Like => Some(BinaryOperator::Like),
_ => None,
};
if op.is_some() {
self.advance().ok();
}
op
}
fn parse_primary_expression(&mut self) -> Result<Expression> {
let token = self.peek()?;
match &token.token_type {
TokenType::Not => {
self.advance()?;
let operand = self.parse_primary_expression()?;
Ok(Expression::UnaryOp {
op: UnaryOperator::Not,
operand: Box::new(operand),
})
}
TokenType::String(s) => {
let s = s.clone();
self.advance()?;
Ok(Expression::Literal(Literal::String(s)))
}
TokenType::Number(n_str) => {
let n_str = n_str.clone();
self.advance()?;
if let Ok(i) = n_str.parse::<i64>() {
Ok(Expression::Literal(Literal::Integer(i)))
} else if let Ok(f) = n_str.parse::<f64>() {
Ok(Expression::Literal(Literal::Number(f)))
} else {
Err(DdbError::ParseError(format!("Invalid number: {}", n_str)))
}
}
TokenType::True => {
self.advance()?;
Ok(Expression::Literal(Literal::Boolean(true)))
}
TokenType::False => {
self.advance()?;
Ok(Expression::Literal(Literal::Boolean(false)))
}
TokenType::Null => {
self.advance()?;
Ok(Expression::Literal(Literal::Null))
}
TokenType::Identifier(_) => {
let name = self.expect_identifier()?;
if name.starts_with("@@") {
let var_name = name[2..].to_string();
return Ok(Expression::SystemVariable(var_name));
}
if self.match_token(TokenType::LeftParen) {
let args = self.parse_function_args()?;
self.expect(TokenType::RightParen)?;
Ok(Expression::Function { name, args })
} else {
if self.match_token(TokenType::Is) {
self.expect(TokenType::Null)?;
Ok(Expression::UnaryOp {
op: UnaryOperator::IsNull,
operand: Box::new(Expression::Column(name)),
})
} else {
Ok(Expression::Column(name))
}
}
}
TokenType::LeftParen => {
self.advance()?;
let expr = self.parse_expression()?;
self.expect(TokenType::RightParen)?;
Ok(expr)
}
_ => Err(DdbError::ParseError(format!(
"Unexpected token in expression: {:?}",
token.token_type
))),
}
}
fn parse_function_args(&mut self) -> Result<Vec<Expression>> {
let mut args = Vec::new();
if matches!(self.peek()?.token_type, TokenType::RightParen) {
return Ok(args);
}
if self.match_token(TokenType::Star) {
args.push(Expression::Column("*".to_string()));
return Ok(args);
}
loop {
args.push(self.parse_expression()?);
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(args)
}
fn parse_expression_list(&mut self) -> Result<Vec<Expression>> {
let mut exprs = Vec::new();
loop {
exprs.push(self.parse_expression()?);
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(exprs)
}
fn parse_identifier_list(&mut self) -> Result<Vec<String>> {
let mut identifiers = Vec::new();
loop {
identifiers.push(self.expect_identifier()?);
if self.peek().ok().map(|t| matches!(t.token_type, TokenType::Identifier(_))).unwrap_or(false) {
self.advance()?;
}
if !self.match_token(TokenType::Comma) {
break;
}
}
Ok(identifiers)
}
fn peek(&self) -> Result<&Token> {
self.tokens.get(self.position).ok_or_else(|| {
DdbError::ParseError("Unexpected end of input".to_string())
})
}
fn advance(&mut self) -> Result<&Token> {
let pos = self.position;
if pos >= self.tokens.len() {
return Err(DdbError::ParseError("Unexpected end of input".to_string()));
}
self.position += 1;
Ok(&self.tokens[pos])
}
fn match_token(&mut self, expected: TokenType) -> bool {
if let Ok(token) = self.peek() {
if std::mem::discriminant(&token.token_type) == std::mem::discriminant(&expected) {
self.advance().ok();
return true;
}
}
false
}
fn expect(&mut self, expected: TokenType) -> Result<()> {
let token = self.peek()?;
if std::mem::discriminant(&token.token_type) == std::mem::discriminant(&expected) {
self.advance()?;
Ok(())
} else {
Err(DdbError::ParseError(format!(
"Expected {:?}, got {:?}",
expected, token.token_type
)))
}
}
fn expect_identifier(&mut self) -> Result<String> {
let token = self.advance()?;
match &token.token_type {
TokenType::Identifier(name) => Ok(name.clone()),
_ => Err(DdbError::ParseError(format!(
"Expected identifier, got {:?}",
token.token_type
))),
}
}
fn expect_string(&mut self) -> Result<String> {
let token = self.advance()?;
match &token.token_type {
TokenType::String(s) => Ok(s.clone()),
_ => Err(DdbError::ParseError(format!(
"Expected string, got {:?}",
token.token_type
))),
}
}
fn expect_integer(&mut self) -> Result<i64> {
let token = self.advance()?;
match &token.token_type {
TokenType::Number(n_str) => {
n_str.parse::<i64>().map_err(|_| {
DdbError::ParseError(format!("Expected integer, got: {}", n_str))
})
}
_ => Err(DdbError::ParseError(format!(
"Expected integer, got {:?}",
token.token_type
))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::Tokenizer;
fn parse_sql(sql: &str) -> Result<Statement> {
let mut tokenizer = Tokenizer::new();
let tokens = tokenizer.tokenize(sql)?;
let mut parser = Parser::new(tokens);
parser.parse()
}
#[test]
fn test_parse_simple_select() {
let stmt = parse_sql("SELECT * FROM users").unwrap();
match stmt {
Statement::Select(sel) => {
assert!(!sel.distinct);
assert_eq!(sel.columns.len(), 1);
assert!(matches!(sel.columns[0], SelectColumn::Wildcard));
assert_eq!(sel.from, Some("users".to_string()));
assert!(sel.where_clause.is_none());
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_select_with_columns() {
let stmt = parse_sql("SELECT id, name, email FROM users").unwrap();
match stmt {
Statement::Select(sel) => {
assert_eq!(sel.columns.len(), 3);
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_select_with_where() {
let stmt = parse_sql("SELECT * FROM users WHERE age > 18").unwrap();
match stmt {
Statement::Select(sel) => {
assert!(sel.where_clause.is_some());
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_select_with_functions() {
let stmt = parse_sql("SELECT COUNT(*), AVG(age) FROM users").unwrap();
match stmt {
Statement::Select(sel) => {
assert_eq!(sel.columns.len(), 2);
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_select_with_order_by() {
let stmt = parse_sql("SELECT * FROM users ORDER BY name ASC, age DESC").unwrap();
match stmt {
Statement::Select(sel) => {
assert_eq!(sel.order_by.len(), 2);
assert!(matches!(sel.order_by[0].direction, OrderDirection::Asc));
assert!(matches!(sel.order_by[1].direction, OrderDirection::Desc));
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_select_with_limit() {
let stmt = parse_sql("SELECT * FROM users LIMIT 10").unwrap();
match stmt {
Statement::Select(sel) => {
assert!(sel.limit.is_some());
let limit = sel.limit.unwrap();
assert_eq!(limit.count, 10);
assert!(limit.offset.is_none());
}
_ => panic!("Expected SELECT statement"),
}
}
#[test]
fn test_parse_insert() {
let stmt = parse_sql("INSERT INTO users (name, email) VALUES ('John', 'john@test.com')").unwrap();
match stmt {
Statement::Insert(ins) => {
assert_eq!(ins.table, "users");
assert_eq!(ins.columns.len(), 2);
assert_eq!(ins.values.len(), 1);
}
_ => panic!("Expected INSERT statement"),
}
}
#[test]
fn test_parse_update() {
let stmt = parse_sql("UPDATE users SET name = 'Jane' WHERE id = 1").unwrap();
match stmt {
Statement::Update(upd) => {
assert_eq!(upd.table, "users");
assert_eq!(upd.assignments.len(), 1);
assert!(upd.where_clause.is_some());
}
_ => panic!("Expected UPDATE statement"),
}
}
#[test]
fn test_parse_delete() {
let stmt = parse_sql("DELETE FROM users WHERE id = 1").unwrap();
match stmt {
Statement::Delete(del) => {
assert_eq!(del.table, "users");
assert!(del.where_clause.is_some());
}
_ => panic!("Expected DELETE statement"),
}
}
#[test]
fn test_parse_create_table() {
let stmt = parse_sql(
"CREATE TABLE users (id INTEGER NOT NULL, name STRING, age INTEGER)"
).unwrap();
match stmt {
Statement::CreateTable(create) => {
assert_eq!(create.name, "users");
assert_eq!(create.columns.len(), 3);
assert_eq!(create.columns[0].name, "id");
assert!(matches!(create.columns[0].data_type, ColumnType::Integer));
assert!(!create.columns[0].nullable);
assert_eq!(create.columns[1].name, "name");
assert!(matches!(create.columns[1].data_type, ColumnType::String));
assert!(create.columns[1].nullable);
assert_eq!(create.columns[2].name, "age");
assert!(matches!(create.columns[2].data_type, ColumnType::Integer));
assert!(create.columns[2].nullable);
assert!(!create.if_not_exists);
}
_ => panic!("Expected CREATE TABLE statement"),
}
}
#[test]
fn test_parse_create_table_if_not_exists() {
let stmt = parse_sql(
"CREATE TABLE IF NOT EXISTS users (id INTEGER NOT NULL)"
).unwrap();
match stmt {
Statement::CreateTable(create) => {
assert_eq!(create.name, "users");
assert!(create.if_not_exists);
assert_eq!(create.columns.len(), 1);
}
_ => panic!("Expected CREATE TABLE statement"),
}
}
#[test]
fn test_parse_create_table_with_options() {
let stmt = parse_sql(
"CREATE TABLE users (id INTEGER, name STRING) FILE '/path/to/file.csv' DELIMITER ',' DATA_STARTS_ON 2 COMMENT_CHAR '#' QUOTE_CHAR '\"'"
).unwrap();
match stmt {
Statement::CreateTable(create) => {
assert_eq!(create.name, "users");
assert_eq!(create.file_path, "/path/to/file.csv");
assert_eq!(create.delimiter, Some(','));
assert_eq!(create.data_starts_on, Some(2));
assert_eq!(create.comment_char, Some('#'));
assert_eq!(create.quote_char, Some('"'));
}
_ => panic!("Expected CREATE TABLE statement"),
}
}
#[test]
fn test_parse_drop_table() {
let stmt = parse_sql("DROP TABLE users").unwrap();
match stmt {
Statement::DropTable(drop) => {
assert_eq!(drop.name, "users");
assert!(!drop.if_exists);
}
_ => panic!("Expected DROP TABLE statement"),
}
}
#[test]
fn test_parse_drop_table_if_exists() {
let stmt = parse_sql("DROP TABLE IF EXISTS users").unwrap();
match stmt {
Statement::DropTable(drop) => {
assert_eq!(drop.name, "users");
assert!(drop.if_exists);
}
_ => panic!("Expected DROP TABLE statement"),
}
}
#[test]
fn test_parse_set() {
let stmt = parse_sql("SET output_format = 'json'").unwrap();
match stmt {
Statement::Set(set) => {
assert_eq!(set.variable, "output_format");
match set.value {
Expression::Literal(Literal::String(s)) => assert_eq!(s, "json"),
_ => panic!("Expected string literal"),
}
}
_ => panic!("Expected SET statement"),
}
}
#[test]
fn test_parse_set_with_number() {
let stmt = parse_sql("SET max_rows = 100").unwrap();
match stmt {
Statement::Set(set) => {
assert_eq!(set.variable, "max_rows");
match set.value {
Expression::Literal(Literal::Integer(i)) => assert_eq!(i, 100),
_ => panic!("Expected integer literal"),
}
}
_ => panic!("Expected SET statement"),
}
}
}