fdo#70664 Allow reading firebird Blob as InputStream.
Change-Id: Ie0cb93a902961b3f37daf435828263478c9d2997
This commit is contained in:
parent
cb4b290bcf
commit
2b24eec3d4
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "connectivity/dbexception.hxx"
|
#include "connectivity/dbexception.hxx"
|
||||||
|
|
||||||
|
#include <com/sun/star/lang/IllegalArgumentException.hpp>
|
||||||
|
|
||||||
using namespace ::connectivity::firebird;
|
using namespace ::connectivity::firebird;
|
||||||
|
|
||||||
using namespace ::cppu;
|
using namespace ::cppu;
|
||||||
@ -32,7 +34,8 @@ Blob::Blob(isc_db_handle* pDatabaseHandle,
|
|||||||
m_blobID(aBlobID),
|
m_blobID(aBlobID),
|
||||||
m_blobHandle(0),
|
m_blobHandle(0),
|
||||||
m_bBlobOpened(false),
|
m_bBlobOpened(false),
|
||||||
m_blobData(0)
|
m_nBlobLength(0),
|
||||||
|
m_nBlobPosition(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,44 +55,12 @@ void Blob::ensureBlobIsOpened()
|
|||||||
&m_blobID,
|
&m_blobID,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (aErr)
|
if (aErr)
|
||||||
evaluateStatusVector(m_statusVector, "isc_open_blob2", *this);
|
evaluateStatusVector(m_statusVector, "isc_open_blob2", *this);
|
||||||
|
|
||||||
}
|
m_bBlobOpened = true;
|
||||||
|
m_nBlobPosition = 0;
|
||||||
void SAL_CALL Blob::disposing(void)
|
|
||||||
{
|
|
||||||
MutexGuard aGuard(m_aMutex);
|
|
||||||
|
|
||||||
if (m_bBlobOpened)
|
|
||||||
{
|
|
||||||
ISC_STATUS aErr;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Blob_BASE::disposing();
|
|
||||||
}
|
|
||||||
|
|
||||||
sal_Int64 SAL_CALL Blob::length()
|
|
||||||
throw(SQLException, RuntimeException)
|
|
||||||
{
|
|
||||||
MutexGuard aGuard(m_aMutex);
|
|
||||||
checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
|
||||||
ensureBlobIsOpened();
|
|
||||||
|
|
||||||
char aBlobItems[] = {
|
char aBlobItems[] = {
|
||||||
isc_info_blob_total_length
|
isc_info_blob_total_length
|
||||||
@ -103,106 +74,186 @@ sal_Int64 SAL_CALL Blob::length()
|
|||||||
sizeof(aResultBuffer),
|
sizeof(aResultBuffer),
|
||||||
aResultBuffer);
|
aResultBuffer);
|
||||||
|
|
||||||
|
if (aErr)
|
||||||
evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
|
evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
|
||||||
|
|
||||||
if (*aResultBuffer == isc_info_blob_total_length)
|
if (*aResultBuffer == isc_info_blob_total_length)
|
||||||
{
|
{
|
||||||
short aResultLength = (short) isc_vax_integer(aResultBuffer, 2);
|
short aResultLength = (short) isc_vax_integer(aResultBuffer+1, 2);
|
||||||
return isc_vax_integer(aResultBuffer+2, aResultLength);
|
m_nBlobLength = isc_vax_integer(aResultBuffer+3, aResultLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 aPosition, sal_Int32 aLength)
|
void Blob::closeBlob()
|
||||||
|
throw (SQLException)
|
||||||
|
{
|
||||||
|
MutexGuard aGuard(m_aMutex);
|
||||||
|
|
||||||
|
if (m_bBlobOpened)
|
||||||
|
{
|
||||||
|
ISC_STATUS aErr;
|
||||||
|
aErr = isc_close_blob(m_statusVector,
|
||||||
|
&m_blobHandle);
|
||||||
|
if (aErr)
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
sal_Int64 SAL_CALL Blob::length()
|
||||||
throw(SQLException, RuntimeException)
|
throw(SQLException, RuntimeException)
|
||||||
{
|
{
|
||||||
MutexGuard aGuard(m_aMutex);
|
MutexGuard aGuard(m_aMutex);
|
||||||
checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
||||||
ensureBlobIsOpened();
|
ensureBlobIsOpened();
|
||||||
|
|
||||||
sal_Int64 aTotalLength = length();
|
return m_nBlobLength;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(aPosition + aLength < aTotalLength))
|
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();
|
||||||
|
|
||||||
|
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 (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())
|
skipBytes(nPosition - m_nBlobPosition);
|
||||||
{
|
|
||||||
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
|
// Don't bother preallocating: readBytes does the appropriate calculations
|
||||||
|
// and reallocates for us.
|
||||||
ISC_STATUS aErr;
|
uno::Sequence< sal_Int8 > aBytes;
|
||||||
do
|
readBytes(aBytes, nBytes);
|
||||||
{
|
return aBytes;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream()
|
uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream()
|
||||||
throw(SQLException, RuntimeException)
|
throw(SQLException, RuntimeException)
|
||||||
{
|
{
|
||||||
// MutexGuard aGuard(m_aMutex);
|
return this;
|
||||||
// checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
|
||||||
// ensureBlobIsOpened();
|
|
||||||
|
|
||||||
::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& rPattern,
|
sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/,
|
||||||
sal_Int64 aStart)
|
sal_Int64 /*nStart*/)
|
||||||
throw(SQLException, RuntimeException)
|
throw(SQLException, RuntimeException)
|
||||||
{
|
{
|
||||||
// MutexGuard aGuard(m_aMutex);
|
::dbtools::throwFeatureNotImplementedException("Blob::position", *this);
|
||||||
// checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
return 0;
|
||||||
// ensureBlobIsOpened();
|
}
|
||||||
|
|
||||||
(void) rPattern;
|
sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
|
||||||
(void) aStart;
|
sal_Int64 /*aStart*/)
|
||||||
|
throw(SQLException, RuntimeException)
|
||||||
|
{
|
||||||
::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
|
::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& rPattern,
|
// ---- XInputStream ----------------------------------------------------------
|
||||||
sal_Int64 aStart)
|
|
||||||
throw(SQLException, RuntimeException)
|
|
||||||
{
|
|
||||||
// MutexGuard aGuard(m_aMutex);
|
|
||||||
// checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
|
||||||
// ensureBlobIsOpened();
|
|
||||||
|
|
||||||
(void) rPattern;
|
sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut,
|
||||||
(void) aStart;
|
sal_Int32 nBytes)
|
||||||
::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
|
throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
|
||||||
return 0;
|
{
|
||||||
|
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: */
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
@ -12,15 +12,17 @@
|
|||||||
|
|
||||||
#include <ibase.h>
|
#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>
|
#include <com/sun/star/sdbc/XBlob.hpp>
|
||||||
|
|
||||||
namespace connectivity
|
namespace connectivity
|
||||||
{
|
{
|
||||||
namespace firebird
|
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;
|
Blob_BASE;
|
||||||
|
|
||||||
class Blob :
|
class Blob :
|
||||||
@ -38,13 +40,19 @@ namespace connectivity
|
|||||||
isc_blob_handle m_blobHandle;
|
isc_blob_handle m_blobHandle;
|
||||||
|
|
||||||
bool m_bBlobOpened;
|
bool m_bBlobOpened;
|
||||||
|
sal_Int64 m_nBlobLength;
|
||||||
|
sal_Int64 m_nBlobPosition;
|
||||||
|
|
||||||
ISC_STATUS_ARRAY m_statusVector;
|
ISC_STATUS_ARRAY m_statusVector;
|
||||||
|
|
||||||
::com::sun::star::uno::Sequence< sal_Int8 > m_blobData;
|
|
||||||
|
|
||||||
void ensureBlobIsOpened()
|
void ensureBlobIsOpened()
|
||||||
throw(::com::sun::star::sdbc::SQLException);
|
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:
|
public:
|
||||||
Blob(isc_db_handle* pDatabaseHandle,
|
Blob(isc_db_handle* pDatabaseHandle,
|
||||||
@ -75,6 +83,38 @@ namespace connectivity
|
|||||||
throw(::com::sun::star::sdbc::SQLException,
|
throw(::com::sun::star::sdbc::SQLException,
|
||||||
::com::sun::star::uno::RuntimeException);
|
::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 ------------------------------------------
|
// ---- OComponentHelper ------------------------------------------
|
||||||
virtual void SAL_CALL disposing();
|
virtual void SAL_CALL disposing();
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user