Use correct object
[LibreOffice.git] / connectivity / source / drivers / firebird / PreparedStatement.cxx
blobc1ebd314ea7e90fceb3f70f8bfe7feaeb4f063cb
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,u"com.sun.star.sdbcx.firebird.PreparedStatement"_ustr,u"com.sun.star.sdbc.PreparedStatement"_ustr);
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 assert(m_pInSqlda && "Don't handle OOM conditions");
81 m_pInSqlda->version = SQLDA_VERSION1;
82 m_pInSqlda->sqln = 10;
85 prepareAndDescribeStatement(m_sSqlStatement, m_pOutSqlda);
87 aErr = isc_dsql_describe_bind(m_statusVector,
88 &m_aStatementHandle,
90 m_pInSqlda);
92 if (aErr)
94 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
96 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
98 short nItems = m_pInSqlda->sqld;
99 free(m_pInSqlda);
100 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
101 assert(m_pInSqlda && "Don't handle OOM conditions");
102 m_pInSqlda->version = SQLDA_VERSION1;
103 m_pInSqlda->sqln = nItems;
104 aErr = isc_dsql_describe_bind(m_statusVector,
105 &m_aStatementHandle,
107 m_pInSqlda);
108 SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
111 if (!aErr)
112 mallocSQLVAR(m_pInSqlda);
113 else
114 evaluateStatusVector(m_statusVector, m_sSqlStatement, *this);
117 OPreparedStatement::~OPreparedStatement()
121 void SAL_CALL OPreparedStatement::acquire() noexcept
123 OStatementCommonBase::acquire();
126 void SAL_CALL OPreparedStatement::release() noexcept
128 OStatementCommonBase::release();
131 Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
133 Any aRet = OStatementCommonBase::queryInterface(rType);
134 if(!aRet.hasValue())
135 aRet = OPreparedStatement_Base::queryInterface(rType);
136 return aRet;
139 uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
141 return concatSequences(OPreparedStatement_Base::getTypes(),
142 OStatementCommonBase::getTypes());
145 Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData()
147 ::osl::MutexGuard aGuard( m_aMutex );
148 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
149 ensurePrepared();
151 if(!m_xMetaData.is())
152 m_xMetaData = new OResultSetMetaData(m_pConnection.get()
153 , m_pOutSqlda);
155 return m_xMetaData;
158 void SAL_CALL OPreparedStatement::close()
160 MutexGuard aGuard( m_aMutex );
161 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
163 OStatementCommonBase::close();
164 if (m_pInSqlda)
166 freeSQLVAR(m_pInSqlda);
167 free(m_pInSqlda);
168 m_pInSqlda = nullptr;
170 if (m_pOutSqlda)
172 freeSQLVAR(m_pOutSqlda);
173 free(m_pOutSqlda);
174 m_pOutSqlda = nullptr;
178 void SAL_CALL OPreparedStatement::disposing()
180 close();
183 void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
184 const OUString& sInput)
186 SAL_INFO("connectivity.firebird",
187 "setString(" << nParameterIndex << " , " << sInput << ")");
189 MutexGuard aGuard( m_aMutex );
190 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
191 ensurePrepared();
193 checkParameterIndex(nParameterIndex);
194 setParameterNull(nParameterIndex, false);
196 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
197 ColumnTypeInfo columnType(*pVar);
199 switch (auto sdbcType = columnType.getSdbcType()) {
200 case DataType::VARCHAR:
201 case DataType::CHAR:
203 OString str = OUStringToOString(sInput, RTL_TEXTENCODING_UTF8);
204 const ISC_SHORT nLength = std::min(str.getLength(), static_cast<sal_Int32>(pVar->sqllen));
205 int offset = 0;
206 if (sdbcType == DataType::VARCHAR)
208 // First 2 bytes indicate string size
209 static_assert(sizeof(nLength) == 2, "must match dest memcpy len");
210 memcpy(pVar->sqldata, &nLength, 2);
211 offset = 2;
213 // Actual data
214 memcpy(pVar->sqldata + offset, str.getStr(), nLength);
215 if (sdbcType == DataType::CHAR)
217 // Fill remainder with spaces
218 memset(pVar->sqldata + offset + nLength, ' ', pVar->sqllen - nLength);
220 break;
222 case DataType::CLOB:
223 setClob(nParameterIndex, sInput );
224 break;
225 case DataType::SMALLINT:
227 sal_Int32 int32Value = sInput.toInt32();
228 if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
229 (int32Value > std::numeric_limits<sal_Int16>::max()) )
231 ::dbtools::throwSQLException(
232 u"Value out of range for SQL_SHORT type"_ustr,
233 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
234 *this);
236 setShort(nParameterIndex, int32Value);
237 break;
239 case DataType::INTEGER:
241 sal_Int32 int32Value = sInput.toInt32();
242 setInt(nParameterIndex, int32Value);
243 break;
245 case DataType::BIGINT:
247 sal_Int64 int64Value = sInput.toInt64();
248 setLong(nParameterIndex, int64Value);
249 break;
251 case DataType::FLOAT:
253 float floatValue = sInput.toFloat();
254 setFloat(nParameterIndex, floatValue);
255 break;
257 case DataType::DOUBLE:
258 setDouble(nParameterIndex, sInput.toDouble());
259 break;
260 case DataType::NUMERIC:
261 case DataType::DECIMAL:
262 return setObjectWithInfo(nParameterIndex, Any{ sInput }, sdbcType, 0);
263 break;
264 case DataType::BOOLEAN:
266 bool boolValue = sInput.toBoolean();
267 setBoolean(nParameterIndex, boolValue);
268 break;
270 case DataType::SQLNULL:
272 // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
273 pVar->sqldata = nullptr;
274 break;
276 default:
277 ::dbtools::throwSQLException(
278 u"Incorrect type for setString"_ustr,
279 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
280 *this);
284 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
286 MutexGuard aGuard( m_aMutex );
287 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
289 return m_pConnection;
292 sal_Bool SAL_CALL OPreparedStatement::execute()
294 SAL_INFO("connectivity.firebird", "executeQuery(). "
295 "Got called with sql: " << m_sSqlStatement);
297 MutexGuard aGuard( m_aMutex );
298 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
300 ensurePrepared();
302 ISC_STATUS aErr;
304 if (m_xResultSet.is()) // Checks whether we have already run the statement.
306 disposeResultSet();
307 // Closes the cursor from the last run.
308 // This doesn't actually free the statement -- using DSQL_close closes
309 // the cursor and keeps the statement, using DSQL_drop frees the statement
310 // (and associated cursors).
311 aErr = isc_dsql_free_statement(m_statusVector,
312 &m_aStatementHandle,
313 DSQL_close);
314 if (aErr)
316 // Do not throw error. Trying to close a closed cursor is not a
317 // critical mistake.
318 OUString sErrMsg = StatusVectorToString(m_statusVector,
319 u"isc_dsql_free_statement: close cursor");
320 SAL_WARN("connectivity.firebird", sErrMsg);
324 aErr = isc_dsql_execute(m_statusVector,
325 &m_pConnection->getTransaction(),
326 &m_aStatementHandle,
328 m_pInSqlda);
329 if (aErr)
331 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
332 evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
335 m_xResultSet = new OResultSet(m_pConnection.get(),
336 m_aMutex,
337 uno::Reference< XInterface >(*this),
338 m_aStatementHandle,
339 m_pOutSqlda);
341 return m_xResultSet.is();
342 // TODO: implement handling of multiple ResultSets.
345 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
347 execute();
348 return getStatementChangeCount();
351 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
353 execute();
354 return m_xResultSet;
357 namespace {
359 sal_Int64 toPowOf10AndRound(double n, int powOf10)
361 static constexpr sal_Int64 powers[] = {
364 100,
365 1000,
366 10000,
367 100000,
368 1000000,
369 10000000,
370 100000000,
371 1000000000,
372 10000000000,
373 100000000000,
374 1000000000000,
375 10000000000000,
376 100000000000000,
377 1000000000000000,
378 10000000000000000,
379 100000000000000000,
380 1000000000000000000,
382 powOf10 = std::clamp(powOf10, 0, int(std::size(powers) - 1));
383 return n * powers[powOf10] + (n >= 0 ? 0.5 : -0.5);
387 * Take out the number part of a fix point decimal without
388 * the information of where is the fractional part from a
389 * string representation of a number. (e.g. 54.654 -> 54654)
391 sal_Int64 toNumericWithoutDecimalPlace(const Any& x, sal_Int32 scale)
393 if (double value = 0; x >>= value)
394 return toPowOf10AndRound(value, scale);
396 // Can't use conversion of string to double, because it could be not representable in double
398 OUString s;
399 x >>= s;
400 std::u16string_view num(o3tl::trim(s));
401 size_t end = num.starts_with('-') ? 1 : 0;
402 for (bool seenDot = false; end < num.size(); ++end)
404 if (num[end] == '.')
406 if (seenDot)
407 break;
408 seenDot = true;
410 else if (!rtl::isAsciiDigit(num[end]))
411 break;
413 num = num.substr(0, end);
415 // fill in the number with nulls in fractional part.
416 // We need this because e.g. 0.450 != 0.045 despite
417 // their scale is equal
418 OUStringBuffer buffer(num);
419 if (auto dotPos = num.find('.'); dotPos != std::u16string_view::npos) // there is a dot
421 scale -= num.substr(dotPos + 1).size();
422 buffer.remove(dotPos, 1);
423 if (scale < 0)
425 const sal_Int32 n = std::min(buffer.getLength(), -scale);
426 buffer.truncate(buffer.getLength() - n);
427 scale = 0;
430 for (sal_Int32 i = 0; i < scale; ++i)
431 buffer.append('0');
433 return OUString::unacquired(buffer).toInt64();
436 double toDouble(const Any& x)
438 if (double value = 0; x >>= value)
439 return value;
440 OUString s;
441 x >>= s;
442 return s.toDouble();
446 //----- XParameters -----------------------------------------------------------
447 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
449 MutexGuard aGuard( m_aMutex );
450 ensurePrepared();
452 checkParameterIndex(nIndex);
453 setParameterNull(nIndex);
456 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
458 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
461 template <typename T>
462 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
464 MutexGuard aGuard( m_aMutex );
465 ensurePrepared();
467 checkParameterIndex(nIndex);
468 setParameterNull(nIndex, false);
470 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
472 if ((pVar->sqltype & ~1) != nType)
474 ::dbtools::throwSQLException(
475 u"Incorrect type for setValue"_ustr,
476 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
477 *this);
480 memcpy(pVar->sqldata, &nValue, sizeof(nValue));
483 // Integral type setters convert transparently to bigger types
485 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
487 // there's no TINYINT or equivalent on Firebird,
488 // so do the same as setShort
489 setShort(nIndex, nValue);
492 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
494 MutexGuard aGuard(m_aMutex);
495 ensurePrepared();
497 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
498 switch (columnType.getSdbcType())
500 case DataType::INTEGER:
501 return setValue<sal_Int32>(nIndex, nValue, columnType.getType());
502 case DataType::BIGINT:
503 return setValue<sal_Int64>(nIndex, nValue, columnType.getType());
504 case DataType::FLOAT:
505 return setValue<float>(nIndex, nValue, columnType.getType());
506 case DataType::DOUBLE:
507 return setValue<double>(nIndex, nValue, columnType.getType());
509 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
512 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
514 MutexGuard aGuard(m_aMutex);
515 ensurePrepared();
517 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
518 switch (columnType.getSdbcType())
520 case DataType::BIGINT:
521 return setValue<sal_Int64>(nIndex, nValue, columnType.getType());
522 case DataType::DOUBLE:
523 return setValue<double>(nIndex, nValue, columnType.getType());
525 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
528 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
530 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
533 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
535 setValue< float >(nIndex, nValue, SQL_FLOAT);
538 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
540 MutexGuard aGuard( m_aMutex );
541 ensurePrepared();
543 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
544 // Assume it is a sub type of a number.
545 if (columnType.getSubType() < 0 || columnType.getSubType() > 2)
547 ::dbtools::throwSQLException(
548 u"Incorrect number sub type"_ustr,
549 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
550 *this);
553 // Caller might try to set an integer type here. It makes sense to convert
554 // it instead of throwing an error.
555 switch(auto sdbcType = columnType.getSdbcType())
557 case DataType::SMALLINT:
558 return setValue(nIndex, static_cast<sal_Int16>(nValue), columnType.getType());
559 case DataType::INTEGER:
560 return setValue(nIndex, static_cast<sal_Int32>(nValue), columnType.getType());
561 case DataType::BIGINT:
562 return setValue(nIndex, static_cast<sal_Int64>(nValue), columnType.getType());
563 case DataType::NUMERIC:
564 case DataType::DECIMAL:
565 return setObjectWithInfo(nIndex, Any{ nValue }, sdbcType, 0);
566 // TODO: SQL_D_FLOAT?
568 setValue<double>(nIndex, nValue, SQL_DOUBLE);
571 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
573 struct tm aCTime;
574 aCTime.tm_mday = rDate.Day;
575 aCTime.tm_mon = rDate.Month -1;
576 aCTime.tm_year = rDate.Year -1900;
578 ISC_DATE aISCDate;
579 isc_encode_sql_date(&aCTime, &aISCDate);
581 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
584 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
586 struct tm aCTime;
587 aCTime.tm_sec = rTime.Seconds;
588 aCTime.tm_min = rTime.Minutes;
589 aCTime.tm_hour = rTime.Hours;
591 ISC_TIME aISCTime;
592 isc_encode_sql_time(&aCTime, &aISCTime);
594 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
595 // other funkiness, so we can simply add the fraction of a second.
596 aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
598 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
601 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
603 struct tm aCTime;
604 aCTime.tm_sec = rTimestamp.Seconds;
605 aCTime.tm_min = rTimestamp.Minutes;
606 aCTime.tm_hour = rTimestamp.Hours;
607 aCTime.tm_mday = rTimestamp.Day;
608 aCTime.tm_mon = rTimestamp.Month - 1;
609 aCTime.tm_year = rTimestamp.Year - 1900;
611 ISC_TIMESTAMP aISCTimestamp;
612 isc_encode_timestamp(&aCTime, &aISCTimestamp);
614 // As in previous function
615 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
617 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
621 // void OPreparedStatement::set
622 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
624 ISC_STATUS aErr;
626 aErr = isc_create_blob2(m_statusVector,
627 &m_pConnection->getDBHandle(),
628 &m_pConnection->getTransaction(),
629 &rBlobHandle,
630 &rBlobId,
631 0, // Blob parameter buffer length
632 nullptr); // Blob parameter buffer handle
634 if (aErr)
636 evaluateStatusVector(m_statusVector,
637 Concat2View("setBlob failed on " + m_sSqlStatement),
638 *this);
639 assert(false);
643 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
645 ISC_STATUS aErr;
647 aErr = isc_close_blob(m_statusVector,
648 &rBlobHandle);
649 if (aErr)
651 evaluateStatusVector(m_statusVector,
652 u"isc_close_blob failed",
653 *this);
654 assert(false);
658 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
660 ::osl::MutexGuard aGuard( m_aMutex );
661 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
663 // value-initialization: isc_blob_handle may be either a pointer of an integer
664 isc_blob_handle aBlobHandle{};
665 ISC_QUAD aBlobId;
667 openBlobForWriting(aBlobHandle, aBlobId);
670 // Max segment size is 2^16 == SAL_MAX_UINT16
671 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
672 // TODO apply max segment size to character encoding
673 sal_Int64 nCharWritten = 1; // XClob is indexed from 1
674 ISC_STATUS aErr = 0;
675 sal_Int64 nLen = xClob->length();
676 while ( nLen >= nCharWritten )
678 sal_Int64 nCharRemain = nLen - nCharWritten + 1;
679 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
680 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
681 OString sData = OUStringToOString(
682 xClob->getSubString(nCharWritten, nWriteSize),
683 RTL_TEXTENCODING_UTF8);
684 aErr = isc_put_segment( m_statusVector,
685 &aBlobHandle,
686 sData.getLength(),
687 sData.getStr() );
688 nCharWritten += nWriteSize;
690 if (aErr)
691 break;
694 // We need to make sure we close the Blob even if there are errors, hence evaluate
695 // errors after closing.
696 closeBlobAfterWriting(aBlobHandle);
698 if (aErr)
700 evaluateStatusVector(m_statusVector,
701 u"isc_put_segment failed",
702 *this);
703 assert(false);
706 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
709 void OPreparedStatement::setClob(sal_Int32 nParameterIndex, std::u16string_view rStr)
711 ::osl::MutexGuard aGuard( m_aMutex );
712 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
713 checkParameterIndex(nParameterIndex);
715 // value-initialization: isc_blob_handle may be either a pointer of an integer
716 isc_blob_handle aBlobHandle{};
717 ISC_QUAD aBlobId;
719 openBlobForWriting(aBlobHandle, aBlobId);
721 OString sData = OUStringToOString(
722 rStr,
723 RTL_TEXTENCODING_UTF8);
724 size_t nDataSize = sData.getLength();
725 ISC_STATUS aErr = 0;
726 // we can't store more than MAX_SIZE_SEGMENT in a segment
727 if (nDataSize <= MAX_SIZE_SEGMENT)
729 aErr = isc_put_segment( m_statusVector,
730 &aBlobHandle,
731 sData.getLength(),
732 sData.getStr() );
734 else
736 // if we need more, let's split the input and first let's calculate the nb of entire chunks needed
737 size_t nNbEntireChunks = nDataSize / MAX_SIZE_SEGMENT;
738 for (size_t i = 0; i < nNbEntireChunks; ++i)
740 OString strCurrentChunk = sData.copy(i * MAX_SIZE_SEGMENT, MAX_SIZE_SEGMENT);
741 aErr = isc_put_segment( m_statusVector,
742 &aBlobHandle,
743 strCurrentChunk.getLength(),
744 strCurrentChunk.getStr() );
745 if (aErr)
746 break;
748 size_t nRemainingBytes = nDataSize - (nNbEntireChunks * MAX_SIZE_SEGMENT);
749 if (nRemainingBytes && !aErr)
751 // then copy the remaining
752 OString strCurrentChunk = sData.copy(nNbEntireChunks * MAX_SIZE_SEGMENT, nRemainingBytes);
753 aErr = isc_put_segment( m_statusVector,
754 &aBlobHandle,
755 strCurrentChunk.getLength(),
756 strCurrentChunk.getStr() );
760 // We need to make sure we close the Blob even if there are errors, hence evaluate
761 // errors after closing.
762 closeBlobAfterWriting(aBlobHandle);
764 if (aErr)
766 evaluateStatusVector(m_statusVector,
767 u"isc_put_segment failed",
768 *this);
769 assert(false);
772 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
775 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
776 const Reference< XBlob >& xBlob)
778 ::osl::MutexGuard aGuard(m_aMutex);
779 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
780 checkParameterIndex(nParameterIndex);
782 // value-initialization: isc_blob_handle may be either a pointer of an integer
783 isc_blob_handle aBlobHandle{};
784 ISC_QUAD aBlobId;
786 openBlobForWriting(aBlobHandle, aBlobId);
788 ISC_STATUS aErr = 0;
789 const sal_Int64 nBlobLen = xBlob->length();
790 if (nBlobLen > 0)
792 // Max write size is 0xFFFF == SAL_MAX_UINT16
793 sal_uInt64 nDataWritten = 0;
794 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
796 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
797 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
798 aErr = isc_put_segment(m_statusVector,
799 &aBlobHandle,
800 nWriteSize,
801 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
802 nDataWritten += nWriteSize;
804 if (aErr)
805 break;
809 // We need to make sure we close the Blob even if there are errors, hence evaluate
810 // errors after closing.
811 closeBlobAfterWriting(aBlobHandle);
813 if (aErr)
815 evaluateStatusVector(m_statusVector,
816 u"isc_put_segment failed",
817 *this);
818 assert(false);
821 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
825 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
827 ::osl::MutexGuard aGuard( m_aMutex );
828 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
829 checkParameterIndex(nIndex);
833 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
835 ::osl::MutexGuard aGuard( m_aMutex );
836 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
837 checkParameterIndex(nIndex);
841 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
843 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
844 ::osl::MutexGuard aGuard( m_aMutex );
845 ensurePrepared();
847 checkParameterIndex(parameterIndex);
848 setParameterNull(parameterIndex, false);
850 ColumnTypeInfo columnType{ m_pInSqlda, parameterIndex };
851 int dType = columnType.getType() & ~1; // drop null flag
853 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
855 switch(dType)
857 case SQL_SHORT:
858 return setValue(
859 parameterIndex,
860 static_cast<sal_Int16>(toNumericWithoutDecimalPlace(x, columnType.getScale())),
861 dType);
862 case SQL_LONG:
863 return setValue(
864 parameterIndex,
865 static_cast<sal_Int32>(toNumericWithoutDecimalPlace(x, columnType.getScale())),
866 dType);
867 case SQL_INT64:
868 return setValue(parameterIndex,
869 toNumericWithoutDecimalPlace(x, columnType.getScale()), dType);
870 case SQL_DOUBLE:
871 return setValue(parameterIndex, toDouble(x), dType);
872 default:
873 SAL_WARN("connectivity.firebird",
874 "No Firebird sql type found for numeric or decimal types");
875 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
878 else
880 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
886 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
888 ::osl::MutexGuard aGuard( m_aMutex );
889 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
890 checkParameterIndex(nIndex);
894 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
896 ::osl::MutexGuard aGuard( m_aMutex );
897 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
898 checkParameterIndex(nIndex);
901 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
902 const Sequence< sal_Int8 >& xBytes)
904 ::osl::MutexGuard aGuard(m_aMutex);
905 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
906 checkParameterIndex(nParameterIndex);
908 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
909 int dType = (pVar->sqltype & ~1); // drop flag bit for now
911 if( dType == SQL_BLOB )
913 // value-initialization: isc_blob_handle may be either a pointer of an integer
914 isc_blob_handle aBlobHandle{};
915 ISC_QUAD aBlobId;
917 openBlobForWriting(aBlobHandle, aBlobId);
919 ISC_STATUS aErr = 0;
920 const sal_Int32 nBytesLen = xBytes.getLength();
921 if (nBytesLen > 0)
923 // Max write size is 0xFFFF == SAL_MAX_UINT16
924 sal_uInt32 nDataWritten = 0;
925 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
927 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
928 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
929 aErr = isc_put_segment(m_statusVector,
930 &aBlobHandle,
931 nWriteSize,
932 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
933 nDataWritten += nWriteSize;
935 if (aErr)
936 break;
940 // We need to make sure we close the Blob even if there are errors, hence evaluate
941 // errors after closing.
942 closeBlobAfterWriting(aBlobHandle);
944 if (aErr)
946 evaluateStatusVector(m_statusVector,
947 u"isc_put_segment failed",
948 *this);
949 assert(false);
952 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
954 else if( dType == SQL_VARYING )
956 setParameterNull(nParameterIndex, false);
957 const sal_Int32 nMaxSize = 0xFFFF;
958 const sal_uInt16 nSize = std::min(xBytes.getLength(), nMaxSize);
959 // 8000 corresponds to value from lcl_addDefaultParameters
960 // in dbaccess/source/filter/hsqldb/createparser.cxx
961 if (nSize > 8000)
963 free(pVar->sqldata);
964 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
966 static_assert(sizeof(nSize) == 2, "must match dest memcpy len");
967 // First 2 bytes indicate string size
968 memcpy(pVar->sqldata, &nSize, 2);
969 // Actual data
970 memcpy(pVar->sqldata + 2, xBytes.getConstArray(), nSize);
972 else if( dType == SQL_TEXT )
974 if (pVar->sqllen < xBytes.getLength())
975 dbtools::throwSQLException(u"Data too big for this field"_ustr,
976 dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, *this);
977 setParameterNull(nParameterIndex, false);
978 memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
979 // Fill remainder with zeroes
980 memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
982 else
984 ::dbtools::throwSQLException(
985 u"Incorrect type for setBytes"_ustr,
986 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
987 *this);
992 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
994 ::osl::MutexGuard aGuard( m_aMutex );
995 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
996 checkParameterIndex(nIndex);
1000 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
1002 ::osl::MutexGuard aGuard( m_aMutex );
1003 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
1004 checkParameterIndex(nIndex);
1008 void SAL_CALL OPreparedStatement::clearParameters( )
1012 // ---- Batch methods -- unsupported -----------------------------------------
1013 void SAL_CALL OPreparedStatement::clearBatch()
1015 // Unsupported
1018 void SAL_CALL OPreparedStatement::addBatch()
1020 // Unsupported by firebird
1023 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
1025 // Unsupported by firebird
1026 return Sequence< sal_Int32 >();
1029 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
1031 switch(nHandle)
1033 case PROPERTY_ID_RESULTSETCONCURRENCY:
1034 break;
1035 case PROPERTY_ID_RESULTSETTYPE:
1036 break;
1037 case PROPERTY_ID_FETCHDIRECTION:
1038 break;
1039 case PROPERTY_ID_USEBOOKMARKS:
1040 break;
1041 default:
1042 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
1046 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
1048 ensurePrepared();
1049 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
1051 ::dbtools::throwSQLException(
1052 "No column " + OUString::number(nParameterIndex),
1053 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
1054 *this);
1058 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
1059 bool bSetNull)
1061 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
1062 if (bSetNull)
1064 pVar->sqltype |= 1;
1065 *pVar->sqlind = -1;
1067 else
1068 *pVar->sqlind = 0;
1071 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */