changeset 6415:dbacf6bf2a2f

Implement expressions for Link properties
author Ralf Schlatterbeck <rsc@runtux.com>
date Tue, 18 May 2021 08:50:46 +0200
parents 3dbf1bc5e567
children 99d344aa825d
files roundup/backends/rdbms_common.py test/db_test_base.py
diffstat 2 files changed, 51 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/roundup/backends/rdbms_common.py	Mon May 17 15:25:17 2021 -0400
+++ b/roundup/backends/rdbms_common.py	Tue May 18 08:50:46 2021 +0200
@@ -2453,6 +2453,53 @@
         # we have ids of the classname table
         return ids.where("_%s.id" % classname, self.db.arg)
 
+    def _filter_link_expression(self, proptree, v):
+        """ Filter elements in the table that match the given expression
+        """
+        pln = proptree.parent.uniqname
+        prp = proptree.name
+        try:
+            opcodes = [int(x) for x in v]
+            if min(opcodes) >= -1:
+                raise ValueError()
+            expr = compile_expression(opcodes)
+            # NULL doesn't compare to NULL in SQL
+            # So not (x = '1') will *not* include NULL values for x
+            # That's why we need that and clause:
+            atom = "_%s._%s = %s and _%s._%s is not NULL" % (
+                pln, prp, self.db.arg, pln, prp)
+            atom_nil = "_%s._%s is NULL" % (pln, prp)
+            lambda_atom = lambda n: atom if n.x >= 0 else atom_nil
+            values = []
+            w = expr.generate(lambda_atom)
+            def collect_values(n):
+                if n.x >= 0:
+                    values.append(n.x)
+            expr.visit(collect_values)
+            return w, values
+        except:
+            pass
+        # Fallback to original code
+        args = []
+        where = None
+        d = {}
+        for entry in v:
+            if entry == '-1':
+                entry = None
+            d[entry] = entry
+        l = []
+        if None in d or not d:
+            if None in d: del d[None]
+            l.append('_%s._%s is NULL'%(pln, prp))
+        if d:
+            v = list(d)
+            s = ','.join([self.db.arg for x in v])
+            l.append('(_%s._%s in (%s))'%(pln, prp, s))
+            args = v
+        if l:
+            where = '(' + ' or '.join(l) +')'
+        return where, args
+
     def _filter_multilink_expression(self, proptree, v):
         """ Filters out elements of the classname table that do not
             match the given expression.
@@ -2678,22 +2725,10 @@
                             where.append('_%s._%s=_%s.id'%(pln, k, ln))
                     if p.has_values:
                         if isinstance(v, type([])):
-                            d = {}
-                            for entry in v:
-                                if entry == '-1':
-                                    entry = None
-                                d[entry] = entry
-                            l = []
-                            if None in d or not d:
-                                if None in d: del d[None]
-                                l.append('_%s._%s is NULL'%(pln, k))
-                            if d:
-                                v = list(d)
-                                s = ','.join([a for x in v])
-                                l.append('(_%s._%s in (%s))'%(pln, k, s))
-                                args = args + v
-                            if l:
-                                where.append('(' + ' or '.join(l) +')')
+                            w, arg = self._filter_link_expression(p, v)
+                            if w:
+                                where.append(w)
+                                args += arg
                         else:
                             if v in ('-1', None):
                                 v = None
--- a/test/db_test_base.py	Mon May 17 15:25:17 2021 -0400
+++ b/test/db_test_base.py	Tue May 18 08:50:46 2021 +0200
@@ -1859,7 +1859,6 @@
             ae(filt(None, {a: ['-1', None]}, ('+','id'), grp), ['3','4'])
             ae(filt(None, {a: ['1', None]}, ('+','id'), grp), ['1', '3','4'])
 
-    @pytest.mark.xfail
     def testFilteringLinkExpression(self):
         ae, iiter = self.filteringSetup()
         a = 'assignedto'

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