Skip to content

Commit d5d909b

Browse files
author
Richard Jones
committed
support sqlite3
1 parent 9183c91 commit d5d909b

File tree

9 files changed

+101
-55
lines changed

9 files changed

+101
-55
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ are given with the most recent entry first.
33

44
2006-??-?? 1.1.3
55
Feature:
6+
- supports Python 2.5, including the sqlite3 module
67
- full timezone support (sf patch 1465296)
78
- handle connection loss when responding to web requests
89
- match incoming mail In-Reply-To against existing messages when no issue
@@ -23,6 +24,7 @@ Feature:
2324
- added filter_sql to SQL backends which takes an arbitrary SQL statement
2425
and returns a list of item ids
2526

27+
2628
Fixed:
2729
- Verbose option for import and export (sf bug 1505645)
2830
- -c option for roundup-mailgw won't accept parameter (sf bug 1505649)

doc/announcement.txt

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
I'm proud to release version 1.1.2 of Roundup.
1+
I'm proud to release version 1.1.3 of Roundup.
22

33
Feature:
4-
5-
- server-ctl script uses server configuration file (sf bug 1443805)
6-
7-
Fixed:
8-
9-
- indexing may be turned off for FileClass "content" now
10-
("content" and "type" properties are now automatically included in the
11-
FileClass schema where previously the "content" property was faked and
12-
"type" was optional)
13-
- reduced frequency of session timestamp update
14-
- progress display in roundup-admin reindex
15-
- bug in menu() permission filter (sf bug 1444440)
16-
- verbose output during import is optional now (sf bug 1475624)
17-
- escape *all* uses of "schema" in mysql backend (sf bug 1472120)
18-
- responses to user rego email (sf bug 1470254)
19-
- dangling connections in session handling (sf bug 1463359)
20-
- classhelp popup pagination forgot about "type" (sf bug 1465836)
21-
- umask is now configurable (with the same 0002 default)
22-
- sorting of entries in classhelp popup (sf bug 1449000)
23-
- allow single digit seconds in date spec (sf bug 1447141)
24-
- prevent generation of new single-digit seconds dates (sf bug 1429390)
25-
- implement close() on all indexers (sf bug 1242477)
26-
4+
- supports Python 2.5, including the sqlite3 module
5+
- full timezone support (sf patch 1465296)
6+
- handle connection loss when responding to web requests
7+
- match incoming mail In-Reply-To against existing messages when no issue
8+
id is specified in the Subject
9+
- added StringHTMLProperty wrapped() method to wrap long lines in issue
10+
display
11+
- include the popcal in Date field editing and search fields by default
12+
- @required in forms may now specify properties of linked items (sf patch
13+
1507093)
14+
- update for latest version of pysqlite (sf bug 1487098; patch 1534227)
15+
- update for latest version of psycopg2 (sf patch 1429391)
16+
- new "exporttables" command in roundup-admin (sf bug 1533791)
17+
- roundup-admin "export" may specify classes to exclude (sf bug 1533791)
18+
- sorting and grouping by multiple properties is now supported by the
19+
backends *and* the classic template.
20+
- sorting, grouping, and searching by transitive properties (e.g.,
21+
messages.author.supervisor) is now supported in all backends
22+
- added filter_sql to SQL backends which takes an arbitrary SQL statement
23+
and returns a list of item ids
24+
25+
There was also a lot of bugfixes - see the bundled CHANGES.txt file for the
26+
list.
2727

2828
If you're upgrading from an older version of Roundup you *must* follow
2929
the "Software Upgrade" guidelines given in the maintenance documentation.

doc/customizing.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.208 $
5+
:Version: $Revision: 1.209 $
66

77
.. This document borrows from the ZopeBook section on ZPT. The original is at:
88
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -1869,8 +1869,14 @@ filter lists of items from this class, filtered and sorted. Two
18691869
where dir is '+', '-' or None
18701870
and prop is a prop name or None.
18711871

1872-
eg. ``issue.filter(filterspec={"priority": "1"},
1873-
sort=[('activity', '+')])``
1872+
The propname in filterspec and prop in a sort/group spec
1873+
may be transitive, i.e., it may contain properties of
1874+
the form link.link.link.name.
1875+
1876+
eg. All issues with a priority of "1" with messages added in
1877+
the last week, sorted by activity date:
1878+
``issue.filter(filterspec={"priority": "1",
1879+
'messages.creation' : '.-1w;'}, sort=[('activity', '+')])``
18741880

18751881
filter_sql **Only in SQL backends**
18761882

