Document storage: the default db encoding is base64 in bytea

The storage class has to consider that. Also, prepare a class for raw bytea
storage.

bzr revid: p_christ@hol.gr-20100701175133-xxgu3cdlff0u7086
This commit is contained in:
P. Christeas 2010-07-01 20:51:33 +03:00
parent 2c5082001e
commit 7c26eb67fb
1 changed files with 59 additions and 5 deletions

View File

@ -115,7 +115,9 @@ class nodefd_db(StringIO, nodes.node_descriptor):
mode = mode[:-1]
if mode in ('r', 'r+'):
StringIO.__init__(self, ira_browse.db_datas)
cr.execute('SELECT db_datas FROM ir_attachment WHERE id = %s', ira_browse.id)
data = cr.fetchone()[0]
StringIO.__init__(self, data)
elif mode in ('w', 'w+'):
StringIO.__init__(self, None)
# at write, we start at 0 (= overwrite), but have the original
@ -136,13 +138,13 @@ class nodefd_db(StringIO, nodes.node_descriptor):
try:
if self.mode in ('w', 'w+', 'r+'):
out = self.getvalue()
cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s',
cr.execute("UPDATE ir_attachment SET db_datas = decode(%s,'escape'), file_size=%s WHERE id = %s",
(out, len(out), par.file_id))
elif self.mode == 'a':
out = self.getvalue()
cr.execute("UPDATE ir_attachment " \
"SET db_datas = COALESCE(db_datas,'') || %s, " \
" file_size = COALESCE(file_size, 0) + %d " \
"SET db_datas = COALESCE(db_datas,'') || decode(%s, 'escape'), " \
" file_size = COALESCE(file_size, 0) + %s " \
" WHERE id = %s",
(out, len(out), par.file_id))
cr.commit()
@ -153,6 +155,58 @@ class nodefd_db(StringIO, nodes.node_descriptor):
cr.close()
StringIO.close(self)
class nodefd_db64(StringIO, nodes.node_descriptor):
""" A descriptor to db data, base64 (the old way)
It stores the data in base64 encoding at the db. Not optimal, but
the transparent compression of Postgres will save the day.
"""
def __init__(self, parent, ira_browse, mode):
nodes.node_descriptor.__init__(self, parent)
if mode.endswith('b'):
mode = mode[:-1]
if mode in ('r', 'r+'):
StringIO.__init__(self, base64.decodestring(ira_browse.db_datas))
elif mode in ('w', 'w+'):
StringIO.__init__(self, None)
# at write, we start at 0 (= overwrite), but have the original
# data available, in case of a seek()
elif mode == 'a':
StringIO.__init__(self, None)
else:
logging.getLogger('document.storage').error("Incorrect mode %s specified", mode)
raise IOError(errno.EINVAL, "Invalid file mode")
self.mode = mode
def close(self):
# we now open a *separate* cursor, to update the data.
# FIXME: this may be improved, for concurrency handling
par = self._get_parent()
uid = par.context.uid
cr = pooler.get_db(par.context.dbname).cursor()
try:
if self.mode in ('w', 'w+', 'r+'):
out = self.getvalue()
cr.execute('UPDATE ir_attachment SET db_datas = %s::bytea, file_size=%s WHERE id = %s',
(base64.encodestring(out), len(out), par.file_id))
elif self.mode == 'a':
out = self.getvalue()
# Yes, we're obviously using the wrong representation for storing our
# data as base64-in-bytea
cr.execute("UPDATE ir_attachment " \
"SET db_datas = encode( (COALESCE(decode(encode(db_datas,'escape'),'base64'),'') || decode(%s, 'base64')),'base64')::bytea , " \
" file_size = COALESCE(file_size, 0) + %s " \
" WHERE id = %s",
(base64.encodestring(out), len(out), par.file_id))
cr.commit()
except Exception, e:
logging.getLogger('document.storage').exception('Cannot update db file #%d for close:', par.file_id)
raise
finally:
cr.close()
StringIO.close(self)
class document_storage(osv.osv):
""" The primary object for data storage.
Each instance of this object is a storage media, in which our application
@ -239,7 +293,7 @@ class document_storage(osv.osv):
# TODO: we need a better api for large files
if self._debug:
self._doclog.debug("Trying to obtain db_datas for ir.attachment[%d]", ira.id)
return nodefd_db(file_node, ira_browse=ira, mode=mode)
return nodefd_db64(file_node, ira_browse=ira, mode=mode)
elif boo.type == 'realstore':
if not ira.store_fname: