Skip to content

Commit 3ce5830

Browse files
author
Richard Jones
committed
fix bug introduced in 1.4.5 in RDBMS full-text indexing;
fix tests so the code is actually exercised; fix another bug discovered in process
1 parent 62483b7 commit 3ce5830

File tree

4 files changed

+95
-16
lines changed

4 files changed

+95
-16
lines changed

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
This file contains the changes to the Roundup system over time. The entries
22
are given with the most recent entry first.
33

4+
2008-09-01 1.4.6
5+
Fixed:
6+
- Bug introduced in 1.4.5 in RDBMS full-text indexing
7+
8+
49
2008-08-19 1.4.5
510
Feature:
611
- Add use of username/password stored in ~/.netrc in mailgw (sf patch

roundup/backends/indexer_rdbms.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#$Id: indexer_rdbms.py,v 1.17 2008-08-07 22:02:30 richard Exp $
1+
#$Id: indexer_rdbms.py,v 1.18 2008-09-01 00:43:02 richard Exp $
22
''' This implements the full-text indexer over two RDBMS tables. The first
33
is a mapping of words to occurance IDs. The second maps the IDs to (Class,
44
propname, itemid) instances.
@@ -41,7 +41,7 @@ def add_text(self, identifier, text, mime_type='text/plain'):
4141
# Ensure all elements of the identifier are strings 'cos the itemid
4242
# column is varchar even if item ids may be numbers elsewhere in the
4343
# code. ugh.
44-
identifier = map(str, identifier)
44+
identifier = tuple(map(str, identifier))
4545

4646
# first, find the id of the (classname, itemid, property)
4747
a = self.db.arg
@@ -82,12 +82,12 @@ def find(self, wordlist):
8282
* more rules here
8383
'''
8484
if not wordlist:
85-
return {}
85+
return []
8686

8787
l = [word.upper() for word in wordlist if 26 > len(word) > 2]
8888

8989
if not l:
90-
return {}
90+
return []
9191

9292
if self.db.implements_intersect:
9393
# simple AND search
@@ -96,7 +96,7 @@ def find(self, wordlist):
9696
self.db.cursor.execute(sql, tuple(l))
9797
r = self.db.cursor.fetchall()
9898
if not r:
99-
return {}
99+
return []
100100
a = ','.join([self.db.arg] * len(r))
101101
sql = 'select _class, _itemid, _prop from __textids '\
102102
'where _textid in (%s)'%a
@@ -125,7 +125,7 @@ def find(self, wordlist):
125125

126126
r = map(lambda x: x[0], self.db.cursor.fetchall())
127127
if not r:
128-
return {}
128+
return []
129129

130130
a = ','.join([self.db.arg] * len(r))
131131
sql = 'select _class, _itemid, _prop from __textids '\

test/test_indexer.py

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@
1818
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919
# SOFTWARE.
2020

21-
# $Id: test_indexer.py,v 1.10 2006-02-07 04:59:05 richard Exp $
21+
# $Id: test_indexer.py,v 1.11 2008-09-01 00:43:02 richard Exp $
2222

2323
import os, unittest, shutil
2424

25+
from roundup.backends import get_backend, have_backend
26+
from roundup.backends.indexer_rdbms import Indexer
27+
28+
# borrow from other tests
29+
from db_test_base import setupSchema, config
30+
from test_postgresql import postgresqlOpener
31+
from test_mysql import mysqlOpener
32+
from test_sqlite import sqliteOpener
33+
2534
class db:
2635
class config(dict):
2736
DATABASE = 'test-index'
@@ -38,29 +47,35 @@ def setUp(self):
3847
self.dex = Indexer(db)
3948
self.dex.load_index()
4049

50+
def assertSeqEqual(self, s1, s2):
51+
# first argument is the db result we're testing, second is the desired result
52+
# some db results don't have iterable rows, so we have to work around that
53+
if [i for x,y in zip(s1, s2) for i,j in enumerate(y) if x[i] != j]:
54+
self.fail('contents of %r != %r'%(s1, s2))
55+
4156
def test_basics(self):
4257
self.dex.add_text(('test', '1', 'foo'), 'a the hello world')
4358
self.dex.add_text(('test', '2', 'foo'), 'blah blah the world')
44-
self.assertEqual(self.dex.find(['world']), [('test', '1', 'foo'),
59+
self.assertSeqEqual(self.dex.find(['world']), [('test', '1', 'foo'),
4560
('test', '2', 'foo')])
46-
self.assertEqual(self.dex.find(['blah']), [('test', '2', 'foo')])
47-
self.assertEqual(self.dex.find(['blah', 'hello']), [])
61+
self.assertSeqEqual(self.dex.find(['blah']), [('test', '2', 'foo')])
62+
self.assertSeqEqual(self.dex.find(['blah', 'hello']), [])
4863

4964
def test_change(self):
5065
self.dex.add_text(('test', '1', 'foo'), 'a the hello world')
5166
self.dex.add_text(('test', '2', 'foo'), 'blah blah the world')
52-
self.assertEqual(self.dex.find(['world']), [('test', '1', 'foo'),
67+
self.assertSeqEqual(self.dex.find(['world']), [('test', '1', 'foo'),
5368
('test', '2', 'foo')])
5469
self.dex.add_text(('test', '1', 'foo'), 'a the hello')
55-
self.assertEqual(self.dex.find(['world']), [('test', '2', 'foo')])
70+
self.assertSeqEqual(self.dex.find(['world']), [('test', '2', 'foo')])
5671

5772
def test_clear(self):
5873
self.dex.add_text(('test', '1', 'foo'), 'a the hello world')
5974
self.dex.add_text(('test', '2', 'foo'), 'blah blah the world')
60-
self.assertEqual(self.dex.find(['world']), [('test', '1', 'foo'),
75+
self.assertSeqEqual(self.dex.find(['world']), [('test', '1', 'foo'),
6176
('test', '2', 'foo')])
6277
self.dex.add_text(('test', '1', 'foo'), '')
63-
self.assertEqual(self.dex.find(['world']), [('test', '2', 'foo')])
78+
self.assertSeqEqual(self.dex.find(['world']), [('test', '2', 'foo')])
6479

6580
def tearDown(self):
6681
shutil.rmtree('test-index')
@@ -75,15 +90,74 @@ def setUp(self):
7590
def tearDown(self):
7691
shutil.rmtree('test-index')
7792

93+
class RDBMSIndexerTest(IndexerTest):
94+
def setUp(self):
95+
# remove previous test, ignore errors
96+
if os.path.exists(config.DATABASE):
97+
shutil.rmtree(config.DATABASE)
98+
self.db = self.module.Database(config, 'admin')
99+
self.dex = Indexer(self.db)
100+
def tearDown(self):
101+
if hasattr(self, 'db'):
102+
self.db.close()
103+
if os.path.exists(config.DATABASE):
104+
shutil.rmtree(config.DATABASE)
105+
106+
class postgresqlIndexerTest(postgresqlOpener, RDBMSIndexerTest):
107+
def setUp(self):
108+
postgresqlOpener.setUp(self)
109+
RDBMSIndexerTest.setUp(self)
110+
def tearDown(self):
111+
RDBMSIndexerTest.tearDown(self)
112+
postgresqlOpener.tearDown(self)
113+
114+
class mysqlIndexerTest(mysqlOpener, RDBMSIndexerTest):
115+
def setUp(self):
116+
mysqlOpener.setUp(self)
117+
RDBMSIndexerTest.setUp(self)
118+
def tearDown(self):
119+
RDBMSIndexerTest.tearDown(self)
120+
mysqlOpener.tearDown(self)
121+
122+
class sqliteIndexerTest(sqliteOpener, RDBMSIndexerTest):
123+
pass
124+
78125
def test_suite():
79126
suite = unittest.TestSuite()
127+
80128
suite.addTest(unittest.makeSuite(IndexerTest))
129+
81130
try:
82131
import xapian
83132
suite.addTest(unittest.makeSuite(XapianIndexerTest))
84133
except ImportError:
85134
print "Skipping Xapian indexer tests"
86135
pass
136+
137+
if have_backend('postgresql'):
138+
# make sure we start with a clean slate
139+
if postgresqlOpener.module.db_exists(config):
140+
postgresqlOpener.module.db_nuke(config, 1)
141+
suite.addTest(unittest.makeSuite(postgresqlIndexerTest))
142+
else:
143+
print "Skipping postgresql indexer tests"
144+
145+
if have_backend('mysql'):
146+
# make sure we start with a clean slate
147+
if mysqlOpener.module.db_exists(config):
148+
mysqlOpener.module.db_nuke(config)
149+
suite.addTest(unittest.makeSuite(mysqlIndexerTest))
150+
else:
151+
print "Skipping mysql indexer tests"
152+
153+
if have_backend('sqlite'):
154+
# make sure we start with a clean slate
155+
if sqliteOpener.module.db_exists(config):
156+
sqliteOpener.module.db_nuke(config)
157+
suite.addTest(unittest.makeSuite(sqliteIndexerTest))
158+
else:
159+
print "Skipping sqlite indexer tests"
160+
87161
return suite
88162

89163
if __name__ == '__main__':

test/test_sqlite.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17-
#
18-
# $Id: test_sqlite.py,v 1.5 2004-11-03 01:34:21 richard Exp $
17+
#
18+
# $Id: test_sqlite.py,v 1.6 2008-09-01 00:43:02 richard Exp $
1919

2020
import unittest, os, shutil, time
2121
from roundup.backends import get_backend, have_backend

0 commit comments

Comments
 (0)