@@ -2199,7 +2205,8 @@ show a convenience access to columns - request/show/colname will
21992205
sort index sort columns [(direction, column name)]
22002206
group index grouping properties [(direction, column name)]
22012207
filter properties to filter the index on
2202-
filterspec values to filter the index on
2208+
filterspec values to filter the index on (property=value, eg
2209+
``priority=1`` or ``messages.author=42``
22032210
search_text text to perform a full-text search on for an index
22042211
=========== ============================================================
22052212

doc/design.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,11 @@ Here is the interface provided by the hyperdatabase::
490490
1. String properties must match all elements in the list, and
491491
2. Other properties must match any of the elements in the list.
492492

493-
Note that now the propname in filterspec and prop in a
494-
sort/group spec may be transitive, i.e., it may contain
495-
properties of the form link.link.link.name, e.g. you can search
496-
for all issues where a message was added by a certain user in
497-
the last week with a filterspec of
493+
The propname in filterspec and prop in a sort/group spec may be
494+
transitive, i.e., it may contain properties of the form
495+
link.link.link.name, e.g. you can search for all issues where
496+
a message was added by a certain user in the last week with a
497+
filterspec of
498498
{'messages.author' : '42', 'messages.creation' : '.-1w;'}
499499
"""
500500

roundup/backends/__init__.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: __init__.py,v 1.36 2006-01-25 03:24:09 richard Exp $
18+
# $Id: __init__.py,v 1.37 2006-10-04 01:12:00 richard Exp $
1919

2020
'''Container for the hyperdb storage backend implementations.
2121
'''
@@ -28,9 +28,10 @@
2828
# module name, have_backend quietly returns False.
2929
# Otherwise the error is reraised.
3030
_modules = {
31-
'mysql': 'MySQLdb',
32-
'postgresql': 'psycopg',
33-
'tsearch2': 'psycopg',
31+
'mysql': ('MySQLdb',),
32+
'postgresql': ('psycopg',),
33+
'tsearch2': ('psycopg',),
34+
'sqlite': ('pysqlite', 'pysqlite2'),
3435
}
3536

3637
def get_backend(name):
@@ -65,10 +66,10 @@ def have_backend(name):
6566
get_backend(name)
6667
return 1
6768
except ImportError, e:
68-
global _modules
69-
if not str(e).startswith('No module named %s'
70-
% _modules.get(name, name)):
71-
raise
69+
for name in _modules.get(name, (name,)):
70+
if str(e).startswith('No module named %s'%name):
71+
return 0
72+
raise
7273
return 0
7374

7475
def list_backends():

roundup/backends/back_sqlite.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_sqlite.py,v 1.46 2006-08-30 09:05:30 schlatterbeck Exp $
1+
# $Id: back_sqlite.py,v 1.47 2006-10-04 01:12:00 richard Exp $
22
'''Implements a backend for SQLite.
33
44
See https://pysqlite.sourceforge.net/ for pysqlite info
@@ -13,10 +13,15 @@
1313

1414
from roundup import hyperdb, date, password
1515
from roundup.backends import rdbms_common
16+
is_sqlite3 = False
1617
try:
1718
import sqlite
1819
except ImportError:
19-
from pysqlite2 import dbapi2 as sqlite
20+
try:
21+
from pysqlite2 import dbapi2 as sqlite
22+
except ImportError:
23+
import sqlite3 as sqlite
24+
is_sqlite3 = True
2025

2126
def db_exists(config):
2227
return os.path.exists(os.path.join(config.DATABASE, 'db'))
@@ -26,7 +31,10 @@ def db_nuke(config):
2631

2732
class Database(rdbms_common.Database):
2833
# char to use for positional arguments
29-
arg = '%s'
34+
if is_sqlite3:
35+
arg = '?'
36+
else:
37+
arg = '%s'
3038

3139
# used by some code to switch styles of query
3240
implements_intersect = 1
@@ -88,10 +96,14 @@ def sql_open_connection(self):
8896

8997
db = os.path.join(self.config.DATABASE, 'db')
9098
logging.getLogger('hyperdb').info('open database %r'%db)
91-
conn = sqlite.connect(db=db)
9299
# set a 30 second timeout (extraordinarily generous) for handling
93100
# locked database
94-
conn.db.sqlite_busy_handler(self.sqlite_busy_handler)
101+
if is_sqlite3:
102+
conn = sqlite.connect(db, 30)
103+
conn.row_factory = sqlite.Row
104+
else:
105+
conn = sqlite.connect(db=db)
106+
conn.db.sqlite_busy_handler(self.sqlite_busy_handler)
95107
cursor = conn.cursor()
96108
return (conn, cursor)
97109

@@ -252,11 +264,21 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
252264
# generate the new value for the Interval int column
253265
if name.endswith('_int__'):
254266
name = name[2:-6]
255-
if entry.has_key(name):
267+
if is_sqlite3:
268+
try:
269+
v = hyperdb.Interval(entry[name]).as_seconds()
270+
except IndexError:
271+
v = None
272+
elif entry.has_key(name):
256273
v = hyperdb.Interval(entry[name]).as_seconds()
257274
else:
258275
v = None
259-
elif entry.has_key(name):
276+
elif is_sqlite3:
277+
try:
278+
v = entry[name]
279+
except IndexError:
280+
v = None
281+
elif (not is_sqlite3 and entry.has_key(name)):
260282
v = entry[name]
261283
else:
262284
v = None
@@ -342,10 +364,19 @@ def clear(self):
342364

343365
def create_class(self, spec):
344366
rdbms_common.Database.create_class(self, spec)
345-
sql = 'insert into ids (name, num) values (%s, %s)'
367+
sql = 'insert into ids (name, num) values (%s, %s)'%(self.arg, self.arg)
346368
vals = (spec.classname, 1)
347369
self.sql(sql, vals)
348370

371+
if is_sqlite3:
372+
def load_journal(self, classname, cols, nodeid):
373+
'''We need to turn the sqlite3.Row into a tuple so it can be
374+
unpacked'''
375+
l = rdbms_common.Database.load_journal(self,
376+
classname, cols, nodeid)
377+
cols = range(5)
378+
return [[row[col] for col in cols] for row in l]
379+
349380
class sqliteClass:
350381
def filter(self, search_matches, filterspec, sort=(None,None),
351382
group=(None,None)):

roundup/backends/indexer_common.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#$Id: indexer_common.py,v 1.6 2006-04-27 05:48:26 richard Exp $
1+
#$Id: indexer_common.py,v 1.7 2006-10-04 01:12:00 richard Exp $
22
import re, sets
33

44
from roundup import hyperdb
@@ -49,12 +49,17 @@ def search(self, search_terms, klass, ignore={}):
4949
propspec = {} # used to do the klass.find
5050
for propname in designator_propname.values():
5151
propspec[propname] = {} # used as a set (value doesn't matter)
52-
for classname, nodeid, property in hits:
52+
53+
# don't unpack hits entries as sqlite3's Row can't be unpacked :(
54+
for entry in hits:
5355
# skip this result if we don't care about this class/property
56+
classname = entry[0]
57+
property = entry[2]
5458
if ignore.has_key((classname, property)):
5559
continue
5660

5761
# if it's a property on klass, it's easy
62+
nodeid = entry[1]
5863
if classname == klass.classname:
5964
if not nodeids.has_key(nodeid):
6065
nodeids[nodeid] = {}

roundup/backends/indexer_rdbms.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#$Id: indexer_rdbms.py,v 1.14 2006-05-06 16:19:27 a1s Exp $
1+
#$Id: indexer_rdbms.py,v 1.15 2006-10-04 01:12:00 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.
@@ -95,7 +95,7 @@ def find(self, wordlist):
9595
a = ','.join([self.db.arg] * len(r))
9696
sql = 'select _class, _itemid, _prop from __textids '\
9797
'where _textid in (%s)'%a
98-
self.db.cursor.execute(sql, tuple([int(id) for (id,) in r]))
98+
self.db.cursor.execute(sql, tuple([int(row[0]) for row in r]))
9999

100100
else:
101101
# A more complex version for MySQL since it doesn't implement INTERSECT

roundup/backends/rdbms_common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: rdbms_common.py,v 1.181 2006-08-30 09:28:26 schlatterbeck Exp $
18+
#$Id: rdbms_common.py,v 1.182 2006-10-04 01:12:00 richard Exp $
1919
''' Relational database (SQL) backend common code.
2020
2121
Basics:
@@ -191,7 +191,7 @@ def save_dbschema(self):
191191
'''
192192
s = repr(self.database_schema)
193193
self.sql('delete from schema')
194-
self.sql('insert into schema values (%s)', (s,))
194+
self.sql('insert into schema values (%s)'%self.arg, (s,))
195195

196196
def post_init(self):
197197
''' Called once the schema initialisation has finished.

0 commit comments

Comments
 (0)