mod clauses;
mod model;
mod projection;
mod statement;
#[cfg(test)]
mod tests;
use crate::{
db::sql_shared::{Keyword, SqlTokenCursor, TokenKind, tokenize_sql},
value::Value,
};
pub(crate) use crate::db::sql_shared::SqlParseError;
pub(crate) use model::{
SqlAggregateCall, SqlAggregateInputExpr, SqlAggregateKind, SqlArithmeticProjectionCall,
SqlArithmeticProjectionOp, SqlAssignment, SqlCaseArm, SqlDeleteStatement, SqlDescribeStatement,
SqlExplainMode, SqlExplainStatement, SqlExplainTarget, SqlExpr, SqlExprBinaryOp,
SqlExprUnaryOp, SqlInsertSource, SqlInsertStatement, SqlOrderDirection, SqlOrderTerm,
SqlProjection, SqlProjectionOperand, SqlReturningProjection, SqlRoundProjectionCall,
SqlRoundProjectionInput, SqlSelectItem, SqlSelectStatement, SqlShowColumnsStatement,
SqlShowEntitiesStatement, SqlShowIndexesStatement, SqlStatement, SqlTextFunction,
SqlTextFunctionCall, SqlUpdateStatement,
};
pub(crate) fn parse_sql(sql: &str) -> Result<SqlStatement, SqlParseError> {
let tokens = tokenize_sql(sql)?;
if tokens.is_empty() {
return Err(SqlParseError::EmptyInput);
}
let mut parser = Parser::new(SqlTokenCursor::new(tokens));
let statement = parser.parse_statement()?;
if parser.eat_semicolon() && !parser.is_eof() {
return Err(SqlParseError::unsupported_feature(
"multi-statement SQL input",
));
}
if !parser.is_eof() {
if let Some(err) = parser.trailing_clause_order_error(&statement) {
return Err(err);
}
if let Some(feature) = parser.peek_unsupported_feature() {
return Err(SqlParseError::unsupported_feature(feature));
}
return Err(SqlParseError::expected_end_of_input(parser.peek_kind()));
}
Ok(statement)
}
struct Parser {
cursor: SqlTokenCursor,
}
impl Parser {
const fn new(cursor: SqlTokenCursor) -> Self {
Self { cursor }
}
fn parse_literal(&mut self) -> Result<Value, SqlParseError> {
self.cursor.parse_literal()
}
fn parse_u32_literal(&mut self, clause: &str) -> Result<u32, SqlParseError> {
let Some(TokenKind::Number(value)) = self.peek_kind() else {
return Err(SqlParseError::expected(
&format!("integer literal after {clause}"),
self.peek_kind(),
));
};
let value = value.as_str();
if value.contains('.') || value.starts_with('-') {
return Err(SqlParseError::invalid_syntax(format!(
"{clause} requires a non-negative integer literal"
)));
}
let parsed = value.parse::<u32>().map_err(|_| {
SqlParseError::invalid_syntax(format!("{clause} value exceeds supported u32 bound"))
})?;
self.cursor.advance();
Ok(parsed)
}
fn expect_keyword(&mut self, keyword: Keyword) -> Result<(), SqlParseError> {
self.cursor.expect_keyword(keyword)
}
fn expect_identifier(&mut self) -> Result<String, SqlParseError> {
self.cursor.expect_identifier()
}
fn expect_lparen(&mut self) -> Result<(), SqlParseError> {
self.cursor.expect_lparen()
}
fn expect_rparen(&mut self) -> Result<(), SqlParseError> {
self.cursor.expect_rparen()
}
fn eat_keyword(&mut self, keyword: Keyword) -> bool {
self.cursor.eat_keyword(keyword)
}
fn eat_identifier_keyword(&mut self, keyword: &str) -> bool {
self.cursor.eat_identifier_keyword(keyword)
}
fn eat_comma(&mut self) -> bool {
self.cursor.eat_comma()
}
fn eat_plus(&mut self) -> bool {
self.cursor.eat_plus()
}
fn eat_minus(&mut self) -> bool {
self.cursor.eat_minus()
}
fn eat_slash(&mut self) -> bool {
self.cursor.eat_slash()
}
fn eat_semicolon(&mut self) -> bool {
self.cursor.eat_semicolon()
}
fn eat_star(&mut self) -> bool {
self.cursor.eat_star()
}
fn peek_keyword(&self, keyword: Keyword) -> bool {
self.cursor.peek_keyword(keyword)
}
fn peek_lparen(&self) -> bool {
self.cursor.peek_lparen()
}
fn peek_unsupported_feature(&self) -> Option<&'static str> {
self.cursor.peek_unsupported_feature()
}
fn peek_kind(&self) -> Option<&TokenKind> {
self.cursor.peek_kind()
}
fn expect_identifier_keyword(&mut self, keyword: &str) -> Result<(), SqlParseError> {
if self.eat_identifier_keyword(keyword) {
return Ok(());
}
Err(SqlParseError::expected(keyword, self.peek_kind()))
}
const fn is_eof(&self) -> bool {
self.cursor.is_eof()
}
}