From e6a514935de4b01e8c0f9e69447b33d26035ef3f Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Mon, 5 Oct 2026 16:35:24 +0200 Subject: [PATCH 1/2] SQLite: Reject colon-based semi-structured data traversal --- src/dialect/sqlite.rs | 2 ++ tests/sqlparser_sqlite.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index 41b0e8be16..e10e47ced9 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -103,6 +103,8 @@ impl Dialect for SQLiteDialect { fn get_next_precedence(&self, parser: &Parser) -> Option> { match parser.peek_token_ref().token { Token::Ampersand | Token::Pipe => Some(Ok(self.prec_value(Precedence::Ampersand))), + // lowest prec to prevent it from turning into a binary op + Token::Colon => Some(Ok(self.prec_unknown())), _ => None, } } diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 1752e976cb..0aa14d65da 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -1161,6 +1161,25 @@ fn parse_unterminated_block_comment() { ); } +#[test] +fn parse_colon_does_not_start_json_access() { + // SQLite rejects `SELECT c:se` (`near ":se": syntax error`); no `:` traversal. + assert!(sqlite().parse_sql_statements("SELECT c:se").is_err()); + assert!(sqlite().parse_sql_statements("SELECT a:b FROM t").is_err()); + + let ast = sqlite().verified_only_select("SELECT :xxx"); + assert_eq!( + ast.projection[0], + UnnamedExpr(Expr::Value( + (Value::Placeholder(":xxx".into())).with_empty_span() + )), + ); + + assert!(TestedDialects::new(vec![Box::new(GenericDialect {})]) + .parse_sql_statements("SELECT a:b FROM t") + .is_ok()); +} + fn sqlite() -> TestedDialects { TestedDialects::new(vec![Box::new(SQLiteDialect {})]) } From 5d9e3a6b98cdce7329b0fd6b48dfe1bc1405f381 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Fri, 16 Oct 2026 23:29:28 +0200 Subject: [PATCH 2/2] Assert exact errors for the new deny lints --- tests/sqlparser_sqlite.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 0aa14d65da..2c4a900ed4 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -1164,8 +1164,14 @@ fn parse_unterminated_block_comment() { #[test] fn parse_colon_does_not_start_json_access() { // SQLite rejects `SELECT c:se` (`near ":se": syntax error`); no `:` traversal. - assert!(sqlite().parse_sql_statements("SELECT c:se").is_err()); - assert!(sqlite().parse_sql_statements("SELECT a:b FROM t").is_err()); + for sql in ["SELECT c:se", "SELECT a:b FROM t"] { + assert_eq!( + sqlite().parse_sql_statements(sql), + Err(ParserError::ParserError( + "Expected: end of statement, found: :".to_string() + )) + ); + } let ast = sqlite().verified_only_select("SELECT :xxx"); assert_eq!( @@ -1175,9 +1181,9 @@ fn parse_colon_does_not_start_json_access() { )), ); - assert!(TestedDialects::new(vec![Box::new(GenericDialect {})]) + TestedDialects::new(vec![Box::new(GenericDialect {})]) .parse_sql_statements("SELECT a:b FROM t") - .is_ok()); + .unwrap(); } fn sqlite() -> TestedDialects {