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
,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
53 constexpr size_t MAX_SIZE_SEGMENT
= 65535; // max value of a segment of CLOB, if we want more than 65535 bytes, we need more segments
56 OPreparedStatement::OPreparedStatement( Connection
* _pConnection
,
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 m_pInSqlda
->version
= SQLDA_VERSION1
;
81 m_pInSqlda
->sqln
= 10;
84 prepareAndDescribeStatement(m_sSqlStatement
, m_pOutSqlda
);
86 aErr
= isc_dsql_describe_bind(m_statusVector
,
93 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
95 else if (m_pInSqlda
->sqld
> m_pInSqlda
->sqln
) // Not large enough
97 short nItems
= m_pInSqlda
->sqld
;
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
,
106 SAL_WARN_IF(aErr
, "connectivity.firebird", "isc_dsql_describe_bind failed");
110 mallocSQLVAR(m_pInSqlda
);
112 evaluateStatusVector(m_statusVector
, m_sSqlStatement
, *this);
115 OPreparedStatement::~OPreparedStatement()
119 void SAL_CALL
OPreparedStatement::acquire() noexcept
121 OStatementCommonBase::acquire();
124 void SAL_CALL
OPreparedStatement::release() noexcept
126 OStatementCommonBase::release();
129 Any SAL_CALL
OPreparedStatement::queryInterface(const Type
& rType
)
131 Any aRet
= OStatementCommonBase::queryInterface(rType
);
133 aRet
= OPreparedStatement_Base::queryInterface(rType
);
137 uno::Sequence
< Type
> SAL_CALL
OPreparedStatement::getTypes()
139 return concatSequences(OPreparedStatement_Base::getTypes(),
140 OStatementCommonBase::getTypes());
143 Reference
< XResultSetMetaData
> SAL_CALL
OPreparedStatement::getMetaData()
145 ::osl::MutexGuard
aGuard( m_aMutex
);
146 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
149 if(!m_xMetaData
.is())
150 m_xMetaData
= new OResultSetMetaData(m_pConnection
.get()
156 void SAL_CALL
OPreparedStatement::close()
158 MutexGuard
aGuard( m_aMutex
);
159 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
161 OStatementCommonBase::close();
164 freeSQLVAR(m_pInSqlda
);
166 m_pInSqlda
= nullptr;
170 freeSQLVAR(m_pOutSqlda
);
172 m_pOutSqlda
= nullptr;
176 void SAL_CALL
OPreparedStatement::disposing()
181 void SAL_CALL
OPreparedStatement::setString(sal_Int32 nParameterIndex
,
182 const OUString
& sInput
)
184 SAL_INFO("connectivity.firebird",
185 "setString(" << nParameterIndex
<< " , " << sInput
<< ")");
187 MutexGuard
aGuard( m_aMutex
);
188 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
191 checkParameterIndex(nParameterIndex
);
192 setParameterNull(nParameterIndex
, false);
194 OString str
= OUStringToOString(sInput
, RTL_TEXTENCODING_UTF8
);
196 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
198 int dtype
= (pVar
->sqltype
& ~1); // drop flag bit for now
200 if (str
.getLength() > pVar
->sqllen
)
201 str
= str
.copy(0, pVar
->sqllen
);
206 const sal_Int32 max_varchar_len
= 0xFFFF;
207 // First 2 bytes indicate string size
208 if (str
.getLength() > max_varchar_len
)
210 str
= str
.copy(0, max_varchar_len
);
212 const sal_uInt16 nLength
= str
.getLength();
213 static_assert(sizeof(nLength
) == 2, "must match dest memcpy len");
214 memcpy(pVar
->sqldata
, &nLength
, 2);
216 memcpy(pVar
->sqldata
+ 2, str
.getStr(), str
.getLength());
220 memcpy(pVar
->sqldata
, str
.getStr(), str
.getLength());
221 // Fill remainder with spaces
222 memset(pVar
->sqldata
+ str
.getLength(), ' ', pVar
->sqllen
- str
.getLength());
224 case SQL_BLOB
: // Clob
225 assert( pVar
->sqlsubtype
== static_cast<short>(BlobSubtype::Clob
) );
226 setClob(nParameterIndex
, sInput
);
230 sal_Int32 int32Value
= sInput
.toInt32();
231 if ( (int32Value
< std::numeric_limits
<sal_Int16
>::min()) ||
232 (int32Value
> std::numeric_limits
<sal_Int16
>::max()) )
234 ::dbtools::throwSQLException(
235 "Value out of range for SQL_SHORT type",
236 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
239 setShort(nParameterIndex
, int32Value
);
244 sal_Int32 int32Value
= sInput
.toInt32();
245 setInt(nParameterIndex
, int32Value
);
250 sal_Int64 int64Value
= sInput
.toInt64();
251 setLong(nParameterIndex
, int64Value
);
256 float floatValue
= sInput
.toFloat();
257 setFloat(nParameterIndex
, floatValue
);
262 bool boolValue
= sInput
.toBoolean();
263 setBoolean(nParameterIndex
, boolValue
);
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;
273 ::dbtools::throwSQLException(
274 "Incorrect type for setString",
275 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
280 Reference
< XConnection
> SAL_CALL
OPreparedStatement::getConnection()
282 MutexGuard
aGuard( m_aMutex
);
283 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
285 return m_pConnection
;
288 sal_Bool SAL_CALL
OPreparedStatement::execute()
290 SAL_INFO("connectivity.firebird", "executeQuery(). "
291 "Got called with sql: " << m_sSqlStatement
);
293 MutexGuard
aGuard( m_aMutex
);
294 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
300 if (m_xResultSet
.is()) // Checks whether we have already run the statement.
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
,
312 // Do not throw error. Trying to close a closed cursor is not a
314 OUString sErrMsg
= StatusVectorToString(m_statusVector
,
315 u
"isc_dsql_free_statement: close cursor");
316 SAL_WARN("connectivity.firebird", sErrMsg
);
320 aErr
= isc_dsql_execute(m_statusVector
,
321 &m_pConnection
->getTransaction(),
327 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
328 evaluateStatusVector(m_statusVector
, u
"isc_dsql_execute", *this);
331 m_xResultSet
= new OResultSet(m_pConnection
.get(),
333 uno::Reference
< XInterface
>(*this),
337 if (getStatementChangeCount() > 0)
338 m_pConnection
->notifyDatabaseModified();
340 return m_xResultSet
.is();
341 // TODO: implement handling of multiple ResultSets.
344 sal_Int32 SAL_CALL
OPreparedStatement::executeUpdate()
347 return getStatementChangeCount();
350 Reference
< XResultSet
> SAL_CALL
OPreparedStatement::executeQuery()
359 * Take out the number part of a fix point decimal without
360 * the information of where is the fractional part from a
361 * string representation of a number. (e.g. 54.654 -> 54654)
363 sal_Int64
toNumericWithoutDecimalPlace(const OUString
& sSource
)
365 OUString
sNumber(sSource
);
367 // cut off leading 0 eventually ( eg. 0.567 -> .567)
368 (void)sSource
.startsWith("0", &sNumber
);
370 sal_Int32 nDotIndex
= sNumber
.indexOf('.');
374 return sNumber
.toInt64(); // no dot -> it's an integer
379 OUStringBuffer
sBuffer(15);
382 sBuffer
.append(sNumber
.subView(0, nDotIndex
));
384 sBuffer
.append(sNumber
.subView(nDotIndex
+ 1));
385 return o3tl::toInt64(sBuffer
);
391 //----- XParameters -----------------------------------------------------------
392 void SAL_CALL
OPreparedStatement::setNull(sal_Int32 nIndex
, sal_Int32
/*nSqlType*/)
394 MutexGuard
aGuard( m_aMutex
);
395 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
398 checkParameterIndex(nIndex
);
399 setParameterNull(nIndex
);
402 void SAL_CALL
OPreparedStatement::setBoolean(sal_Int32 nIndex
, sal_Bool bValue
)
404 setValue
< sal_Bool
>(nIndex
, bValue
, SQL_BOOLEAN
);
407 template <typename T
>
408 void OPreparedStatement::setValue(sal_Int32 nIndex
, const T
& nValue
, ISC_SHORT nType
)
410 MutexGuard
aGuard( m_aMutex
);
411 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
414 checkParameterIndex(nIndex
);
415 setParameterNull(nIndex
, false);
417 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nIndex
- 1);
419 if ((pVar
->sqltype
& ~1) != nType
)
421 ::dbtools::throwSQLException(
422 "Incorrect type for setValue",
423 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
427 memcpy(pVar
->sqldata
, &nValue
, sizeof(nValue
));
430 void SAL_CALL
OPreparedStatement::setByte(sal_Int32 nIndex
, sal_Int8 nValue
)
432 // there's no TINYINT or equivalent on Firebird,
433 // so do the same as setShort
434 setValue
< sal_Int16
>(nIndex
, nValue
, SQL_SHORT
);
437 void SAL_CALL
OPreparedStatement::setShort(sal_Int32 nIndex
, sal_Int16 nValue
)
439 setValue
< sal_Int16
>(nIndex
, nValue
, SQL_SHORT
);
442 void SAL_CALL
OPreparedStatement::setInt(sal_Int32 nIndex
, sal_Int32 nValue
)
444 setValue
< sal_Int32
>(nIndex
, nValue
, SQL_LONG
);
447 void SAL_CALL
OPreparedStatement::setLong(sal_Int32 nIndex
, sal_Int64 nValue
)
449 setValue
< sal_Int64
>(nIndex
, nValue
, SQL_INT64
);
452 void SAL_CALL
OPreparedStatement::setFloat(sal_Int32 nIndex
, float nValue
)
454 setValue
< float >(nIndex
, nValue
, SQL_FLOAT
);
457 void SAL_CALL
OPreparedStatement::setDouble(sal_Int32 nIndex
, double nValue
)
459 MutexGuard
aGuard( m_aMutex
);
460 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
463 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nIndex
- 1);
464 short dType
= (pVar
->sqltype
& ~1); // drop flag bit for now
465 short dSubType
= pVar
->sqlsubtype
;
466 // Assume it is a sub type of a number.
467 if(dSubType
< 0 || dSubType
> 2)
469 ::dbtools::throwSQLException(
470 "Incorrect number sub type",
471 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
474 // firebird stores scale as a negative number
475 ColumnTypeInfo columnType
{ dType
, dSubType
,
476 static_cast<short>(-pVar
->sqlscale
) };
478 // Caller might try to set an integer type here. It makes sense to convert
479 // it instead of throwing an error.
480 switch(columnType
.getSdbcType())
482 case DataType::SMALLINT
:
483 setValue
< sal_Int16
>(nIndex
,
484 static_cast<sal_Int16
>(nValue
),
487 case DataType::INTEGER
:
488 setValue
< sal_Int32
>(nIndex
,
489 static_cast<sal_Int32
>(nValue
),
492 case DataType::BIGINT
:
493 setValue
< sal_Int64
>(nIndex
,
494 static_cast<sal_Int64
>(nValue
),
497 case DataType::NUMERIC
:
498 case DataType::DECIMAL
:
499 // take decimal places into account, later on they are removed in makeNumericString
500 setObjectWithInfo(nIndex
,Any
{nValue
}, columnType
.getSdbcType(), columnType
.getScale());
503 setValue
< double >(nIndex
, nValue
, SQL_DOUBLE
); // TODO: SQL_D_FLOAT?
507 void SAL_CALL
OPreparedStatement::setDate(sal_Int32 nIndex
, const Date
& rDate
)
510 aCTime
.tm_mday
= rDate
.Day
;
511 aCTime
.tm_mon
= rDate
.Month
-1;
512 aCTime
.tm_year
= rDate
.Year
-1900;
515 isc_encode_sql_date(&aCTime
, &aISCDate
);
517 setValue
< ISC_DATE
>(nIndex
, aISCDate
, SQL_TYPE_DATE
);
520 void SAL_CALL
OPreparedStatement::setTime( sal_Int32 nIndex
, const css::util::Time
& rTime
)
523 aCTime
.tm_sec
= rTime
.Seconds
;
524 aCTime
.tm_min
= rTime
.Minutes
;
525 aCTime
.tm_hour
= rTime
.Hours
;
528 isc_encode_sql_time(&aCTime
, &aISCTime
);
530 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
531 // other funkiness, so we can simply add the fraction of a second.
532 aISCTime
+= rTime
.NanoSeconds
/ (1000000000 / ISC_TIME_SECONDS_PRECISION
);
534 setValue
< ISC_TIME
>(nIndex
, aISCTime
, SQL_TYPE_TIME
);
537 void SAL_CALL
OPreparedStatement::setTimestamp(sal_Int32 nIndex
, const DateTime
& rTimestamp
)
540 aCTime
.tm_sec
= rTimestamp
.Seconds
;
541 aCTime
.tm_min
= rTimestamp
.Minutes
;
542 aCTime
.tm_hour
= rTimestamp
.Hours
;
543 aCTime
.tm_mday
= rTimestamp
.Day
;
544 aCTime
.tm_mon
= rTimestamp
.Month
- 1;
545 aCTime
.tm_year
= rTimestamp
.Year
- 1900;
547 ISC_TIMESTAMP aISCTimestamp
;
548 isc_encode_timestamp(&aCTime
, &aISCTimestamp
);
550 // As in previous function
551 aISCTimestamp
.timestamp_time
+= rTimestamp
.NanoSeconds
/ (1000000000 / ISC_TIME_SECONDS_PRECISION
);
553 setValue
< ISC_TIMESTAMP
>(nIndex
, aISCTimestamp
, SQL_TIMESTAMP
);
557 // void OPreparedStatement::set
558 void OPreparedStatement::openBlobForWriting(isc_blob_handle
& rBlobHandle
, ISC_QUAD
& rBlobId
)
562 aErr
= isc_create_blob2(m_statusVector
,
563 &m_pConnection
->getDBHandle(),
564 &m_pConnection
->getTransaction(),
567 0, // Blob parameter buffer length
568 nullptr); // Blob parameter buffer handle
572 evaluateStatusVector(m_statusVector
,
573 Concat2View("setBlob failed on " + m_sSqlStatement
),
579 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle
& rBlobHandle
)
583 aErr
= isc_close_blob(m_statusVector
,
587 evaluateStatusVector(m_statusVector
,
588 u
"isc_close_blob failed",
594 void SAL_CALL
OPreparedStatement::setClob(sal_Int32 nParameterIndex
, const Reference
< XClob
>& xClob
)
596 ::osl::MutexGuard
aGuard( m_aMutex
);
597 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
599 #if SAL_TYPES_SIZEOFPOINTER == 8
600 isc_blob_handle aBlobHandle
= 0;
602 isc_blob_handle aBlobHandle
= nullptr;
606 openBlobForWriting(aBlobHandle
, aBlobId
);
609 // Max segment size is 2^16 == SAL_MAX_UINT16
610 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
611 // TODO apply max segment size to character encoding
612 sal_Int64 nCharWritten
= 1; // XClob is indexed from 1
614 sal_Int64 nLen
= xClob
->length();
615 while ( nLen
>= nCharWritten
)
617 sal_Int64 nCharRemain
= nLen
- nCharWritten
+ 1;
618 constexpr sal_uInt16 MAX_SIZE
= SAL_MAX_UINT16
/ 4;
619 sal_uInt16 nWriteSize
= std::min
<sal_Int64
>(nCharRemain
, MAX_SIZE
);
620 OString sData
= OUStringToOString(
621 xClob
->getSubString(nCharWritten
, nWriteSize
),
622 RTL_TEXTENCODING_UTF8
);
623 aErr
= isc_put_segment( m_statusVector
,
627 nCharWritten
+= nWriteSize
;
633 // We need to make sure we close the Blob even if there are errors, hence evaluate
634 // errors after closing.
635 closeBlobAfterWriting(aBlobHandle
);
639 evaluateStatusVector(m_statusVector
,
640 u
"isc_put_segment failed",
645 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
648 void OPreparedStatement::setClob( sal_Int32 nParameterIndex
, const OUString
& rStr
)
650 ::osl::MutexGuard
aGuard( m_aMutex
);
651 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
652 checkParameterIndex(nParameterIndex
);
654 #if SAL_TYPES_SIZEOFPOINTER == 8
655 isc_blob_handle aBlobHandle
= 0;
657 isc_blob_handle aBlobHandle
= nullptr;
661 openBlobForWriting(aBlobHandle
, aBlobId
);
663 OString sData
= OUStringToOString(
665 RTL_TEXTENCODING_UTF8
);
666 size_t nDataSize
= sData
.getLength();
668 // we can't store more than MAX_SIZE_SEGMENT in a segment
669 if (nDataSize
<= MAX_SIZE_SEGMENT
)
671 aErr
= isc_put_segment( m_statusVector
,
678 // if we need more, let's split the input and first let's calculate the nb of entire chunks needed
679 size_t nNbEntireChunks
= nDataSize
/ MAX_SIZE_SEGMENT
;
680 for (size_t i
= 0; i
< nNbEntireChunks
; ++i
)
682 OString strCurrentChunk
= sData
.copy(i
* MAX_SIZE_SEGMENT
, MAX_SIZE_SEGMENT
);
683 aErr
= isc_put_segment( m_statusVector
,
685 strCurrentChunk
.getLength(),
686 strCurrentChunk
.getStr() );
690 size_t nRemainingBytes
= nDataSize
- (nNbEntireChunks
* MAX_SIZE_SEGMENT
);
691 if (nRemainingBytes
&& !aErr
)
693 // then copy the remaining
694 OString strCurrentChunk
= sData
.copy(nNbEntireChunks
* MAX_SIZE_SEGMENT
, nRemainingBytes
);
695 aErr
= isc_put_segment( m_statusVector
,
697 strCurrentChunk
.getLength(),
698 strCurrentChunk
.getStr() );
702 // We need to make sure we close the Blob even if there are errors, hence evaluate
703 // errors after closing.
704 closeBlobAfterWriting(aBlobHandle
);
708 evaluateStatusVector(m_statusVector
,
709 u
"isc_put_segment failed",
714 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
717 void SAL_CALL
OPreparedStatement::setBlob(sal_Int32 nParameterIndex
,
718 const Reference
< XBlob
>& xBlob
)
720 ::osl::MutexGuard
aGuard(m_aMutex
);
721 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
722 checkParameterIndex(nParameterIndex
);
724 #if SAL_TYPES_SIZEOFPOINTER == 8
725 isc_blob_handle aBlobHandle
= 0;
727 isc_blob_handle aBlobHandle
= nullptr;
731 openBlobForWriting(aBlobHandle
, aBlobId
);
734 const sal_Int64 nBlobLen
= xBlob
->length();
737 // Max write size is 0xFFFF == SAL_MAX_UINT16
738 sal_uInt64 nDataWritten
= 0;
739 while (sal::static_int_cast
<sal_uInt64
>(nBlobLen
) > nDataWritten
)
741 sal_uInt64 nDataRemaining
= nBlobLen
- nDataWritten
;
742 sal_uInt16 nWriteSize
= std::min(nDataRemaining
, sal_uInt64(SAL_MAX_UINT16
));
743 aErr
= isc_put_segment(m_statusVector
,
746 reinterpret_cast<const char*>(xBlob
->getBytes(nDataWritten
, nWriteSize
).getConstArray()));
747 nDataWritten
+= nWriteSize
;
754 // We need to make sure we close the Blob even if there are errors, hence evaluate
755 // errors after closing.
756 closeBlobAfterWriting(aBlobHandle
);
760 evaluateStatusVector(m_statusVector
,
761 u
"isc_put_segment failed",
766 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
770 void SAL_CALL
OPreparedStatement::setArray( sal_Int32 nIndex
, const Reference
< XArray
>& )
772 ::osl::MutexGuard
aGuard( m_aMutex
);
773 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
774 checkParameterIndex(nIndex
);
778 void SAL_CALL
OPreparedStatement::setRef( sal_Int32 nIndex
, const Reference
< XRef
>& )
780 ::osl::MutexGuard
aGuard( m_aMutex
);
781 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
782 checkParameterIndex(nIndex
);
786 void SAL_CALL
OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex
, const Any
& x
, sal_Int32 sqlType
, sal_Int32 scale
)
788 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
789 ::osl::MutexGuard
aGuard( m_aMutex
);
792 checkParameterIndex(parameterIndex
);
793 setParameterNull(parameterIndex
, false);
795 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (parameterIndex
- 1);
796 int dType
= (pVar
->sqltype
& ~1); // drop null flag
798 if(sqlType
== DataType::DECIMAL
|| sqlType
== DataType::NUMERIC
)
804 // truncate and round to 'scale' number of decimal places
805 sValue
= OUString::number( std::floor((dbValue
* pow10Integer(scale
)) + .5) / pow10Integer(scale
) );
812 // fill in the number with nulls in fractional part.
813 // We need this because e.g. 0.450 != 0.045 despite
814 // their scale is equal
815 OUStringBuffer
sBuffer(15);
816 sBuffer
.append(sValue
);
817 if(sValue
.indexOf('.') != -1) // there is a dot
819 for(sal_Int32 i
=sValue
.subView(sValue
.indexOf('.')+1).size(); i
<scale
;i
++)
826 for (sal_Int32 i
=0; i
<scale
; i
++)
832 sValue
= sBuffer
.makeStringAndClear();
836 setValue
< sal_Int16
>(parameterIndex
,
837 static_cast<sal_Int16
>( toNumericWithoutDecimalPlace(sValue
) ),
842 setValue
< sal_Int32
>(parameterIndex
,
843 static_cast<sal_Int32
>( toNumericWithoutDecimalPlace(sValue
) ),
847 setValue
< sal_Int64
>(parameterIndex
,
848 toNumericWithoutDecimalPlace(sValue
),
852 SAL_WARN("connectivity.firebird",
853 "No Firebird sql type found for numeric or decimal types");
854 ::dbtools::setObjectWithInfo(this,parameterIndex
,x
,sqlType
,scale
);
859 ::dbtools::setObjectWithInfo(this,parameterIndex
,x
,sqlType
,scale
);
865 void SAL_CALL
OPreparedStatement::setObjectNull( sal_Int32 nIndex
, sal_Int32
, const OUString
& )
867 ::osl::MutexGuard
aGuard( m_aMutex
);
868 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
869 checkParameterIndex(nIndex
);
873 void SAL_CALL
OPreparedStatement::setObject( sal_Int32 nIndex
, const Any
& )
875 ::osl::MutexGuard
aGuard( m_aMutex
);
876 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
877 checkParameterIndex(nIndex
);
880 void SAL_CALL
OPreparedStatement::setBytes(sal_Int32 nParameterIndex
,
881 const Sequence
< sal_Int8
>& xBytes
)
883 ::osl::MutexGuard
aGuard(m_aMutex
);
884 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
885 checkParameterIndex(nParameterIndex
);
887 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
888 int dType
= (pVar
->sqltype
& ~1); // drop flag bit for now
890 if( dType
== SQL_BLOB
)
892 #if SAL_TYPES_SIZEOFPOINTER == 8
893 isc_blob_handle aBlobHandle
= 0;
895 isc_blob_handle aBlobHandle
= nullptr;
899 openBlobForWriting(aBlobHandle
, aBlobId
);
902 const sal_Int32 nBytesLen
= xBytes
.getLength();
905 // Max write size is 0xFFFF == SAL_MAX_UINT16
906 sal_uInt32 nDataWritten
= 0;
907 while (sal::static_int_cast
<sal_uInt32
>(nBytesLen
) > nDataWritten
)
909 sal_uInt32 nDataRemaining
= nBytesLen
- nDataWritten
;
910 sal_uInt16 nWriteSize
= std::min(nDataRemaining
, sal_uInt32(SAL_MAX_UINT16
));
911 aErr
= isc_put_segment(m_statusVector
,
914 reinterpret_cast<const char*>(xBytes
.getConstArray()) + nDataWritten
);
915 nDataWritten
+= nWriteSize
;
922 // We need to make sure we close the Blob even if there are errors, hence evaluate
923 // errors after closing.
924 closeBlobAfterWriting(aBlobHandle
);
928 evaluateStatusVector(m_statusVector
,
929 u
"isc_put_segment failed",
934 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
936 else if( dType
== SQL_VARYING
)
938 setParameterNull(nParameterIndex
, false);
939 const sal_Int32 nMaxSize
= 0xFFFF;
940 Sequence
<sal_Int8
> xBytesCopy(xBytes
);
941 if (xBytesCopy
.getLength() > nMaxSize
)
943 xBytesCopy
.realloc( nMaxSize
);
945 const sal_uInt16 nSize
= xBytesCopy
.getLength();
946 // 8000 corresponds to value from lcl_addDefaultParameters
947 // in dbaccess/source/filter/hsqldb/createparser.cxx
951 pVar
->sqldata
= static_cast<char *>(malloc(sizeof(char) * nSize
+ 2));
953 static_assert(sizeof(nSize
) == 2, "must match dest memcpy len");
954 // First 2 bytes indicate string size
955 memcpy(pVar
->sqldata
, &nSize
, 2);
957 memcpy(pVar
->sqldata
+ 2, xBytesCopy
.getConstArray(), nSize
);
959 else if( dType
== SQL_TEXT
)
961 if (pVar
->sqllen
< xBytes
.getLength())
962 dbtools::throwSQLException("Data too big for this field",
963 dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
, *this);
964 setParameterNull(nParameterIndex
, false);
965 memcpy(pVar
->sqldata
, xBytes
.getConstArray(), xBytes
.getLength() );
966 // Fill remainder with zeroes
967 memset(pVar
->sqldata
+ xBytes
.getLength(), 0, pVar
->sqllen
- xBytes
.getLength());
971 ::dbtools::throwSQLException(
972 "Incorrect type for setBytes",
973 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE
,
979 void SAL_CALL
OPreparedStatement::setCharacterStream( sal_Int32 nIndex
, const Reference
< css::io::XInputStream
>&, sal_Int32
)
981 ::osl::MutexGuard
aGuard( m_aMutex
);
982 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
983 checkParameterIndex(nIndex
);
987 void SAL_CALL
OPreparedStatement::setBinaryStream( sal_Int32 nIndex
, const Reference
< css::io::XInputStream
>&, sal_Int32
)
989 ::osl::MutexGuard
aGuard( m_aMutex
);
990 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
991 checkParameterIndex(nIndex
);
995 void SAL_CALL
OPreparedStatement::clearParameters( )
999 // ---- Batch methods -- unsupported -----------------------------------------
1000 void SAL_CALL
OPreparedStatement::clearBatch()
1005 void SAL_CALL
OPreparedStatement::addBatch()
1007 // Unsupported by firebird
1010 Sequence
< sal_Int32
> SAL_CALL
OPreparedStatement::executeBatch()
1012 // Unsupported by firebird
1013 return Sequence
< sal_Int32
>();
1016 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
,const Any
& rValue
)
1020 case PROPERTY_ID_RESULTSETCONCURRENCY
:
1022 case PROPERTY_ID_RESULTSETTYPE
:
1024 case PROPERTY_ID_FETCHDIRECTION
:
1026 case PROPERTY_ID_USEBOOKMARKS
:
1029 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle
,rValue
);
1033 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex
)
1036 if ((nParameterIndex
== 0) || (nParameterIndex
> m_pInSqlda
->sqld
))
1038 ::dbtools::throwSQLException(
1039 "No column " + OUString::number(nParameterIndex
),
1040 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND
,
1045 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex
,
1048 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
1058 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */