Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / connectivity / source / drivers / firebird / PreparedStatement.cxx
blob608d05c274e08c265d90afd4a040db0dd51775c2
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.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <cmath>
23 #include "Connection.hxx"
24 #include "PreparedStatement.hxx"
25 #include "ResultSet.hxx"
26 #include "ResultSetMetaData.hxx"
27 #include "Util.hxx"
29 #include <comphelper/sequence.hxx>
30 #include <connectivity/dbexception.hxx>
31 #include <propertyids.hxx>
32 #include <connectivity/dbtools.hxx>
33 #include <sal/log.hxx>
35 #include <com/sun/star/sdbc/DataType.hpp>
37 using namespace connectivity::firebird;
39 using namespace ::comphelper;
40 using namespace ::osl;
42 using namespace com::sun::star;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::lang;
45 using namespace com::sun::star::beans;
46 using namespace com::sun::star::sdbc;
47 using namespace com::sun::star::container;
48 using namespace com::sun::star::io;
49 using namespace com::sun::star::util;
51 IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
53 constexpr size_t MAX_SIZE_SEGMENT = 65535; // max value of a segment of CLOB, if we want more than 65535 bytes, we need more segments
56 OPreparedStatement::OPreparedStatement( Connection* _pConnection,
57 const OUString& sql)
58 :OStatementCommonBase(_pConnection)
59 ,m_sSqlStatement(sql)
60 ,m_pOutSqlda(nullptr)
61 ,m_pInSqlda(nullptr)
63 SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
64 "sql: " << sql);
67 void OPreparedStatement::ensurePrepared()
69 MutexGuard aGuard(m_aMutex);
70 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
72 if (m_aStatementHandle)
73 return;
75 ISC_STATUS aErr = 0;
77 if (!m_pInSqlda)
79 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10)));
80 m_pInSqlda->version = SQLDA_VERSION1;
81 m_pInSqlda->sqln = 10;
84 prepareAndDescribeStatement(m_sSqlStatement, m_pOutSqlda);
86 aErr = isc_dsql_describe_bind(m_statusVector,
87 &m_aStatementHandle,
89 m_pInSqlda);
91 if (aErr)
93 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
95 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
97 short nItems = m_pInSqlda->sqld;
98 free(m_pInSqlda);
99 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
100 m_pInSqlda->version = SQLDA_VERSION1;
101 m_pInSqlda->sqln = nItems;
102 aErr = isc_dsql_describe_bind(m_statusVector,
103 &m_aStatementHandle,
105 m_pInSqlda);
106 SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
109 if (!aErr)
110 mallocSQLVAR(m_pInSqlda);
111 else
112 evaluateStatusVector(m_statusVector, m_sSqlStatement, *this);
115 OPreparedStatement::~OPreparedStatement()
119 void SAL_CALL OPreparedStatement::acquire() noexcept
121 OStatementCommonBase::acquire();
124 void SAL_CALL OPreparedStatement::release() noexcept
126 OStatementCommonBase::release();
129 Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
131 Any aRet = OStatementCommonBase::queryInterface(rType);
132 if(!aRet.hasValue())
133 aRet = OPreparedStatement_Base::queryInterface(rType);
134 return aRet;
137 uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
139 return concatSequences(OPreparedStatement_Base::getTypes(),
140 OStatementCommonBase::getTypes());
143 Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData()
145 ::osl::MutexGuard aGuard( m_aMutex );
146 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
147 ensurePrepared();
149 if(!m_xMetaData.is())
150 m_xMetaData = new OResultSetMetaData(m_pConnection.get()
151 , m_pOutSqlda);
153 return m_xMetaData;
156 void SAL_CALL OPreparedStatement::close()
158 MutexGuard aGuard( m_aMutex );
159 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
161 OStatementCommonBase::close();
162 if (m_pInSqlda)
164 freeSQLVAR(m_pInSqlda);
165 free(m_pInSqlda);
166 m_pInSqlda = nullptr;
168 if (m_pOutSqlda)
170 freeSQLVAR(m_pOutSqlda);
171 free(m_pOutSqlda);
172 m_pOutSqlda = nullptr;
176 void SAL_CALL OPreparedStatement::disposing()
178 close();
181 void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
182 const OUString& sInput)
184 SAL_INFO("connectivity.firebird",
185 "setString(" << nParameterIndex << " , " << sInput << ")");
187 MutexGuard aGuard( m_aMutex );
188 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
189 ensurePrepared();
191 checkParameterIndex(nParameterIndex);
192 setParameterNull(nParameterIndex, false);
194 OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
196 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
198 int dtype = (pVar->sqltype & ~1); // drop flag bit for now
200 if (str.getLength() > pVar->sqllen)
201 str = str.copy(0, pVar->sqllen);
203 switch (dtype) {
204 case SQL_VARYING:
206 const sal_Int32 max_varchar_len = 0xFFFF;
207 // First 2 bytes indicate string size
208 if (str.getLength() > max_varchar_len)
210 str = str.copy(0, max_varchar_len);
212 const sal_uInt16 nLength = str.getLength();
213 static_assert(sizeof(nLength) == 2, "must match dest memcpy len");
214 memcpy(pVar->sqldata, &nLength, 2);
215 // Actual data
216 memcpy(pVar->sqldata + 2, str.getStr(), str.getLength());
217 break;
219 case SQL_TEXT:
220 memcpy(pVar->sqldata, str.getStr(), str.getLength());
221 // Fill remainder with spaces
222 memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
223 break;
224 case SQL_BLOB: // Clob
225 assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) );
226 setClob(nParameterIndex, sInput );
227 break;
228 case SQL_SHORT:
230 sal_Int32 int32Value = sInput.toInt32();
231 if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
232 (int32Value > std::numeric_limits<sal_Int16>::max()) )
234 ::dbtools::throwSQLException(
235 "Value out of range for SQL_SHORT type",
236 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
237 *this);
239 setShort(nParameterIndex, int32Value);
240 break;
242 case SQL_LONG:
244 sal_Int32 int32Value = sInput.toInt32();
245 setInt(nParameterIndex, int32Value);
246 break;
248 case SQL_INT64:
250 sal_Int64 int64Value = sInput.toInt64();
251 setLong(nParameterIndex, int64Value);
252 break;
254 case SQL_FLOAT:
256 float floatValue = sInput.toFloat();
257 setFloat(nParameterIndex, floatValue);
258 break;
260 case SQL_BOOLEAN:
262 bool boolValue = sInput.toBoolean();
263 setBoolean(nParameterIndex, boolValue);
264 break;
266 case SQL_NULL:
268 // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
269 pVar->sqldata = nullptr;
270 break;
272 default:
273 ::dbtools::throwSQLException(
274 "Incorrect type for setString",
275 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
276 *this);
280 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
282 MutexGuard aGuard( m_aMutex );
283 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
285 return m_pConnection;
288 sal_Bool SAL_CALL OPreparedStatement::execute()
290 SAL_INFO("connectivity.firebird", "executeQuery(). "
291 "Got called with sql: " << m_sSqlStatement);
293 MutexGuard aGuard( m_aMutex );
294 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
296 ensurePrepared();
298 ISC_STATUS aErr;
300 if (m_xResultSet.is()) // Checks whether we have already run the statement.
302 disposeResultSet();
303 // Closes the cursor from the last run.
304 // This doesn't actually free the statement -- using DSQL_close closes
305 // the cursor and keeps the statement, using DSQL_drop frees the statement
306 // (and associated cursors).
307 aErr = isc_dsql_free_statement(m_statusVector,
308 &m_aStatementHandle,
309 DSQL_close);
310 if (aErr)
312 // Do not throw error. Trying to close a closed cursor is not a
313 // critical mistake.
314 OUString sErrMsg = StatusVectorToString(m_statusVector,
315 u"isc_dsql_free_statement: close cursor");
316 SAL_WARN("connectivity.firebird", sErrMsg);
320 aErr = isc_dsql_execute(m_statusVector,
321 &m_pConnection->getTransaction(),
322 &m_aStatementHandle,
324 m_pInSqlda);
325 if (aErr)
327 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
328 evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
331 m_xResultSet = new OResultSet(m_pConnection.get(),
332 m_aMutex,
333 uno::Reference< XInterface >(*this),
334 m_aStatementHandle,
335 m_pOutSqlda);
337 if (getStatementChangeCount() > 0)
338 m_pConnection->notifyDatabaseModified();
340 return m_xResultSet.is();
341 // TODO: implement handling of multiple ResultSets.
344 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
346 execute();
347 return getStatementChangeCount();
350 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
352 execute();
353 return m_xResultSet;
356 namespace {
359 * Take out the number part of a fix point decimal without
360 * the information of where is the fractional part from a
361 * string representation of a number. (e.g. 54.654 -> 54654)
363 sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource)
365 OUString sNumber(sSource);
367 // cut off leading 0 eventually ( eg. 0.567 -> .567)
368 (void)sSource.startsWith("0", &sNumber);
370 sal_Int32 nDotIndex = sNumber.indexOf('.');
372 if( nDotIndex < 0)
374 return sNumber.toInt64(); // no dot -> it's an integer
376 else
378 // remove dot
379 OUStringBuffer sBuffer(15);
380 if(nDotIndex > 0)
382 sBuffer.append(sNumber.subView(0, nDotIndex));
384 sBuffer.append(sNumber.subView(nDotIndex + 1));
385 return o3tl::toInt64(sBuffer);
391 //----- XParameters -----------------------------------------------------------
392 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
394 MutexGuard aGuard( m_aMutex );
395 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
396 ensurePrepared();
398 checkParameterIndex(nIndex);
399 setParameterNull(nIndex);
402 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
404 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
407 template <typename T>
408 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
410 MutexGuard aGuard( m_aMutex );
411 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
412 ensurePrepared();
414 checkParameterIndex(nIndex);
415 setParameterNull(nIndex, false);
417 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
419 if ((pVar->sqltype & ~1) != nType)
421 ::dbtools::throwSQLException(
422 "Incorrect type for setValue",
423 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
424 *this);
427 memcpy(pVar->sqldata, &nValue, sizeof(nValue));
430 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
432 // there's no TINYINT or equivalent on Firebird,
433 // so do the same as setShort
434 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
437 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
439 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
442 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
444 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
447 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
449 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
452 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
454 setValue< float >(nIndex, nValue, SQL_FLOAT);
457 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
459 MutexGuard aGuard( m_aMutex );
460 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
461 ensurePrepared();
463 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
464 short dType = (pVar->sqltype & ~1); // drop flag bit for now
465 short dSubType = pVar->sqlsubtype;
466 // Assume it is a sub type of a number.
467 if(dSubType < 0 || dSubType > 2)
469 ::dbtools::throwSQLException(
470 "Incorrect number sub type",
471 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
472 *this);
474 // firebird stores scale as a negative number
475 ColumnTypeInfo columnType{ dType, dSubType,
476 static_cast<short>(-pVar->sqlscale) };
478 // Caller might try to set an integer type here. It makes sense to convert
479 // it instead of throwing an error.
480 switch(columnType.getSdbcType())
482 case DataType::SMALLINT:
483 setValue< sal_Int16 >(nIndex,
484 static_cast<sal_Int16>(nValue),
485 dType);
486 break;
487 case DataType::INTEGER:
488 setValue< sal_Int32 >(nIndex,
489 static_cast<sal_Int32>(nValue),
490 dType);
491 break;
492 case DataType::BIGINT:
493 setValue< sal_Int64 >(nIndex,
494 static_cast<sal_Int64>(nValue),
495 dType);
496 break;
497 case DataType::NUMERIC:
498 case DataType::DECIMAL:
499 // take decimal places into account, later on they are removed in makeNumericString
500 setObjectWithInfo(nIndex,Any{nValue}, columnType.getSdbcType(), columnType.getScale());
501 break;
502 default:
503 setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT?
507 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
509 struct tm aCTime;
510 aCTime.tm_mday = rDate.Day;
511 aCTime.tm_mon = rDate.Month -1;
512 aCTime.tm_year = rDate.Year -1900;
514 ISC_DATE aISCDate;
515 isc_encode_sql_date(&aCTime, &aISCDate);
517 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
520 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
522 struct tm aCTime;
523 aCTime.tm_sec = rTime.Seconds;
524 aCTime.tm_min = rTime.Minutes;
525 aCTime.tm_hour = rTime.Hours;
527 ISC_TIME aISCTime;
528 isc_encode_sql_time(&aCTime, &aISCTime);
530 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
531 // other funkiness, so we can simply add the fraction of a second.
532 aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
534 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
537 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
539 struct tm aCTime;
540 aCTime.tm_sec = rTimestamp.Seconds;
541 aCTime.tm_min = rTimestamp.Minutes;
542 aCTime.tm_hour = rTimestamp.Hours;
543 aCTime.tm_mday = rTimestamp.Day;
544 aCTime.tm_mon = rTimestamp.Month - 1;
545 aCTime.tm_year = rTimestamp.Year - 1900;
547 ISC_TIMESTAMP aISCTimestamp;
548 isc_encode_timestamp(&aCTime, &aISCTimestamp);
550 // As in previous function
551 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
553 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
557 // void OPreparedStatement::set
558 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
560 ISC_STATUS aErr;
562 aErr = isc_create_blob2(m_statusVector,
563 &m_pConnection->getDBHandle(),
564 &m_pConnection->getTransaction(),
565 &rBlobHandle,
566 &rBlobId,
567 0, // Blob parameter buffer length
568 nullptr); // Blob parameter buffer handle
570 if (aErr)
572 evaluateStatusVector(m_statusVector,
573 Concat2View("setBlob failed on " + m_sSqlStatement),
574 *this);
575 assert(false);
579 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
581 ISC_STATUS aErr;
583 aErr = isc_close_blob(m_statusVector,
584 &rBlobHandle);
585 if (aErr)
587 evaluateStatusVector(m_statusVector,
588 u"isc_close_blob failed",
589 *this);
590 assert(false);
594 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
596 ::osl::MutexGuard aGuard( m_aMutex );
597 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
599 #if SAL_TYPES_SIZEOFPOINTER == 8
600 isc_blob_handle aBlobHandle = 0;
601 #else
602 isc_blob_handle aBlobHandle = nullptr;
603 #endif
604 ISC_QUAD aBlobId;
606 openBlobForWriting(aBlobHandle, aBlobId);
609 // Max segment size is 2^16 == SAL_MAX_UINT16
610 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
611 // TODO apply max segment size to character encoding
612 sal_Int64 nCharWritten = 1; // XClob is indexed from 1
613 ISC_STATUS aErr = 0;
614 sal_Int64 nLen = xClob->length();
615 while ( nLen >= nCharWritten )
617 sal_Int64 nCharRemain = nLen - nCharWritten + 1;
618 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
619 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
620 OString sData = OUStringToOString(
621 xClob->getSubString(nCharWritten, nWriteSize),
622 RTL_TEXTENCODING_UTF8);
623 aErr = isc_put_segment( m_statusVector,
624 &aBlobHandle,
625 sData.getLength(),
626 sData.getStr() );
627 nCharWritten += nWriteSize;
629 if (aErr)
630 break;
633 // We need to make sure we close the Blob even if there are errors, hence evaluate
634 // errors after closing.
635 closeBlobAfterWriting(aBlobHandle);
637 if (aErr)
639 evaluateStatusVector(m_statusVector,
640 u"isc_put_segment failed",
641 *this);
642 assert(false);
645 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
648 void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
650 ::osl::MutexGuard aGuard( m_aMutex );
651 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
652 checkParameterIndex(nParameterIndex);
654 #if SAL_TYPES_SIZEOFPOINTER == 8
655 isc_blob_handle aBlobHandle = 0;
656 #else
657 isc_blob_handle aBlobHandle = nullptr;
658 #endif
659 ISC_QUAD aBlobId;
661 openBlobForWriting(aBlobHandle, aBlobId);
663 OString sData = OUStringToOString(
664 rStr,
665 RTL_TEXTENCODING_UTF8);
666 size_t nDataSize = sData.getLength();
667 ISC_STATUS aErr = 0;
668 // we can't store more than MAX_SIZE_SEGMENT in a segment
669 if (nDataSize <= MAX_SIZE_SEGMENT)
671 aErr = isc_put_segment( m_statusVector,
672 &aBlobHandle,
673 sData.getLength(),
674 sData.getStr() );
676 else
678 // if we need more, let's split the input and first let's calculate the nb of entire chunks needed
679 size_t nNbEntireChunks = nDataSize / MAX_SIZE_SEGMENT;
680 for (size_t i = 0; i < nNbEntireChunks; ++i)
682 OString strCurrentChunk = sData.copy(i * MAX_SIZE_SEGMENT, MAX_SIZE_SEGMENT);
683 aErr = isc_put_segment( m_statusVector,
684 &aBlobHandle,
685 strCurrentChunk.getLength(),
686 strCurrentChunk.getStr() );
687 if (aErr)
688 break;
690 size_t nRemainingBytes = nDataSize - (nNbEntireChunks * MAX_SIZE_SEGMENT);
691 if (nRemainingBytes && !aErr)
693 // then copy the remaining
694 OString strCurrentChunk = sData.copy(nNbEntireChunks * MAX_SIZE_SEGMENT, nRemainingBytes);
695 aErr = isc_put_segment( m_statusVector,
696 &aBlobHandle,
697 strCurrentChunk.getLength(),
698 strCurrentChunk.getStr() );
702 // We need to make sure we close the Blob even if there are errors, hence evaluate
703 // errors after closing.
704 closeBlobAfterWriting(aBlobHandle);
706 if (aErr)
708 evaluateStatusVector(m_statusVector,
709 u"isc_put_segment failed",
710 *this);
711 assert(false);
714 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
717 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
718 const Reference< XBlob >& xBlob)
720 ::osl::MutexGuard aGuard(m_aMutex);
721 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
722 checkParameterIndex(nParameterIndex);
724 #if SAL_TYPES_SIZEOFPOINTER == 8
725 isc_blob_handle aBlobHandle = 0;
726 #else
727 isc_blob_handle aBlobHandle = nullptr;
728 #endif
729 ISC_QUAD aBlobId;
731 openBlobForWriting(aBlobHandle, aBlobId);
733 ISC_STATUS aErr = 0;
734 const sal_Int64 nBlobLen = xBlob->length();
735 if (nBlobLen > 0)
737 // Max write size is 0xFFFF == SAL_MAX_UINT16
738 sal_uInt64 nDataWritten = 0;
739 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
741 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
742 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
743 aErr = isc_put_segment(m_statusVector,
744 &aBlobHandle,
745 nWriteSize,
746 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
747 nDataWritten += nWriteSize;
749 if (aErr)
750 break;
754 // We need to make sure we close the Blob even if there are errors, hence evaluate
755 // errors after closing.
756 closeBlobAfterWriting(aBlobHandle);
758 if (aErr)
760 evaluateStatusVector(m_statusVector,
761 u"isc_put_segment failed",
762 *this);
763 assert(false);
766 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
770 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
772 ::osl::MutexGuard aGuard( m_aMutex );
773 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
774 checkParameterIndex(nIndex);
778 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
780 ::osl::MutexGuard aGuard( m_aMutex );
781 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
782 checkParameterIndex(nIndex);
786 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
788 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
789 ::osl::MutexGuard aGuard( m_aMutex );
790 ensurePrepared();
792 checkParameterIndex(parameterIndex);
793 setParameterNull(parameterIndex, false);
795 XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1);
796 int dType = (pVar->sqltype & ~1); // drop null flag
798 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
800 double dbValue =0.0;
801 OUString sValue;
802 if( x >>= dbValue )
804 // truncate and round to 'scale' number of decimal places
805 sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) );
807 else
809 x >>= sValue;
812 // fill in the number with nulls in fractional part.
813 // We need this because e.g. 0.450 != 0.045 despite
814 // their scale is equal
815 OUStringBuffer sBuffer(15);
816 sBuffer.append(sValue);
817 if(sValue.indexOf('.') != -1) // there is a dot
819 for(sal_Int32 i=sValue.subView(sValue.indexOf('.')+1).size(); i<scale;i++)
821 sBuffer.append('0');
824 else
826 for (sal_Int32 i=0; i<scale; i++)
828 sBuffer.append('0');
832 sValue = sBuffer.makeStringAndClear();
833 switch(dType)
835 case SQL_SHORT:
836 setValue< sal_Int16 >(parameterIndex,
837 static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ),
838 dType);
839 break;
840 case SQL_LONG:
841 case SQL_DOUBLE:
842 setValue< sal_Int32 >(parameterIndex,
843 static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ),
844 dType);
845 break;
846 case SQL_INT64:
847 setValue< sal_Int64 >(parameterIndex,
848 toNumericWithoutDecimalPlace(sValue),
849 dType);
850 break;
851 default:
852 SAL_WARN("connectivity.firebird",
853 "No Firebird sql type found for numeric or decimal types");
854 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
857 else
859 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
865 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
867 ::osl::MutexGuard aGuard( m_aMutex );
868 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
869 checkParameterIndex(nIndex);
873 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
875 ::osl::MutexGuard aGuard( m_aMutex );
876 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
877 checkParameterIndex(nIndex);
880 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
881 const Sequence< sal_Int8 >& xBytes)
883 ::osl::MutexGuard aGuard(m_aMutex);
884 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
885 checkParameterIndex(nParameterIndex);
887 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
888 int dType = (pVar->sqltype & ~1); // drop flag bit for now
890 if( dType == SQL_BLOB )
892 #if SAL_TYPES_SIZEOFPOINTER == 8
893 isc_blob_handle aBlobHandle = 0;
894 #else
895 isc_blob_handle aBlobHandle = nullptr;
896 #endif
897 ISC_QUAD aBlobId;
899 openBlobForWriting(aBlobHandle, aBlobId);
901 ISC_STATUS aErr = 0;
902 const sal_Int32 nBytesLen = xBytes.getLength();
903 if (nBytesLen > 0)
905 // Max write size is 0xFFFF == SAL_MAX_UINT16
906 sal_uInt32 nDataWritten = 0;
907 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
909 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
910 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
911 aErr = isc_put_segment(m_statusVector,
912 &aBlobHandle,
913 nWriteSize,
914 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
915 nDataWritten += nWriteSize;
917 if (aErr)
918 break;
922 // We need to make sure we close the Blob even if there are errors, hence evaluate
923 // errors after closing.
924 closeBlobAfterWriting(aBlobHandle);
926 if (aErr)
928 evaluateStatusVector(m_statusVector,
929 u"isc_put_segment failed",
930 *this);
931 assert(false);
934 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
936 else if( dType == SQL_VARYING )
938 setParameterNull(nParameterIndex, false);
939 const sal_Int32 nMaxSize = 0xFFFF;
940 Sequence<sal_Int8> xBytesCopy(xBytes);
941 if (xBytesCopy.getLength() > nMaxSize)
943 xBytesCopy.realloc( nMaxSize );
945 const sal_uInt16 nSize = xBytesCopy.getLength();
946 // 8000 corresponds to value from lcl_addDefaultParameters
947 // in dbaccess/source/filter/hsqldb/createparser.cxx
948 if (nSize > 8000)
950 free(pVar->sqldata);
951 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
953 static_assert(sizeof(nSize) == 2, "must match dest memcpy len");
954 // First 2 bytes indicate string size
955 memcpy(pVar->sqldata, &nSize, 2);
956 // Actual data
957 memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize);
959 else if( dType == SQL_TEXT )
961 if (pVar->sqllen < xBytes.getLength())
962 dbtools::throwSQLException("Data too big for this field",
963 dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, *this);
964 setParameterNull(nParameterIndex, false);
965 memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
966 // Fill remainder with zeroes
967 memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
969 else
971 ::dbtools::throwSQLException(
972 "Incorrect type for setBytes",
973 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
974 *this);
979 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
981 ::osl::MutexGuard aGuard( m_aMutex );
982 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
983 checkParameterIndex(nIndex);
987 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
989 ::osl::MutexGuard aGuard( m_aMutex );
990 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
991 checkParameterIndex(nIndex);
995 void SAL_CALL OPreparedStatement::clearParameters( )
999 // ---- Batch methods -- unsupported -----------------------------------------
1000 void SAL_CALL OPreparedStatement::clearBatch()
1002 // Unsupported
1005 void SAL_CALL OPreparedStatement::addBatch()
1007 // Unsupported by firebird
1010 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
1012 // Unsupported by firebird
1013 return Sequence< sal_Int32 >();
1016 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
1018 switch(nHandle)
1020 case PROPERTY_ID_RESULTSETCONCURRENCY:
1021 break;
1022 case PROPERTY_ID_RESULTSETTYPE:
1023 break;
1024 case PROPERTY_ID_FETCHDIRECTION:
1025 break;
1026 case PROPERTY_ID_USEBOOKMARKS:
1027 break;
1028 default:
1029 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
1033 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
1035 ensurePrepared();
1036 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
1038 ::dbtools::throwSQLException(
1039 "No column " + OUString::number(nParameterIndex),
1040 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
1041 *this);
1045 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
1046 bool bSetNull)
1048 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
1049 if (bSetNull)
1051 pVar->sqltype |= 1;
1052 *pVar->sqlind = -1;
1054 else
1055 *pVar->sqlind = 0;
1058 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */