/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "Blob.hxx"
#include "Util.hxx"
#include <com/sun/star/io/BufferSizeExceededException.hpp>
#include <com/sun/star/io/NotConnectedException.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <connectivity/CommonTools.hxx>
#include <connectivity/dbexception.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <comphelper/diagnose_ex.hxx>
using namespace ::connectivity::firebird;
using namespace ::cppu;
using namespace ::osl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
Blob::Blob(isc_db_handle* pDatabaseHandle,
isc_tr_handle* pTransactionHandle,
ISC_QUAD const & aBlobID):
Blob_BASE(m_aMutex),
m_pDatabaseHandle(pDatabaseHandle),
m_pTransactionHandle(pTransactionHandle),
m_blobID(aBlobID),
#if SAL_TYPES_SIZEOFPOINTER == 8
m_blobHandle(0),
#else
m_blobHandle(nullptr),
#endif
m_bBlobOpened(false),
m_nBlobLength(0),
m_nMaxSegmentSize(0),
m_nBlobPosition(0)
{
}
void Blob::ensureBlobIsOpened()
{
MutexGuard aGuard(m_aMutex);
if (m_bBlobOpened)
return;
ISC_STATUS aErr;
aErr = isc_open_blob2(m_statusVector,
m_pDatabaseHandle,
m_pTransactionHandle,
&m_blobHandle,
&m_blobID,
0,
nullptr);
if (aErr)
evaluateStatusVector(m_statusVector, u"isc_open_blob2", *this);
m_bBlobOpened = true;
m_nBlobPosition = 0;
char aBlobItems[] = {
isc_info_blob_total_length,
isc_info_blob_max_segment
};
// Assuming a data (e.g. length of blob) is maximum 64 bit.
// That means we need 8 bytes for data + 2 for length of data + 1 for item
// identifier for each item.
char aResultBuffer[11 + 11];
aErr = isc_blob_info(m_statusVector,
&m_blobHandle,
sizeof(aBlobItems),
aBlobItems,
sizeof(aResultBuffer),
aResultBuffer);
if (aErr)
evaluateStatusVector(m_statusVector, u"isc_blob_info", *this);
char* pIt = aResultBuffer;
while( *pIt != isc_info_end ) // info is in clusters
{
char item = *pIt++;
short aResultLength = static_cast<short>(isc_vax_integer(pIt, 2));
pIt += 2;
switch(item)
{
case isc_info_blob_total_length:
m_nBlobLength = isc_vax_integer(pIt, aResultLength);
break;
case isc_info_blob_max_segment:
m_nMaxSegmentSize = isc_vax_integer(pIt, aResultLength);
break;
default:
assert(false);
break;
}
pIt += aResultLength;
}
}
sal_uInt16 Blob::getMaximumSegmentSize()
{
ensureBlobIsOpened();
return m_nMaxSegmentSize;
}
bool Blob::readOneSegment(std::vector<char>& rDataOut)
{
checkDisposed(Blob_BASE::rBHelper.bDisposed);
ensureBlobIsOpened();
sal_uInt16 nMaxSize = getMaximumSegmentSize();
if(rDataOut.size() < nMaxSize)
rDataOut.resize(nMaxSize);
sal_uInt16 nActualSize = 0;
ISC_STATUS aRet = isc_get_segment(m_statusVector,
&m_blobHandle,
&nActualSize,
nMaxSize,
rDataOut.data() );
if (aRet && aRet != isc_segstr_eof && IndicatesError(m_statusVector))
{
OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
throw IOException(sError, *this);
}
if (rDataOut.size() > nActualSize)
rDataOut.resize(nActualSize);
m_nBlobPosition += nActualSize;
return aRet == isc_segstr_eof; // last segment read
}
void Blob::closeBlob()
{
MutexGuard aGuard(m_aMutex);
if (!m_bBlobOpened)
return;
ISC_STATUS aErr;
aErr = isc_close_blob(m_statusVector,
&m_blobHandle);
if (aErr)
evaluateStatusVector(m_statusVector, u"isc_close_blob", *this);
m_bBlobOpened = false;
#if SAL_TYPES_SIZEOFPOINTER == 8
m_blobHandle = 0;
#else
m_blobHandle = nullptr;
#endif
}
void SAL_CALL Blob::disposing()
{
try
{
closeBlob();
}
catch (const SQLException &)
{
// we cannot throw any exceptions here...
TOOLS_WARN_EXCEPTION("connectivity.firebird", "isc_close_blob failed");
assert(false);
}
Blob_BASE::disposing();
}
sal_Int64 SAL_CALL Blob::length()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(Blob_BASE::rBHelper.bDisposed);
ensureBlobIsOpened();
return m_nBlobLength;
}
uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 nPosition,
sal_Int32 nBytes)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(Blob_BASE::rBHelper.bDisposed);
ensureBlobIsOpened();
if (nPosition > m_nBlobLength || nPosition < 1)
throw lang::IllegalArgumentException(u"nPosition out of range"_ustr, *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 -1 < m_nBlobPosition)
{
// Resets to the beginning (we can't seek these blobs)
closeBlob();
ensureBlobIsOpened();
}
// nPosition is indexed from 1.
skipBytes(nPosition - m_nBlobPosition -1 );
// 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()
{
return this;
}
sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/,
sal_Int64 /*nStart*/)
{
::dbtools::throwFeatureNotImplementedSQLException(u"Blob::position"_ustr, *this);
}
sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
sal_Int64 /*aStart*/)
{
::dbtools::throwFeatureNotImplementedSQLException(u"Blob::positionOfBlob"_ustr, *this);
}
// ---- XInputStream ----------------------------------------------------------
sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut,
sal_Int32 nBytes)
{
MutexGuard aGuard(m_aMutex);
try
{
checkDisposed(Blob_BASE::rBHelper.bDisposed);
ensureBlobIsOpened();
}
catch (const NotConnectedException&)
{
throw;
}
catch (const BufferSizeExceededException&)
{
throw;
}
catch (const IOException&)
{
throw;
}
catch (const RuntimeException&)
{
throw;
}
catch (const Exception& e)
{
css::uno::Any a(cppu::getCaughtException());
throw css::lang::WrappedTargetRuntimeException(
"wrapped Exception " + e.Message,
css::uno::Reference<css::uno::XInterface>(), a);
}
// 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 = std::min<sal_Int64>(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 = std::min<sal_uInt64>(nDataRemaining, SAL_MAX_UINT16);
aErr = isc_get_segment(m_statusVector,
&m_blobHandle,
&nBytesRead,
nReadSize,
reinterpret_cast<char*>(rDataOut.getArray()) + nTotalBytesRead);
if (aErr && IndicatesError(m_statusVector))
{
OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
throw IOException(sError, *this);
}
nTotalBytesRead += nBytesRead;
m_nBlobPosition += nBytesRead;
}
return nTotalBytesRead;
}
sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut,
sal_Int32 nMaximumBytes)
{
// 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)
{
// 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()
{
MutexGuard aGuard(m_aMutex);
try
{
checkDisposed(Blob_BASE::rBHelper.bDisposed);
ensureBlobIsOpened();
}
catch (const NotConnectedException&)
{
throw;
}
catch (const IOException&)
{
throw;
}
catch (const RuntimeException&)
{
throw;
}
catch (const Exception& e)
{
css::uno::Any a(cppu::getCaughtException());
throw css::lang::WrappedTargetRuntimeException(
"wrapped Exception " + e.Message,
css::uno::Reference<css::uno::XInterface>(), a);
}
return m_nBlobLength - m_nBlobPosition;
}
void SAL_CALL Blob::closeInput()
{
try
{
closeBlob();
}
catch (const NotConnectedException&)
{
throw;
}
catch (const IOException&)
{
throw;
}
catch (const RuntimeException&)
{
throw;
}
catch (const Exception& e)
{
css::uno::Any a(cppu::getCaughtException());
throw css::lang::WrappedTargetRuntimeException(
"wrapped Exception " + e.Message,
css::uno::Reference<css::uno::XInterface>(), a);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_statusVector.