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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include "Connection.hxx"
25 #include "PreparedStatement.hxx"
26 #include "ResultSet.hxx"
27 #include "ResultSetMetaData.hxx"
28 #include "Util.hxx"
29 
30 #include <comphelper/sequence.hxx>
31 #include <connectivity/dbexception.hxx>
32 #include <cppuhelper/typeprovider.hxx>
33 #include <propertyids.hxx>
34 #include <time.h>
35 #include <connectivity/dbtools.hxx>
36 #include <sal/log.hxx>
37 
38 #include <com/sun/star/sdbc/DataType.hpp>
39 #include <com/sun/star/lang/DisposedException.hpp>
40 
41 using namespace connectivity::firebird;
42 
43 using namespace ::comphelper;
44 using namespace ::osl;
45 
46 using namespace com::sun::star;
47 using namespace com::sun::star::uno;
48 using namespace com::sun::star::lang;
49 using namespace com::sun::star::beans;
50 using namespace com::sun::star::sdbc;
51 using namespace com::sun::star::container;
52 using namespace com::sun::star::io;
53 using namespace com::sun::star::util;
54 
55 IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
56 
57 
58 OPreparedStatement::OPreparedStatement( Connection* _pConnection,
59                                         const OUString& sql)
60     :OStatementCommonBase(_pConnection)
61     ,m_sSqlStatement(sql)
62     ,m_pOutSqlda(nullptr)
63     ,m_pInSqlda(nullptr)
64 {
65     SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
66              "sql: " << sql);
67 }
68 
69 void OPreparedStatement::ensurePrepared()
70 {
71     MutexGuard aGuard(m_aMutex);
72     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
73 
74     if (m_aStatementHandle)
75         return;
76 
77     ISC_STATUS aErr = 0;
78 
79     if (!m_pInSqlda)
80     {
81         m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10)));
82         m_pInSqlda->version = SQLDA_VERSION1;
83         m_pInSqlda->sqln = 10;
84     }
85 
86     prepareAndDescribeStatement(m_sSqlStatement,
87                                m_pOutSqlda,
88                                m_pInSqlda);
89 
90     aErr = isc_dsql_describe_bind(m_statusVector,
91                                   &m_aStatementHandle,
92                                   1,
93                                   m_pInSqlda);
94 
95     if (aErr)
96     {
97         SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
98     }
99     else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
100     {
101         short nItems = m_pInSqlda->sqld;
102         free(m_pInSqlda);
103         m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
104         m_pInSqlda->version = SQLDA_VERSION1;
105         m_pInSqlda->sqln = nItems;
106         aErr = isc_dsql_describe_bind(m_statusVector,
107                                       &m_aStatementHandle,
108                                       1,
109                                       m_pInSqlda);
110         SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
111     }
112 
113     if (!aErr)
114         mallocSQLVAR(m_pInSqlda);
115     else
116         evaluateStatusVector(m_statusVector, m_sSqlStatement, *this);
117 }
118 
119 OPreparedStatement::~OPreparedStatement()
120 {
121 }
122 
123 void SAL_CALL OPreparedStatement::acquire() throw()
124 {
125     OStatementCommonBase::acquire();
126 }
127 
128 void SAL_CALL OPreparedStatement::release() throw()
129 {
130     OStatementCommonBase::release();
131 }
132 
133 Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
134 {
135     Any aRet = OStatementCommonBase::queryInterface(rType);
136     if(!aRet.hasValue())
137         aRet = OPreparedStatement_Base::queryInterface(rType);
138     return aRet;
139 }
140 
141 uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
142 {
143     return concatSequences(OPreparedStatement_Base::getTypes(),
144                            OStatementCommonBase::getTypes());
145 }
146 
147 Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData()
148 {
149     ::osl::MutexGuard aGuard( m_aMutex );
150     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
151     ensurePrepared();
152 
153     if(!m_xMetaData.is())
154         m_xMetaData = new OResultSetMetaData(m_pConnection.get()
155                                            , m_pOutSqlda);
156 
157     return m_xMetaData;
158 }
159 
160 void SAL_CALL OPreparedStatement::close()
161 {
162     MutexGuard aGuard( m_aMutex );
163     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
164 
165     OStatementCommonBase::close();
166     if (m_pInSqlda)
167     {
168         freeSQLVAR(m_pInSqlda);
169         free(m_pInSqlda);
170         m_pInSqlda = nullptr;
171     }
172     if (m_pOutSqlda)
173     {
174         freeSQLVAR(m_pOutSqlda);
175         free(m_pOutSqlda);
176         m_pOutSqlda = nullptr;
177     }
178 }
179 
180 void SAL_CALL OPreparedStatement::disposing()
181 {
182     close();
183 }
184 
185 void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
186                                             const OUString& sInput)
187 {
188     SAL_INFO("connectivity.firebird",
189              "setString(" << nParameterIndex << " , " << sInput << ")");
190 
191     MutexGuard aGuard( m_aMutex );
192     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
193     ensurePrepared();
194 
195     checkParameterIndex(nParameterIndex);
196     setParameterNull(nParameterIndex, false);
197 
198     OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
199 
200     XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
201 
202     int dtype = (pVar->sqltype & ~1); // drop flag bit for now
203 
204     if (str.getLength() > pVar->sqllen)
205         str = str.copy(0, pVar->sqllen);
206 
207     switch (dtype) {
208     case SQL_VARYING:
209     {
210         const sal_Int32 max_varchar_len = 0xFFFF;
211         // First 2 bytes indicate string size
212         if (str.getLength() > max_varchar_len)
213         {
214             str = str.copy(0, max_varchar_len);
215         }
216         const auto nLength = str.getLength();
217         memcpy(pVar->sqldata, &nLength, 2);
218         // Actual data
219         memcpy(pVar->sqldata + 2, str.getStr(), str.getLength());
220         break;
221     }
222     case SQL_TEXT:
223         memcpy(pVar->sqldata, str.getStr(), str.getLength());
224         // Fill remainder with spaces
225         memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
226         break;
227     case SQL_BLOB: // Clob
228         assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) );
229         setClob(nParameterIndex, sInput );
230         break;
231     default:
232         ::dbtools::throwSQLException(
233             "Incorrect type for setString",
234             ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
235             *this);
236     }
237 }
238 
239 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
240 {
241     MutexGuard aGuard( m_aMutex );
242     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
243 
244     return Reference<XConnection>(m_pConnection.get());
245 }
246 
247 sal_Bool SAL_CALL OPreparedStatement::execute()
248 {
249     SAL_INFO("connectivity.firebird", "executeQuery(). "
250         "Got called with sql: " <<  m_sSqlStatement);
251 
252     MutexGuard aGuard( m_aMutex );
253     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
254 
255     ensurePrepared();
256 
257     ISC_STATUS aErr;
258 
259     if (m_xResultSet.is()) // Checks whether we have already run the statement.
260     {
261         disposeResultSet();
262         // Closes the cursor from the last run.
263         // This doesn't actually free the statement -- using DSQL_close closes
264         // the cursor and keeps the statement, using DSQL_drop frees the statement
265         // (and associated cursors).
266         aErr = isc_dsql_free_statement(m_statusVector,
267                                        &m_aStatementHandle,
268                                        DSQL_close);
269         if (aErr)
270         {
271             // Do not throw error. Trying to close a closed cursor is not a
272             // critical mistake.
273             OUString sErrMsg  = StatusVectorToString(m_statusVector,
274                     "isc_dsql_free_statement: close cursor");
275             SAL_WARN("connectivity.firebird", sErrMsg);
276         }
277     }
278 
279     aErr = isc_dsql_execute(m_statusVector,
280                                 &m_pConnection->getTransaction(),
281                                 &m_aStatementHandle,
282                                 1,
283                                 m_pInSqlda);
284     if (aErr)
285     {
286         SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
287         evaluateStatusVector(m_statusVector, "isc_dsql_execute", *this);
288     }
289 
290     m_xResultSet = new OResultSet(m_pConnection.get(),
291                                   m_aMutex,
292                                   uno::Reference< XInterface >(*this),
293                                   m_aStatementHandle,
294                                   m_pOutSqlda);
295 
296     if (getStatementChangeCount() > 0)
297         m_pConnection->notifyDatabaseModified();
298 
299     return m_xResultSet.is();
300     // TODO: implement handling of multiple ResultSets.
301 }
302 
303 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
304 {
305     execute();
306     return getStatementChangeCount();
307 }
308 
309 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
310 {
311     execute();
312     return m_xResultSet;
313 }
314 
315 namespace {
316 
317 /**
318  * Take out the number part of a fix point decimal without
319  * the information of where is the fractional part from a
320  * string representation of a number. (e.g. 54.654 -> 54654)
321  */
322 sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource)
323 {
324     OUString sNumber(sSource);
325 
326     // cut off leading 0 eventually ( eg. 0.567 -> .567)
327     (void)sSource.startsWith("0", &sNumber);
328 
329     sal_Int32 nDotIndex = sNumber.indexOf('.');
330 
331     if( nDotIndex < 0)
332     {
333         return sNumber.toInt64(); // no dot -> it's an integer
334     }
335     else
336     {
337         // remove dot
338         OUStringBuffer sBuffer(15);
339         if(nDotIndex > 0)
340         {
341             sBuffer.append(std::u16string_view(sNumber).substr(0, nDotIndex));
342         }
343         sBuffer.append(std::u16string_view(sNumber).substr(nDotIndex + 1));
344         return sBuffer.makeStringAndClear().toInt64();
345     }
346 }
347 
348 }
349 
350 //----- XParameters -----------------------------------------------------------
351 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
352 {
353     MutexGuard aGuard( m_aMutex );
354     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
355     ensurePrepared();
356 
357     checkParameterIndex(nIndex);
358     setParameterNull(nIndex);
359 }
360 
361 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
362 {
363     setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
364 }
365 
366 template <typename T>
367 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
368 {
369     MutexGuard aGuard( m_aMutex );
370     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
371     ensurePrepared();
372 
373     checkParameterIndex(nIndex);
374     setParameterNull(nIndex, false);
375 
376     XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
377 
378     if ((pVar->sqltype & ~1) != nType)
379     {
380        ::dbtools::throwSQLException(
381             "Incorrect type for setValue",
382             ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
383             *this);
384     }
385 
386     memcpy(pVar->sqldata, &nValue, sizeof(nValue));
387 }
388 
389 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
390 {
391     // there's no TINYINT or equivalent on Firebird,
392     // so do the same as setShort
393     setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
394 }
395 
396 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
397 {
398     setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
399 }
400 
401 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
402 {
403     setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
404 }
405 
406 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
407 {
408     setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
409 }
410 
411 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
412 {
413     setValue< float >(nIndex, nValue, SQL_FLOAT);
414 }
415 
416 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
417 {
418     MutexGuard aGuard( m_aMutex );
419     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
420     ensurePrepared();
421 
422     XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
423     int dType = (pVar->sqltype & ~1); // drop flag bit for now
424 
425     // Caller might try to set an integer type here. It makes sense to convert
426     // it instead of throwing an error.
427     switch(dType)
428     {
429         case SQL_SHORT:
430             setValue< sal_Int16 >(nIndex,
431                     static_cast<sal_Int16>(nValue),
432                     dType);
433             break;
434         case SQL_LONG:
435             setValue< sal_Int32 >(nIndex,
436                     static_cast<sal_Int32>(nValue),
437                     dType);
438             break;
439         case SQL_INT64:
440             setValue< sal_Int64 >(nIndex,
441                     static_cast<sal_Int64>(nValue),
442                     dType);
443             break;
444         default:
445             setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT?
446     }
447 }
448 
449 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
450 {
451     struct tm aCTime;
452     aCTime.tm_mday = rDate.Day;
453     aCTime.tm_mon = rDate.Month -1;
454     aCTime.tm_year = rDate.Year -1900;
455 
456     ISC_DATE aISCDate;
457     isc_encode_sql_date(&aCTime, &aISCDate);
458 
459     setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
460 }
461 
462 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
463 {
464     struct tm aCTime;
465     aCTime.tm_sec = rTime.Seconds;
466     aCTime.tm_min = rTime.Minutes;
467     aCTime.tm_hour = rTime.Hours;
468 
469     ISC_TIME aISCTime;
470     isc_encode_sql_time(&aCTime, &aISCTime);
471 
472     // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
473     // other funkiness, so we can simply add the fraction of a second.
474     aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
475 
476     setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
477 }
478 
479 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
480 {
481     struct tm aCTime;
482     aCTime.tm_sec = rTimestamp.Seconds;
483     aCTime.tm_min = rTimestamp.Minutes;
484     aCTime.tm_hour = rTimestamp.Hours;
485     aCTime.tm_mday = rTimestamp.Day;
486     aCTime.tm_mon = rTimestamp.Month - 1;
487     aCTime.tm_year = rTimestamp.Year - 1900;
488 
489     ISC_TIMESTAMP aISCTimestamp;
490     isc_encode_timestamp(&aCTime, &aISCTimestamp);
491 
492     // As in previous function
493     aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
494 
495     setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
496 }
497 
498 
499 // void OPreaparedStatement::set
500 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
501 {
502     ISC_STATUS aErr;
503 
504     aErr = isc_create_blob2(m_statusVector,
505                             &m_pConnection->getDBHandle(),
506                             &m_pConnection->getTransaction(),
507                             &rBlobHandle,
508                             &rBlobId,
509                             0, // Blob parameter buffer length
510                             nullptr); // Blob parameter buffer handle
511 
512     if (aErr)
513     {
514         evaluateStatusVector(m_statusVector,
515                              "setBlob failed on " + m_sSqlStatement,
516                              *this);
517         assert(false);
518     }
519 }
520 
521 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
522 {
523     ISC_STATUS aErr;
524 
525     aErr = isc_close_blob(m_statusVector,
526             &rBlobHandle);
527     if (aErr)
528     {
529         evaluateStatusVector(m_statusVector,
530                 "isc_close_blob failed",
531                 *this);
532         assert(false);
533     }
534 }
535 
536 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
537 {
538     ::osl::MutexGuard aGuard( m_aMutex );
539     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
540 
541 #if SAL_TYPES_SIZEOFPOINTER == 8
542     isc_blob_handle aBlobHandle = 0;
543 #else
544     isc_blob_handle aBlobHandle = nullptr;
545 #endif
546     ISC_QUAD aBlobId;
547 
548     openBlobForWriting(aBlobHandle, aBlobId);
549 
550 
551     // Max segment size is 2^16 == SAL_MAX_UINT16
552     // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
553     // TODO apply max segment size to character encoding
554     sal_Int64 nCharWritten = 1; // XClob is indexed from 1
555     ISC_STATUS aErr = 0;
556     sal_Int64 nLen = xClob->length();
557     while ( nLen > nCharWritten )
558     {
559         sal_Int64 nCharRemain = nLen - nCharWritten;
560         constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
561         sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
562         OString sData = OUStringToOString(
563                 xClob->getSubString(nCharWritten, nWriteSize),
564                 RTL_TEXTENCODING_UTF8);
565         aErr = isc_put_segment( m_statusVector,
566                 &aBlobHandle,
567                 sData.getLength(),
568                 sData.getStr() );
569         nCharWritten += nWriteSize;
570 
571         if (aErr)
572             break;
573     }
574 
575     // We need to make sure we close the Blob even if there are errors, hence evaluate
576     // errors after closing.
577     closeBlobAfterWriting(aBlobHandle);
578 
579     if (aErr)
580     {
581         evaluateStatusVector(m_statusVector,
582                 "isc_put_segment failed",
583                 *this);
584         assert(false);
585     }
586 
587     setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
588 }
589 
590 void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
591 {
592     ::osl::MutexGuard aGuard( m_aMutex );
593     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
594     checkParameterIndex(nParameterIndex);
595 
596 #if SAL_TYPES_SIZEOFPOINTER == 8
597     isc_blob_handle aBlobHandle = 0;
598 #else
599     isc_blob_handle aBlobHandle = nullptr;
600 #endif
601     ISC_QUAD aBlobId;
602 
603     openBlobForWriting(aBlobHandle, aBlobId);
604 
605     OString sData = OUStringToOString(
606             rStr,
607             RTL_TEXTENCODING_UTF8);
608     ISC_STATUS aErr = isc_put_segment( m_statusVector,
609                             &aBlobHandle,
610                             sData.getLength(),
611                             sData.getStr() );
612 
613     // We need to make sure we close the Blob even if there are errors, hence evaluate
614     // errors after closing.
615     closeBlobAfterWriting(aBlobHandle);
616 
617     if (aErr)
618     {
619         evaluateStatusVector(m_statusVector,
620                              "isc_put_segment failed",
621                              *this);
622         assert(false);
623     }
624 
625     setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
626 }
627 
628 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
629                                           const Reference< XBlob >& xBlob)
630 {
631     ::osl::MutexGuard aGuard(m_aMutex);
632     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
633     checkParameterIndex(nParameterIndex);
634 
635 #if SAL_TYPES_SIZEOFPOINTER == 8
636     isc_blob_handle aBlobHandle = 0;
637 #else
638     isc_blob_handle aBlobHandle = nullptr;
639 #endif
640     ISC_QUAD aBlobId;
641 
642     openBlobForWriting(aBlobHandle, aBlobId);
643 
644     ISC_STATUS aErr = 0;
645     const sal_Int64 nBlobLen = xBlob->length();
646     if (nBlobLen > 0)
647     {
648         // Max write size is 0xFFFF == SAL_MAX_UINT16
649         sal_uInt64 nDataWritten = 0;
650         while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
651         {
652             sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
653             sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
654             aErr = isc_put_segment(m_statusVector,
655                                    &aBlobHandle,
656                                    nWriteSize,
657                                    reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
658             nDataWritten += nWriteSize;
659 
660             if (aErr)
661                 break;
662         }
663     }
664 
665     // We need to make sure we close the Blob even if there are errors, hence evaluate
666     // errors after closing.
667     closeBlobAfterWriting(aBlobHandle);
668 
669     if (aErr)
670     {
671         evaluateStatusVector(m_statusVector,
672                              "isc_put_segment failed",
673                              *this);
674         assert(false);
675     }
676 
677     setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
678 }
679 
680 
681 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
682 {
683     ::osl::MutexGuard aGuard( m_aMutex );
684     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
685     checkParameterIndex(nIndex);
686 }
687 
688 
689 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
690 {
691     ::osl::MutexGuard aGuard( m_aMutex );
692     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
693     checkParameterIndex(nIndex);
694 }
695 
696 
697 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
698 {
699     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
700     ::osl::MutexGuard aGuard( m_aMutex );
701     ensurePrepared();
702 
703     checkParameterIndex(parameterIndex);
704     setParameterNull(parameterIndex, false);
705 
706     XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1);
707     int dType = (pVar->sqltype & ~1); // drop null flag
708 
709     if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
710     {
711         double dbValue =0.0;
712         OUString sValue;
713         if( x >>= dbValue )
714         {
715             sValue = OUString::number( dbValue );
716         }
717         else
718         {
719             x >>= sValue;
720         }
721 
722         // fill in the number with nulls in fractional part.
723         // We need this because  e.g. 0.450 != 0.045 despite
724         // their scale is equal
725         OUStringBuffer sBuffer(15);
726         sBuffer.append(sValue);
727         if(sValue.indexOf('.') != -1) // there is a dot
728         {
729             for(sal_Int32 i=sValue.copy(sValue.indexOf('.')+1).getLength(); i<scale;i++)
730             {
731                 sBuffer.append('0');
732             }
733         }
734         else
735         {
736             for (sal_Int32 i=0; i<scale; i++)
737             {
738                 sBuffer.append('0');
739             }
740         }
741 
742         sValue = sBuffer.makeStringAndClear();
743         switch(dType)
744         {
745             case SQL_SHORT:
746                 setValue< sal_Int16 >(parameterIndex,
747                         static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ),
748                         dType);
749                 break;
750             case SQL_LONG:
751             case SQL_DOUBLE:
752                 setValue< sal_Int32 >(parameterIndex,
753                         static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ),
754                         dType);
755                 break;
756             case SQL_INT64:
757                 setValue< sal_Int64 >(parameterIndex,
758                         toNumericWithoutDecimalPlace(sValue),
759                         dType);
760                 break;
761             default:
762                 SAL_WARN("connectivity.firebird",
763                         "No Firebird sql type found for numeric or decimal types");
764                 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
765         }
766     }
767     else
768     {
769         ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
770     }
771 
772 }
773 
774 
775 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
776 {
777     ::osl::MutexGuard aGuard( m_aMutex );
778     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
779     checkParameterIndex(nIndex);
780 }
781 
782 
783 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
784 {
785     ::osl::MutexGuard aGuard( m_aMutex );
786     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
787     checkParameterIndex(nIndex);
788 }
789 
790 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
791                                            const Sequence< sal_Int8 >& xBytes)
792 {
793     ::osl::MutexGuard aGuard(m_aMutex);
794     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
795     checkParameterIndex(nParameterIndex);
796 
797     XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
798     int dType = (pVar->sqltype & ~1); // drop flag bit for now
799 
800     if( dType == SQL_BLOB )
801     {
802 #if SAL_TYPES_SIZEOFPOINTER == 8
803         isc_blob_handle aBlobHandle = 0;
804 #else
805         isc_blob_handle aBlobHandle = nullptr;
806 #endif
807         ISC_QUAD aBlobId;
808 
809         openBlobForWriting(aBlobHandle, aBlobId);
810 
811         ISC_STATUS aErr = 0;
812         const sal_Int32 nBytesLen = xBytes.getLength();
813         if (nBytesLen > 0)
814         {
815             // Max write size is 0xFFFF == SAL_MAX_UINT16
816             sal_uInt32 nDataWritten = 0;
817             while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
818             {
819                 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
820                 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
821                 aErr = isc_put_segment(m_statusVector,
822                                        &aBlobHandle,
823                                        nWriteSize,
824                                        reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
825                 nDataWritten += nWriteSize;
826 
827                 if (aErr)
828                     break;
829             }
830         }
831 
832         // We need to make sure we close the Blob even if there are errors, hence evaluate
833         // errors after closing.
834         closeBlobAfterWriting(aBlobHandle);
835 
836         if (aErr)
837         {
838             evaluateStatusVector(m_statusVector,
839                                  "isc_put_segment failed",
840                                  *this);
841             assert(false);
842         }
843 
844         setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
845     }
846     else if( dType == SQL_VARYING )
847     {
848             setParameterNull(nParameterIndex, false);
849             const sal_Int32 nMaxSize = 0xFFFF;
850             Sequence<sal_Int8> xBytesCopy(xBytes);
851             if (xBytesCopy.getLength() > nMaxSize)
852             {
853                 xBytesCopy.realloc( nMaxSize );
854             }
855             const auto nSize = xBytesCopy.getLength();
856             // 8000 corresponds to value from lcl_addDefaultParameters
857             // in dbaccess/source/filter/hsqldb/createparser.cxx
858             if (nSize > 8000)
859             {
860                 free(pVar->sqldata);
861                 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
862             }
863             // First 2 bytes indicate string size
864             memcpy(pVar->sqldata, &nSize, 2);
865             // Actual data
866             memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize);
867     }
868     else if( dType == SQL_TEXT )
869     {
870             setParameterNull(nParameterIndex, false);
871             memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
872             // Fill remainder with spaces
873             memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
874     }
875     else
876     {
877         ::dbtools::throwSQLException(
878             "Incorrect type for setBytes",
879             ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
880             *this);
881     }
882 }
883 
884 
885 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
886 {
887     ::osl::MutexGuard aGuard( m_aMutex );
888     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
889     checkParameterIndex(nIndex);
890 }
891 
892 
893 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
894 {
895     ::osl::MutexGuard aGuard( m_aMutex );
896     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
897     checkParameterIndex(nIndex);
898 }
899 
900 
901 void SAL_CALL OPreparedStatement::clearParameters(  )
902 {
903 }
904 
905 // ---- Batch methods -- unsupported -----------------------------------------
906 void SAL_CALL OPreparedStatement::clearBatch()
907 {
908     // Unsupported
909 }
910 
911 void SAL_CALL OPreparedStatement::addBatch()
912 {
913     // Unsupported by firebird
914 }
915 
916 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
917 {
918     // Unsupported by firebird
919     return Sequence< sal_Int32 >();
920 }
921 
922 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
923 {
924     switch(nHandle)
925     {
926         case PROPERTY_ID_RESULTSETCONCURRENCY:
927             break;
928         case PROPERTY_ID_RESULTSETTYPE:
929             break;
930         case PROPERTY_ID_FETCHDIRECTION:
931             break;
932         case PROPERTY_ID_USEBOOKMARKS:
933             break;
934         default:
935             OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
936     }
937 }
938 
939 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
940 {
941     ensurePrepared();
942     if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
943     {
944         ::dbtools::throwSQLException(
945             "No column " + OUString::number(nParameterIndex),
946             ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
947             *this);
948     }
949 }
950 
951 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
952                                           bool bSetNull)
953 {
954     XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
955     if (bSetNull)
956     {
957         pVar->sqltype |= 1;
958         *pVar->sqlind = -1;
959     }
960     else
961         *pVar->sqlind = 0;
962 }
963 
964 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
965