bump product version to 7.2.5.1
[LibreOffice.git] / connectivity / source / drivers / firebird / PreparedStatement.cxx
blob3b5511518957206c8fb01f1edc8e3975474c5d4f
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 <string_view>
25 #include "Connection.hxx"
26 #include "PreparedStatement.hxx"
27 #include "ResultSet.hxx"
28 #include "ResultSetMetaData.hxx"
29 #include "Util.hxx"
31 #include <comphelper/sequence.hxx>
32 #include <connectivity/dbexception.hxx>
33 #include <propertyids.hxx>
34 #include <connectivity/dbtools.hxx>
35 #include <sal/log.hxx>
37 #include <com/sun/star/sdbc/DataType.hpp>
39 using namespace connectivity::firebird;
41 using namespace ::comphelper;
42 using namespace ::osl;
44 using namespace com::sun::star;
45 using namespace com::sun::star::uno;
46 using namespace com::sun::star::lang;
47 using namespace com::sun::star::beans;
48 using namespace com::sun::star::sdbc;
49 using namespace com::sun::star::container;
50 using namespace com::sun::star::io;
51 using namespace com::sun::star::util;
53 IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
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,
85 m_pOutSqlda,
86 m_pInSqlda);
88 aErr = isc_dsql_describe_bind(m_statusVector,
89 &m_aStatementHandle,
91 m_pInSqlda);
93 if (aErr)
95 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
97 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
99 short nItems = m_pInSqlda->sqld;
100 free(m_pInSqlda);
101 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
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 OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
198 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
200 int dtype = (pVar->sqltype & ~1); // drop flag bit for now
202 if (str.getLength() > pVar->sqllen)
203 str = str.copy(0, pVar->sqllen);
205 switch (dtype) {
206 case SQL_VARYING:
208 const sal_Int32 max_varchar_len = 0xFFFF;
209 // First 2 bytes indicate string size
210 if (str.getLength() > max_varchar_len)
212 str = str.copy(0, max_varchar_len);
214 const auto nLength = str.getLength();
215 memcpy(pVar->sqldata, &nLength, 2);
216 // Actual data
217 memcpy(pVar->sqldata + 2, str.getStr(), str.getLength());
218 break;
220 case SQL_TEXT:
221 memcpy(pVar->sqldata, str.getStr(), str.getLength());
222 // Fill remainder with spaces
223 memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
224 break;
225 case SQL_BLOB: // Clob
226 assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) );
227 setClob(nParameterIndex, sInput );
228 break;
229 case SQL_SHORT:
231 sal_Int32 int32Value = sInput.toInt32();
232 if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
233 (int32Value > std::numeric_limits<sal_Int16>::max()) )
235 ::dbtools::throwSQLException(
236 "Value out of range for SQL_SHORT type",
237 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
238 *this);
240 setShort(nParameterIndex, int32Value);
241 break;
243 case SQL_LONG:
245 sal_Int32 int32Value = sInput.toInt32();
246 setInt(nParameterIndex, int32Value);
247 break;
249 case SQL_INT64:
251 sal_Int64 int64Value = sInput.toInt64();
252 setLong(nParameterIndex, int64Value);
253 break;
255 case SQL_FLOAT:
257 float floatValue = sInput.toFloat();
258 setFloat(nParameterIndex, floatValue);
259 break;
261 case SQL_BOOLEAN:
263 bool boolValue = sInput.toBoolean();
264 setBoolean(nParameterIndex, boolValue);
265 break;
267 default:
268 ::dbtools::throwSQLException(
269 "Incorrect type for setString",
270 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
271 *this);
275 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
277 MutexGuard aGuard( m_aMutex );
278 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
280 return m_pConnection;
283 sal_Bool SAL_CALL OPreparedStatement::execute()
285 SAL_INFO("connectivity.firebird", "executeQuery(). "
286 "Got called with sql: " << m_sSqlStatement);
288 MutexGuard aGuard( m_aMutex );
289 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
291 ensurePrepared();
293 ISC_STATUS aErr;
295 if (m_xResultSet.is()) // Checks whether we have already run the statement.
297 disposeResultSet();
298 // Closes the cursor from the last run.
299 // This doesn't actually free the statement -- using DSQL_close closes
300 // the cursor and keeps the statement, using DSQL_drop frees the statement
301 // (and associated cursors).
302 aErr = isc_dsql_free_statement(m_statusVector,
303 &m_aStatementHandle,
304 DSQL_close);
305 if (aErr)
307 // Do not throw error. Trying to close a closed cursor is not a
308 // critical mistake.
309 OUString sErrMsg = StatusVectorToString(m_statusVector,
310 u"isc_dsql_free_statement: close cursor");
311 SAL_WARN("connectivity.firebird", sErrMsg);
315 aErr = isc_dsql_execute(m_statusVector,
316 &m_pConnection->getTransaction(),
317 &m_aStatementHandle,
319 m_pInSqlda);
320 if (aErr)
322 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
323 evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
326 m_xResultSet = new OResultSet(m_pConnection.get(),
327 m_aMutex,
328 uno::Reference< XInterface >(*this),
329 m_aStatementHandle,
330 m_pOutSqlda);
332 if (getStatementChangeCount() > 0)
333 m_pConnection->notifyDatabaseModified();
335 return m_xResultSet.is();
336 // TODO: implement handling of multiple ResultSets.
339 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
341 execute();
342 return getStatementChangeCount();
345 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
347 execute();
348 return m_xResultSet;
351 namespace {
354 * Take out the number part of a fix point decimal without
355 * the information of where is the fractional part from a
356 * string representation of a number. (e.g. 54.654 -> 54654)
358 sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource)
360 OUString sNumber(sSource);
362 // cut off leading 0 eventually ( eg. 0.567 -> .567)
363 (void)sSource.startsWith("0", &sNumber);
365 sal_Int32 nDotIndex = sNumber.indexOf('.');
367 if( nDotIndex < 0)
369 return sNumber.toInt64(); // no dot -> it's an integer
371 else
373 // remove dot
374 OUStringBuffer sBuffer(15);
375 if(nDotIndex > 0)
377 sBuffer.append(sNumber.subView(0, nDotIndex));
379 sBuffer.append(sNumber.subView(nDotIndex + 1));
380 return sBuffer.makeStringAndClear().toInt64();
386 //----- XParameters -----------------------------------------------------------
387 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
389 MutexGuard aGuard( m_aMutex );
390 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
391 ensurePrepared();
393 checkParameterIndex(nIndex);
394 setParameterNull(nIndex);
397 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
399 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
402 template <typename T>
403 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
405 MutexGuard aGuard( m_aMutex );
406 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
407 ensurePrepared();
409 checkParameterIndex(nIndex);
410 setParameterNull(nIndex, false);
412 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
414 if ((pVar->sqltype & ~1) != nType)
416 ::dbtools::throwSQLException(
417 "Incorrect type for setValue",
418 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
419 *this);
422 memcpy(pVar->sqldata, &nValue, sizeof(nValue));
425 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
427 // there's no TINYINT or equivalent on Firebird,
428 // so do the same as setShort
429 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
432 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
434 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
437 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
439 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
442 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
444 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
447 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
449 setValue< float >(nIndex, nValue, SQL_FLOAT);
452 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
454 MutexGuard aGuard( m_aMutex );
455 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
456 ensurePrepared();
458 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
459 short dType = (pVar->sqltype & ~1); // drop flag bit for now
460 short dSubType = pVar->sqlsubtype;
461 // Assume it is a sub type of a number.
462 if(dSubType < 0 || dSubType > 2)
464 ::dbtools::throwSQLException(
465 "Incorrect number sub type",
466 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
467 *this);
469 // firebird stores scale as a negative number
470 ColumnTypeInfo columnType{ dType, dSubType,
471 static_cast<short>(-pVar->sqlscale) };
473 // Caller might try to set an integer type here. It makes sense to convert
474 // it instead of throwing an error.
475 switch(columnType.getSdbcType())
477 case DataType::SMALLINT:
478 setValue< sal_Int16 >(nIndex,
479 static_cast<sal_Int16>(nValue),
480 dType);
481 break;
482 case DataType::INTEGER:
483 setValue< sal_Int32 >(nIndex,
484 static_cast<sal_Int32>(nValue),
485 dType);
486 break;
487 case DataType::BIGINT:
488 setValue< sal_Int64 >(nIndex,
489 static_cast<sal_Int64>(nValue),
490 dType);
491 break;
492 case DataType::NUMERIC:
493 case DataType::DECIMAL:
494 // take decimal places into account, later on they are removed in makeNumericString
495 setObjectWithInfo(nIndex,Any{nValue}, columnType.getSdbcType(), columnType.getScale());
496 break;
497 default:
498 setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT?
502 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
504 struct tm aCTime;
505 aCTime.tm_mday = rDate.Day;
506 aCTime.tm_mon = rDate.Month -1;
507 aCTime.tm_year = rDate.Year -1900;
509 ISC_DATE aISCDate;
510 isc_encode_sql_date(&aCTime, &aISCDate);
512 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
515 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
517 struct tm aCTime;
518 aCTime.tm_sec = rTime.Seconds;
519 aCTime.tm_min = rTime.Minutes;
520 aCTime.tm_hour = rTime.Hours;
522 ISC_TIME aISCTime;
523 isc_encode_sql_time(&aCTime, &aISCTime);
525 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
526 // other funkiness, so we can simply add the fraction of a second.
527 aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
529 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
532 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
534 struct tm aCTime;
535 aCTime.tm_sec = rTimestamp.Seconds;
536 aCTime.tm_min = rTimestamp.Minutes;
537 aCTime.tm_hour = rTimestamp.Hours;
538 aCTime.tm_mday = rTimestamp.Day;
539 aCTime.tm_mon = rTimestamp.Month - 1;
540 aCTime.tm_year = rTimestamp.Year - 1900;
542 ISC_TIMESTAMP aISCTimestamp;
543 isc_encode_timestamp(&aCTime, &aISCTimestamp);
545 // As in previous function
546 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
548 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
552 // void OPreparedStatement::set
553 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
555 ISC_STATUS aErr;
557 aErr = isc_create_blob2(m_statusVector,
558 &m_pConnection->getDBHandle(),
559 &m_pConnection->getTransaction(),
560 &rBlobHandle,
561 &rBlobId,
562 0, // Blob parameter buffer length
563 nullptr); // Blob parameter buffer handle
565 if (aErr)
567 evaluateStatusVector(m_statusVector,
568 OUString("setBlob failed on " + m_sSqlStatement),
569 *this);
570 assert(false);
574 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
576 ISC_STATUS aErr;
578 aErr = isc_close_blob(m_statusVector,
579 &rBlobHandle);
580 if (aErr)
582 evaluateStatusVector(m_statusVector,
583 u"isc_close_blob failed",
584 *this);
585 assert(false);
589 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
591 ::osl::MutexGuard aGuard( m_aMutex );
592 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
594 #if SAL_TYPES_SIZEOFPOINTER == 8
595 isc_blob_handle aBlobHandle = 0;
596 #else
597 isc_blob_handle aBlobHandle = nullptr;
598 #endif
599 ISC_QUAD aBlobId;
601 openBlobForWriting(aBlobHandle, aBlobId);
604 // Max segment size is 2^16 == SAL_MAX_UINT16
605 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
606 // TODO apply max segment size to character encoding
607 sal_Int64 nCharWritten = 1; // XClob is indexed from 1
608 ISC_STATUS aErr = 0;
609 sal_Int64 nLen = xClob->length();
610 while ( nLen > nCharWritten )
612 sal_Int64 nCharRemain = nLen - nCharWritten;
613 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
614 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
615 OString sData = OUStringToOString(
616 xClob->getSubString(nCharWritten, nWriteSize),
617 RTL_TEXTENCODING_UTF8);
618 aErr = isc_put_segment( m_statusVector,
619 &aBlobHandle,
620 sData.getLength(),
621 sData.getStr() );
622 nCharWritten += nWriteSize;
624 if (aErr)
625 break;
628 // We need to make sure we close the Blob even if there are errors, hence evaluate
629 // errors after closing.
630 closeBlobAfterWriting(aBlobHandle);
632 if (aErr)
634 evaluateStatusVector(m_statusVector,
635 u"isc_put_segment failed",
636 *this);
637 assert(false);
640 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
643 void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
645 ::osl::MutexGuard aGuard( m_aMutex );
646 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
647 checkParameterIndex(nParameterIndex);
649 #if SAL_TYPES_SIZEOFPOINTER == 8
650 isc_blob_handle aBlobHandle = 0;
651 #else
652 isc_blob_handle aBlobHandle = nullptr;
653 #endif
654 ISC_QUAD aBlobId;
656 openBlobForWriting(aBlobHandle, aBlobId);
658 OString sData = OUStringToOString(
659 rStr,
660 RTL_TEXTENCODING_UTF8);
661 ISC_STATUS aErr = isc_put_segment( m_statusVector,
662 &aBlobHandle,
663 sData.getLength(),
664 sData.getStr() );
666 // We need to make sure we close the Blob even if there are errors, hence evaluate
667 // errors after closing.
668 closeBlobAfterWriting(aBlobHandle);
670 if (aErr)
672 evaluateStatusVector(m_statusVector,
673 u"isc_put_segment failed",
674 *this);
675 assert(false);
678 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
681 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
682 const Reference< XBlob >& xBlob)
684 ::osl::MutexGuard aGuard(m_aMutex);
685 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
686 checkParameterIndex(nParameterIndex);
688 #if SAL_TYPES_SIZEOFPOINTER == 8
689 isc_blob_handle aBlobHandle = 0;
690 #else
691 isc_blob_handle aBlobHandle = nullptr;
692 #endif
693 ISC_QUAD aBlobId;
695 openBlobForWriting(aBlobHandle, aBlobId);
697 ISC_STATUS aErr = 0;
698 const sal_Int64 nBlobLen = xBlob->length();
699 if (nBlobLen > 0)
701 // Max write size is 0xFFFF == SAL_MAX_UINT16
702 sal_uInt64 nDataWritten = 0;
703 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
705 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
706 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
707 aErr = isc_put_segment(m_statusVector,
708 &aBlobHandle,
709 nWriteSize,
710 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
711 nDataWritten += nWriteSize;
713 if (aErr)
714 break;
718 // We need to make sure we close the Blob even if there are errors, hence evaluate
719 // errors after closing.
720 closeBlobAfterWriting(aBlobHandle);
722 if (aErr)
724 evaluateStatusVector(m_statusVector,
725 u"isc_put_segment failed",
726 *this);
727 assert(false);
730 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
734 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
736 ::osl::MutexGuard aGuard( m_aMutex );
737 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
738 checkParameterIndex(nIndex);
742 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
744 ::osl::MutexGuard aGuard( m_aMutex );
745 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
746 checkParameterIndex(nIndex);
750 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
752 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
753 ::osl::MutexGuard aGuard( m_aMutex );
754 ensurePrepared();
756 checkParameterIndex(parameterIndex);
757 setParameterNull(parameterIndex, false);
759 XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1);
760 int dType = (pVar->sqltype & ~1); // drop null flag
762 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
764 double dbValue =0.0;
765 OUString sValue;
766 if( x >>= dbValue )
768 // truncate and round to 'scale' number of decimal places
769 sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) );
771 else
773 x >>= sValue;
776 // fill in the number with nulls in fractional part.
777 // We need this because e.g. 0.450 != 0.045 despite
778 // their scale is equal
779 OUStringBuffer sBuffer(15);
780 sBuffer.append(sValue);
781 if(sValue.indexOf('.') != -1) // there is a dot
783 for(sal_Int32 i=sValue.copy(sValue.indexOf('.')+1).getLength(); i<scale;i++)
785 sBuffer.append('0');
788 else
790 for (sal_Int32 i=0; i<scale; i++)
792 sBuffer.append('0');
796 sValue = sBuffer.makeStringAndClear();
797 switch(dType)
799 case SQL_SHORT:
800 setValue< sal_Int16 >(parameterIndex,
801 static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ),
802 dType);
803 break;
804 case SQL_LONG:
805 case SQL_DOUBLE:
806 setValue< sal_Int32 >(parameterIndex,
807 static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ),
808 dType);
809 break;
810 case SQL_INT64:
811 setValue< sal_Int64 >(parameterIndex,
812 toNumericWithoutDecimalPlace(sValue),
813 dType);
814 break;
815 default:
816 SAL_WARN("connectivity.firebird",
817 "No Firebird sql type found for numeric or decimal types");
818 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
821 else
823 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
829 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
831 ::osl::MutexGuard aGuard( m_aMutex );
832 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
833 checkParameterIndex(nIndex);
837 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
839 ::osl::MutexGuard aGuard( m_aMutex );
840 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
841 checkParameterIndex(nIndex);
844 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
845 const Sequence< sal_Int8 >& xBytes)
847 ::osl::MutexGuard aGuard(m_aMutex);
848 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
849 checkParameterIndex(nParameterIndex);
851 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
852 int dType = (pVar->sqltype & ~1); // drop flag bit for now
854 if( dType == SQL_BLOB )
856 #if SAL_TYPES_SIZEOFPOINTER == 8
857 isc_blob_handle aBlobHandle = 0;
858 #else
859 isc_blob_handle aBlobHandle = nullptr;
860 #endif
861 ISC_QUAD aBlobId;
863 openBlobForWriting(aBlobHandle, aBlobId);
865 ISC_STATUS aErr = 0;
866 const sal_Int32 nBytesLen = xBytes.getLength();
867 if (nBytesLen > 0)
869 // Max write size is 0xFFFF == SAL_MAX_UINT16
870 sal_uInt32 nDataWritten = 0;
871 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
873 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
874 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
875 aErr = isc_put_segment(m_statusVector,
876 &aBlobHandle,
877 nWriteSize,
878 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
879 nDataWritten += nWriteSize;
881 if (aErr)
882 break;
886 // We need to make sure we close the Blob even if there are errors, hence evaluate
887 // errors after closing.
888 closeBlobAfterWriting(aBlobHandle);
890 if (aErr)
892 evaluateStatusVector(m_statusVector,
893 u"isc_put_segment failed",
894 *this);
895 assert(false);
898 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
900 else if( dType == SQL_VARYING )
902 setParameterNull(nParameterIndex, false);
903 const sal_Int32 nMaxSize = 0xFFFF;
904 Sequence<sal_Int8> xBytesCopy(xBytes);
905 if (xBytesCopy.getLength() > nMaxSize)
907 xBytesCopy.realloc( nMaxSize );
909 const auto nSize = xBytesCopy.getLength();
910 // 8000 corresponds to value from lcl_addDefaultParameters
911 // in dbaccess/source/filter/hsqldb/createparser.cxx
912 if (nSize > 8000)
914 free(pVar->sqldata);
915 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
917 // First 2 bytes indicate string size
918 memcpy(pVar->sqldata, &nSize, 2);
919 // Actual data
920 memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize);
922 else if( dType == SQL_TEXT )
924 setParameterNull(nParameterIndex, false);
925 memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
926 // Fill remainder with spaces
927 memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
929 else
931 ::dbtools::throwSQLException(
932 "Incorrect type for setBytes",
933 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
934 *this);
939 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
941 ::osl::MutexGuard aGuard( m_aMutex );
942 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
943 checkParameterIndex(nIndex);
947 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
949 ::osl::MutexGuard aGuard( m_aMutex );
950 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
951 checkParameterIndex(nIndex);
955 void SAL_CALL OPreparedStatement::clearParameters( )
959 // ---- Batch methods -- unsupported -----------------------------------------
960 void SAL_CALL OPreparedStatement::clearBatch()
962 // Unsupported
965 void SAL_CALL OPreparedStatement::addBatch()
967 // Unsupported by firebird
970 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
972 // Unsupported by firebird
973 return Sequence< sal_Int32 >();
976 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
978 switch(nHandle)
980 case PROPERTY_ID_RESULTSETCONCURRENCY:
981 break;
982 case PROPERTY_ID_RESULTSETTYPE:
983 break;
984 case PROPERTY_ID_FETCHDIRECTION:
985 break;
986 case PROPERTY_ID_USEBOOKMARKS:
987 break;
988 default:
989 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
993 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
995 ensurePrepared();
996 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
998 ::dbtools::throwSQLException(
999 "No column " + OUString::number(nParameterIndex),
1000 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
1001 *this);
1005 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
1006 bool bSetNull)
1008 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
1009 if (bSetNull)
1011 pVar->sqltype |= 1;
1012 *pVar->sqlind = -1;
1014 else
1015 *pVar->sqlind = 0;
1018 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */