Fixes #1979544 [stored search gives error about recursive search](https://bugs.launchpad.net/calibre/+bug/1979544)
This commit is contained in:
Kovid Goyal 2022-06-23 06:56:03 +05:30
commit fc81cfda7c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -338,7 +338,6 @@ class SearchQueryParser:
def get_queried_fields(self, query): def get_queried_fields(self, query):
# empty the list of searches used for recursion testing # empty the list of searches used for recursion testing
self.recurse_level = 0
self.searches_seen = set() self.searches_seen = set()
tree = self._get_tree(query) tree = self._get_tree(query)
yield from self._walk_expr(tree) yield from self._walk_expr(tree)
@ -351,20 +350,19 @@ class SearchQueryParser:
yield from self._walk_expr(tree[1]) yield from self._walk_expr(tree[1])
else: else:
if tree[1] == 'search': if tree[1] == 'search':
yield from self._walk_expr(self._get_tree( query, search_name_lower = self._check_saved_search_recursion(tree[2])
self._get_saved_search_text(tree[2]))) yield from self._walk_expr(self._get_tree(query))
self.searches_seen.discard(search_name_lower)
else: else:
yield tree[1], tree[2] yield tree[1], tree[2]
def parse(self, query, candidates=None): def parse(self, query, candidates=None):
# empty the list of searches used for recursion testing # empty the list of searches used for recursion testing
self.recurse_level = 0
self.searches_seen = set() self.searches_seen = set()
candidates = self.universal_set() candidates = self.universal_set()
return self._parse(query, candidates=candidates) return self._parse(query, candidates=candidates)
def _get_tree(self, query): def _get_tree(self, query):
self.recurse_level += 1
try: try:
res = self.sqp_parse_cache.get(query, None) res = self.sqp_parse_cache.get(query, None)
except AttributeError: except AttributeError:
@ -380,16 +378,12 @@ class SearchQueryParser:
return res return res
# this parse is used internally because it doesn't clear the # this parse is used internally because it doesn't clear the
# recursive search test list. However, we permit seeing the # recursive search test list.
# same search a few times because the search might appear within
# another search.
def _parse(self, query, candidates=None): def _parse(self, query, candidates=None):
self.recurse_level += 1
tree = self._get_tree(query) tree = self._get_tree(query)
if candidates is None: if candidates is None:
candidates = self.universal_set() candidates = self.universal_set()
t = self.evaluate(tree, candidates) t = self.evaluate(tree, candidates)
self.recurse_level -= 1
return t return t
def method(self, group_name): def method(self, group_name):
@ -421,14 +415,18 @@ class SearchQueryParser:
# def evaluate_parenthesis(self, argument, candidates): # def evaluate_parenthesis(self, argument, candidates):
# return self.evaluate(argument[0], candidates) # return self.evaluate(argument[0], candidates)
def _get_saved_search_text(self, query): def _check_saved_search_recursion(self, query):
if query.startswith('='): if query.startswith('='):
query = query[1:] query = query[1:]
try: search_name_lower = query.lower()
if query.lower() in self.searches_seen: if search_name_lower in self.searches_seen:
raise ParseException(_('Recursive saved search: {0}').format(query)) raise ParseException(_('Recursive saved search: {0}').format(query))
if self.recurse_level > 10: self.searches_seen.add(search_name_lower)
self.searches_seen.add(query.lower()) query = self._get_saved_search_text(query)
return (query, search_name_lower)
def _get_saved_search_text(self, query):
try:
ss = self.lookup_saved_search(query) ss = self.lookup_saved_search(query)
if ss is None: if ss is None:
raise ParseException(_('Unknown saved search: {}').format(query)) raise ParseException(_('Unknown saved search: {}').format(query))
@ -444,7 +442,10 @@ class SearchQueryParser:
location = argument[0] location = argument[0]
query = argument[1] query = argument[1]
if location.lower() == 'search': 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) return self._get_matches(location, query, candidates)
def _get_matches(self, location, query, candidates): def _get_matches(self, location, query, candidates):