use super::CachedStatement;
use crate::error::Error;
use crate::parser::Parser;
pub(super) struct SqlParser<'a> {
parser: Parser<'a>,
returning_keyword_found: bool,
}
impl SqlParser<'_> {
pub fn new(source: &str) -> SqlParser<'_> {
SqlParser {
parser: Parser::new(source),
returning_keyword_found: false,
}
}
pub fn parse(
&mut self,
statement: &mut CachedStatement,
) -> Result<(), Error> {
let mut initial_keyword_found = false;
let mut last_was_string = false;
let mut last_was_alpha = false;
let mut last_ch = ' ';
while let Some(ch) = self.parser.peek_char() {
let is_alpha = ch.is_ascii_alphabetic();
if is_alpha && !last_was_alpha {
self.parser.save_pos();
} else if !is_alpha && last_was_alpha {
let keyword = self.parser.save_pos();
if !initial_keyword_found {
initial_keyword_found = true;
statement.determine_statement_type(keyword);
} else if statement.is_dml && !self.returning_keyword_found {
if keyword.to_uppercase() == "RETURNING" {
self.returning_keyword_found = true;
}
} else if self.returning_keyword_found
&& keyword.to_uppercase() == "INTO"
{
statement.is_returning = true;
}
}
if ch == '\'' {
last_was_string = true;
if last_ch.eq_ignore_ascii_case(&'q') {
self.parse_qstring()?;
} else {
self.parse_quoted_string()?;
}
} else if !ch.is_whitespace() {
if ch == '-' {
self.parse_single_line_comment()?;
} else if ch == '/' {
self.parse_multiple_line_comment()?;
} else if ch == '"' {
self.parse_quoted_string()?;
} else if ch == ':' && !last_was_string {
self.parse_bind_name(statement)?;
}
last_was_string = false;
}
last_was_alpha = is_alpha;
self.parser.next_char();
last_ch = ch;
}
Ok(())
}
fn parse_bind_name(
&mut self,
statement: &mut CachedStatement,
) -> Result<(), Error> {
let mut quoted_name = false;
let mut in_bind = false;
let mut digits_only = false;
self.parser.save_pos();
self.parser.next_char();
while let Some(ch) = self.parser.peek_char() {
if !in_bind {
if ch.is_whitespace() {
self.parser.next_char();
continue;
} else if ch == '"' {
quoted_name = true;
} else if ch.is_ascii_digit() {
digits_only = true;
} else if !ch.is_alphabetic() {
self.parser.restore_pos();
break;
}
in_bind = true;
} else if (digits_only && !ch.is_ascii_digit())
|| (quoted_name && ch == '"')
|| (!digits_only
&& !quoted_name
&& !ch.is_alphanumeric()
&& ch != '_'
&& ch != '$'
&& ch != '#')
{
break;
}
self.parser.next_char();
}
if in_bind {
let bind_name = if quoted_name {
self.parser.save_pos()[1..].trim_start()[1..].to_string()
} else if digits_only {
self.parser.save_pos()[1..].trim_start().to_string()
} else {
self.parser.save_pos()[1..].trim_start().to_uppercase()
};
statement.add_bind(bind_name);
}
Ok(())
}
fn parse_multiple_line_comment(&mut self) -> Result<(), Error> {
todo!();
}
fn parse_qstring(&mut self) -> Result<(), Error> {
todo!();
}
fn parse_quoted_string(&mut self) -> Result<(), Error> {
self.parser.save_pos();
let quote_char = self.parser.next_char().unwrap();
while let Some(ch) = self.parser.next_char() {
if ch == quote_char {
self.parser.save_pos();
return Ok(());
}
}
self.parser.restore_pos();
Ok(())
}
fn parse_single_line_comment(&mut self) -> Result<(), Error> {
let mut in_comment = false;
self.parser.save_pos();
self.parser.next_char();
while let Some(ch) = self.parser.peek_char() {
if !in_comment {
if ch != '-' {
self.parser.restore_pos();
return Ok(());
}
in_comment = true;
} else if ch == '\n' {
break;
}
self.parser.next_char();
}
self.parser.save_pos();
Ok(())
}
}