@@ -3,8 +3,8 @@ use super::sqlpage_functions::functions::SqlPageFunctionName;
3
3
use super :: sqlpage_functions:: { are_params_extractable, func_call_to_param} ;
4
4
use super :: syntax_tree:: StmtParam ;
5
5
use crate :: file_cache:: AsyncFromStrWithState ;
6
+ use crate :: webserver:: database:: error_highlighting:: quote_source_with_highlight;
6
7
use crate :: { AppState , Database } ;
7
- use anyhow:: Context ;
8
8
use async_trait:: async_trait;
9
9
use sqlparser:: ast:: {
10
10
BinaryOperator , CastKind , CharacterLength , DataType , Expr , Function , FunctionArg ,
@@ -16,7 +16,6 @@ use sqlparser::parser::{Parser, ParserError};
16
16
use sqlparser:: tokenizer:: Token :: { SemiColon , EOF } ;
17
17
use sqlparser:: tokenizer:: Tokenizer ;
18
18
use sqlx:: any:: AnyKind ;
19
- use std:: fmt:: Write ;
20
19
use std:: ops:: ControlFlow ;
21
20
use std:: str:: FromStr ;
22
21
@@ -92,21 +91,28 @@ fn parse_sql<'a>(
92
91
log:: trace!( "Parsing SQL: {sql}" ) ;
93
92
let tokens = Tokenizer :: new ( dialect, sql)
94
93
. 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
+ } ) ?;
96
98
let mut parser = Parser :: new ( dialect) . with_tokens_with_locations ( tokens) ;
97
99
let db_kind = kind_of_dialect ( dialect) ;
98
100
Ok ( std:: iter:: from_fn ( move || {
99
- parse_single_statement ( & mut parser, db_kind)
101
+ parse_single_statement ( & mut parser, db_kind, sql )
100
102
} ) )
101
103
}
102
104
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 > {
104
110
if parser. peek_token ( ) == EOF {
105
111
return None ;
106
112
}
107
113
let mut stmt = match parser. parse_statement ( ) {
108
114
Ok ( stmt) => stmt,
109
- Err ( err) => return Some ( syntax_error ( err, parser) ) ,
115
+ Err ( err) => return Some ( syntax_error ( err, parser, source_sql ) ) ,
110
116
} ;
111
117
log:: debug!( "Parsed statement: {stmt}" ) ;
112
118
let mut semicolon = false ;
@@ -145,25 +151,13 @@ fn parse_single_statement(parser: &mut Parser<'_>, db_kind: AnyKind) -> Option<P
145
151
} ) )
146
152
}
147
153
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
+ ) ) )
167
161
}
168
162
169
163
fn dialect_for_db ( db_kind : AnyKind ) -> Box < dyn Dialect > {
@@ -856,7 +850,8 @@ mod test {
856
850
#[ test]
857
851
fn test_sqlpage_function_with_argument ( ) {
858
852
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) ;
860
855
let parameters = ParameterExtractor :: extract_parameters ( & mut ast, kind) ;
861
856
assert_eq ! (
862
857
parameters,
@@ -874,7 +869,7 @@ mod test {
874
869
let sql = "set x = $y" ;
875
870
for & ( dialect, db_kind) in ALL_DIALECTS {
876
871
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 ) ;
878
873
if let Some ( ParsedStatement :: SetVariable {
879
874
variable,
880
875
value : StmtWithParams { query, params, .. } ,
0 commit comments