Improve SQL parsing errors

This commit is contained in:
Tobie Morgan Hitchcock 2022-01-15 09:51:57 +00:00
parent 91c53e4188
commit f202bd5ab4
2 changed files with 42 additions and 11 deletions

View file

@ -39,9 +39,10 @@ pub enum Error {
#[error("The query was cancelled before completion")] #[error("The query was cancelled before completion")]
CancelledError, CancelledError,
#[error("Parse error at position {pos} when parsing '{sql}'")] #[error("Parse error on line {line} at character {char} when parsing '{sql}'")]
ParseError { ParseError {
pos: usize, line: usize,
char: usize,
sql: String, sql: String,
}, },

View file

@ -3,25 +3,55 @@ use crate::sql::query::{query, Query};
use nom::Err; use nom::Err;
use std::str; use std::str;
#[allow(dead_code)]
pub fn parse(input: &str) -> Result<Query, Error> { pub fn parse(input: &str) -> Result<Query, Error> {
match input.trim().len() { match input.trim().len() {
0 => Err(Error::EmptyError), 0 => Err(Error::EmptyError),
_ => match query(input) { _ => match query(input) {
Ok((_, query)) => Ok(query), Ok((_, query)) => Ok(query),
Err(Err::Error(e)) => Err(Error::ParseError { Err(Err::Error(e)) => match locate(input, e.input) {
pos: input.len() - e.input.len(), (s, l, c) => Err(Error::ParseError {
sql: String::from(e.input), line: l,
char: c,
sql: s.to_string(),
}), }),
Err(Err::Failure(e)) => Err(Error::ParseError { },
pos: input.len() - e.input.len(), Err(Err::Failure(e)) => match locate(input, e.input) {
sql: String::from(e.input), (s, l, c) => Err(Error::ParseError {
line: l,
char: c,
sql: s.to_string(),
}), }),
},
Err(Err::Incomplete(_)) => Err(Error::EmptyError), Err(Err::Incomplete(_)) => Err(Error::EmptyError),
}, },
} }
} }
fn truncate(s: &str, l: usize) -> &str {
match s.char_indices().nth(l) {
None => s,
Some((i, _)) => &s[..i],
}
}
fn locate<'a>(input: &str, tried: &'a str) -> (&'a str, usize, usize) {
let index = input.len() - tried.len();
let tried = truncate(&tried, 100);
let lines = input.split('\n').collect::<Vec<&str>>();
let lines = lines.iter().map(|l| l.len()).enumerate();
let (mut total, mut chars) = (0, 0);
for (line, size) in lines {
total += size + 1;
if index < total {
let line_num = line + 1;
let char_num = index - chars;
return (tried, line_num, char_num);
}
chars += size + 1;
}
return (tried, 0, 0);
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {