Commit 67193c35 authored by Christian Theune's avatar Christian Theune

parent acc6eaef
...@@ -901,6 +901,7 @@ class ClientStorage(object): ...@@ -901,6 +901,7 @@ class ClientStorage(object):
self._server.storeBlobEnd(oid, serial, data, version, id(txn)) self._server.storeBlobEnd(oid, serial, data, version, id(txn))
break break
self._server.storeBlob(oid, serial, chunk, version, id(txn)) self._server.storeBlob(oid, serial, chunk, version, id(txn))
os.unlink(blobfilename)
return serials return serials
def _getDirtyFilename(self, oid, serial): def _getDirtyFilename(self, oid, serial):
...@@ -917,6 +918,7 @@ class ClientStorage(object): ...@@ -917,6 +918,7 @@ class ClientStorage(object):
utils.tid_repr(tid), utils.tid_repr(tid),
BLOB_SUFFIX,) BLOB_SUFFIX,)
) )
def loadBlob(self, oid, serial, version): def loadBlob(self, oid, serial, version):
blob_filename = self._getCleanFilename(oid, serial) blob_filename = self._getCleanFilename(oid, serial)
if os.path.exists(blob_filename): # XXX see race condition below if os.path.exists(blob_filename): # XXX see race condition below
......
...@@ -109,7 +109,10 @@ class BlobStorage(ProxyBase): ...@@ -109,7 +109,10 @@ class BlobStorage(ProxyBase):
def loadBlob(self, oid, serial, version): def loadBlob(self, oid, serial, version):
"""Return the filename where the blob file can be found. """Return the filename where the blob file can be found.
""" """
return self._getCleanFilename(oid, serial) filename = self._getCleanFilename(oid, serial)
if not os.path.exists(filename):
raise POSKeyError, "Not an existing blob."
return filename
def _getNewestBlobSerial(self, oid): def _getNewestBlobSerial(self, oid):
blob_path = self._getBlobPath(oid) blob_path = self._getBlobPath(oid)
......
- Support database import/export - Support database import/export
- Support ZEO - Support selection of text/binary mode for opening blobs
- Support selection of text/binary mode for blobs - Generic wrapper configuration for BlobStorage
- implement loadBlob and storeBlob Tests
-----
- loadBlob needs to handle the BLOB_CACHE_DIRECTORY - Importing backward compatible ZEXP files (no \0BLOBSTART) used
- storeBlob needs to hand the actual file data off to the server
...@@ -25,6 +25,8 @@ class IBlobStorage(Interface): ...@@ -25,6 +25,8 @@ class IBlobStorage(Interface):
serial. serial.
Returns a filename or None if no Blob data is connected with this OID. Returns a filename or None if no Blob data is connected with this OID.
Raises POSKeyError if the blobfile cannot be found.
""" """
def getBlobDirectory(): def getBlobDirectory():
......
...@@ -13,13 +13,16 @@ ...@@ -13,13 +13,16 @@
############################################################################## ##############################################################################
"""Support for database export and import.""" """Support for database export and import."""
import os
from cStringIO import StringIO from cStringIO import StringIO
from cPickle import Pickler, Unpickler from cPickle import Pickler, Unpickler
from tempfile import TemporaryFile from tempfile import TemporaryFile
import logging import logging
from ZODB.POSException import ExportError from ZODB.POSException import ExportError, POSKeyError
from ZODB.utils import p64, u64 from ZODB.utils import p64, u64, cp, mktemp
from ZODB.Blobs.interfaces import IBlobStorage
from ZODB.serialize import referencesf from ZODB.serialize import referencesf
logger = logging.getLogger('ZODB.ExportImport') logger = logging.getLogger('ZODB.ExportImport')
...@@ -49,6 +52,21 @@ class ExportImport: ...@@ -49,6 +52,21 @@ class ExportImport:
else: else:
referencesf(p, oids) referencesf(p, oids)
f.writelines([oid, p64(len(p)), p]) f.writelines([oid, p64(len(p)), p])
# Blob support
if not IBlobStorage.providedBy(self._storage):
continue
try:
blobfilename = self._storage.loadBlob(oid,
serial, self._version)
except POSKeyError: # Looks like this is not a blob
continue
f.write(blob_begin_marker)
f.write(p64(os.stat(blobfilename).st_size))
blobdata = open(blobfilename, "rb")
cp(blobdata, f)
blobdata.close()
f.write(export_end_marker) f.write(export_end_marker)
return f return f
...@@ -109,17 +127,20 @@ class ExportImport: ...@@ -109,17 +127,20 @@ class ExportImport:
version = self._version version = self._version
while 1: while 1:
h = f.read(16) header = f.read(16)
if h == export_end_marker: if header == export_end_marker:
break break
if len(h) != 16: if len(header) != 16:
raise ExportError("Truncated export file") raise ExportError("Truncated export file")
l = u64(h[8:16])
p = f.read(l) # Extract header information
if len(p) != l: ooid = header[:8]
length = u64(header[8:16])
data = f.read(length)
if len(data) != length:
raise ExportError("Truncated export file") raise ExportError("Truncated export file")
ooid = h[:8]
if oids: if oids:
oid = oids[ooid] oid = oids[ooid]
if isinstance(oid, tuple): if isinstance(oid, tuple):
...@@ -128,7 +149,21 @@ class ExportImport: ...@@ -128,7 +149,21 @@ class ExportImport:
oids[ooid] = oid = self._storage.new_oid() oids[ooid] = oid = self._storage.new_oid()
return_oid_list.append(oid) return_oid_list.append(oid)
pfile = StringIO(p) # Blob support
blob_begin = f.read(len(blob_begin_marker))
if blob_begin == blob_begin_marker:
# Copy the blob data to a temporary file
# and remember the name
blob_len = u64(f.read(8))
blob_filename = mktemp()
blob_file = open(blob_filename, "wb")
cp(f, blob_file, blob_len)
blob_file.close()
else:
f.seek(-len(blob_begin_marker),1)
blob_filename = None
pfile = StringIO(data)
unpickler = Unpickler(pfile) unpickler = Unpickler(pfile)
unpickler.persistent_load = persistent_load unpickler.persistent_load = persistent_load
...@@ -138,12 +173,17 @@ class ExportImport: ...@@ -138,12 +173,17 @@ class ExportImport:
pickler.dump(unpickler.load()) pickler.dump(unpickler.load())
pickler.dump(unpickler.load()) pickler.dump(unpickler.load())
p = newp.getvalue() data = newp.getvalue()
self._storage.store(oid, None, p, version, transaction) if blob_filename is not None:
self._storage.storeBlob(oid, None, data, blob_filename,
version, transaction)
else:
self._storage.store(oid, None, data, version, transaction)
export_end_marker = '\377'*16 export_end_marker = '\377'*16
blob_begin_marker = '\000BLOBSTART'
class Ghost(object): class Ghost(object):
__slots__ = ("oid",) __slots__ = ("oid",)
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
# #
############################################################################## ##############################################################################
import os
from zope.interface import implements from zope.interface import implements
from ZODB.Blobs.interfaces import IBlobStorage from ZODB.Blobs.interfaces import IBlobStorage
...@@ -150,6 +152,6 @@ class TmpStore: ...@@ -150,6 +152,6 @@ class TmpStore:
def generateBlobFile(self, oid): def generateBlobFile(self, oid):
if not self.blob_files.has_key(oid): if not self.blob_files.has_key(oid):
handle, name = tempfile.mkstemp() handle, name = tempfile.mkstemp()
handle.close() os.close(handle)
self.blob_files[oid] = name self.blob_files[oid] = name
return self.blob_files[oid] return self.blob_files[oid]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment