1 """
2 GtkListStore wrapping around Generic DB Abstraction layer, including a lru
3 cache to speed up operation.
4 Copyright (C) John Stowers 2007 <john.stowers@gmail.com>
5
6 Based on http://vwdude.com/dropbox/pystore/
7 Copyright (C) Christian Hergert 2007 <christian.hergert@gmail.com>
8
9 You may redistribute it and/or modify it under the terms of the
10 GNU General Public License, as published by the Free Software
11 Foundation; either version 2 of the License, or (at your option)
12 any later version.
13
14 main.c is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with main.c. If not, write to:
21 The Free Software Foundation, Inc.,
22 51 Franklin Street, Fifth Floor
23 Boston, MA 02110-1301, USA.
24 """
25 import gobject
26 import gtk
27 import logging
28 log = logging.getLogger("gtkui.Database")
29
30 import conduit.Database as DB
31 import conduit.utils as Utils
32
34 """
35 gtk.TreeModel implementation that saves and stores data directly
36 to and from a sqlite database. A simple LRU cache is included to
37 lower the number of required SQL queries.
38 """
39
40 OID_CACHE = True
41
43 """
44 Creates a new GenericDBListStore.
45
46 Parameters:
47 filename -- the filename of the sqlite database.
48 table -- the name of the table to manage.
49 """
50 gtk.GenericTreeModel.__init__(self)
51 genericDB.connect("row-inserted",self._on_inserted)
52 genericDB.connect("row-deleted",self._on_deleted)
53 genericDB.connect("row-modified",self._on_modified)
54
55 self.table = table
56 self.db = genericDB
57 self.oidcache = []
58 self.columns = self._get_columns()
59
61 self.oidcache = []
62 offset = self._get_offset(oid)
63 rowref = self.get_iter(offset)
64 path = self.get_path(rowref)
65 self.row_inserted(path, rowref)
66
68 self.oidcache = []
69 offset = self._get_offset(oid)
70 rowref = self.get_iter(offset)
71 path = self.get_path(rowref)
72 self.row_changed(path, rowref)
73
75 self.oidcache = []
76 offset = self._get_offset(oid)
77 rowref = self.get_iter(offset)
78 path = self.get_path(rowref)
79 self.row_deleted(path)
80
82 """
83 Returns the number of rows found in our loaded table inside
84 the sqlite database.
85 """
86 (rows,) = self.db.select_one("SELECT COUNT(oid) FROM %s" % self.table)
87 return rows
88
90 """
91 Returns the number of columns found in our sqlite table.
92 """
93 return ('oid',) + tuple(self.db.get_fields(self.table))
94
95 @DB.lru_cache(0)
97 """
98 Returns the oid of the row at offset.
99
100 Parameters:
101 offset -- the rows offset from 0.
102 """
103 (oid,) = self.db.select_one("SELECT oid FROM %s LIMIT 1 OFFSET %d" % (self.table, offset))
104 return oid
105
106 @DB.lru_cache(0)
108 """
109 Returns the value for a column in the table with a row id
110 of oid.
111
112 Parameters:
113 oid -- the rows internal oid.
114 column -- the column index.
115 """
116 (value,) = self.db.select_one("SELECT %s FROM %s WHERE oid = %d" % (self.columns[index], self.table, oid))
117 return value
118
119 @DB.lru_cache(0)
121 """
122 Returns the next oid after passed oid.
123
124 Note: for some reason unknown to me, gtk.TreeView or
125 perhaps the GenericTreeModel finds it neccessary to
126 iterate through every iter from the root node through
127 n_children. Because of this, we will fetch row ids in
128 sets of 1024 and cache them to speed things up.
129
130 Parameters:
131 oid -- the current oid.
132 """
133
134 if not oid:
135 oid = -1
136
137 if GenericDBListStore.OID_CACHE:
138 try:
139 index = self.oidcache.index(oid)
140 return self.oidcache[index+1]
141 except (ValueError, IndexError):
142 sql = "SELECT oid FROM %s WHERE oid >= %d LIMIT 1024" % (self.table, oid)
143 oids = [oid for (oid,) in self.db.select(sql)]
144 self.oidcache.extend(oids)
145 self.oidcache = Utils.unique_list(self.oidcache)
146
147 if len(oids) > 1:
148 oid = oids[1]
149 else:
150 oid = None
151 else:
152 try:
153 (oid,) = self.db.select_one("SELECT oid FROM %s WHERE oid > %d LIMIT 1" % (self.table, oid))
154 except TypeError:
155 oid = None
156
157 return oid
158
160 """
161 Returns the offset of oid in the sqlite table.
162
163 Parameters:
164 oid -- the oid of the row to check
165 """
166 (offset,) = self.db.select_one("SELECT COUNT(oid) FROM %s WHERE oid < %d" % (self.table, oid))
167 return offset
168
170 """
171 Returns the gtk.TreeModelFlags for the gtk.TreeModel
172 implementation. The gtk.TreeIter data is derived from
173 the database oids for records and therefore is persistant
174 across row deletion and inserts.
175 """
176 return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
177
179 """
180 Returns the number of columns found in the table metadata.
181 """
182 return len(self.columns)
183
185 """
186 All columns in sqlite are accessed via (char*). Therefore,
187 all of our column types will pass that right along and
188 allow the consumers to typecast as needed.
189 """
190 return gobject.TYPE_STRING
191
193 """
194 Traslates a gtk.TreePath to a gtk.TreeIter. This is done by
195 finding the oid for the row in the database at the same
196 offset as the path.
197 """
198 if len(path) > 1:
199 return None
200 try:
201 return self._get_oid(path[0])
202 except TypeError:
203 return None
204
206 """
207 Returns the rowrefs offset in the table which is used to
208 generate the gtk.TreePath.
209 """
210 return self._get_offset(rowref)
211
213 """
214 Returns the data for a rowref at the givin column.
215
216 Parameters:
217 rowref -- the rowref passed back in the on_get_iter
218 method.
219 column -- the integer offset of the column desired.
220 """
221 if column > len(self.columns):
222 return None
223 if column == 0:
224 return rowref
225 return self._get_value(rowref, column)
226
228 """
229 Returns the next oid found in the sqlite table.
230
231 Parameters:
232 rowref -- the oid of the current iter.
233 """
234 return self._get_next_oid(rowref)
235
237 """
238 Retruns children for a given rowref. This will always be
239 None unless the rowref is None, which is our root node.
240
241 Parameters:
242 rowref -- the oid of the desired row.
243 """
244 if rowref:
245 return None
246 return self._get_next_oid(-1)
247
249 """
250 Always returns False as List based TreeModels do not have
251 children.
252 """
253 return False
254
256 """
257 Returns the number of children a row has. Since only the
258 root node may have children, we return 0 unless the request
259 is made for the count of all rows. Requesting the row count
260 is done by passing None as the rowref.
261 """
262 if rowref:
263 return 0
264 return self._get_n_rows()
265
267 """
268 Returns the oid of the nth child from rowref. This will
269 only return a value if rowref is None, which is the
270 root node.
271
272 Parameters:
273 rowref -- the oid of the row.
274 n -- the row offset to retrieve.
275 """
276 if rowref:
277 return None
278 return self._get_oid(n)
279
281 """
282 Always returns None as lists do not have parent nodes.
283 """
284 return None
285