1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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>
23 #include "Connection.hxx"
24 #include "PreparedStatement.hxx"
25 #include "ResultSet.hxx"
26 #include "ResultSetMetaData.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
,
58 :OStatementCommonBase(_pConnection
)
63 SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
67 void OPreparedStatement::ensurePrepared()
69 MutexGuard
aGuard(m_aMutex
);
70 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
72 if (m_aStatementHandle
)
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
,
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
;
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
,
108 SAL_WARN_IF(aErr
, "connectivity.firebird", "isc_dsql_describe_bind failed");
112 mallocSQLVAR(m_pInSqlda
);
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
);
135 aRet
= OPreparedStatement_Base::queryInterface(rType
);
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
);
151 if(!m_xMetaData
.is())
152 m_xMetaData
= new OResultSetMetaData(m_pConnection
.get()
158 void SAL_CALL
OPreparedStatement::close()
160 MutexGuard
aGuard( m_aMutex
);
161 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
163 OStatementCommonBase::close();
166 freeSQLVAR(m_pInSqlda
);
168 m_pInSqlda
= nullptr;
172 freeSQLVAR(m_pOutSqlda
);
174 m_pOutSqlda
= nullptr;
178 void SAL_CALL
OPreparedStatement::disposing()
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
);
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
:
203 OString str
= OUStringToOString(sInput
, RTL_TEXTENCODING_UTF8
);
204 const ISC_SHORT nLength
= std::min(str
.getLength(), static_cast<sal_Int32
>(pVar
->sqllen
));
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);
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
);
223 setClob(nParameterIndex
, sInput
);
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
,
236 setShort(nParameterIndex
, int32Value
);
239 case DataType::INTEGER
:
241 sal_Int32 int32Value
= sInput
.toInt32();
242 setInt(nParameterIndex
, int32Value
);
245 case DataType::BIGINT
:
247 sal_Int64 int64Value
= sInput
.toInt64();
248 setLong(nParameterIndex
, int64Value
);
251 case DataType::FLOAT
:
253 float floatValue
= sInput
.toFloat();
254 setFloat(nParameterIndex
, floatValue
);
257 case DataType::DOUBLE
:
258 setDouble(nParameterIndex
, sInput
.toDouble());
260 case DataType::NUMERIC
:
261 case DataType::DECIMAL
:
262 return setObjectWithInfo(nParameterIndex
, Any
{ sInput
}, sdbcType
, 0);
264 case DataType::BOOLEAN
:
266 bool boolValue
= sInput
.toBoolean();
267 setBoolean(nParameterIndex
, boolValue
);
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;
277 ::dbtools::throwSQLException(
278 u
"Incorrect type for setString"_ustr
,
279 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
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
);
304 if (m_xResultSet
.is()) // Checks whether we have already run the statement.
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
,
316 // Do not throw error. Trying to close a closed cursor is not a
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(),
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(),
337 uno::Reference
< XInterface
>(*this),
341 return m_xResultSet
.is();
342 // TODO: implement handling of multiple ResultSets.
345 sal_Int32 SAL_CALL
OPreparedStatement::executeUpdate()
348 return getStatementChangeCount();
351 Reference
< XResultSet
> SAL_CALL
OPreparedStatement::executeQuery()
359 sal_Int64
toPowOf10AndRound(double n
, int powOf10
)
361 static constexpr sal_Int64 powers
[] = {
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
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
)
410 else if (!rtl::isAsciiDigit(num
[end
]))
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);
425 const sal_Int32 n
= std::min(buffer
.getLength(), -scale
);
426 buffer
.truncate(buffer
.getLength() - n
);
430 for (sal_Int32 i
= 0; i
< scale
; ++i
)
433 return OUString::unacquired(buffer
).toInt64();
436 double toDouble(const Any
& x
)
438 if (double value
= 0; x
>>= value
)
446 //----- XParameters -----------------------------------------------------------
447 void SAL_CALL
OPreparedStatement::setNull(sal_Int32 nIndex
, sal_Int32
/*nSqlType*/)
449 MutexGuard
aGuard( m_aMutex
);
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
);
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
,
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
);
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
);
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
);
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
,
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
)
574 aCTime
.tm_mday
= rDate
.Day
;
575 aCTime
.tm_mon
= rDate
.Month
-1;
576 aCTime
.tm_year
= rDate
.Year
-1900;
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
)
587 aCTime
.tm_sec
= rTime
.Seconds
;
588 aCTime
.tm_min
= rTime
.Minutes
;
589 aCTime
.tm_hour
= rTime
.Hours
;
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
)
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
)
626 aErr
= isc_create_blob2(m_statusVector
,
627 &m_pConnection
->getDBHandle(),
628 &m_pConnection
->getTransaction(),
631 0, // Blob parameter buffer length
632 nullptr); // Blob parameter buffer handle
636 evaluateStatusVector(m_statusVector
,
637 Concat2View("setBlob failed on " + m_sSqlStatement
),
643 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle
& rBlobHandle
)
647 aErr
= isc_close_blob(m_statusVector
,
651 evaluateStatusVector(m_statusVector
,
652 u
"isc_close_blob failed",
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
{};
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
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
,
688 nCharWritten
+= nWriteSize
;
694 // We need to make sure we close the Blob even if there are errors, hence evaluate
695 // errors after closing.
696 closeBlobAfterWriting(aBlobHandle
);
700 evaluateStatusVector(m_statusVector
,
701 u
"isc_put_segment failed",
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
{};
719 openBlobForWriting(aBlobHandle
, aBlobId
);
721 OString sData
= OUStringToOString(
723 RTL_TEXTENCODING_UTF8
);
724 size_t nDataSize
= sData
.getLength();
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
,
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
,
743 strCurrentChunk
.getLength(),
744 strCurrentChunk
.getStr() );
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
,
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
);
766 evaluateStatusVector(m_statusVector
,
767 u
"isc_put_segment failed",
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
{};
786 openBlobForWriting(aBlobHandle
, aBlobId
);
789 const sal_Int64 nBlobLen
= xBlob
->length();
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
,
801 reinterpret_cast<const char*>(xBlob
->getBytes(nDataWritten
, nWriteSize
).getConstArray()));
802 nDataWritten
+= nWriteSize
;
809 // We need to make sure we close the Blob even if there are errors, hence evaluate
810 // errors after closing.
811 closeBlobAfterWriting(aBlobHandle
);
815 evaluateStatusVector(m_statusVector
,
816 u
"isc_put_segment failed",
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
);
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
)
860 static_cast<sal_Int16
>(toNumericWithoutDecimalPlace(x
, columnType
.getScale())),
865 static_cast<sal_Int32
>(toNumericWithoutDecimalPlace(x
, columnType
.getScale())),
868 return setValue(parameterIndex
,
869 toNumericWithoutDecimalPlace(x
, columnType
.getScale()), dType
);
871 return setValue(parameterIndex
, toDouble(x
), dType
);
873 SAL_WARN("connectivity.firebird",
874 "No Firebird sql type found for numeric or decimal types");
875 ::dbtools::setObjectWithInfo(this,parameterIndex
,x
,sqlType
,scale
);
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
{};
917 openBlobForWriting(aBlobHandle
, aBlobId
);
920 const sal_Int32 nBytesLen
= xBytes
.getLength();
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
,
932 reinterpret_cast<const char*>(xBytes
.getConstArray()) + nDataWritten
);
933 nDataWritten
+= nWriteSize
;
940 // We need to make sure we close the Blob even if there are errors, hence evaluate
941 // errors after closing.
942 closeBlobAfterWriting(aBlobHandle
);
946 evaluateStatusVector(m_statusVector
,
947 u
"isc_put_segment failed",
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
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);
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());
984 ::dbtools::throwSQLException(
985 u
"Incorrect type for setBytes"_ustr
,
986 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
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()
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
)
1033 case PROPERTY_ID_RESULTSETCONCURRENCY
:
1035 case PROPERTY_ID_RESULTSETTYPE
:
1037 case PROPERTY_ID_FETCHDIRECTION
:
1039 case PROPERTY_ID_USEBOOKMARKS
:
1042 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle
,rValue
);
1046 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex
)
1049 if ((nParameterIndex
== 0) || (nParameterIndex
> m_pInSqlda
->sqld
))
1051 ::dbtools::throwSQLException(
1052 "No column " + OUString::number(nParameterIndex
),
1053 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND
,
1058 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex
,
1061 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
1071 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */