389 lines
11 KiB
C++
389 lines
11 KiB
C++
/* -*- 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: */
|