@@ -3,8 +3,8 @@ use super::sqlpage_functions::functions::SqlPageFunctionName;
33use super :: sqlpage_functions:: { are_params_extractable, func_call_to_param} ;
44use super :: syntax_tree:: StmtParam ;
55use crate :: file_cache:: AsyncFromStrWithState ;
6+ use crate :: webserver:: database:: error_highlighting:: quote_source_with_highlight;
67use crate :: { AppState , Database } ;
7- use anyhow:: Context ;
88use async_trait:: async_trait;
99use sqlparser:: ast:: {
1010 BinaryOperator , CastKind , CharacterLength , DataType , Expr , Function , FunctionArg ,
@@ -16,7 +16,6 @@ use sqlparser::parser::{Parser, ParserError};
1616use sqlparser:: tokenizer:: Token :: { SemiColon , EOF } ;
1717use sqlparser:: tokenizer:: Tokenizer ;
1818use sqlx:: any:: AnyKind ;
19- use std:: fmt:: Write ;
2019use std:: ops:: ControlFlow ;
2120use std:: str:: FromStr ;
2221
@@ -92,21 +91,28 @@ fn parse_sql<'a>(
9291 log:: trace!( "Parsing SQL: {sql}" ) ;
9392 let tokens = Tokenizer :: new ( dialect, sql)
9493 . tokenize_with_location ( )
95- . with_context ( || "SQLPage's SQL parser could not tokenize the sql file" ) ?;
94+ . map_err ( |err| {
95+ let location = err. location ;
96+ anyhow:: Error :: new ( err) . context ( format ! ( "The SQLPage parser couldn't understand the SQL file. Tokenization failed. Please check for syntax errors:\n {}" , quote_source_with_highlight( sql, location. line, location. column) ) )
97+ } ) ?;
9698 let mut parser = Parser :: new ( dialect) . with_tokens_with_locations ( tokens) ;
9799 let db_kind = kind_of_dialect ( dialect) ;
98100 Ok ( std:: iter:: from_fn ( move || {
99- parse_single_statement ( & mut parser, db_kind)
101+ parse_single_statement ( & mut parser, db_kind, sql )
100102 } ) )
101103}
102104
103- fn parse_single_statement ( parser : & mut Parser < ' _ > , db_kind : AnyKind ) -> Option < ParsedStatement > {
105+ fn parse_single_statement (
106+ parser : & mut Parser < ' _ > ,
107+ db_kind : AnyKind ,
108+ source_sql : & str ,
109+ ) -> Option < ParsedStatement > {
104110 if parser. peek_token ( ) == EOF {
105111 return None ;
106112 }
107113 let mut stmt = match parser. parse_statement ( ) {
108114 Ok ( stmt) => stmt,
109- Err ( err) => return Some ( syntax_error ( err, parser) ) ,
115+ Err ( err) => return Some ( syntax_error ( err, parser, source_sql ) ) ,
110116 } ;
111117 log:: debug!( "Parsed statement: {stmt}" ) ;
112118 let mut semicolon = false ;
@@ -145,25 +151,13 @@ fn parse_single_statement(parser: &mut Parser<'_>, db_kind: AnyKind) -> Option<P
145151 } ) )
146152}
147153
148- fn syntax_error ( err : ParserError , parser : & mut Parser ) -> ParsedStatement {
149- let mut err_msg = String :: with_capacity ( 128 ) ;
150- parser. prev_token ( ) ; // go back to the token that caused the error
151- for i in 0 ..32 {
152- let next_token = parser. next_token ( ) ;
153- if i == 0 {
154- writeln ! (
155- & mut err_msg,
156- "SQLPage found a syntax error on line {}, character {}:" ,
157- next_token. location. line, next_token. location. column
158- )
159- . unwrap ( ) ;
160- }
161- if next_token == EOF {
162- break ;
163- }
164- write ! ( & mut err_msg, "{next_token} " ) . unwrap ( ) ;
165- }
166- ParsedStatement :: Error ( anyhow:: Error :: from ( err) . context ( err_msg) )
154+ fn syntax_error ( err : ParserError , parser : & Parser , sql : & str ) -> ParsedStatement {
155+ dbg ! ( ( & err, & parser. peek_token_no_skip( ) , & sql) ) ;
156+ let location = parser. peek_token_no_skip ( ) . location ;
157+ ParsedStatement :: Error ( anyhow:: Error :: from ( err) . context ( format ! (
158+ "The SQLPage parser couldn't understand the SQL file. Parsing failed. Please check for syntax errors:\n {}" ,
159+ quote_source_with_highlight( sql, location. line, location. column)
160+ ) ) )
167161}
168162
169163fn dialect_for_db ( db_kind : AnyKind ) -> Box < dyn Dialect > {
@@ -856,7 +850,8 @@ mod test {
856850 #[ test]
857851 fn test_sqlpage_function_with_argument ( ) {
858852 for & ( dialect, kind) in ALL_DIALECTS {
859- let mut ast = parse_stmt ( "select sqlpage.fetch($x)" , dialect) ;
853+ let sql = "select sqlpage.fetch($x)" ;
854+ let mut ast = parse_stmt ( sql, dialect) ;
860855 let parameters = ParameterExtractor :: extract_parameters ( & mut ast, kind) ;
861856 assert_eq ! (
862857 parameters,
@@ -874,7 +869,7 @@ mod test {
874869 let sql = "set x = $y" ;
875870 for & ( dialect, db_kind) in ALL_DIALECTS {
876871 let mut parser = Parser :: new ( dialect) . try_with_sql ( sql) . unwrap ( ) ;
877- let stmt = parse_single_statement ( & mut parser, db_kind) ;
872+ let stmt = parse_single_statement ( & mut parser, db_kind, sql ) ;
878873 if let Some ( ParsedStatement :: SetVariable {
879874 variable,
880875 value : StmtWithParams { query, params, .. } ,
0 commit comments