1 #=======================================================================
3 __version__
= '''0.0.05'''
4 __sub_version__
= '''20070105020543'''
5 __copyright__
= '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
16 #-----------------------------------------------------------------------
17 #-------------------------------------------------------transactioned---
18 # XXX transaction decorator...
26 #-----------------------------------------------------------------------
27 #-------------------------------------------------registertypehandler---
28 # XXX can this be made into a generic dispatch decorator???
29 def registertypehandler(type):
32 handlers
= sys
._getframe
(1).f_locals
['_typehandlers']
39 #-----------------------------------------------------------SQLWriter---
41 ##!!! ADD FUNCTIONS AND OTHER TYPES (fallback to pickle) !!!##
42 # XXX this is not able to pickle extension types... (fix?)
43 # TODO make an atomic type handler constructor... (should this be
45 # TODO make ALL of thefolowing packable into transactions...
46 # XXX needs more pedantic checking...
47 # XXX add value check for mutable objects... (if value exists then
50 class SQLWriter(object):
55 __object_native_attrs__
= ('__dict__', '__class__')
57 def __init__(self
, sql
):
61 # atomic type handlers...
62 @registertypehandler(int)
63 def do_int(self
, o
, oid
=None):
66 obj_id
= self
.sql
.insert('py_object', type='py_int').lastrowid
67 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
68 self
.sql
.insert('py_int', pyoid
=obj_id
, value
=o
)
70 @registertypehandler(long)
71 def do_long(self
, o
, oid
=None):
74 obj_id
= self
.sql
.insert('py_object', type='py_long').lastrowid
75 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
76 self
.sql
.insert('py_long', pyoid
=obj_id
, value
=o
)
78 @registertypehandler(float)
79 def do_float(self
, o
, oid
=None):
82 obj_id
= self
.sql
.insert('py_object', type='py_float').lastrowid
83 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
84 self
.sql
.insert('py_float', pyoid
=obj_id
, value
=o
)
86 ## @registertypehandler(complex)
87 ## def do_complex(self, o, oid=None):
91 @registertypehandler(str)
92 def do_str(self
, o
, oid
=None):
95 obj_id
= self
.sql
.insert('py_object', type='py_str').lastrowid
96 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
97 self
.sql
.insert('py_str', pyoid
=obj_id
, value
=o
)
99 @registertypehandler(unicode)
100 def do_unicode(self
, o
, oid
=None):
103 obj_id
= self
.sql
.insert('py_object', type='py_unicode').lastrowid
104 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
105 self
.sql
.insert('py_unicode', pyoid
=obj_id
, value
=o
)
107 @registertypehandler(tuple)
108 def do_tuple(self
, tpl
, oid
=None):
112 pyoid(oid) -> py_object.pyoid
114 py_tuple_items row format:
115 pyoid(oid) -> py_tuple.pyoid
117 value(oid) -> py_object.pyoid
119 obj_id
= self
.sql
.insert('py_object', type='py_tuple').lastrowid
120 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
121 self
.sql
.insert('py_tuple', pyoid
=obj_id
)
122 # XXX this might be bad as it will not track the HL object OIDs
123 # and thus might split some mutable objects in two or more
124 # independent versions...
125 for i
, o
in enumerate(tpl
):
126 # insert the element...
127 item_id
= self
.write(o
)
128 self
.sql
.insert('py_tuple_item', order
=i
, pyoid
=obj_id
, value
=item_id
)
130 # mutable handlers...
131 @registertypehandler(list)
132 def do_list(self
, lst
, oid
=None):
136 pyoid(oid) -> py_object.pyoid
138 py_list_items row format:
139 pyoid(oid) -> py_list.pyoid
141 value(oid) -> py_object.pyoid
143 NOTE: if object id (oid) is given, the object will be updated.
146 # XXX use the strategy of "keep the existing, add the new,
148 self
.sql
.delete('py_list_item', self
.sql
.where(pyoid
=oid
))
151 obj_id
= self
.sql
.insert('py_object', type='py_list').lastrowid
152 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
153 self
.sql
.insert('py_list', pyoid
=obj_id
)
154 # insert the list items...
155 # XXX this might be bad as it will not track the HL object OIDs
156 # and thus might split some mutable objects in two or more
157 # independent versions...
158 for i
, o
in enumerate(lst
):
159 item_id
= self
.write(o
)
160 self
.sql
.insert('py_list_item', order
=i
, pyoid
=obj_id
, value
=item_id
)
162 @registertypehandler(dict)
163 def do_dict(self
, dct
, oid
=None):
167 pyoid(oid) -> py_object.pyoid
169 py_dict_items row format:
170 pyoid(oid) -> py_dict.pyoid
171 key(oid) -> py_object.pyoid
172 value(oid) -> py_object.pyoid
174 NOTE: if object id (oid) is given, the object will be updated.
177 # XXX use the strategy of "keep the existing, add the new,
179 self
.sql
.delete('py_dict_item', self
.sql
.where(pyoid
=oid
))
182 obj_id
= self
.sql
.insert('py_object', type='py_dict').lastrowid
183 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
184 self
.sql
.insert('py_dict', pyoid
=obj_id
)
185 # insert the items...
186 # XXX this might be bad as it will not track the HL object OIDs
187 # and thus might split some mutable objects in two or more
188 # independent versions...
189 for k
, v
in dct
.items():
190 key_id
= self
.write(k
)
191 val_id
= self
.write(v
)
192 self
.sql
.insert('py_dict_item', pyoid
=obj_id
, key
=key_id
, value
=val_id
)
194 @registertypehandler(object)
195 def do_object(self
, obj
, oid
=None):
198 NOTE: if object id (oid) is given, the object will be updated.
201 # XXX use the strategy of "keep the existing, add the new,
203 self
.sql
.delete('py_object_attribute', self
.sql
.where(pyoid
=oid
))
206 obj_id
= self
.sql
.insert('py_object').lastrowid
207 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
209 for n
in self
.__object
_native
_attrs
__:
210 # insert the element...
211 ## name_id = self.write(n)
212 ## self.sql.insert('py_object_attribute', pyoid=obj_id, name=name_id, value=val_id)
213 # the if here is to avoid special cases like None and Guido
214 # knows what that may not have a special attr like __dict__
215 # in the case of None.... #$%^&*
217 val_id
= self
.write(getattr(obj
, n
))
218 self
.sql
.insert('py_object_attribute', pyoid
=obj_id
, name
=n
, value
=val_id
)
221 @registertypehandler(type)
222 def do_class(self
, cls
, oid
=None):
225 obj_id
= self
.sql
.insert('py_object', type='py_pickled_class').lastrowid
226 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
228 # for some reason pickle can't pickle NoneType....
229 if cls
== types
.NoneType
:
232 self
.sql
.insert('py_pickled_class',
234 pickle
=pickle
.dumps(cls
))
236 @registertypehandler(types
.FunctionType
)
237 def do_function(self
, cls
, oid
=None):
240 obj_id
= self
.sql
.insert('py_object', type='py_pickled_function').lastrowid
241 obj_id
= self
.sql
.select('pyoid', 'py_object', self
.sql
.where(oid
=obj_id
)).fetchone()[0]
243 self
.sql
.insert('py_pickled_function',
245 pickle
=pickle
.dumps(cls
))
247 # HL interface methods...
248 # XXX make this support the pickle protocols...
250 def write(self
, obj
, oid
=None):
254 handler
= self
._typehandlers
.get(t
, None)
256 return self
.do_object(obj
, oid
)
257 return handler(self
, obj
, oid
)
259 def writebyname(self
, name
, obj
):
262 oid
= self
.write(obj
)
263 obj_id
= self
.sql
.insert('py_registry', name
=name
, pyoid
=oid
)
268 #-----------------------------------------------------------------------
269 #--------------------------------------------------------------Object---
270 # WARNING: do not modify this here!
271 class Object(object):
273 abstract class used in object reconstruction.
278 #------------------------------------------------registertablehandler---
279 # XXX can this be made into a generic dispatch decorator???
280 def registertablehandler(table_name
):
283 handlers
= sys
._getframe
(1).f_locals
['_tablehandlers']
285 handlers
[table_name
] = func
290 #-----------------------------------------------------------SQLReader---
292 ##!!! ADD FUNCTIONS AND OTHER TYPES (fallback to pickle) !!!##
293 # TODO add lazy reconstruction option for mutable and deep objects...
294 # TODO make an atomic type handler constructor... (should this be
296 # TODO make the table names configurable....
297 class SQLReader(object):
302 def __init__(self
, sql
):
307 @registertablehandler('py_int')
308 def do_int(self
, oid
):
312 # XXX check if object exists (else panic?)
314 o
= self
.sql
.select('value', 'py_int', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
315 # XXX reconstruct attrs...
317 @registertablehandler('py_long')
318 def do_long(self
, oid
):
322 # XXX check if object exists (else panic?)
324 o
= self
.sql
.select('value', 'py_long', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
325 # XXX reconstruct attrs...
327 @registertablehandler('py_float')
328 def do_float(self
, oid
):
332 # XXX check if object exists (else panic?)
334 o
= self
.sql
.select('value', 'py_float', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
335 # XXX reconstruct attrs...
337 ## @registertablehandler('py_complex')
338 ## def do_complex(self, oid):
342 @registertablehandler('py_str')
343 def do_str(self
, oid
):
347 # XXX check if object exists (else panic?)
349 o
= self
.sql
.select('value', 'py_str', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
350 # XXX reconstruct attrs...
352 @registertablehandler('py_unicode')
353 def do_unicode(self
, oid
):
357 # XXX check if object exists (else panic?)
359 o
= self
.sql
.select('value', 'py_unicode', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
360 # XXX reconstruct attrs...
362 @registertablehandler('py_tuple')
363 def do_tuple(self
, oid
):
367 # XXX check if object exists (else panic?)
370 o
= list(self
.sql
.select(('order', 'value'), 'py_tuple_item', self
.sql
.where(pyoid
=oid
)).fetchall())
372 o
= tuple([ self
.get(e
) for (i
, e
) in o
])
373 # XXX reconstruct attrs...
375 # mutable handlers...
376 @registertablehandler('py_list')
377 def do_list(self
, oid
):
381 # XXX check if object exists (else panic?)
383 o
= list(self
.sql
.select(('order', 'value'), 'py_list_item', self
.sql
.where(pyoid
=oid
)).fetchall())
385 o
= [ self
.get(e
) for (i
, e
) in o
]
386 # XXX reconstruct attrs...
388 @registertablehandler('py_dict')
389 def do_dict(self
, oid
):
393 # XXX check if object exists (else panic?)
395 o
= list(self
.sql
.select(('key', 'value'), 'py_dict_item', self
.sql
.where(pyoid
=oid
)).fetchall())
396 o
= dict([ (self
.get(k
), self
.get(v
)) for (k
, v
) in o
])
397 # XXX reconstruct attrs...
399 @registertablehandler('py_object')
400 def do_object(self
, oid
):
404 # XXX check if object exists (else panic?)
406 dct
= dict(self
.sql
.select(('name', 'value'), 'py_object_attribute', self
.sql
.where(pyoid
=oid
)).fetchall())
407 # reconstruct attrs...
408 for n
, v
in dct
.items():
410 cls
= dct
.pop('__class__')
411 # there is only one None object and it is already created...
412 if cls
in (types
.NoneType
, None):
414 # generate the object...
416 for n
, v
in dct
.items():
421 @registertablehandler('py_pickled_class')
422 def do_class(self
, oid
):
426 # XXX check if object exists (else panic?)
428 o
= self
.sql
.select('pickle', 'py_pickled_class', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
430 # NoneType is represented as None...
432 return types
.NoneType
433 # XXX reconstruct attrs...
435 @registertablehandler('py_pickled_function')
436 def do_function(self
, oid
):
440 # XXX check if object exists (else panic?)
442 o
= self
.sql
.select('pickle', 'py_pickled_function', self
.sql
.where(pyoid
=oid
)).fetchone()[0]
444 # XXX reconstruct attrs...
446 # HL interface methods...
447 def get_oid(self
, name
):
451 return self
.sql
.select('pyoid', 'py_registry', self
.sql
.where(name
=name
)).fetchone()[0]
455 # XXX make this support the pickle protocols...
460 oid
= self
.sql
.select('pyoid', self
.sql
.where(name
=oid
)).fetchone()[0].rstrip()
461 t
= self
.sql
.select('type', 'py_object', self
.sql
.where(pyoid
=oid
)).fetchone()[0].rstrip()
462 # NOTE: here we compensate for a sideeffect of decorating
463 # methods while the class is not there yet...
464 return self
._tablehandlers
[t
](self
, oid
)
468 #-----------------------------------------------------------------------
469 #--------------------------------------------------------SQLInterface---
470 # TODO special interfaces for item access of lists and dicts...
471 # TODO special interfaces for object length and partial data (like dict
472 # keys, values... etc.)
473 # TODO compleat the types...
474 # TODO pass the transation id arround to enable:
475 # 1) query collection into one big SQL expression.
476 # 2) manage multiple transactions over one or several connections
477 # at the same time...
478 # TODO keep in mind the object id of mutable objects.
479 # NOTE: might be a good idea to track the object id in two layers:
480 # 1) save time (id in the database. unique in the db) -- sOID
481 # 2) restore time (id in runtime) -- pOID
482 # the idea is to keep a record of both ids to be able to link the
483 # stored object to its' live version.
484 # when only one id is present, it means that the object is either
485 # not yet saved or not yet read from db. if both are present,
486 # then we have both versions of the object.
489 # TODO add transaction hooks and wrappers...
490 # TODO split this into an abstract sql interface and a caching
492 class AbstractSQLInterface(object):
495 __sql_reader__
= None
496 __sql_writer__
= None
501 self
._liveobjects
= {}
503 def __update__(self
, oid
, obj
):
505 be stupid and update.
509 WARNING: not intended for direct use.
512 return self
.__sql
_writer
__.write(obj
, oid
)
513 def __insert__(self
, a
, b
=None):
515 be stupid and insert.
519 WARNING: not intended for direct use.
522 return self
.__sql
_writer
__.write(a
)
524 return self
.__sql
_writer
__.writebyname(a
, b
)
525 def __select__(self
, oid
):
531 WARNING: not intended for direct use.
534 return self
.__sql
_reader
__.get(oid
)
535 def __name2oid__(self
, name
):
538 return self
.__sql
_reader
__.get_oid(name
)
540 # XXX make this simpler!
541 def write(self
, a
, b
=None):
546 write(name, obj) -> OID
548 NOTE: in case #2 obj can not be None.
556 # 1) see if object has a sOID, if yes check if it is locked, if
558 # 2) disect and write/update...
561 tbl
= dict([ (b
, a
) for a
, b
in self
._liveobjects
.keys() ])
562 if pOID
in tbl
.keys():
565 return self
.__update
__(sOID
, obj
)
569 # insert name to registry...
570 sOID
= self
.__insert
__(name
, obj
)
572 sOID
= self
.__insert
__(obj
)
573 self
._liveobjects
[(sOID
, pOID
)] = obj
575 # TODO add hook for live obj condition...
579 # 1) see if object is live, if yes see if it is locked or dirty (in
580 # a transaction?), if so then warn.... (???)
581 # 2) construct object.
582 # 3) save sOID, pOID, ref in self._liveobjects
585 # get object id by name...
586 if not self
.isoid(sOID
):
588 sOID
= self
.__name
2oid
__(sOID
)
590 raise KeyError, 'object "%s" does not exist in DB.'
592 tbl
= dict(self
._liveobjects
.keys())
593 if sOID
in tbl
.keys():
595 ## print 'WARNING: object already open.'
596 return self
._liveobjects
[(sOID
, tbl
[sOID
])]
597 obj
= self
.__select
__(sOID
)
598 self
._liveobjects
[(sOID
, id(obj
))] = obj
601 def delete(self
, sOID
):
604 raise NotImplementedError
605 # registry specific methods...
609 return type(o
) in (int, long)
613 if __name__
== '__main__':
615 import psycopg2
as psycopg
619 ## DBHOST = 'mozg.cis.bigur.ru'
624 dbcon
= psycopg
.connect('host=%s dbname=%s user=%s password=%s' \
625 % (DBHOST
, DBDATABASE
, DBUSER
, PASSWORD
))
627 sqlobj
= sql
.SQL(dbcon
)
629 sqlinterface
= AbstractSQLInterface()
631 sqlinterface
.__sql
_reader
__ = SQLReader(sqlobj
)
632 sqlinterface
.__sql
_writer
__ = SQLWriter(sqlobj
)
636 d
= sqlinterface
.get(i
)
639 ## #sqlinterface.write('aaaa', d)
640 ## i = sqlinterface.write(d)
641 ## sqlinterface.__sql_reader__.sql.connection.commit()
643 print '>>>', i
, sqlinterface
.get(i
)
648 i
= sqlinterface
.write(d
)
649 sqlinterface
.__sql
_reader
__.sql
.connection
.commit()
651 print '>>>', i
, sqlinterface
.get(i
)
653 sqlinterface
.__sql
_reader
__.sql
.connection
.commit()
658 #=======================================================================
659 # vim:set ts=4 sw=4 nowrap :