1
2
3 ''' Dbm based on sqlite -- Needed to support shelves
4
5 Key and values are always stored as bytes. This means that when strings are
6 used they are implicitly converted to the default encoding before being
7 stored.
8
9 @todo Issues:
10 - ??? how to coordinate with whichdb
11 - ??? Size of text fields fixed or varchar (do we need blobs)
12 - ??? does default encoding affect str-->bytes or PySqlite3 always use UTF-8
13 - ??? if pure python overhead and pysqlite overhead is too high, rewrite in C
14 '''
15
16 __all__ = ['error', 'open']
17
18 import sqlite3, itertools, collections, sys, shelve, cPickle, threading
19 if sys.version_info < (3,0):
20 from itertools import imap as map
21
22 error = sqlite3.DatabaseError
23
24 -class SQLhash(collections.MutableMapping):
25
26 - def __init__(self, filename=':memory:', flags='r', mode=None):
27
28
29
30
31
32
33 MAKE_SHELF = 'CREATE TABLE IF NOT EXISTS shelf (key BLOB NOT NULL, value BLOB NOT NULL)'
34 MAKE_INDEX = 'CREATE UNIQUE INDEX IF NOT EXISTS keyndx ON shelf (key)'
35 self.conn = sqlite3.connect(filename)
36 self.conn.text_factory = bytes
37 self.conn.execute(MAKE_SHELF)
38 self.conn.execute(MAKE_INDEX)
39 self.conn.commit()
40
42 GET_LEN = 'SELECT COUNT(*) FROM shelf'
43 return self.conn.execute(GET_LEN).fetchone()[0]
44
47
50
53
55 return iter(self.keys())
56
58 GET_ITEM = 'SELECT value FROM shelf WHERE key = ?'
59 return self.conn.execute(GET_ITEM, (sqlite3.Binary(key),)).fetchone() is not None
60
62 GET_ITEM = 'SELECT value FROM shelf WHERE key = ?'
63 item = self.conn.execute(GET_ITEM, (sqlite3.Binary(key),)).fetchone()
64 if item is None:
65 raise KeyError(key)
66 return str(item[0])
67
69 ADD_ITEM = 'REPLACE INTO shelf (key, value) VALUES (?,?)'
70 self.conn.execute(ADD_ITEM, (sqlite3.Binary(key), sqlite3.Binary(value)))
71 self.conn.commit()
72
74 if key not in self:
75 raise KeyError(key)
76 DEL_ITEM = 'DELETE FROM shelf WHERE key = ?'
77 self.conn.execute(DEL_ITEM, (sqlite3.Binary(key),))
78 self.conn.commit()
79
80 - def update(self, items=(), **kwds):
81 if isinstance(items, collections.Mapping):
82 items = items.items()
83 items = ((sqlite3.Binary(k),sqlite3.Binary(v)) for k,v in items)
84 UPDATE_ITEMS = 'REPLACE INTO shelf (key, value) VALUES (?, ?)'
85 self.conn.executemany(UPDATE_ITEMS, items)
86 self.conn.commit()
87 if kwds:
88 self.update(kwds)
89
91 CLEAR_ALL = 'DELETE FROM shelf; VACUUM;'
92 self.conn.executescript(CLEAR_ALL)
93 self.conn.commit()
94
96 if self.conn is not None:
97 self.conn.commit()
98 self.conn.close()
99 self.conn = None
100
103
105
107 return repr(list(self))
108
110
112 GET_KEYS = 'SELECT key FROM shelf ORDER BY ROWID'
113 return (str(row[0]) for row in self._mapping.conn.cursor().execute(GET_KEYS))
114
116
118 GET_VALUES = 'SELECT value FROM shelf ORDER BY ROWID'
119 return (str(row[0]) for row in self._mapping.conn.cursor().execute(GET_VALUES))
120
122
124 GET_ITEMS = 'SELECT key, value FROM shelf ORDER BY ROWID'
125 return ((str(k), str(v)) for k,v in
126 iter(self._mapping.conn.cursor().execute(GET_ITEMS)))
127
128 -def open(file=None, *args):
129 if file is not None:
130 return SQLhash(file)
131 return SQLhash()
132
133 -class Shelf(shelve.Shelf):
135 kwargs = okwargs.copy()
136 try: del kwargs['cache']
137 except KeyError: pass
138 shelve.Shelf.__init__(self, *args, **kwargs)
139 if not okwargs.get('cache', True): self.cache = None
141 val = lambda: cPickle.loads(self.dict[k])
142 if self.cache is None:
143 return val()
144 else:
145 try: return self.cache[k]
146 except KeyError: return self.cache.setdefault(k, val())
148 if self.cache is not None and self.writeback: self.cache[key] = value
149 else: self.dict[key] = cPickle.dumps(value, self._protocol)
151 try:
152 del self.dict[key]
153 except:
154 if not self.writeback: raise
155
156
157
158
159
160 del self.cache[key]
161 else:
162
163 try: del self.cache[key]
164 except KeyError: pass
166 return ((k, cPickle.loads(v)) for k,v in self.dict.items())
168 if self.cache:
169 self.dict.update( (k, cPickle.dumps(v, self._protocol)) for k,v in self.cache.items() )
170 self.cache.clear()
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 if __name__ in '__main___':
204 for d in SQLhash(), SQLhash('example'):
205 print(list(d), "start")
206 d['abc'] = 'lmno'
207 print(d['abc'])
208 d['abc'] = 'rsvp'
209 d['xyz'] = 'pdq'
210 print(d.items())
211 print(d.values())
212 print(d.keys())
213 print(list(d), 'list')
214 d.update(p='x', q='y', r='z')
215 print(d.items())
216
217 del d['abc']
218 try:
219 print(d['abc'])
220 except KeyError:
221 pass
222 else:
223 raise Exception('oh noooo!')
224
225 try:
226 del d['abc']
227 except KeyError:
228 pass
229 else:
230 raise Exception('drat!')
231
232 print(list(d))
233 d.clear()
234 print(list(d))
235 d.update(p='x', q='y', r='z')
236 print(list(d))
237 d['xyz'] = 'pdq'
238
239 print()
240 d.close()
241