diff options
author | Andrzej Hunt <andrzej.hunt@collabora.com> | 2013-12-07 20:32:55 +0000 |
---|---|---|
committer | Andrzej Hunt <andrzej.hunt@collabora.com> | 2014-01-01 19:12:09 +0000 |
commit | 3f65b5066c80a6af4f1f04c7f6f194d789a043cd (patch) | |
tree | 49ee5200dff9d3ada6e2ea7af2fc5cfad197a671 | |
parent | 990c8f57b673eec03296ca72b19f946775840395 (diff) |
fdo#70664 Allow reading firebird Blob as InputStream.
Change-Id: Ie0cb93a902961b3f37daf435828263478c9d2997
(cherry picked from commit 2b24eec3d4ca50e676c0c3af86ecb92a8eb0a8a2)
-rw-r--r-- | connectivity/source/drivers/firebird/Blob.cxx | 241 | ||||
-rw-r--r-- | connectivity/source/drivers/firebird/Blob.hxx | 48 |
2 files changed, 190 insertions, 99 deletions
diff --git a/connectivity/source/drivers/firebird/Blob.cxx b/connectivity/source/drivers/firebird/Blob.cxx index f27835d5d12c..e1968ba7ce5b 100644 --- a/connectivity/source/drivers/firebird/Blob.cxx +++ b/connectivity/source/drivers/firebird/Blob.cxx @@ -13,6 +13,8 @@ #include "connectivity/dbexception.hxx" +#include <com/sun/star/lang/IllegalArgumentException.hpp> + using namespace ::connectivity::firebird; using namespace ::cppu; @@ -32,7 +34,8 @@ Blob::Blob(isc_db_handle* pDatabaseHandle, m_blobID(aBlobID), m_blobHandle(0), m_bBlobOpened(false), - m_blobData(0) + m_nBlobLength(0), + m_nBlobPosition(0) { } @@ -52,12 +55,41 @@ void Blob::ensureBlobIsOpened() &m_blobID, 0, NULL); + if (aErr) evaluateStatusVector(m_statusVector, "isc_open_blob2", *this); + m_bBlobOpened = true; + m_nBlobPosition = 0; + + char aBlobItems[] = { + isc_info_blob_total_length + }; + char aResultBuffer[20]; + + isc_blob_info(m_statusVector, + &m_blobHandle, + sizeof(aBlobItems), + aBlobItems, + sizeof(aResultBuffer), + aResultBuffer); + + if (aErr) + evaluateStatusVector(m_statusVector, "isc_blob_info", *this); + + if (*aResultBuffer == isc_info_blob_total_length) + { + short aResultLength = (short) isc_vax_integer(aResultBuffer+1, 2); + m_nBlobLength = isc_vax_integer(aResultBuffer+3, aResultLength); + } + else + { + assert(false); + } } -void SAL_CALL Blob::disposing(void) +void Blob::closeBlob() + throw (SQLException) { MutexGuard aGuard(m_aMutex); @@ -67,20 +99,26 @@ void SAL_CALL Blob::disposing(void) aErr = isc_close_blob(m_statusVector, &m_blobHandle); if (aErr) - { - try - { - evaluateStatusVector(m_statusVector, "isc_close_blob", *this); - } - catch (SQLException e) - { - // we cannot throw any exceptions here anyway - SAL_WARN("connectivity.firebird", "isc_close_blob failed\n" << - e.Message); - } - } + evaluateStatusVector(m_statusVector, "isc_close_blob", *this); + + m_bBlobOpened = false; + m_blobHandle = 0; } +} +void SAL_CALL Blob::disposing(void) +{ + try + { + closeBlob(); + } + catch (SQLException e) + { + // we cannot throw any exceptions here... + SAL_WARN("connectivity.firebird", "isc_close_blob failed\n" << + e.Message); + assert(false); + } Blob_BASE::disposing(); } @@ -91,118 +129,131 @@ sal_Int64 SAL_CALL Blob::length() checkDisposed(Blob_BASE::rBHelper.bDisposed); ensureBlobIsOpened(); - char aBlobItems[] = { - isc_info_blob_total_length - }; - char aResultBuffer[20]; - - isc_blob_info(m_statusVector, - &m_blobHandle, - sizeof(aBlobItems), - aBlobItems, - sizeof(aResultBuffer), - aResultBuffer); - - evaluateStatusVector(m_statusVector, "isc_blob_info", *this); - if (*aResultBuffer == isc_info_blob_total_length) - { - short aResultLength = (short) isc_vax_integer(aResultBuffer, 2); - return isc_vax_integer(aResultBuffer+2, aResultLength); - } - return 0; + return m_nBlobLength; } -uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 aPosition, sal_Int32 aLength) +uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 nPosition, + sal_Int32 nBytes) throw(SQLException, RuntimeException) { MutexGuard aGuard(m_aMutex); checkDisposed(Blob_BASE::rBHelper.bDisposed); ensureBlobIsOpened(); - sal_Int64 aTotalLength = length(); + if (nPosition > m_nBlobLength) + throw lang::IllegalArgumentException("nPosition out of range", *this, 0); + // We only have to read as many bytes as are available, i.e. nPosition+nBytes + // can legally be greater than the total length, hence we don't bother to check. - if (!(aPosition + aLength < aTotalLength)) + if (nPosition > m_nBlobPosition) { - throw SQLException("Byte array requested outwith valid range", *this, OUString(), 1, Any() ); + // Resets to the beginning (we can't seek these blobs) + closeBlob(); + ensureBlobIsOpened(); } - if (aTotalLength != m_blobData.getLength()) - { - m_blobData = uno::Sequence< sal_Int8 >(aTotalLength); - char* pArray = (char*) m_blobData.getArray(); - sal_Int64 aBytesRead = 0; - - unsigned short aLengthRead; // The amount read in in a isc_get_segment call - - ISC_STATUS aErr; - do - { - aErr = isc_get_segment(m_statusVector, - &m_blobHandle, - &aLengthRead, - aTotalLength - aBytesRead, - pArray + aBytesRead); - } - while (aErr == 0 || m_statusVector[1] == isc_segment); - // Denotes either sucessful read, or only part of segment read successfully. - if (aErr) - { - m_blobData = uno::Sequence< sal_Int8 >(0); - evaluateStatusVector(m_statusVector, "isc_get_segment", *this); - } - } + skipBytes(nPosition - m_nBlobPosition); - if (aLength<aTotalLength) - { - uno::Sequence< sal_Int8 > aRet(aLength); - memcpy(aRet.getArray(), m_blobData.getArray() + aLength, aLength); - return aRet; - } - else - { - return m_blobData; // TODO: subsequence - } + // Don't bother preallocating: readBytes does the appropriate calculations + // and reallocates for us. + uno::Sequence< sal_Int8 > aBytes; + readBytes(aBytes, nBytes); + return aBytes; } uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream() throw(SQLException, RuntimeException) { -// MutexGuard aGuard(m_aMutex); -// checkDisposed(Blob_BASE::rBHelper.bDisposed); -// ensureBlobIsOpened(); - - ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this); - return NULL; + return this; } -sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& rPattern, - sal_Int64 aStart) +sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/, + sal_Int64 /*nStart*/) throw(SQLException, RuntimeException) { -// MutexGuard aGuard(m_aMutex); -// checkDisposed(Blob_BASE::rBHelper.bDisposed); -// ensureBlobIsOpened(); - - (void) rPattern; - (void) aStart; - ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this); + ::dbtools::throwFeatureNotImplementedException("Blob::position", *this); return 0; } -sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& rPattern, - sal_Int64 aStart) +sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/, + sal_Int64 /*aStart*/) throw(SQLException, RuntimeException) { -// MutexGuard aGuard(m_aMutex); -// checkDisposed(Blob_BASE::rBHelper.bDisposed); -// ensureBlobIsOpened(); - - (void) rPattern; - (void) aStart; ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this); return 0; } +// ---- XInputStream ---------------------------------------------------------- + +sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nBytes) + throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + + // Ensure we have enough space for the amount of data we can actually read. + const sal_Int64 nBytesAvailable = m_nBlobLength - m_nBlobPosition; + const sal_Int32 nBytesToRead = nBytes < nBytesAvailable ? nBytes : nBytesAvailable; + + if (rDataOut.getLength() < nBytesToRead) + rDataOut.realloc(nBytesToRead); + sal_Int32 nTotalBytesRead = 0; + ISC_STATUS aErr; + while (nTotalBytesRead < nBytesToRead) + { + sal_uInt16 nBytesRead = 0; + sal_uInt64 nDataRemaining = nBytesToRead - nTotalBytesRead; + sal_uInt16 nReadSize = (nDataRemaining > SAL_MAX_UINT16) ? SAL_MAX_UINT16 : nDataRemaining; + aErr = isc_get_segment(m_statusVector, + &m_blobHandle, + &nBytesRead, + nReadSize, + (char*) rDataOut.getArray() + nTotalBytesRead); + if (aErr) + evaluateStatusVector(m_statusVector, "isc_get_segment", *this); + nTotalBytesRead += nBytesRead; + m_nBlobPosition += nBytesRead; + } + + return nTotalBytesRead; +} + +sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nMaximumBytes) + throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) +{ + // We don't have any way of verifying how many bytes are immediately available, + // hence we just pass through direct to readBytes + // (Spec: "reads the available number of bytes, at maximum nMaxBytesToRead.") + return readBytes(rDataOut, nMaximumBytes); +} + +void SAL_CALL Blob::skipBytes(sal_Int32 nBytesToSkip) + throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) +{ + // There is no way of directly skipping, hence we have to pretend to skip + // by reading & discarding the data. + uno::Sequence< sal_Int8 > aBytes; + readBytes(aBytes, nBytesToSkip); +} + +sal_Int32 SAL_CALL Blob::available() + throw (NotConnectedException, IOException, RuntimeException) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + + return m_nBlobLength - m_nBlobPosition; +} + +void SAL_CALL Blob::closeInput() + throw(NotConnectedException, IOException, RuntimeException) +{ + closeBlob(); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/connectivity/source/drivers/firebird/Blob.hxx b/connectivity/source/drivers/firebird/Blob.hxx index f4eb792f2edf..3814f59005a7 100644 --- a/connectivity/source/drivers/firebird/Blob.hxx +++ b/connectivity/source/drivers/firebird/Blob.hxx @@ -12,15 +12,17 @@ #include <ibase.h> -#include <cppuhelper/compbase1.hxx> +#include <cppuhelper/compbase2.hxx> +#include <com/sun/star/io/XInputStream.hpp> #include <com/sun/star/sdbc/XBlob.hpp> namespace connectivity { namespace firebird { - typedef ::cppu::WeakComponentImplHelper1< ::com::sun::star::sdbc::XBlob > + typedef ::cppu::WeakComponentImplHelper2< ::com::sun::star::sdbc::XBlob, + ::com::sun::star::io::XInputStream > Blob_BASE; class Blob : @@ -38,13 +40,19 @@ namespace connectivity isc_blob_handle m_blobHandle; bool m_bBlobOpened; + sal_Int64 m_nBlobLength; + sal_Int64 m_nBlobPosition; ISC_STATUS_ARRAY m_statusVector; - ::com::sun::star::uno::Sequence< sal_Int8 > m_blobData; - void ensureBlobIsOpened() throw(::com::sun::star::sdbc::SQLException); + /** + * Closes the blob and cleans up resources -- can be used to reset + * the blob if we e.g. want to read from the beginning again. + */ + void closeBlob() + throw(::com::sun::star::sdbc::SQLException); public: Blob(isc_db_handle* pDatabaseHandle, @@ -75,6 +83,38 @@ namespace connectivity throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException); + // ---- XInputStream ---------------------------------------------- + virtual sal_Int32 SAL_CALL + readBytes(::com::sun::star::uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nBytes) + throw(::com::sun::star::io::NotConnectedException, + ::com::sun::star::io::BufferSizeExceededException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL + readSomeBytes(::com::sun::star::uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nMaximumBytes) + throw(::com::sun::star::io::NotConnectedException, + ::com::sun::star::io::BufferSizeExceededException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + skipBytes(sal_Int32 nBytes) + throw(::com::sun::star::io::NotConnectedException, + ::com::sun::star::io::BufferSizeExceededException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL + available() + throw(::com::sun::star::io::NotConnectedException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + closeInput() + throw(::com::sun::star::io::NotConnectedException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + // ---- OComponentHelper ------------------------------------------ virtual void SAL_CALL disposing(); }; |