comparison roundup/hyperdb.py @ 6403:9957d8d10783

Tests and bug-fix for issue2551119 .. and some other failing tests I came up with when trying to reproduce the problem of the issue.
author Ralf Schlatterbeck <rsc@runtux.com>
date Mon, 10 May 2021 16:19:37 +0200
parents 619807d9a2df
children ce99e0d39262
comparison
equal deleted inserted replaced
6402:619807d9a2df 6403:9957d8d10783
463 is a dictionary containing one or several of the values 'sort', 463 is a dictionary containing one or several of the values 'sort',
464 'search', 'retrieve'. 464 'search', 'retrieve'.
465 465
466 The Proptree is also used for transitively searching attributes for 466 The Proptree is also used for transitively searching attributes for
467 backends that do not support transitive search (e.g. anydbm). The 467 backends that do not support transitive search (e.g. anydbm). The
468 _val attribute with set_val is used for this. 468 val attribute with set_val is used for this.
469 """ 469 """
470 470
471 def __init__(self, db, cls, name, props, parent=None, retr=False): 471 def __init__(self, db, cls, name, props, parent=None, retr=False):
472 self.db = db 472 self.db = db
473 self.name = name 473 self.name = name
474 self.props = props 474 self.props = props
475 self.parent = parent 475 self.parent = parent
476 self._val = None 476 self.val = None
477 self.has_values = False 477 self.has_values = False
478 self.has_result = False
478 self.cls = cls 479 self.cls = cls
479 self.classname = None 480 self.classname = None
480 self.uniqname = None 481 self.uniqname = None
481 self.children = [] 482 self.children = []
482 self.sortattr = [] 483 self.sortattr = []
632 by_id[x].add(id) 633 by_id[x].add(id)
633 for k in by_id: 634 for k in by_id:
634 if expr.evaluate(by_id[k]): 635 if expr.evaluate(by_id[k]):
635 items.add(k) 636 items.add(k)
636 637
638 # The subquery has found nothing. So it doesn't make
639 # sense to search further.
640 if not items:
641 self.set_val([], force=True)
642 return self.val
637 filterspec[p.name] = list(sorted(items, key=int)) 643 filterspec[p.name] = list(sorted(items, key=int))
638 elif isinstance(p.val, type([])): 644 elif isinstance(p.val, type([])):
639 exact = [] 645 exact = []
640 subst = [] 646 subst = []
641 for v in p.val: 647 for v in p.val:
646 if exact: 652 if exact:
647 exact_match_spec[p.name] = exact 653 exact_match_spec[p.name] = exact
648 if subst: 654 if subst:
649 filterspec[p.name] = subst 655 filterspec[p.name] = subst
650 elif not exact: # don't set if we have exact criteria 656 elif not exact: # don't set if we have exact criteria
651 filterspec[p.name] =[ '-1' ] # no match was found 657 if p.has_result:
658 # A subquery already has found nothing. So
659 # it doesn't make sense to search further.
660 self.set_val([], force=True)
661 return self.val
662 else:
663 filterspec[p.name] = ['-1'] # no match was found
652 else: 664 else:
653 assert not isinstance(p.val, Exact_Match) 665 assert not isinstance(p.val, Exact_Match)
654 filterspec[p.name] = p.val 666 filterspec[p.name] = p.val
655 self.val = self.cls._filter(search_matches, filterspec, sort and self, 667 self.set_val(self.cls._filter(search_matches, filterspec, sort and self,
656 retired=retired, 668 retired=retired,
657 exact_match_spec=exact_match_spec) 669 exact_match_spec=exact_match_spec))
658 return self.val 670 return self.val
659 671
660 def sort(self, ids=None): 672 def sort(self, ids=None):
661 """ Sort ids by the order information stored in self. With 673 """ Sort ids by the order information stored in self. With
662 optimisations: Some order attributes may be precomputed (by the 674 optimisations: Some order attributes may be precomputed (by the
740 pt.sort_ids = None 752 pt.sort_ids = None
741 for pt in self.sortattr: 753 for pt in self.sortattr:
742 pt.sort_result = None 754 pt.sort_result = None
743 return ids 755 return ids
744 756
745 def _set_val(self, val): 757 def set_val(self, val, force=False, result=True):
746 """ Check if self._val is already defined. If yes, we compute the 758 """ Check if self.val is already defined (it is not None and
759 has_values is True). If yes, we compute the
747 intersection of the old and the new value(s) 760 intersection of the old and the new value(s)
748 Note: If self is a Leaf node we need to compute a 761 Note: If self is a Leaf node we need to compute a
749 union: Normally we intersect (logical and) different 762 union: Normally we intersect (logical and) different
750 subqueries into a Link or Multilink property. But for 763 subqueries into a Link or Multilink property. But for
751 leaves we might have a part of a query in a filterspec and 764 leaves we might have a part of a query in a filterspec and
752 in an exact_match_spec. These have to be all there, the 765 in an exact_match_spec. These have to be all there, the
753 generated search will ensure a logical and of all tests for 766 generated search will ensure a logical and of all tests for
754 equality/substring search. 767 equality/substring search.
755 """ 768 """
769 if force:
770 assert val == []
771 assert result
772 self.val = val
773 self.has_values = True
774 self.has_result = True
775 return
756 if self.has_values: 776 if self.has_values:
757 v = self._val 777 v = self.val
758 if not isinstance(self._val, type([])): 778 if not isinstance(self.val, type([])):
759 v = [self._val] 779 v = [self.val]
760 vals = set(v) 780 vals = set(v)
761 if not isinstance(val, type([])): 781 if not isinstance(val, type([])):
762 val = [val] 782 val = [val]
783 if self.has_result:
784 assert result
763 # if cls is None we're a leaf 785 # if cls is None we're a leaf
764 if self.cls: 786 if self.cls:
765 vals.intersection_update(val) 787 vals.intersection_update(val)
766 else: 788 else:
767 vals.update(val) 789 vals.update(val)
768 self._val = [v for v in vals] 790 self.val = list(vals)
769 else: 791 else:
770 self._val = val 792 # If a subquery found nothing we don't care if there is an
793 # expression
794 if not self.has_values or not val:
795 self.val = val
796 if result:
797 self.has_result = True
798 else:
799 if not result:
800 assert not self.cls
801 vals.update(val)
802 self.val = list(vals)
803 else:
804 assert self.cls
805 is_expression = min(int(i) for i in self.val) < -1
806 if is_expression:
807 # Tag on the ORed values with an AND
808 l = val
809 for i in range(len(val)-1):
810 l.append('-4')
811 l.append('-3')
812 self.val = self.val + l
813 else:
814 vals.intersection_update(val)
815 self.val = list(vals)
816 self.has_result = True
771 self.has_values = True 817 self.has_values = True
772
773 val = property(lambda self: self._val, _set_val)
774 818
775 def _sort(self, val): 819 def _sort(self, val):
776 """Finally sort by the given sortattr.sort_result. Note that we 820 """Finally sort by the given sortattr.sort_result. Note that we
777 do not sort by attrs having attr_sort_done set. The caller is 821 do not sort by attrs having attr_sort_done set. The caller is
778 responsible for setting attr_sort_done only for trailing 822 responsible for setting attr_sort_done only for trailing
1540 if exact: 1584 if exact:
1541 if isinstance(v, type([])): 1585 if isinstance(v, type([])):
1542 vv = [] 1586 vv = []
1543 for x in v: 1587 for x in v:
1544 vv.append(Exact_Match(x)) 1588 vv.append(Exact_Match(x))
1545 p.val = vv 1589 p.set_val(vv, result=False)
1546 else: 1590 else:
1547 p.val = [Exact_Match(v)] 1591 p.set_val([Exact_Match(v)], result=False)
1548 else: 1592 else:
1549 p.val = v 1593 p.set_val(v, result=False)
1550 multilinks = {} 1594 multilinks = {}
1551 for s in sortattr: 1595 for s in sortattr:
1552 keys = s[1].split('.') 1596 keys = s[1].split('.')
1553 p = proptree 1597 p = proptree
1554 mlseen = False 1598 mlseen = False

Roundup Issue Tracker: http://roundup-tracker.org/