Mercurial > pylearn
annotate pylearn/dbdict/api0.py @ 621:7041749cf804
untested changes to close sessions in api0
author | James Bergstra <bergstrj@iro.umontreal.ca> |
---|---|
date | Sun, 18 Jan 2009 21:14:14 -0500 |
parents | fa6585609e01 |
children | d2d582bcf7dc |
rev | line source |
---|---|
538 | 1 from sqlalchemy import create_engine, desc |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
2 import sqlalchemy.pool |
538 | 3 from sqlalchemy.orm import sessionmaker |
4 from sqlalchemy import Table, Column, MetaData, ForeignKey | |
5 from sqlalchemy import Integer, String, Float, Boolean, DateTime, Text, Binary | |
6 from sqlalchemy.orm import mapper, relation, backref, eagerload | |
7 from sqlalchemy.sql import operators, select | |
8 from sql_commands import crazy_sql_command | |
9 | |
10 class Todo(Exception): """Replace this with some working code!""" | |
11 | |
12 class DbHandle (object): | |
13 """ | |
14 This class also provides filtering shortcuts that hide the names of the | |
15 DbHandle internal databases. | |
16 | |
17 Attributes: | |
18 dict_table | |
19 pair_table | |
20 link_table | |
21 | |
22 | |
23 | |
24 dict_table | |
25 | |
26 An SqlAlchemy-mapped class corresponding to database table with the | |
27 following schema: | |
28 | |
29 Column('id', Integer, primary_key=True) | |
30 Column('create', DateTime) | |
31 Column('write', DateTime) | |
32 Column('read', DateTime) | |
33 | |
34 #TODO: reconsider create/read/write | |
35 | |
36 pair_table | |
37 | |
38 An SqlAlchemy-mapped class corresponding to database table with the | |
39 following schema: | |
40 | |
41 Column('id', Integer, primary_key=True) | |
42 Column('name', String(128)) | |
43 Column('ntype', Boolean) | |
44 Column('fval', Double) | |
45 Column('sval', Text)) | |
46 Column('bval', Blob) | |
47 | |
48 #TODO: Consider difference between text and binary | |
49 #TODO: Consider adding a 'type' column | |
50 #TODO: Consider union? | |
51 #TODO: Are there stanard ways of doing this kind of thing? | |
52 | |
53 link_table | |
54 | |
55 An SqlAlchemy-mapped class corresponding to database table with the | |
56 following schema: | |
57 | |
58 Column('dict_id', Integer, ForeignKey('%s.id' % t_trial), primary_key=True), | |
59 Column('keyval_id', Integer, ForeignKey('%s.id' % t_keyval), primary_key=True)) | |
60 | |
61 """ | |
62 | |
63 e_bad_table = 'incompatible columns in table' | |
64 | |
65 def __init__(h_self, Session, engine, dict_table, pair_table, link_table): | |
66 h_self._engine = engine; | |
67 h_self._dict_table = dict_table | |
68 h_self._pair_table = pair_table | |
69 h_self._link_table = link_table | |
70 | |
71 #TODO: replace this crude algorithm (ticket #17) | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
72 if ['id', 'create', 'write', 'read', 'status', 'priority'] != [c.name for c in dict_table.c]: |
538 | 73 raise ValueError(h_self.e_bad_table, dict_table) |
74 if ['id', 'name', 'ntype', 'fval', 'sval', 'bval'] != [c.name for c in pair_table.c]: | |
75 raise ValueError(h_self.e_bad_table, pair_table) | |
76 if ['dict_id', 'pair_id'] != [c.name for c in link_table.c]: | |
77 raise ValueError(h_self.e_bad_table, pair_table) | |
78 | |
79 h_self._session_fn = Session | |
80 | |
81 class KeyVal (object): | |
82 """KeyVal interfaces between python types and the database. | |
83 | |
84 It encapsulates heuristics for type conversion. | |
85 """ | |
86 def __init__(k_self, name, val): | |
87 k_self.name = name | |
88 k_self.val = val | |
89 def __repr__(k_self): | |
90 return "<Param(%s,'%s', %s)>" % (k_self.id, k_self.name, repr(k_self.val)) | |
91 def __get_val(k_self): | |
92 val = None | |
93 if k_self.fval is not None: val = [int, float][k_self.ntype](k_self.fval) | |
94 if k_self.bval is not None: val = eval(str(k_self.bval)) | |
95 if k_self.sval is not None: val = k_self.sval | |
96 return val | |
97 def __set_val(k_self, val): | |
98 if isinstance(val, (str,unicode)): | |
99 k_self.fval = None | |
100 k_self.bval = None | |
101 k_self.sval = val | |
102 else: | |
103 k_self.sval = None | |
104 try: | |
105 f = float(val) | |
106 except (TypeError, ValueError): | |
107 f = None | |
108 if f is None: #binary data | |
109 k_self.bval = repr(val) | |
110 assert eval(k_self.bval) == val | |
111 k_self.fval = None | |
112 k_self.ntype = None | |
113 else: | |
114 k_self.bval = None | |
115 k_self.fval = f | |
116 k_self.ntype = isinstance(val,float) | |
117 val = property(__get_val, __set_val) | |
118 | |
119 mapper(KeyVal, pair_table) | |
120 | |
121 class Dict (object): | |
122 """ | |
123 Instances are dict-like objects with additional features for | |
124 communicating with an active database. | |
125 | |
126 This class will be mapped by SqlAlchemy to the dict_table. | |
127 | |
128 Attributes: | |
129 handle - reference to L{DbHandle} (creator) | |
130 | |
131 """ | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
132 def __init__(d_self, session=None): |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
133 if session is None: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
134 s = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
135 s.add(d_self) #d_self transient -> pending |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
136 s.commit() #d_self -> persistent |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
137 s.close() #d_self -> detached |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
138 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
139 s = session |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
140 s.save(d_self) |
538 | 141 |
142 _forbidden_keys = set(['session']) | |
143 | |
144 # | |
145 # dictionary interface | |
146 # | |
147 | |
148 def __contains__(d_self, key): | |
149 for a in d_self._attrs: | |
150 if a.name == key: | |
151 return True | |
152 return False | |
153 | |
154 def __eq__(self, other): | |
155 return dict(self) == dict(other) | |
156 def __neq__(self, other): | |
157 return dict(self) != dict(other) | |
158 | |
159 def __getitem__(d_self, key): | |
160 for a in d_self._attrs: | |
161 if a.name == key: | |
162 return a.val | |
163 raise KeyError(key) | |
164 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
165 def __setitem__(d_self, key, val, session=None): |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
166 if session is None: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
167 s = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
168 s.add(d_self) |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
169 d_self._set_in_session(key, val, s) |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
170 s.commit() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
171 s.close() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
172 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
173 s = session |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
174 s.add(d_self) |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
175 d_self._set_in_session(key, val, s) |
538 | 176 |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
177 def __delitem__(d_self, key, session=None): |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
178 if session is None: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
179 s = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
180 commit_close = True |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
181 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
182 s = session |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
183 commit_close = False |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
184 s.add(d_self) |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
185 |
538 | 186 #find the item to delete in d_self._attrs |
187 to_del = None | |
188 for i,a in enumerate(d_self._attrs): | |
189 if a.name == key: | |
190 assert to_del is None | |
191 to_del = (i,a) | |
192 if to_del is None: | |
193 raise KeyError(key) | |
194 else: | |
195 i, a = to_del | |
196 s.delete(a) | |
197 del d_self._attrs[i] | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
198 if commit_close: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
199 s.commit() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
200 #s.add(d_self) #why is this here? maybe it should go earlier?? |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
201 s.close() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
202 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
203 pass |
538 | 204 |
568
1f036d934ad9
improvements to dbdict interface
Olivier Breuleux <breuleuo@iro.umontreal.ca>
parents:
538
diff
changeset
|
205 def iteritems(d_self): |
1f036d934ad9
improvements to dbdict interface
Olivier Breuleux <breuleuo@iro.umontreal.ca>
parents:
538
diff
changeset
|
206 return d_self.items() |
1f036d934ad9
improvements to dbdict interface
Olivier Breuleux <breuleuo@iro.umontreal.ca>
parents:
538
diff
changeset
|
207 |
538 | 208 def items(d_self): |
209 return [(kv.name, kv.val) for kv in d_self._attrs] | |
210 | |
211 def keys(d_self): | |
212 return [kv.name for kv in d_self._attrs] | |
213 | |
214 def values(d_self): | |
215 return [kv.val for kv in d_self._attrs] | |
216 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
217 def update(d_self, dct, session=None, **kwargs): |
538 | 218 """Like dict.update(), set keys from kwargs""" |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
219 if session is None: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
220 s = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
221 commit_close = True |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
222 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
223 s = session |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
224 commit_close = False |
538 | 225 for k, v in dct.items(): |
226 d_self._set_in_session(k, v, s) | |
227 for k, v in kwargs.items(): | |
228 d_self._set_in_session(k, v, s) | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
229 |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
230 if commit_close: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
231 s.commit() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
232 s.close() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
233 else: |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
234 s.add(d_self) #why is this here? maybe it should go earlier?? |
538 | 235 |
236 def get(d_self, key, default): | |
237 try: | |
238 return d_self[key] | |
239 except KeyError: | |
240 return default | |
241 | |
589 | 242 def __str__(self): |
243 return 'Dict'+ str(dict(self)) | |
244 | |
538 | 245 # |
246 # database stuff | |
247 # | |
248 | |
249 def refresh(d_self, session=None): | |
250 """Sync key-value pairs from database to self | |
251 | |
252 @param session: use the given session, and do not commit. | |
253 | |
254 """ | |
255 if session is None: | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
256 session = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
257 session.add(d_self) #so session knows about us |
538 | 258 session.refresh(d_self) |
259 session.commit() | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
260 session.close() |
538 | 261 else: |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
262 session.add(d_self) #so session knows about us |
538 | 263 session.refresh(self.dbrow) |
264 | |
265 def delete(d_self, session=None): | |
266 """Delete this dictionary from the database | |
267 | |
268 @param session: use the given session, and do not commit. | |
269 """ | |
270 if session is None: | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
271 session = h_self._session_fn() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
272 session.add(d_self) #so session knows about us |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
273 session.delete(d_self) #mark for deletion |
538 | 274 session.commit() |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
275 session.close() |
538 | 276 else: |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
277 session.add(d_self) #so session knows about us |
538 | 278 session.delete(d_self) |
279 | |
280 # helper routine by update() and __setitem__ | |
281 def _set_in_session(d_self, key, val, session): | |
282 """Modify an existing key or create a key to hold val""" | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
283 #FIRST SOME MIRRORING HACKS |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
284 if key == 'dbdict.status': |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
285 ival = int(val) |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
286 d_self.status = ival |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
287 if key == 'dbdict.priority': |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
288 fval = float(val) |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
289 d_self.priority = fval |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
290 |
538 | 291 if key in d_self._forbidden_keys: |
292 raise KeyError(key) | |
293 created = None | |
294 for i,a in enumerate(d_self._attrs): | |
295 if a.name == key: | |
296 assert created == None | |
297 created = h_self._KeyVal(key, val) | |
298 d_self._attrs[i] = created | |
299 if not created: | |
300 created = h_self._KeyVal(key, val) | |
301 d_self._attrs.append(created) | |
302 session.save(created) | |
303 | |
304 mapper(Dict, dict_table, | |
305 properties = { | |
306 '_attrs': relation(KeyVal, | |
307 secondary=link_table, | |
308 cascade="all, delete-orphan") | |
309 }) | |
310 | |
311 class _Query (object): | |
312 """ | |
313 Attributes: | |
314 _query - SqlAlchemy.Query object | |
315 """ | |
316 | |
317 def __init__(q_self, query): | |
318 q_self._query = query | |
319 | |
320 def __iter__(q_self): | |
321 return q_self.all().__iter__() | |
322 | |
323 def __getitem__(q_self, item): | |
324 return q_self._query.__getitem__(item) | |
325 | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
326 def filter_eq(q_self, kw, arg): |
538 | 327 """Return a Query object that restricts to dictionaries containing |
328 the given kwargs""" | |
329 | |
330 #Note: when we add new types to the key columns, add them here | |
331 q = q_self._query | |
332 T = h_self._Dict | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
333 if isinstance(arg, (str,unicode)): |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
334 q = q.filter(T._attrs.any(name=kw, sval=arg)) |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
335 else: |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
336 try: |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
337 f = float(arg) |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
338 except (TypeError, ValueError): |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
339 f = None |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
340 if f is None: |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
341 q = q.filter(T._attrs.any(name=kw, bval=repr(arg))) |
538 | 342 else: |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
343 q = q.filter(T._attrs.any(name=kw, fval=f)) |
538 | 344 |
345 return h_self._Query(q) | |
346 | |
619
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
347 def filter_eq_dct(q_self, dct): |
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
348 rval = q_self |
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
349 for key, val in dct.items(): |
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
350 rval = rval.filter_eq(key,val) |
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
351 return rval |
fa6585609e01
added api0.filter_eq_dct
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
615
diff
changeset
|
352 |
538 | 353 def all(q_self): |
354 """Return an iterator over all matching dictionaries. | |
355 | |
356 See L{SqlAlchemy.Query} | |
357 """ | |
358 return q_self._query.all() | |
359 | |
360 def count(q_self): | |
361 """Return the number of matching dictionaries. | |
362 | |
363 See L{SqlAlchemy.Query} | |
364 """ | |
365 return q_self._query.count() | |
366 | |
367 def first(q_self): | |
368 """Return some matching dictionary, or None | |
369 See L{SqlAlchemy.Query} | |
370 """ | |
371 return q_self._query.first() | |
372 | |
373 def all_ordered_by(q_self, key, desc=False): | |
374 """Return query results, sorted. | |
375 | |
376 @type key: string or tuple of string or list of string | |
377 @param: keys by which to sort the results. | |
378 | |
379 @rtype: list of L{DbHandle._Dict} instances | |
380 @return: query results, sorted by given keys | |
381 """ | |
382 | |
383 # order_by is not easy to do in SQL based on the data structures we're | |
384 # using. Considering we support different data types, it may not be | |
385 # possible at all. | |
386 # | |
387 # It would be easy if 'pivot' or 'crosstab' were provided as part of the | |
388 # underlying API, but they are not. For example, read this: | |
389 # http://www.simple-talk.com/sql/t-sql-programming/creating-cross-tab-queries-and-pivot-tables-in-sql/ | |
390 | |
391 # load query results | |
392 results = list(q_self.all()) | |
393 | |
394 if isinstance(key, (tuple, list)): | |
395 val_results = [([d[k] for k in key], d) for d in results] | |
396 else: | |
397 val_results = [(d[key], d) for d in results] | |
398 | |
399 val_results.sort() #interesting: there is an optional key parameter | |
400 if desc: | |
401 val_results.reverse() | |
402 return [vr[-1] for vr in val_results] | |
403 | |
404 h_self._KeyVal = KeyVal | |
405 h_self._Dict = Dict | |
406 h_self._Query = _Query | |
407 | |
408 def __iter__(h_self): | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
409 return h_self.query.__iter__() |
538 | 410 |
411 def insert_kwargs(h_self, **dct): | |
412 """ | |
413 @rtype: DbHandle with reference to self | |
414 @return: a DbHandle initialized as a copy of dct | |
415 | |
416 @type dct: dict-like instance whose keys are strings, and values are | |
417 either strings, integers, floats | |
418 | |
419 @param dct: dictionary to insert | |
420 | |
421 """ | |
422 rval = h_self._Dict() | |
423 if dct: rval.update(dct) | |
424 return rval | |
425 def insert(h_self, dct): | |
426 """ | |
427 @rtype: DbHandle with reference to self | |
428 @return: a DbHandle initialized as a copy of dct | |
429 | |
430 @type dct: dict-like instance whose keys are strings, and values are | |
431 either strings, integers, floats | |
432 | |
433 @param dct: dictionary to insert | |
434 | |
435 """ | |
436 rval = h_self._Dict() | |
437 if dct: rval.update(dct) | |
438 return rval | |
439 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
440 def query(h_self, session): |
538 | 441 """Construct an SqlAlchemy query, which can be subsequently filtered |
442 using the instance methods of DbQuery""" | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
443 return h_self._Query(session.query(h_self._Dict)\ |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
444 .options(eagerload('_attrs'))) |
538 | 445 |
446 def createView(h_self, view): | |
447 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
448 s = h_self.session() |
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
449 cols = [] |
538 | 450 |
451 for col in view.columns: | |
452 if col.name is "id": | |
453 continue; | |
454 elif isinstance(col.type, (Integer,Float)): | |
455 cols.append([col.name,'fval']); | |
456 elif isinstance(col.type,String): | |
457 cols.append([col.name,'sval']); | |
458 elif isinstance(col.type,Binary): | |
459 cols.append([col.name,'bval']); | |
460 else: | |
461 assert "Error: wrong column type in view",view.name; | |
462 | |
463 # generate raw sql command string | |
464 viewsql = crazy_sql_command(view.name, cols, \ | |
465 h_self._pair_table.name, \ | |
466 h_self._link_table.name); | |
467 | |
468 #print 'Creating sql view with command:\n', viewsql; | |
469 | |
470 h_self._engine.execute(viewsql); | |
471 s.commit(); | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
472 s.close() |
538 | 473 |
474 class MappedClass(object): | |
475 pass | |
476 | |
477 mapper(MappedClass, view) | |
478 | |
479 return MappedClass | |
480 | |
481 def session(h_self): | |
482 return h_self._session_fn() | |
483 | |
484 | |
485 def db_from_engine(engine, | |
486 table_prefix='DbHandle_default_', | |
487 trial_suffix='trial', | |
488 keyval_suffix='keyval', | |
489 link_suffix='link'): | |
490 """Create a DbHandle instance | |
491 | |
492 @type engine: sqlalchemy engine (e.g. from create_engine) | |
493 @param engine: connect to this database for transactions | |
494 | |
495 @type table_prefix: string | |
496 @type trial_suffix: string | |
497 @type keyval_suffix: string | |
498 @type link_suffix: string | |
499 | |
500 @rtype: DbHandle instance | |
501 | |
502 @note: The returned DbHandle will use three tables to implement the | |
503 many-to-many pattern that it needs: | |
504 - I{table_prefix + trial_suffix}, | |
505 - I{table_prefix + keyval_suffix} | |
506 - I{table_prefix + link_suffix} | |
507 | |
508 """ | |
509 Session = sessionmaker(autoflush=True, autocommit=False) | |
510 | |
511 metadata = MetaData() | |
512 | |
513 t_trial = Table(table_prefix+trial_suffix, metadata, | |
514 Column('id', Integer, primary_key=True), | |
515 Column('create', DateTime), | |
516 Column('write', DateTime), | |
615
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
517 Column('read', DateTime), |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
518 Column('status', Integer), |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
519 Column('priority', Float(53)) |
a88ead1a1537
updated query to support new dot syntax, and hacked api0 to mirror dbdict.status and dbdict.priority to true columns
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
589
diff
changeset
|
520 ) |
538 | 521 |
522 t_keyval = Table(table_prefix+keyval_suffix, metadata, | |
523 Column('id', Integer, primary_key=True), | |
524 Column('name', String(128), nullable=False), #name of attribute | |
525 Column('ntype', Boolean), | |
526 Column('fval', Float(53)), | |
527 Column('sval', Text), | |
528 Column('bval', Binary)) | |
529 | |
530 t_link = Table(table_prefix+link_suffix, metadata, | |
531 Column('dict_id', Integer, ForeignKey('%s.id' % t_trial), | |
532 primary_key=True), | |
533 Column('pair_id', Integer, ForeignKey('%s.id' % t_keyval), | |
534 primary_key=True)) | |
535 | |
536 metadata.bind = engine | |
537 metadata.create_all() # no-op when tables already exist | |
538 #warning: tables can exist, but have incorrect schema | |
539 # see bug mentioned in DbHandle constructor | |
540 | |
541 return DbHandle(Session, engine, t_trial, t_keyval, t_link) | |
542 | |
543 def sqlite_memory_db(echo=False, **kwargs): | |
544 """Return a DbHandle backed by a memory-based database""" | |
545 engine = create_engine('sqlite:///:memory:', echo=False) | |
546 return db_from_engine(engine, **kwargs) | |
547 | |
548 def sqlite_file_db(filename, echo=False, **kwargs): | |
549 """Return a DbHandle backed by a file-based database""" | |
550 engine = create_engine('sqlite:///%s' % filename, echo=False) | |
551 return db_from_engine(engine, **kwargs) | |
552 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
553 def postgres_db(user, password, host, database, echo=False, poolclass=sqlalchemy.pool.NullPool, **kwargs): |
538 | 554 """Create an engine to access a postgres_dbhandle |
555 """ | |
556 db_str ='postgres://%(user)s:%(password)s@%(host)s/%(database)s' % locals() | |
557 | |
621
7041749cf804
untested changes to close sessions in api0
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
619
diff
changeset
|
558 engine = create_engine(db_str, pool_size=pool_size, echo=echo, poolclass=poolclass) |
538 | 559 |
560 return db_from_engine(engine, **kwargs) | |
561 |