Skip to content

Commit 2d012fb

Browse files
committed
Allow '-1' (empty) in multilink expression
1 parent 17ddd0c commit 2d012fb

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

roundup/backends/back_anydbm.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ def evaluate(self, v):
7777
def visit(self, visitor):
7878
visitor(self)
7979

80+
class Empty(Unary):
81+
82+
def evaluate(self, v):
83+
return not v
84+
85+
def visit(self, visitor):
86+
visitor(self)
87+
8088
class Not(Unary):
8189

8290
def evaluate(self, v):
@@ -110,7 +118,8 @@ def compile_expression(opcodes):
110118
stack = []
111119
push, pop = stack.append, stack.pop
112120
for opcode in opcodes:
113-
if opcode == -2: push(Not(pop()))
121+
if opcode == -1: push(Empty(opcode))
122+
elif opcode == -2: push(Not(pop()))
114123
elif opcode == -3: push(And(pop(), pop()))
115124
elif opcode == -4: push(Or(pop(), pop()))
116125
else: push(Equals(opcode))

roundup/backends/rdbms_common.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,7 +2373,7 @@ def getnodeids(self, retired=None):
23732373
ids = [str(x[0]) for x in self.db.cursor.fetchall()]
23742374
return ids
23752375

2376-
def _subselect(self, proptree):
2376+
def _subselect(self, proptree, parentname=None):
23772377
"""Create a subselect. This is factored out because some
23782378
databases (hmm only one, so far) doesn't support subselects
23792379
look for "I can't believe it's not a toy RDBMS" in the mysql
@@ -2382,6 +2382,9 @@ def _subselect(self, proptree):
23822382
multilink_table = proptree.propclass.table_name
23832383
nodeid_name = proptree.propclass.nodeid_name
23842384
linkid_name = proptree.propclass.linkid_name
2385+
if parentname is None:
2386+
parentname = '_' + proptree.parent.classname
2387+
23852388
w = ''
23862389
if proptree.need_retired:
23872390
w = ' where %s.__retired__=0'%(multilink_table)
@@ -2390,8 +2393,7 @@ def _subselect(self, proptree):
23902393
tn2 = '_' + proptree.classname
23912394
w = ', %s where %s.%s=%s.id and %s.__retired__=0'%(tn2,
23922395
tn1, linkid_name, tn2, tn2)
2393-
classname = proptree.parent.classname
2394-
return '_%s.id not in (select %s from %s%s)'%(classname, nodeid_name,
2396+
return '%s.id not in (select %s from %s%s)'%(parentname, nodeid_name,
23952397
multilink_table, w)
23962398

23972399
def _filter_multilink_expression_fallback(
@@ -2465,15 +2467,20 @@ def _filter_multilink_expression(self, proptree, v):
24652467
atom = \
24662468
"%s IN(SELECT %s FROM %s WHERE %s=a.id)" % (
24672469
self.db.arg, lid, multilink_table, nid)
2470+
atom_nil = self._subselect(proptree, 'a')
2471+
2472+
lambda_atom = lambda n: atom if n.x >= 0 else atom_nil
24682473

24692474
intron = \
24702475
"_%(classname)s.id in (SELECT id " \
24712476
"FROM _%(classname)s AS a WHERE %(condition)s) " % {
24722477
'classname' : classname,
2473-
'condition' : expr.generate(lambda n: atom) }
2478+
'condition' : expr.generate(lambda_atom) }
24742479

24752480
values = []
2476-
def collect_values(n): values.append(n.x)
2481+
def collect_values(n):
2482+
if n.x >= 0:
2483+
values.append(n.x)
24772484
expr.visit(collect_values)
24782485

24792486
return intron, values

test/db_test_base.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,10 +1980,21 @@ def testFilteringMultilinkExpression(self):
19801980
# '3' or empty (without explicit 'or')
19811981
ae(filt(None, {kw: ['3', '-1']}),
19821982
['1', '2', '3'])
1983-
# This does not work with any of the backends currently:
19841983
# '3' or empty (with explicit 'or')
1985-
#ae(filt(None, {kw: ['3', '-1', '-4']}),
1986-
# ['1', '2', '3'])
1984+
ae(filt(None, {kw: ['3', '-1', '-4']}),
1985+
['1', '2', '3'])
1986+
# empty or '3' (with explicit 'or')
1987+
ae(filt(None, {kw: ['-1', '3', '-4']}),
1988+
['1', '2', '3'])
1989+
# '3' and empty (should always return empty list)
1990+
ae(filt(None, {kw: ['3', '-1', '-3']}),
1991+
[])
1992+
# empty and '3' (should always return empty list)
1993+
ae(filt(None, {kw: ['3', '-1', '-3']}),
1994+
[])
1995+
# ('4' and empty) or ('3' or empty)
1996+
ae(filt(None, {kw: ['4', '-1', '-3', '3', '-1', '-4', '-4']}),
1997+
['1', '2', '3'])
19871998

19881999
def testFilteringRevMultilink(self):
19892000
ae, iiter = self.filteringSetupTransitiveSearch('user')
@@ -2047,6 +2058,7 @@ def testFilteringRevMultilinkExpression(self):
20472058
# Retire users '9' and '10' to reduce list
20482059
self.db.user.retire ('9')
20492060
self.db.user.retire ('10')
2061+
self.db.commit ()
20502062
for filt in iiter():
20512063
# '1' or '2'
20522064
ae(filt(None, {ni: ['1', '2', '-4']}), ['4', '5'])
@@ -2058,6 +2070,19 @@ def testFilteringRevMultilinkExpression(self):
20582070
ae(filt(None, {ni: ['6', '1', '-2', '-3']}), ['3', '5'])
20592071
# '2' or empty (implicit or)
20602072
ae(filt(None, {ni: ['-1', '2']}), ['1', '2', '5', '6', '7', '8'])
2073+
# '2' or empty (explicit or)
2074+
ae(filt(None, {ni: ['-1', '2', '-4']}),
2075+
['1', '2', '5', '6', '7', '8'])
2076+
# empty or '2' (explicit or)
2077+
ae(filt(None, {ni: ['2', '-1', '-4']}),
2078+
['1', '2', '5', '6', '7', '8'])
2079+
# '2' and empty (should always return empty list)
2080+
ae(filt(None, {ni: ['-1', '2', '-3']}), [])
2081+
# empty and '2' (should always return empty list)
2082+
ae(filt(None, {ni: ['2', '-1', '-3']}), [])
2083+
# ('4' and empty) or ('2' or empty)
2084+
ae(filt(None, {ni: ['4', '-1', '-3', '2', '-1', '-4', '-4']}),
2085+
['1', '2', '5', '6', '7', '8'])
20612086

20622087
def testFilteringMany(self):
20632088
ae, iiter = self.filteringSetup()

0 commit comments

Comments
 (0)