LibreOffice Module connectivity (master) 1
firebird/Blob.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include "Blob.hxx"
11#include "Util.hxx"
12
13#include <com/sun/star/io/BufferSizeExceededException.hpp>
14#include <com/sun/star/io/NotConnectedException.hpp>
15#include <com/sun/star/io/IOException.hpp>
16#include <com/sun/star/lang/IllegalArgumentException.hpp>
17#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
18#include <com/sun/star/sdbc/SQLException.hpp>
23
24using namespace ::connectivity::firebird;
25
26using namespace ::cppu;
27using namespace ::osl;
28
29using namespace ::com::sun::star;
30using namespace ::com::sun::star::io;
31using namespace ::com::sun::star::sdbc;
32using namespace ::com::sun::star::uno;
33
34Blob::Blob(isc_db_handle* pDatabaseHandle,
35 isc_tr_handle* pTransactionHandle,
36 ISC_QUAD const & aBlobID):
38 m_pDatabaseHandle(pDatabaseHandle),
39 m_pTransactionHandle(pTransactionHandle),
40 m_blobID(aBlobID),
41#if SAL_TYPES_SIZEOFPOINTER == 8
42 m_blobHandle(0),
43#else
44 m_blobHandle(nullptr),
45#endif
46 m_bBlobOpened(false),
47 m_nBlobLength(0),
48 m_nMaxSegmentSize(0),
49 m_nBlobPosition(0)
50{
51}
52
54{
55 MutexGuard aGuard(m_aMutex);
56
57 if (m_bBlobOpened)
58 return;
59
60 ISC_STATUS aErr;
61 aErr = isc_open_blob2(m_statusVector,
65 &m_blobID,
66 0,
67 nullptr);
68
69 if (aErr)
70 evaluateStatusVector(m_statusVector, u"isc_open_blob2", *this);
71
72 m_bBlobOpened = true;
74
75 char aBlobItems[] = {
76 isc_info_blob_total_length,
77 isc_info_blob_max_segment
78 };
79
80 // Assuming a data (e.g. length of blob) is maximum 64 bit.
81 // That means we need 8 bytes for data + 2 for length of data + 1 for item
82 // identifier for each item.
83 char aResultBuffer[11 + 11];
84
85 aErr = isc_blob_info(m_statusVector,
87 sizeof(aBlobItems),
88 aBlobItems,
89 sizeof(aResultBuffer),
90 aResultBuffer);
91
92 if (aErr)
93 evaluateStatusVector(m_statusVector, u"isc_blob_info", *this);
94
95 char* pIt = aResultBuffer;
96 while( *pIt != isc_info_end ) // info is in clusters
97 {
98 char item = *pIt++;
99 short aResultLength = static_cast<short>(isc_vax_integer(pIt, 2));
100
101 pIt += 2;
102 switch(item)
103 {
104 case isc_info_blob_total_length:
105 m_nBlobLength = isc_vax_integer(pIt, aResultLength);
106 break;
107 case isc_info_blob_max_segment:
108 m_nMaxSegmentSize = isc_vax_integer(pIt, aResultLength);
109 break;
110 default:
111 assert(false);
112 break;
113 }
114 pIt += aResultLength;
115 }
116}
117
119{
121
122 return m_nMaxSegmentSize;
123}
124
125bool Blob::readOneSegment(std::vector<char>& rDataOut)
126{
127 checkDisposed(Blob_BASE::rBHelper.bDisposed);
129
130 sal_uInt16 nMaxSize = getMaximumSegmentSize();
131
132 if(rDataOut.size() < nMaxSize)
133 rDataOut.resize(nMaxSize);
134
135 sal_uInt16 nActualSize = 0;
136 ISC_STATUS aRet = isc_get_segment(m_statusVector,
138 &nActualSize,
139 nMaxSize,
140 rDataOut.data() );
141
142 if (aRet && aRet != isc_segstr_eof && IndicatesError(m_statusVector))
143 {
144 OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
145 throw IOException(sError, *this);
146 }
147
148 if (rDataOut.size() > nActualSize)
149 rDataOut.resize(nActualSize);
150 m_nBlobPosition += nActualSize;
151 return aRet == isc_segstr_eof; // last segment read
152}
153
154
156{
157 MutexGuard aGuard(m_aMutex);
158
159 if (!m_bBlobOpened)
160 return;
161
162 ISC_STATUS aErr;
163 aErr = isc_close_blob(m_statusVector,
164 &m_blobHandle);
165 if (aErr)
166 evaluateStatusVector(m_statusVector, u"isc_close_blob", *this);
167
168 m_bBlobOpened = false;
169#if SAL_TYPES_SIZEOFPOINTER == 8
170 m_blobHandle = 0;
171#else
172 m_blobHandle = nullptr;
173#endif
174}
175
176void SAL_CALL Blob::disposing()
177{
178 try
179 {
180 closeBlob();
181 }
182 catch (const SQLException &)
183 {
184 // we cannot throw any exceptions here...
185 TOOLS_WARN_EXCEPTION("connectivity.firebird", "isc_close_blob failed");
186 assert(false);
187 }
188 Blob_BASE::disposing();
189}
190
191sal_Int64 SAL_CALL Blob::length()
192{
193 MutexGuard aGuard(m_aMutex);
194 checkDisposed(Blob_BASE::rBHelper.bDisposed);
196
197 return m_nBlobLength;
198}
199
200uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 nPosition,
201 sal_Int32 nBytes)
202{
203 MutexGuard aGuard(m_aMutex);
204 checkDisposed(Blob_BASE::rBHelper.bDisposed);
206
207 if (nPosition > m_nBlobLength || nPosition < 1)
208 throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
209 // We only have to read as many bytes as are available, i.e. nPosition+nBytes
210 // can legally be greater than the total length, hence we don't bother to check.
211
212 if (nPosition -1 < m_nBlobPosition)
213 {
214 // Resets to the beginning (we can't seek these blobs)
215 closeBlob();
217 }
218
219 // nPosition is indexed from 1.
220 skipBytes(nPosition - m_nBlobPosition -1 );
221
222 // Don't bother preallocating: readBytes does the appropriate calculations
223 // and reallocates for us.
224 uno::Sequence< sal_Int8 > aBytes;
225 readBytes(aBytes, nBytes);
226 return aBytes;
227}
228
229uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream()
230{
231 return this;
232}
233
234sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/,
235 sal_Int64 /*nStart*/)
236{
238 return 0;
239}
240
241sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
242 sal_Int64 /*aStart*/)
243{
244 ::dbtools::throwFeatureNotImplementedSQLException("Blob::positionOfBlob", *this);
245 return 0;
246}
247
248// ---- XInputStream ----------------------------------------------------------
249
250sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut,
251 sal_Int32 nBytes)
252{
253 MutexGuard aGuard(m_aMutex);
254
255 try
256 {
257 checkDisposed(Blob_BASE::rBHelper.bDisposed);
259 }
260 catch (const NotConnectedException&)
261 {
262 throw;
263 }
264 catch (const BufferSizeExceededException&)
265 {
266 throw;
267 }
268 catch (const IOException&)
269 {
270 throw;
271 }
272 catch (const RuntimeException&)
273 {
274 throw;
275 }
276 catch (const Exception& e)
277 {
278 css::uno::Any a(cppu::getCaughtException());
279 throw css::lang::WrappedTargetRuntimeException(
280 "wrapped Exception " + e.Message,
281 css::uno::Reference<css::uno::XInterface>(), a);
282 }
283
284 // Ensure we have enough space for the amount of data we can actually read.
285 const sal_Int64 nBytesAvailable = m_nBlobLength - m_nBlobPosition;
286 const sal_Int32 nBytesToRead = std::min<sal_Int64>(nBytes, nBytesAvailable);
287
288 if (rDataOut.getLength() < nBytesToRead)
289 rDataOut.realloc(nBytesToRead);
290
291 sal_Int32 nTotalBytesRead = 0;
292 ISC_STATUS aErr;
293 while (nTotalBytesRead < nBytesToRead)
294 {
295 sal_uInt16 nBytesRead = 0;
296 sal_uInt64 nDataRemaining = nBytesToRead - nTotalBytesRead;
297 sal_uInt16 nReadSize = std::min<sal_uInt64>(nDataRemaining, SAL_MAX_UINT16);
298 aErr = isc_get_segment(m_statusVector,
300 &nBytesRead,
301 nReadSize,
302 reinterpret_cast<char*>(rDataOut.getArray()) + nTotalBytesRead);
303 if (aErr && IndicatesError(m_statusVector))
304 {
305 OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
306 throw IOException(sError, *this);
307 }
308 nTotalBytesRead += nBytesRead;
309 m_nBlobPosition += nBytesRead;
310 }
311
312 return nTotalBytesRead;
313}
314
315sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut,
316 sal_Int32 nMaximumBytes)
317{
318 // We don't have any way of verifying how many bytes are immediately available,
319 // hence we just pass through direct to readBytes
320 // (Spec: "reads the available number of bytes, at maximum nMaxBytesToRead.")
321 return readBytes(rDataOut, nMaximumBytes);
322}
323
324void SAL_CALL Blob::skipBytes(sal_Int32 nBytesToSkip)
325{
326 // There is no way of directly skipping, hence we have to pretend to skip
327 // by reading & discarding the data.
328 uno::Sequence< sal_Int8 > aBytes;
329 readBytes(aBytes, nBytesToSkip);
330}
331
332sal_Int32 SAL_CALL Blob::available()
333{
334 MutexGuard aGuard(m_aMutex);
335
336 try
337 {
338 checkDisposed(Blob_BASE::rBHelper.bDisposed);
340 }
341 catch (const NotConnectedException&)
342 {
343 throw;
344 }
345 catch (const IOException&)
346 {
347 throw;
348 }
349 catch (const RuntimeException&)
350 {
351 throw;
352 }
353 catch (const Exception& e)
354 {
355 css::uno::Any a(cppu::getCaughtException());
356 throw css::lang::WrappedTargetRuntimeException(
357 "wrapped Exception " + e.Message,
358 css::uno::Reference<css::uno::XInterface>(), a);
359 }
360
362}
363
364void SAL_CALL Blob::closeInput()
365{
366 try
367 {
368 closeBlob();
369 }
370 catch (const NotConnectedException&)
371 {
372 throw;
373 }
374 catch (const IOException&)
375 {
376 throw;
377 }
378 catch (const RuntimeException&)
379 {
380 throw;
381 }
382 catch (const Exception& e)
383 {
384 css::uno::Any a(cppu::getCaughtException());
385 throw css::lang::WrappedTargetRuntimeException(
386 "wrapped Exception " + e.Message,
387 css::uno::Reference<css::uno::XInterface>(), a);
388 }
389}
390
391/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
virtual void SAL_CALL closeInput() override
void closeBlob()
Closes the blob and cleans up resources – can be used to reset the blob if we e.g.
virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream() override
bool readOneSegment(std::vector< char > &rDataOut)
virtual sal_Int32 SAL_CALL readSomeBytes(css::uno::Sequence< sal_Int8 > &rDataOut, sal_Int32 nMaximumBytes) override
virtual sal_Int32 SAL_CALL readBytes(css::uno::Sequence< sal_Int8 > &rDataOut, sal_Int32 nBytes) override
virtual sal_Int64 SAL_CALL position(const css::uno::Sequence< sal_Int8 > &rPattern, sal_Int64 aStart) override
virtual sal_Int64 SAL_CALL positionOfBlob(const css::uno::Reference< css::sdbc::XBlob > &rPattern, sal_Int64 aStart) override
virtual sal_Int32 SAL_CALL available() override
virtual void SAL_CALL disposing() override
virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes(sal_Int64 aPosition, sal_Int32 aLength) override
virtual void SAL_CALL skipBytes(sal_Int32 nBytes) override
virtual sal_Int64 SAL_CALL length() override
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
std::mutex m_aMutex
uno_Any a
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
@ Exception
bool IndicatesError(const ISC_STATUS_ARRAY &rStatusVector)
Definition: Util.hxx:92
::cppu::WeakComponentImplHelper< css::sdbc::XBlob, css::io::XInputStream > Blob_BASE
void checkDisposed(bool _bThrow)
Definition: Driver.cxx:208
OUString StatusVectorToString(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view rCause)
void evaluateStatusVector(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view aCause, const css::uno::Reference< css::uno::XInterface > &_rxContext)
Evaluate a firebird status vector and throw exceptions as necessary.
Any SAL_CALL getCaughtException()
void throwFeatureNotImplementedSQLException(const OUString &_rFeatureName, const Reference< XInterface > &_rxContext, const Any &_rNextException)
#define SAL_MAX_UINT16