From cc07ae92fc3681cc5379053b047c1733cca82e50 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Wed, 22 Jun 2022 21:23:38 +0100 Subject: [PATCH] Bug #1979544: incorrect detection of recursion in a saved search --- src/calibre/utils/search_query_parser.py | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index eaed55c6d9..c8b6442244 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -338,7 +338,6 @@ class SearchQueryParser: def get_queried_fields(self, query): # empty the list of searches used for recursion testing - self.recurse_level = 0 self.searches_seen = set() tree = self._get_tree(query) yield from self._walk_expr(tree) @@ -351,20 +350,19 @@ class SearchQueryParser: yield from self._walk_expr(tree[1]) else: if tree[1] == 'search': - yield from self._walk_expr(self._get_tree( - self._get_saved_search_text(tree[2]))) + query, search_name_lower = self._check_saved_search_recursion(tree[2]) + yield from self._walk_expr(self._get_tree(query)) + self.searches_seen.discard(search_name_lower) else: yield tree[1], tree[2] def parse(self, query, candidates=None): # empty the list of searches used for recursion testing - self.recurse_level = 0 self.searches_seen = set() candidates = self.universal_set() return self._parse(query, candidates=candidates) def _get_tree(self, query): - self.recurse_level += 1 try: res = self.sqp_parse_cache.get(query, None) except AttributeError: @@ -380,16 +378,12 @@ class SearchQueryParser: return res # this parse is used internally because it doesn't clear the - # recursive search test list. However, we permit seeing the - # same search a few times because the search might appear within - # another search. + # recursive search test list. def _parse(self, query, candidates=None): - self.recurse_level += 1 tree = self._get_tree(query) if candidates is None: candidates = self.universal_set() t = self.evaluate(tree, candidates) - self.recurse_level -= 1 return t def method(self, group_name): @@ -421,14 +415,18 @@ class SearchQueryParser: # def evaluate_parenthesis(self, argument, candidates): # return self.evaluate(argument[0], candidates) - def _get_saved_search_text(self, query): + def _check_saved_search_recursion(self, query): if query.startswith('='): query = query[1:] + search_name_lower = query.lower() + if search_name_lower in self.searches_seen: + raise ParseException(_('Recursive saved search: {0}').format(query)) + self.searches_seen.add(search_name_lower) + query = self._get_saved_search_text(query) + return (query, search_name_lower) + + def _get_saved_search_text(self, query): try: - if query.lower() in self.searches_seen: - raise ParseException(_('Recursive saved search: {0}').format(query)) - if self.recurse_level > 10: - self.searches_seen.add(query.lower()) ss = self.lookup_saved_search(query) if ss is None: raise ParseException(_('Unknown saved search: {}').format(query)) @@ -444,7 +442,10 @@ class SearchQueryParser: location = argument[0] query = argument[1] if location.lower() == 'search': - return self._parse(self._get_saved_search_text(query), candidates) + query, search_name_lower = self._check_saved_search_recursion(query) + result = self._parse(query, candidates) + self.searches_seen.discard(search_name_lower) + return result return self._get_matches(location, query, candidates) def _get_matches(self, location, query, candidates):