summaryrefslogtreecommitdiffstats
path: root/connectivity/source/drivers/firebird/Blob.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'connectivity/source/drivers/firebird/Blob.cxx')
-rw-r--r--connectivity/source/drivers/firebird/Blob.cxx391
1 files changed, 391 insertions, 0 deletions
diff --git a/connectivity/source/drivers/firebird/Blob.cxx b/connectivity/source/drivers/firebird/Blob.cxx
new file mode 100644
index 000000000..33ab36b8d
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Blob.cxx
@@ -0,0 +1,391 @@
+/* -*- 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 <tools/diagnose_ex.h>
+
+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("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 -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("Blob::position", *this);
+ return 0;
+}
+
+sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
+ sal_Int64 /*aStart*/)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException("Blob::positionOfBlob", *this);
+ return 0;
+}
+
+// ---- 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: */