LibreOffice Module connectivity (master) 1
firebird/PreparedStatement.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * 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#include <cmath>
22
23#include "Connection.hxx"
24#include "PreparedStatement.hxx"
25#include "ResultSet.hxx"
26#include "ResultSetMetaData.hxx"
27#include "Util.hxx"
28
31#include <propertyids.hxx>
33#include <sal/log.hxx>
34
35#include <com/sun/star/sdbc/DataType.hpp>
36
37using namespace connectivity::firebird;
38
39using namespace ::comphelper;
40using namespace ::osl;
41
42using namespace com::sun::star;
43using namespace com::sun::star::uno;
44using namespace com::sun::star::lang;
45using namespace com::sun::star::beans;
46using namespace com::sun::star::sdbc;
47using namespace com::sun::star::container;
48using namespace com::sun::star::io;
49using namespace com::sun::star::util;
50
51IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
52
53constexpr size_t MAX_SIZE_SEGMENT = 65535; // max value of a segment of CLOB, if we want more than 65535 bytes, we need more segments
54
55
56OPreparedStatement::OPreparedStatement( Connection* _pConnection,
57 const OUString& sql)
58 :OStatementCommonBase(_pConnection)
59 ,m_sSqlStatement(sql)
60 ,m_pOutSqlda(nullptr)
61 ,m_pInSqlda(nullptr)
62{
63 SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
64 "sql: " << sql);
65}
66
68{
69 MutexGuard aGuard(m_aMutex);
70 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
71
73 return;
74
75 ISC_STATUS aErr = 0;
76
77 if (!m_pInSqlda)
78 {
79 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10)));
80 m_pInSqlda->version = SQLDA_VERSION1;
81 m_pInSqlda->sqln = 10;
82 }
83
85
86 aErr = isc_dsql_describe_bind(m_statusVector,
88 1,
90
91 if (aErr)
92 {
93 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
94 }
95 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
96 {
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,
104 1,
105 m_pInSqlda);
106 SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
107 }
108
109 if (!aErr)
111 else
113}
114
116{
117}
118
119void SAL_CALL OPreparedStatement::acquire() noexcept
120{
122}
123
124void SAL_CALL OPreparedStatement::release() noexcept
125{
127}
128
129Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
130{
132 if(!aRet.hasValue())
134 return aRet;
135}
136
137uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
138{
141}
142
144{
145 ::osl::MutexGuard aGuard( m_aMutex );
146 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
148
149 if(!m_xMetaData.is())
151 , m_pOutSqlda);
152
153 return m_xMetaData;
154}
155
157{
158 MutexGuard aGuard( m_aMutex );
159 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
160
162 if (m_pInSqlda)
163 {
165 free(m_pInSqlda);
166 m_pInSqlda = nullptr;
167 }
168 if (m_pOutSqlda)
169 {
171 free(m_pOutSqlda);
172 m_pOutSqlda = nullptr;
173 }
174}
175
177{
178 close();
179}
180
181void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
182 const OUString& sInput)
183{
184 SAL_INFO("connectivity.firebird",
185 "setString(" << nParameterIndex << " , " << sInput << ")");
186
187 MutexGuard aGuard( m_aMutex );
188 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
190
191 checkParameterIndex(nParameterIndex);
192 setParameterNull(nParameterIndex, false);
193
194 OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
195
196 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
197
198 int dtype = (pVar->sqltype & ~1); // drop flag bit for now
199
200 if (str.getLength() > pVar->sqllen)
201 str = str.copy(0, pVar->sqllen);
202
203 switch (dtype) {
204 case SQL_VARYING:
205 {
206 const sal_Int32 max_varchar_len = 0xFFFF;
207 // First 2 bytes indicate string size
208 if (str.getLength() > max_varchar_len)
209 {
210 str = str.copy(0, max_varchar_len);
211 }
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;
218 }
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:
229 {
230 sal_Int32 int32Value = sInput.toInt32();
231 if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
232 (int32Value > std::numeric_limits<sal_Int16>::max()) )
233 {
235 "Value out of range for SQL_SHORT type",
236 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
237 *this);
238 }
239 setShort(nParameterIndex, int32Value);
240 break;
241 }
242 case SQL_LONG:
243 {
244 sal_Int32 int32Value = sInput.toInt32();
245 setInt(nParameterIndex, int32Value);
246 break;
247 }
248 case SQL_INT64:
249 {
250 sal_Int64 int64Value = sInput.toInt64();
251 setLong(nParameterIndex, int64Value);
252 break;
253 }
254 case SQL_FLOAT:
255 {
256 float floatValue = sInput.toFloat();
257 setFloat(nParameterIndex, floatValue);
258 break;
259 }
260 case SQL_BOOLEAN:
261 {
262 bool boolValue = sInput.toBoolean();
263 setBoolean(nParameterIndex, boolValue);
264 break;
265 }
266 case SQL_NULL:
267 {
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;
271 }
272 default:
274 "Incorrect type for setString",
275 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
276 *this);
277 }
278}
279
281{
282 MutexGuard aGuard( m_aMutex );
283 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
284
285 return m_pConnection;
286}
287
289{
290 SAL_INFO("connectivity.firebird", "executeQuery(). "
291 "Got called with sql: " << m_sSqlStatement);
292
293 MutexGuard aGuard( m_aMutex );
294 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
295
297
298 ISC_STATUS aErr;
299
300 if (m_xResultSet.is()) // Checks whether we have already run the statement.
301 {
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,
309 DSQL_close);
310 if (aErr)
311 {
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);
317 }
318 }
319
320 aErr = isc_dsql_execute(m_statusVector,
321 &m_pConnection->getTransaction(),
323 1,
324 m_pInSqlda);
325 if (aErr)
326 {
327 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
328 evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
329 }
330
332 m_aMutex,
333 uno::Reference< XInterface >(*this),
336
337 return m_xResultSet.is();
338 // TODO: implement handling of multiple ResultSets.
339}
340
342{
343 execute();
345}
346
348{
349 execute();
350 return m_xResultSet;
351}
352
353namespace {
354
360sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource)
361{
362 OUString sNumber(sSource);
363
364 // cut off leading 0 eventually ( eg. 0.567 -> .567)
365 (void)sSource.startsWith("0", &sNumber);
366
367 sal_Int32 nDotIndex = sNumber.indexOf('.');
368
369 if( nDotIndex < 0)
370 {
371 return sNumber.toInt64(); // no dot -> it's an integer
372 }
373 else
374 {
375 // remove dot
376 OUStringBuffer sBuffer(15);
377 if(nDotIndex > 0)
378 {
379 sBuffer.append(sNumber.subView(0, nDotIndex));
380 }
381 sBuffer.append(sNumber.subView(nDotIndex + 1));
382 return o3tl::toInt64(sBuffer);
383 }
384}
385
386}
387
388//----- XParameters -----------------------------------------------------------
389void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
390{
391 MutexGuard aGuard( m_aMutex );
392 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
394
397}
398
399void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
400{
401 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
402}
403
404template <typename T>
405void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
406{
407 MutexGuard aGuard( m_aMutex );
408 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
410
412 setParameterNull(nIndex, false);
413
414 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
415
416 if ((pVar->sqltype & ~1) != nType)
417 {
419 "Incorrect type for setValue",
420 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
421 *this);
422 }
423
424 memcpy(pVar->sqldata, &nValue, sizeof(nValue));
425}
426
427void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
428{
429 // there's no TINYINT or equivalent on Firebird,
430 // so do the same as setShort
431 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
432}
433
434void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
435{
436 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
437}
438
439void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
440{
441 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
442}
443
444void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
445{
446 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
447}
448
449void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
450{
451 setValue< float >(nIndex, nValue, SQL_FLOAT);
452}
453
454void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
455{
456 MutexGuard aGuard( m_aMutex );
457 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
459
460 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
461 short dType = (pVar->sqltype & ~1); // drop flag bit for now
462 short dSubType = pVar->sqlsubtype;
463 // Assume it is a sub type of a number.
464 if(dSubType < 0 || dSubType > 2)
465 {
467 "Incorrect number sub type",
468 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
469 *this);
470 }
471 // firebird stores scale as a negative number
472 ColumnTypeInfo columnType{ dType, dSubType,
473 static_cast<short>(-pVar->sqlscale) };
474
475 // Caller might try to set an integer type here. It makes sense to convert
476 // it instead of throwing an error.
477 switch(columnType.getSdbcType())
478 {
479 case DataType::SMALLINT:
480 setValue< sal_Int16 >(nIndex,
481 static_cast<sal_Int16>(nValue),
482 dType);
483 break;
484 case DataType::INTEGER:
485 setValue< sal_Int32 >(nIndex,
486 static_cast<sal_Int32>(nValue),
487 dType);
488 break;
489 case DataType::BIGINT:
490 setValue< sal_Int64 >(nIndex,
491 static_cast<sal_Int64>(nValue),
492 dType);
493 break;
494 case DataType::NUMERIC:
495 case DataType::DECIMAL:
496 // take decimal places into account, later on they are removed in makeNumericString
497 setObjectWithInfo(nIndex,Any{nValue}, columnType.getSdbcType(), columnType.getScale());
498 break;
499 default:
500 setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT?
501 }
502}
503
504void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
505{
506 struct tm aCTime;
507 aCTime.tm_mday = rDate.Day;
508 aCTime.tm_mon = rDate.Month -1;
509 aCTime.tm_year = rDate.Year -1900;
510
511 ISC_DATE aISCDate;
512 isc_encode_sql_date(&aCTime, &aISCDate);
513
514 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
515}
516
517void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
518{
519 struct tm aCTime;
520 aCTime.tm_sec = rTime.Seconds;
521 aCTime.tm_min = rTime.Minutes;
522 aCTime.tm_hour = rTime.Hours;
523
524 ISC_TIME aISCTime;
525 isc_encode_sql_time(&aCTime, &aISCTime);
526
527 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
528 // other funkiness, so we can simply add the fraction of a second.
529 aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
530
531 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
532}
533
534void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
535{
536 struct tm aCTime;
537 aCTime.tm_sec = rTimestamp.Seconds;
538 aCTime.tm_min = rTimestamp.Minutes;
539 aCTime.tm_hour = rTimestamp.Hours;
540 aCTime.tm_mday = rTimestamp.Day;
541 aCTime.tm_mon = rTimestamp.Month - 1;
542 aCTime.tm_year = rTimestamp.Year - 1900;
543
544 ISC_TIMESTAMP aISCTimestamp;
545 isc_encode_timestamp(&aCTime, &aISCTimestamp);
546
547 // As in previous function
548 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
549
550 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
551}
552
553
554// void OPreparedStatement::set
555void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
556{
557 ISC_STATUS aErr;
558
559 aErr = isc_create_blob2(m_statusVector,
560 &m_pConnection->getDBHandle(),
561 &m_pConnection->getTransaction(),
562 &rBlobHandle,
563 &rBlobId,
564 0, // Blob parameter buffer length
565 nullptr); // Blob parameter buffer handle
566
567 if (aErr)
568 {
570 Concat2View("setBlob failed on " + m_sSqlStatement),
571 *this);
572 assert(false);
573 }
574}
575
576void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
577{
578 ISC_STATUS aErr;
579
580 aErr = isc_close_blob(m_statusVector,
581 &rBlobHandle);
582 if (aErr)
583 {
585 u"isc_close_blob failed",
586 *this);
587 assert(false);
588 }
589}
590
591void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
592{
593 ::osl::MutexGuard aGuard( m_aMutex );
594 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
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
606 // Max segment size is 2^16 == SAL_MAX_UINT16
607 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
608 // TODO apply max segment size to character encoding
609 sal_Int64 nCharWritten = 1; // XClob is indexed from 1
610 ISC_STATUS aErr = 0;
611 sal_Int64 nLen = xClob->length();
612 while ( nLen >= nCharWritten )
613 {
614 sal_Int64 nCharRemain = nLen - nCharWritten + 1;
615 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
616 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
617 OString sData = OUStringToOString(
618 xClob->getSubString(nCharWritten, nWriteSize),
619 RTL_TEXTENCODING_UTF8);
620 aErr = isc_put_segment( m_statusVector,
621 &aBlobHandle,
622 sData.getLength(),
623 sData.getStr() );
624 nCharWritten += nWriteSize;
625
626 if (aErr)
627 break;
628 }
629
630 // We need to make sure we close the Blob even if there are errors, hence evaluate
631 // errors after closing.
632 closeBlobAfterWriting(aBlobHandle);
633
634 if (aErr)
635 {
637 u"isc_put_segment failed",
638 *this);
639 assert(false);
640 }
641
642 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
643}
644
645void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
646{
647 ::osl::MutexGuard aGuard( m_aMutex );
648 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
649 checkParameterIndex(nParameterIndex);
650
651#if SAL_TYPES_SIZEOFPOINTER == 8
652 isc_blob_handle aBlobHandle = 0;
653#else
654 isc_blob_handle aBlobHandle = nullptr;
655#endif
656 ISC_QUAD aBlobId;
657
658 openBlobForWriting(aBlobHandle, aBlobId);
659
660 OString sData = OUStringToOString(
661 rStr,
662 RTL_TEXTENCODING_UTF8);
663 size_t nDataSize = sData.getLength();
664 ISC_STATUS aErr = 0;
665 // we can't store more than MAX_SIZE_SEGMENT in a segment
666 if (nDataSize <= MAX_SIZE_SEGMENT)
667 {
668 aErr = isc_put_segment( m_statusVector,
669 &aBlobHandle,
670 sData.getLength(),
671 sData.getStr() );
672 }
673 else
674 {
675 // if we need more, let's split the input and first let's calculate the nb of entire chunks needed
676 size_t nNbEntireChunks = nDataSize / MAX_SIZE_SEGMENT;
677 for (size_t i = 0; i < nNbEntireChunks; ++i)
678 {
679 OString strCurrentChunk = sData.copy(i * MAX_SIZE_SEGMENT, MAX_SIZE_SEGMENT);
680 aErr = isc_put_segment( m_statusVector,
681 &aBlobHandle,
682 strCurrentChunk.getLength(),
683 strCurrentChunk.getStr() );
684 if (aErr)
685 break;
686 }
687 size_t nRemainingBytes = nDataSize - (nNbEntireChunks * MAX_SIZE_SEGMENT);
688 if (nRemainingBytes && !aErr)
689 {
690 // then copy the remaining
691 OString strCurrentChunk = sData.copy(nNbEntireChunks * MAX_SIZE_SEGMENT, nRemainingBytes);
692 aErr = isc_put_segment( m_statusVector,
693 &aBlobHandle,
694 strCurrentChunk.getLength(),
695 strCurrentChunk.getStr() );
696 }
697 }
698
699 // We need to make sure we close the Blob even if there are errors, hence evaluate
700 // errors after closing.
701 closeBlobAfterWriting(aBlobHandle);
702
703 if (aErr)
704 {
706 u"isc_put_segment failed",
707 *this);
708 assert(false);
709 }
710
711 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
712}
713
714void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
715 const Reference< XBlob >& xBlob)
716{
717 ::osl::MutexGuard aGuard(m_aMutex);
718 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
719 checkParameterIndex(nParameterIndex);
720
721#if SAL_TYPES_SIZEOFPOINTER == 8
722 isc_blob_handle aBlobHandle = 0;
723#else
724 isc_blob_handle aBlobHandle = nullptr;
725#endif
726 ISC_QUAD aBlobId;
727
728 openBlobForWriting(aBlobHandle, aBlobId);
729
730 ISC_STATUS aErr = 0;
731 const sal_Int64 nBlobLen = xBlob->length();
732 if (nBlobLen > 0)
733 {
734 // Max write size is 0xFFFF == SAL_MAX_UINT16
735 sal_uInt64 nDataWritten = 0;
736 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
737 {
738 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
739 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
740 aErr = isc_put_segment(m_statusVector,
741 &aBlobHandle,
742 nWriteSize,
743 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
744 nDataWritten += nWriteSize;
745
746 if (aErr)
747 break;
748 }
749 }
750
751 // We need to make sure we close the Blob even if there are errors, hence evaluate
752 // errors after closing.
753 closeBlobAfterWriting(aBlobHandle);
754
755 if (aErr)
756 {
758 u"isc_put_segment failed",
759 *this);
760 assert(false);
761 }
762
763 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
764}
765
766
767void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
768{
769 ::osl::MutexGuard aGuard( m_aMutex );
770 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
771 checkParameterIndex(nIndex);
772}
773
774
775void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
776{
777 ::osl::MutexGuard aGuard( m_aMutex );
778 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
779 checkParameterIndex(nIndex);
780}
781
782
783void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
784{
785 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
786 ::osl::MutexGuard aGuard( m_aMutex );
788
789 checkParameterIndex(parameterIndex);
790 setParameterNull(parameterIndex, false);
791
792 XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1);
793 int dType = (pVar->sqltype & ~1); // drop null flag
794
795 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
796 {
797 double dbValue =0.0;
798 OUString sValue;
799 if( x >>= dbValue )
800 {
801 // truncate and round to 'scale' number of decimal places
802 sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) );
803 }
804 else
805 {
806 x >>= sValue;
807 }
808
809 // fill in the number with nulls in fractional part.
810 // We need this because e.g. 0.450 != 0.045 despite
811 // their scale is equal
812 OUStringBuffer sBuffer(15);
813 sBuffer.append(sValue);
814 if(sValue.indexOf('.') != -1) // there is a dot
815 {
816 for(sal_Int32 i=sValue.subView(sValue.indexOf('.')+1).size(); i<scale;i++)
817 {
818 sBuffer.append('0');
819 }
820 }
821 else
822 {
823 for (sal_Int32 i=0; i<scale; i++)
824 {
825 sBuffer.append('0');
826 }
827 }
828
829 sValue = sBuffer.makeStringAndClear();
830 switch(dType)
831 {
832 case SQL_SHORT:
833 setValue< sal_Int16 >(parameterIndex,
834 static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ),
835 dType);
836 break;
837 case SQL_LONG:
838 case SQL_DOUBLE:
839 setValue< sal_Int32 >(parameterIndex,
840 static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ),
841 dType);
842 break;
843 case SQL_INT64:
844 setValue< sal_Int64 >(parameterIndex,
845 toNumericWithoutDecimalPlace(sValue),
846 dType);
847 break;
848 default:
849 SAL_WARN("connectivity.firebird",
850 "No Firebird sql type found for numeric or decimal types");
851 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
852 }
853 }
854 else
855 {
856 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
857 }
858
859}
860
861
862void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
863{
864 ::osl::MutexGuard aGuard( m_aMutex );
865 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
867}
868
869
870void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
871{
872 ::osl::MutexGuard aGuard( m_aMutex );
873 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
874 checkParameterIndex(nIndex);
875}
876
877void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
878 const Sequence< sal_Int8 >& xBytes)
879{
880 ::osl::MutexGuard aGuard(m_aMutex);
881 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
882 checkParameterIndex(nParameterIndex);
883
884 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
885 int dType = (pVar->sqltype & ~1); // drop flag bit for now
886
887 if( dType == SQL_BLOB )
888 {
889#if SAL_TYPES_SIZEOFPOINTER == 8
890 isc_blob_handle aBlobHandle = 0;
891#else
892 isc_blob_handle aBlobHandle = nullptr;
893#endif
894 ISC_QUAD aBlobId;
895
896 openBlobForWriting(aBlobHandle, aBlobId);
897
898 ISC_STATUS aErr = 0;
899 const sal_Int32 nBytesLen = xBytes.getLength();
900 if (nBytesLen > 0)
901 {
902 // Max write size is 0xFFFF == SAL_MAX_UINT16
903 sal_uInt32 nDataWritten = 0;
904 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
905 {
906 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
907 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
908 aErr = isc_put_segment(m_statusVector,
909 &aBlobHandle,
910 nWriteSize,
911 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
912 nDataWritten += nWriteSize;
913
914 if (aErr)
915 break;
916 }
917 }
918
919 // We need to make sure we close the Blob even if there are errors, hence evaluate
920 // errors after closing.
921 closeBlobAfterWriting(aBlobHandle);
922
923 if (aErr)
924 {
926 u"isc_put_segment failed",
927 *this);
928 assert(false);
929 }
930
931 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
932 }
933 else if( dType == SQL_VARYING )
934 {
935 setParameterNull(nParameterIndex, false);
936 const sal_Int32 nMaxSize = 0xFFFF;
937 Sequence<sal_Int8> xBytesCopy(xBytes);
938 if (xBytesCopy.getLength() > nMaxSize)
939 {
940 xBytesCopy.realloc( nMaxSize );
941 }
942 const sal_uInt16 nSize = xBytesCopy.getLength();
943 // 8000 corresponds to value from lcl_addDefaultParameters
944 // in dbaccess/source/filter/hsqldb/createparser.cxx
945 if (nSize > 8000)
946 {
947 free(pVar->sqldata);
948 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
949 }
950 static_assert(sizeof(nSize) == 2, "must match dest memcpy len");
951 // First 2 bytes indicate string size
952 memcpy(pVar->sqldata, &nSize, 2);
953 // Actual data
954 memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize);
955 }
956 else if( dType == SQL_TEXT )
957 {
958 if (pVar->sqllen < xBytes.getLength())
959 dbtools::throwSQLException("Data too big for this field",
961 setParameterNull(nParameterIndex, false);
962 memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
963 // Fill remainder with zeroes
964 memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
965 }
966 else
967 {
969 "Incorrect type for setBytes",
970 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
971 *this);
972 }
973}
974
975
976void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
977{
978 ::osl::MutexGuard aGuard( m_aMutex );
979 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
980 checkParameterIndex(nIndex);
981}
982
983
984void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
985{
986 ::osl::MutexGuard aGuard( m_aMutex );
987 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
988 checkParameterIndex(nIndex);
989}
990
991
993{
994}
995
996// ---- Batch methods -- unsupported -----------------------------------------
998{
999 // Unsupported
1000}
1001
1003{
1004 // Unsupported by firebird
1005}
1006
1008{
1009 // Unsupported by firebird
1010 return Sequence< sal_Int32 >();
1011}
1012
1013void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
1014{
1015 switch(nHandle)
1016 {
1018 break;
1020 break;
1022 break;
1024 break;
1025 default:
1027 }
1028}
1029
1030void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
1031{
1033 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
1034 {
1036 "No column " + OUString::number(nParameterIndex),
1037 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
1038 *this);
1039 }
1040}
1041
1042void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
1043 bool bSetNull)
1044{
1045 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
1046 if (bSetNull)
1047 {
1048 pVar->sqltype |= 1;
1049 *pVar->sqlind = -1;
1050 }
1051 else
1052 *pVar->sqlind = 0;
1053}
1054
1055/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
virtual void SAL_CALL setDate(sal_Int32 parameterIndex, const css::util::Date &x) override
virtual void SAL_CALL setLong(sal_Int32 nIndex, sal_Int64 nValue) override
virtual void SAL_CALL release() noexcept override
virtual void SAL_CALL setCharacterStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream > &x, sal_Int32 length) override
void setParameterNull(sal_Int32 nParameterIndex, bool bSetNull=true)
virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery() override
css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData
virtual void SAL_CALL setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString &typeName) override
virtual void SAL_CALL setTime(sal_Int32 parameterIndex, const css::util::Time &x) override
virtual void SAL_CALL setBoolean(sal_Int32 nIndex, sal_Bool nValue) override
void openBlobForWriting(isc_blob_handle &rBlobHandle, ISC_QUAD &rBlobId)
Assumes that all necessary mutexes have been taken.
void setValue(sal_Int32 nIndex, const T &nValue, ISC_SHORT nType)
Set a numeric value in the input SQLDA.
virtual void SAL_CALL setObject(sal_Int32 parameterIndex, const css::uno::Any &x) override
virtual void SAL_CALL setBytes(sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 > &x) override
virtual void SAL_CALL setDouble(sal_Int32 parameterIndex, double x) override
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
virtual void SAL_CALL setByte(sal_Int32 nIndex, sal_Int8 nValue) override
virtual void SAL_CALL setString(sal_Int32 parameterIndex, const OUString &x) override
virtual void SAL_CALL setArray(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray > &x) override
virtual void SAL_CALL setObjectWithInfo(sal_Int32 parameterIndex, const css::uno::Any &x, sal_Int32 targetSqlType, sal_Int32 scale) override
virtual css::uno::Sequence< sal_Int32 > SAL_CALL executeBatch() override
virtual sal_Int32 SAL_CALL executeUpdate() override
virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection() override
virtual void SAL_CALL setBinaryStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream > &x, sal_Int32 length) override
virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any &rValue) override
void setClob(sal_Int32 nParamIndex, const OUString &rStr)
virtual void SAL_CALL setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime &x) override
virtual void SAL_CALL setInt(sal_Int32 nIndex, sal_Int32 nValue) override
virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData() override
virtual void SAL_CALL setNull(sal_Int32 nIndex, sal_Int32 nValue) override
virtual void SAL_CALL setRef(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef > &x) override
virtual void SAL_CALL setBlob(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob > &x) override
virtual void SAL_CALL setShort(sal_Int32 nIndex, sal_Int16 nValue) override
void closeBlobAfterWriting(isc_blob_handle &rBlobHandle)
Assumes that all necessary mutexes have been taken.
virtual void SAL_CALL setFloat(sal_Int32 parameterIndex, float x) override
virtual void SAL_CALL acquire() noexcept override
This ResultSet does not deal with the management of the SQLDA it is supplied with.
virtual void SAL_CALL release() noexcept override
css::uno::Reference< css::sdbc::XResultSet > m_xResultSet
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
void prepareAndDescribeStatement(std::u16string_view sqlIn, XSQLDA *&pOutSqlda)
virtual void SAL_CALL acquire() noexcept override
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any &rValue) override
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() SAL_OVERRIDE
virtual css::uno::Any SAL_CALL queryInterface(css::uno::Type const &rType) SAL_OVERRIDE
float u
float x
IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement")
constexpr size_t MAX_SIZE_SEGMENT
sal_Int16 nValue
sal_Int32 nIndex
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
css::uno::Sequence< T > concatSequences(const css::uno::Sequence< T > &rS1, const Ss &... rSn)
Type
void freeSQLVAR(XSQLDA *pSqlda)
void checkDisposed(bool _bThrow)
Definition: Driver.cxx:208
OUString StatusVectorToString(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view rCause)
sal_Int64 pow10Integer(int nDecimalCount)
void evaluateStatusVector(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view aCause, const css::uno::Reference< css::uno::XInterface > &_rxContext)
Evaluate a firebird status vector and throw exceptions as necessary.
void mallocSQLVAR(XSQLDA *pSqlda)
void setObjectWithInfo(const Reference< XParameters > &_xParams, sal_Int32 parameterIndex, const Any &x, sal_Int32 sqlType, sal_Int32 scale)
Definition: dbtools.cxx:1765
void throwSQLException(const OUString &_rMessage, const OUString &_rSQLState, const Reference< XInterface > &_rxContext, const sal_Int32 _nErrorCode)
int i
sal_Int64 toInt64(std::u16string_view str, sal_Int16 radix=10)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
Definition: pq_tools.cxx:100
sal_Int32 scale
Definition: pq_statics.cxx:62
#define PROPERTY_ID_RESULTSETTYPE
Definition: propertyids.hxx:44
#define PROPERTY_ID_USEBOOKMARKS
Definition: propertyids.hxx:48
#define PROPERTY_ID_RESULTSETCONCURRENCY
Definition: propertyids.hxx:43
#define PROPERTY_ID_FETCHDIRECTION
Definition: propertyids.hxx:45
QPRO_FUNC_TYPE nType
TransliterationModules tm
#define SAL_MAX_UINT16
unsigned char sal_Bool
signed char sal_Int8
sal_Int32 nLength