Skip to content

Commit 995c070

Browse files
Merge main into PR apache#2557
2 parents 289e3bb + 1ba145b commit 995c070

5 files changed

Lines changed: 200 additions & 16 deletions

File tree

‎src/dialect/mod.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,13 @@ pub trait Dialect: Debug + Any {
19761976
false
19771977
}
19781978

1979+
/// Returns true if the dialect accepts `==` as an alternative to `=` in `UPDATE SET` assignments.
1980+
///
1981+
/// See <https://www.sqlite.org/lang_update.html>
1982+
fn supports_double_eq_assignment(&self) -> bool {
1983+
false
1984+
}
1985+
19791986
/// Returns true if the dialect supports `CAST(expr AS)` with an empty type name.
19801987
///
19811988
/// Example:

‎src/dialect/sqlite.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ impl Dialect for SQLiteDialect {
134134
true
135135
}
136136

137+
fn supports_double_eq_assignment(&self) -> bool {
138+
true
139+
}
140+
137141
fn supports_string_literal_column_names(&self) -> bool {
138142
true
139143
}

‎src/parser/mod.rs‎

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ pub struct Parser<'a> {
355355
/// `parse_table_factor`. See [`Parser::parse_table_factor`] for the 2^N
356356
/// pattern this guards.
357357
failed_derived_table_factor_positions: BTreeSet<usize>,
358+
/// Cached failures from the speculative subquery arm of [`Parser::parse_in`],
359+
/// which `IN` lists nested in a leading subquery would otherwise retry at
360+
/// every level, taking 2^N time. Maps the arm's start to where a parsed
361+
/// query lacked its `)`.
362+
failed_in_subquery_positions: BTreeMap<usize, Option<usize>>,
358363
}
359364

360365
/// Copy marker for a [`ParserError`] cached by the `parse_prefix` failure
@@ -401,6 +406,7 @@ impl<'a> Parser<'a> {
401406
failed_prefix_positions: BTreeMap::new(),
402407
failed_reserved_word_prefix_positions: BTreeMap::new(),
403408
failed_derived_table_factor_positions: BTreeSet::new(),
409+
failed_in_subquery_positions: BTreeMap::new(),
404410
}
405411
}
406412

@@ -469,6 +475,7 @@ impl<'a> Parser<'a> {
469475
self.failed_prefix_positions.clear();
470476
self.failed_reserved_word_prefix_positions.clear();
471477
self.failed_derived_table_factor_positions.clear();
478+
self.failed_in_subquery_positions.clear();
472479
self
473480
}
474481

@@ -4464,24 +4471,51 @@ impl<'a> Parser<'a> {
44644471
});
44654472
}
44664473
self.expect_token(&Token::LParen)?;
4467-
let in_op = match self.maybe_parse(|p| p.parse_query())? {
4468-
Some(subquery) => Expr::InSubquery {
4469-
expr: Box::new(expr),
4470-
subquery,
4471-
negated,
4472-
},
4473-
None => Expr::InList {
4474+
// A leading query is the whole subquery only when `)` follows it,
4475+
// otherwise it is the first item of a list, as in `IN ((SELECT 1), 2)`.
4476+
let start = self.index;
4477+
// Where a parsed query lacked its `)`. Reported if the list fails no
4478+
// further in, since that error would sit inside the query.
4479+
let missing_rparen = match self.failed_in_subquery_positions.get(&start) {
4480+
Some(&cached) => cached,
4481+
None => {
4482+
let mut missing_rparen = None;
4483+
if let Some(subquery) = self.maybe_parse(|p| p.parse_query())? {
4484+
if self.consume_token(&Token::RParen) {
4485+
return Ok(Expr::InSubquery {
4486+
expr: Box::new(expr),
4487+
subquery,
4488+
negated,
4489+
});
4490+
}
4491+
missing_rparen = Some(self.index);
4492+
self.index = start;
4493+
}
4494+
self.failed_in_subquery_positions
4495+
.insert(start, missing_rparen);
4496+
missing_rparen
4497+
}
4498+
};
4499+
let list = if self.dialect.supports_in_empty_list() {
4500+
self.parse_comma_separated0(Parser::parse_expr, Token::RParen)
4501+
} else {
4502+
self.parse_comma_separated(Parser::parse_expr)
4503+
};
4504+
match list.and_then(|list| self.expect_token(&Token::RParen).map(|_| list)) {
4505+
Ok(list) => Ok(Expr::InList {
44744506
expr: Box::new(expr),
4475-
list: if self.dialect.supports_in_empty_list() {
4476-
self.parse_comma_separated0(Parser::parse_expr, Token::RParen)?
4477-
} else {
4478-
self.parse_comma_separated(Parser::parse_expr)?
4479-
},
4507+
list,
44804508
negated,
4509+
}),
4510+
Err(ParserError::RecursionLimitExceeded) => Err(ParserError::RecursionLimitExceeded),
4511+
Err(e) => match missing_rparen {
4512+
Some(index) if self.index <= index => {
4513+
self.index = index;
4514+
self.expected_ref(")", self.peek_token_ref())
4515+
}
4516+
_ => Err(e),
44814517
},
4482-
};
4483-
self.expect_token(&Token::RParen)?;
4484-
Ok(in_op)
4518+
}
44854519
}
44864520

