Skip to content

Commit a8c28a0

Browse files
Store a copy marker in the prefix failure caches instead of the full error
1 parent 7dcc611 commit a8c28a0

2 files changed

Lines changed: 52 additions & 11 deletions

File tree

src/parser/mod.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -363,12 +363,29 @@ pub struct Parser<'a> {
363363
options: ParserOptions,
364364
/// Ensures the stack does not overflow by limiting recursion depth.
365365
recursion_counter: RecursionCounter,
366-
/// Cached errors from `parse_prefix` calls that returned `Err`. See
366+
/// Cached failures from `parse_prefix` calls that returned `Err`. See
367367
/// [`Parser::parse_prefix`] for the 2^N patterns this guards.
368-
failed_prefix_positions: BTreeMap<usize, ParserError>,
369-
/// Cached errors from the speculative reserved-word prefix arm. See
368+
failed_prefix_positions: BTreeMap<usize, ExprPrefixError>,
369+
/// Cached failures from the speculative reserved-word prefix arm. See
370370
/// [`Parser::parse_prefix`] for the 2^N patterns this guards.
371-
failed_reserved_word_prefix_positions: BTreeMap<usize, ParserError>,
371+
failed_reserved_word_prefix_positions: BTreeMap<usize, ExprPrefixError>,
372+
}
373+
374+
/// Copy marker for a [`ParserError`] cached by the `parse_prefix` failure
375+
/// memoization, so the caches hold no strings.
376+
#[derive(Debug, Clone, Copy)]
377+
enum ExprPrefixError {
378+
RecursionLimitExceeded,
379+
Err,
380+
}
381+
382+
impl From<&ParserError> for ExprPrefixError {
383+
fn from(e: &ParserError) -> Self {
384+
match e {
385+
ParserError::RecursionLimitExceeded => Self::RecursionLimitExceeded,
386+
_ => Self::Err,
387+
}
388+
}
372389
}
373390

374391
impl<'a> Parser<'a> {
@@ -1737,16 +1754,24 @@ impl<'a> Parser<'a> {
17371754
// chains where the reserved arm fails but the unreserved fallback
17381755
// succeeds (e.g. `case-case-...c`).
17391756
let start_index = self.index;
1740-
if let Some(cached) = self.failed_prefix_positions.get(&start_index) {
1741-
return Err(cached.clone());
1757+
if let Some(&cached) = self.failed_prefix_positions.get(&start_index) {
1758+
return Err(self.cached_prefix_error(cached, self.peek_token_ref()));
17421759
}
17431760
let result = self.parse_prefix_inner();
17441761
if let Err(ref e) = result {
1745-
self.failed_prefix_positions.insert(start_index, e.clone());
1762+
self.failed_prefix_positions.insert(start_index, e.into());
17461763
}
17471764
result
17481765
}
17491766

1767+
/// Rebuild the error for a cached prefix failure at the `found` token.
1768+
fn cached_prefix_error(&self, cached: ExprPrefixError, found: &TokenWithSpan) -> ParserError {
1769+
match cached {
1770+
ExprPrefixError::RecursionLimitExceeded => ParserError::RecursionLimitExceeded,
1771+
ExprPrefixError::Err => self.expected_ref::<()>("an expression", found).unwrap_err(),
1772+
}
1773+
}
1774+
17501775
fn parse_prefix_inner(&mut self) -> Result<Expr, ParserError> {
17511776
// PostgreSQL allows any string literal to be preceded by a type name, indicating that the
17521777
// string literal represents a literal of that type. Some examples:
@@ -1838,11 +1863,11 @@ impl<'a> Parser<'a> {
18381863
// succeeds, the overall `parse_prefix` returns `Ok` and the
18391864
// outer cache never fires. Chains like `case-case-...c`
18401865
// need this per-arm cache to break the doubling.
1841-
let try_parse_result = if let Some(cached) = self
1866+
let try_parse_result = if let Some(&cached) = self
18421867
.failed_reserved_word_prefix_positions
18431868
.get(&next_token_index)
18441869
{
1845-
Err(cached.clone())
1870+
Err(self.cached_prefix_error(cached, self.get_current_token()))
18461871
} else {
18471872
self.try_parse(|parser| parser.parse_expr_prefix_by_reserved_word(&w, span))
18481873
};
@@ -1861,7 +1886,7 @@ impl<'a> Parser<'a> {
18611886
// special expression (to maintain backwards compatibility of parsing errors).
18621887
Err(e) => {
18631888
self.failed_reserved_word_prefix_positions
1864-
.insert(next_token_index, e.clone());
1889+
.insert(next_token_index, (&e).into());
18651890
if !self.dialect.is_reserved_for_identifier(w.keyword) {
18661891
if let Ok(Some(expr)) = self.maybe_parse(|parser| {
18671892
parser.parse_expr_prefix_by_unreserved_word(&w, span)

tests/sqlparser_common.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15564,7 +15564,10 @@ fn parse_create_table_select() {
1556415564

1556515565
#[test]
1556615566
fn test_reserved_keywords_for_identifiers() {
15567-
let dialects = all_dialects_where(|d| d.is_reserved_for_identifier(Keyword::INTERVAL));
15567+
let dialects = all_dialects_where(|d| {
15568+
d.is_reserved_for_identifier(Keyword::INTERVAL)
15569+
&& !d.supports_named_fn_args_with_expr_name()
15570+
});
1556815571
// Dialects that reserve the word INTERVAL will not allow it as an unquoted identifier
1556915572
let sql = "SELECT MAX(interval) FROM tbl";
1557015573
assert_eq!(
@@ -15574,6 +15577,19 @@ fn test_reserved_keywords_for_identifiers() {
1557415577
))
1557515578
);
1557615579

15580+
// Dialects with expression-named function arguments parse the argument
15581+
// expression twice, so the second attempt reports the memoized failure
15582+
// at the start of the expression
15583+
let dialects = all_dialects_where(|d| {
15584+
d.is_reserved_for_identifier(Keyword::INTERVAL) && d.supports_named_fn_args_with_expr_name()
15585+
});
15586+
assert_eq!(
15587+
dialects.parse_sql_statements(sql),
15588+
Err(ParserError::ParserError(
15589+
"Expected: an expression, found: interval".to_string()
15590+
))
15591+
);
15592+
1557715593
// Dialects that do not reserve the word INTERVAL will allow it
1557815594
let dialects = all_dialects_where(|d| !d.is_reserved_for_identifier(Keyword::INTERVAL));
1557915595
let sql = "SELECT MAX(interval) FROM tbl";

0 commit comments

Comments
 (0)