1515#[cfg(not(feature = "std"))]
1616use alloc::{
1717 boxed::Box,
18+ collections::BTreeMap,
1819 format,
1920 string::{String, ToString},
2021 vec,
@@ -24,6 +25,9 @@ use core::{
2425 fmt::{self, Display},
2526 str::FromStr,
2627};
28+ #[cfg(feature = "std")]
29+ use std::collections::BTreeMap;
30+
2731use helpers::attached_token::AttachedToken;
2832
2933use log::debug;
@@ -359,6 +363,12 @@ pub struct Parser<'a> {
359363 options: ParserOptions,
360364 /// Ensures the stack does not overflow by limiting recursion depth.
361365 recursion_counter: RecursionCounter,
366+ /// Cached errors from `parse_prefix` calls that returned `Err`. See
367+ /// [`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
370+ /// [`Parser::parse_prefix`] for the 2^N patterns this guards.
371+ failed_reserved_word_prefix_positions: BTreeMap<usize, ParserError>,
362372}
363373
364374impl<'a> Parser<'a> {
@@ -385,6 +395,8 @@ impl<'a> Parser<'a> {
385395 dialect,
386396 recursion_counter: RecursionCounter::new(DEFAULT_REMAINING_DEPTH),
387397 options: ParserOptions::new().with_trailing_commas(dialect.supports_trailing_commas()),
398+ failed_prefix_positions: BTreeMap::new(),
399+ failed_reserved_word_prefix_positions: BTreeMap::new(),
388400 }
389401 }
390402
@@ -446,6 +458,8 @@ impl<'a> Parser<'a> {
446458 pub fn with_tokens_with_locations(mut self, tokens: Vec<TokenWithSpan>) -> Self {
447459 self.tokens = tokens;
448460 self.index = 0;
461+ self.failed_prefix_positions.clear();
462+ self.failed_reserved_word_prefix_positions.clear();
449463 self
450464 }
451465
@@ -1717,6 +1731,23 @@ impl<'a> Parser<'a> {
17171731 return prefix;
17181732 }
17191733
1734+ // Memoize parse_prefix failures to break 2^N speculation when both
1735+ // prefix arms fail at every level (e.g. `IF(current_time(...x`).
1736+ // The per-arm cache in `parse_prefix_inner` complements this for
1737+ // chains where the reserved arm fails but the unreserved fallback
1738+ // succeeds (e.g. `case-case-...c`).
1739+ let start_index = self.index;
1740+ if let Some(cached) = self.failed_prefix_positions.get(&start_index) {
1741+ return Err(cached.clone());
1742+ }
1743+ let result = self.parse_prefix_inner();
1744+ if let Err(ref e) = result {
1745+ self.failed_prefix_positions.insert(start_index, e.clone());
1746+ }
1747+ result
1748+ }
1749+
1750+ fn parse_prefix_inner(&mut self) -> Result<Expr, ParserError> {
17201751 // PostgreSQL allows any string literal to be preceded by a type name, indicating that the
17211752 // string literal represents a literal of that type. Some examples:
17221753 //
@@ -1801,7 +1832,21 @@ impl<'a> Parser<'a> {
18011832 // We first try to parse the word and following tokens as a special expression, and if that fails,
18021833 // we rollback and try to parse it as an identifier.
18031834 let w = w.clone();
1804- match self.try_parse(|parser| parser.parse_expr_prefix_by_reserved_word(&w, span)) {
1835+ // Memoize failed speculative reserved-word parses. When
1836+ // the reserved arm (CASE, CURRENT_TIME, etc.) does
1837+ // exponential work but the unreserved fallback ultimately
1838+ // succeeds, the overall `parse_prefix` returns `Ok` and the
1839+ // outer cache never fires. Chains like `case-case-...c`
1840+ // need this per-arm cache to break the doubling.
1841+ let try_parse_result = if let Some(cached) = self
1842+ .failed_reserved_word_prefix_positions
1843+ .get(&next_token_index)
1844+ {
1845+ Err(cached.clone())
1846+ } else {
1847+ self.try_parse(|parser| parser.parse_expr_prefix_by_reserved_word(&w, span))
1848+ };
1849+ match try_parse_result {
18051850 // This word indicated an expression prefix and parsing was successful
18061851 Ok(Some(expr)) => Ok(expr),
18071852
@@ -1815,6 +1860,8 @@ impl<'a> Parser<'a> {
18151860 // we rollback and return the parsing error we got from trying to parse a
18161861 // special expression (to maintain backwards compatibility of parsing errors).
18171862 Err(e) => {
1863+ self.failed_reserved_word_prefix_positions
1864+ .insert(next_token_index, e.clone());
18181865 if !self.dialect.is_reserved_for_identifier(w.keyword) {
18191866 if let Ok(Some(expr)) = self.maybe_parse(|parser| {
18201867 parser.parse_expr_prefix_by_unreserved_word(&w, span)
0 commit comments