44874521
/// Parses `BETWEEN <low> AND <high>`, assuming the `BETWEEN` keyword was already consumed.
@@ -19021,7 +19055,13 @@ impl<'a> Parser<'a> {
1902119055
/// Parse a `var = expr` assignment, used in an UPDATE statement
1902219056
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
1902319057
let target = self.parse_assignment_target()?;
19024-
self.expect_token(&Token::Eq)?;
19058+
if self.dialect.supports_double_eq_assignment() {
19059+
if !self.consume_token(&Token::Eq) && !self.consume_token(&Token::DoubleEq) {
19060+
return self.expected("= or == (for assignment)", self.peek_token());
19061+
}
19062+
} else {
19063+
self.expect_token(&Token::Eq)?;
19064+
}
1902519065
let value = self.parse_expr()?;
1902619066
Ok(Assignment { target, value })
1902719067
}

‎tests/sqlparser_common.rs‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20301,6 +20301,122 @@ fn parse_placeholder_disallows_quoted_ident() {
2030120301
);
2030220302
}
2030320303

20304+
#[test]
20305+
fn parse_in_list_leading_subquery() {
20306+
let x = || Box::new(Expr::Identifier(Ident::new("x")));
20307+
let subquery = || Expr::Subquery(Box::new(verified_query("SELECT a FROM u")));
20308+
20309+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u), 1)");
20310+
assert_eq!(
20311+
Expr::InList {
20312+
expr: x(),
20313+
list: vec![subquery(), Expr::value(number("1"))],
20314+
negated: false,
20315+
},
20316+
select.selection.unwrap()
20317+
);
20318+
20319+
let select = verified_only_select("SELECT * FROM t WHERE x NOT IN (((SELECT a FROM u)), 1)");
20320+
assert_eq!(
20321+
Expr::InList {
20322+
expr: x(),
20323+
list: vec![Expr::Nested(Box::new(subquery())), Expr::value(number("1"))],
20324+
negated: true,
20325+
},
20326+
select.selection.unwrap()
20327+
);
20328+
20329+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u) + 1, 2)");
20330+
assert_eq!(
20331+
Expr::InList {
20332+
expr: x(),
20333+
list: vec![
20334+
Expr::BinaryOp {
20335+
left: Box::new(subquery()),
20336+
op: BinaryOperator::Plus,
20337+
right: Box::new(Expr::value(number("1"))),
20338+
},
20339+
Expr::value(number("2"))
20340+
],
20341+
negated: false,
20342+
},
20343+
select.selection.unwrap()
20344+
);
20345+
20346+
let select = pg_and_generic()
20347+
.verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u)[1], 2)");
20348+
assert_matches!(
20349+
select.selection,
20350+
Some(Expr::InList { ref list, .. })
20351+
if matches!(&list[0], Expr::CompoundFieldAccess { root, .. } if **root == subquery())
20352+
);
20353+
20354+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u))");
20355+
assert_eq!(
20356+
Expr::InSubquery {
20357+
expr: x(),
20358+
subquery: Box::new(verified_query("(SELECT a FROM u)")),
20359+
negated: false,
20360+
},
20361+
select.selection.unwrap()
20362+
);
20363+
20364+
assert_eq!(
20365+
ParserError::ParserError("Expected: ), found: 1".to_string()),
20366+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT a FROM u) 1)").unwrap_err()
20367+
);
20368+
assert_eq!(
20369+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20370+
parse_sql_statements("SELECT * FROM t WHERE x IN (SELECT a FROM u").unwrap_err()
20371+
);
20372+
assert_eq!(
20373+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20374+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT a FROM u), 1").unwrap_err()
20375+
);
20376+
// The inner `IN` is read again for the outer list after its subquery
20377+
// arm was cached as failed.
20378+
assert_eq!(
20379+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20380+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT 1 FROM u WHERE y IN (SELECT 2")
20381+
.unwrap_err()
20382+
);
20383+
}
20384+
20385+
/// Hang guard for `IN` lists nested in the leading subquery of an enclosing
20386+
/// `IN` list, where every level is first tried as the whole subquery and then
20387+
/// read again as a list item.
20388+
#[test]
20389+
fn parse_in_list_nested_leading_subquery_no_exponential_blowup() {
20390+
use std::sync::mpsc;
20391+
use std::thread;
20392+
use std::time::Duration;
20393+
20394+
for level in [
20395+
"x IN ((SELECT {}), 1)",
20396+
"x IN ((SELECT {}) + 1, 2)",
20397+
"x IN (((SELECT {}) + 1), 2)",
20398+
] {
20399+
let sql = format!(
20400+
"SELECT {}",
20401+
(0..30).fold("1".to_string(), |inner, _| level.replace("{}", &inner))
20402+
);
20403+
20404+
let (tx, rx) = mpsc::channel();
20405+
thread::spawn(move || {
20406+
let result = Parser::new(&GenericDialect {})
20407+
.with_recursion_limit(1000)
20408+
.try_with_sql(&sql)
20409+
.and_then(|mut parser| parser.parse_statements());
20410+
let _ = tx.send(result.is_ok());
20411+
});
20412+
20413+
let parsed = rx
20414+
.recv_timeout(Duration::from_secs(5))
20415+
.expect("parser should handle this quickly, not loop exponentially");
20416+
assert!(parsed, "{level} nested 30 deep should parse");
20417+
}
20418+
}
20419+
2030420420
#[test]
2030520421
fn parse_bang_not_renders_apart_from_operand() {
2030620422
let dialects = all_dialects_where(|d| d.supports_bang_not_operator());

‎tests/sqlparser_sqlite.rs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,23 @@ fn parse_pattern_operators_bind_at_like_precedence() {
957957
}
958958
}
959959

960+
#[test]
961+
fn parse_update_set_double_eq() {
962+
// SQLite treats `==` as `=` in all positions, including SET assignments.
963+
sqlite().one_statement_parses_to("UPDATE t SET a == 1", "UPDATE t SET a = 1");
964+
sqlite().one_statement_parses_to("UPDATE t SET a == 1, b == 2", "UPDATE t SET a = 1, b = 2");
965+
// `=` still works
966+
sqlite().verified_stmt("UPDATE t SET a = 1");
967+
// Other dialects reject `==` in SET
968+
let res = ParserError::ParserError("Expected: =, found: ==".to_string());
969+
assert_eq!(
970+
all_dialects_except(|d| d.supports_double_eq_assignment())
971+
.parse_sql_statements("UPDATE t SET a == 1")
972+
.unwrap_err(),
973+
res,
974+
);
975+
}
976+
960977
#[test]
961978
fn test_signed_type_modifiers() {
962979
// signed and decimal modifiers round-trip verbatim for any type name

0 commit comments

Comments
 (0)