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
29 #include <comphelper/sequence.hxx>
30 #include <connectivity/dbexception.hxx>
31 #include <propertyids.hxx>
32 #include <connectivity/dbtools.hxx>
33 #include <sal/log.hxx>
34
35 #include <com/sun/star/sdbc/DataType.hpp>
36
37 using namespace connectivity::firebird;
38
39 using namespace ::comphelper;
40 using namespace ::osl;
41
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;
50
51 IMPLEMENT_SERVICE_INFO(OPreparedStatement,u"com.sun.star.sdbcx.firebird.PreparedStatement"_ustr,u"com.sun.star.sdbc.PreparedStatement"_ustr);
52
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
54
55
OPreparedStatement(Connection * _pConnection,const OUString & sql)56 OPreparedStatement::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
ensurePrepared()67 void OPreparedStatement::ensurePrepared()
68 {
69 MutexGuard aGuard(m_aMutex);
70 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
71
72 if (m_aStatementHandle)
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 assert(m_pInSqlda && "Don't handle OOM conditions");
81 m_pInSqlda->version = SQLDA_VERSION1;
82 m_pInSqlda->sqln = 10;
83 }
84
85 prepareAndDescribeStatement(m_sSqlStatement, m_pOutSqlda);
86
87 aErr = isc_dsql_describe_bind(m_statusVector,
88 &m_aStatementHandle,
89 1,
90 m_pInSqlda);
91
92 if (aErr)
93 {
94 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
95 }
96 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
97 {
98 short nItems = m_pInSqlda->sqld;
99 free(m_pInSqlda);
100 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
101 assert(m_pInSqlda && "Don't handle OOM conditions");
102 m_pInSqlda->version = SQLDA_VERSION1;
103 m_pInSqlda->sqln = nItems;
104 aErr = isc_dsql_describe_bind(m_statusVector,
105 &m_aStatementHandle,
106 1,
107 m_pInSqlda);
108 SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
109 }
110
111 if (!aErr)
112 mallocSQLVAR(m_pInSqlda);
113 else
114 evaluateStatusVector(m_statusVector, m_sSqlStatement, *this);
115 }
116
~OPreparedStatement()117 OPreparedStatement::~OPreparedStatement()
118 {
119 }
120
acquire()121 void SAL_CALL OPreparedStatement::acquire() noexcept
122 {
123 OStatementCommonBase::acquire();
124 }
125
release()126 void SAL_CALL OPreparedStatement::release() noexcept
127 {
128 OStatementCommonBase::release();
129 }
130
queryInterface(const Type & rType)131 Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
132 {
133 Any aRet = OStatementCommonBase::queryInterface(rType);
134 if(!aRet.hasValue())
135 aRet = OPreparedStatement_Base::queryInterface(rType);
136 return aRet;
137 }
138
getTypes()139 uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
140 {
141 return concatSequences(OPreparedStatement_Base::getTypes(),
142 OStatementCommonBase::getTypes());
143 }
144
getMetaData()145 Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData()
146 {
147 ::osl::MutexGuard aGuard( m_aMutex );
148 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
149 ensurePrepared();
150
151 if(!m_xMetaData.is())
152 m_xMetaData = new OResultSetMetaData(m_pConnection.get()
153 , m_pOutSqlda);
154
155 return m_xMetaData;
156 }
157
close()158 void SAL_CALL OPreparedStatement::close()
159 {
160 MutexGuard aGuard( m_aMutex );
161 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
162
163 OStatementCommonBase::close();
164 if (m_pInSqlda)
165 {
166 freeSQLVAR(m_pInSqlda);
167 free(m_pInSqlda);
168 m_pInSqlda = nullptr;
169 }
170 if (m_pOutSqlda)
171 {
172 freeSQLVAR(m_pOutSqlda);
173 free(m_pOutSqlda);
174 m_pOutSqlda = nullptr;
175 }
176 }
177
disposing()178 void SAL_CALL OPreparedStatement::disposing()
179 {
180 close();
181 }
182
setString(sal_Int32 nParameterIndex,const OUString & sInput)183 void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
184 const OUString& sInput)
185 {
186 SAL_INFO("connectivity.firebird",
187 "setString(" << nParameterIndex << " , " << sInput << ")");
188
189 MutexGuard aGuard( m_aMutex );
190 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
191 ensurePrepared();
192
193 checkParameterIndex(nParameterIndex);
194 setParameterNull(nParameterIndex, false);
195
196 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
197 ColumnTypeInfo columnType(*pVar);
198
199 switch (auto sdbcType = columnType.getSdbcType()) {
200 case DataType::VARCHAR:
201 case DataType::CHAR:
202 {
203 OString str = OUStringToOString(sInput, RTL_TEXTENCODING_UTF8);
204 const ISC_SHORT nLength = std::min(str.getLength(), static_cast<sal_Int32>(pVar->sqllen));
205 int offset = 0;
206 if (sdbcType == DataType::VARCHAR)
207 {
208 // First 2 bytes indicate string size
209 static_assert(sizeof(nLength) == 2, "must match dest memcpy len");
210 memcpy(pVar->sqldata, &nLength, 2);
211 offset = 2;
212 }
213 // Actual data
214 memcpy(pVar->sqldata + offset, str.getStr(), nLength);
215 if (sdbcType == DataType::CHAR)
216 {
217 // Fill remainder with spaces
218 memset(pVar->sqldata + offset + nLength, ' ', pVar->sqllen - nLength);
219 }
220 break;
221 }
222 case DataType::CLOB:
223 setClob(nParameterIndex, sInput );
224 break;
225 case DataType::SMALLINT:
226 {
227 sal_Int32 int32Value = sInput.toInt32();
228 if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
229 (int32Value > std::numeric_limits<sal_Int16>::max()) )
230 {
231 ::dbtools::throwSQLException(
232 u"Value out of range for SQL_SHORT type"_ustr,
233 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
234 *this);
235 }
236 setShort(nParameterIndex, int32Value);
237 break;
238 }
239 case DataType::INTEGER:
240 {
241 sal_Int32 int32Value = sInput.toInt32();
242 setInt(nParameterIndex, int32Value);
243 break;
244 }
245 case DataType::BIGINT:
246 {
247 sal_Int64 int64Value = sInput.toInt64();
248 setLong(nParameterIndex, int64Value);
249 break;
250 }
251 case DataType::FLOAT:
252 {
253 float floatValue = sInput.toFloat();
254 setFloat(nParameterIndex, floatValue);
255 break;
256 }
257 case DataType::DOUBLE:
258 setDouble(nParameterIndex, sInput.toDouble());
259 break;
260 case DataType::NUMERIC:
261 case DataType::DECIMAL:
262 return setObjectWithInfo(nParameterIndex, Any{ sInput }, sdbcType, 0);
263 break;
264 case DataType::BOOLEAN:
265 {
266 bool boolValue = sInput.toBoolean();
267 setBoolean(nParameterIndex, boolValue);
268 break;
269 }
270 case DataType::SQLNULL:
271 {
272 // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
273 pVar->sqldata = nullptr;
274 break;
275 }
276 default:
277 ::dbtools::throwSQLException(
278 u"Incorrect type for setString"_ustr,
279 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
280 *this);
281 }
282 }
283
getConnection()284 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
285 {
286 MutexGuard aGuard( m_aMutex );
287 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
288
289 return m_pConnection;
290 }
291
execute()292 sal_Bool SAL_CALL OPreparedStatement::execute()
293 {
294 SAL_INFO("connectivity.firebird", "executeQuery(). "
295 "Got called with sql: " << m_sSqlStatement);
296
297 MutexGuard aGuard( m_aMutex );
298 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
299
300 ensurePrepared();
301
302 ISC_STATUS aErr;
303
304 if (m_xResultSet.is()) // Checks whether we have already run the statement.
305 {
306 disposeResultSet();
307 // Closes the cursor from the last run.
308 // This doesn't actually free the statement -- using DSQL_close closes
309 // the cursor and keeps the statement, using DSQL_drop frees the statement
310 // (and associated cursors).
311 aErr = isc_dsql_free_statement(m_statusVector,
312 &m_aStatementHandle,
313 DSQL_close);
314 if (aErr)
315 {
316 // Do not throw error. Trying to close a closed cursor is not a
317 // critical mistake.
318 OUString sErrMsg = StatusVectorToString(m_statusVector,
319 u"isc_dsql_free_statement: close cursor");
320 SAL_WARN("connectivity.firebird", sErrMsg);
321 }
322 }
323
324 aErr = isc_dsql_execute(m_statusVector,
325 &m_pConnection->getTransaction(),
326 &m_aStatementHandle,
327 1,
328 m_pInSqlda);
329 if (aErr)
330 {
331 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
332 evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
333 }
334
335 m_xResultSet = new OResultSet(m_pConnection.get(),
336 m_aMutex,
337 uno::Reference< XInterface >(*this),
338 m_aStatementHandle,
339 m_pOutSqlda);
340
341 return m_xResultSet.is();
342 // TODO: implement handling of multiple ResultSets.
343 }
344
executeUpdate()345 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
346 {
347 execute();
348 return getStatementChangeCount();
349 }
350
executeQuery()351 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
352 {
353 execute();
354 return m_xResultSet;
355 }
356
357 namespace {
358
toPowOf10AndRound(double n,int powOf10)359 sal_Int64 toPowOf10AndRound(double n, int powOf10)
360 {
361 static constexpr sal_Int64 powers[] = {
362 1,
363 10,
364 100,
365 1000,
366 10000,
367 100000,
368 1000000,
369 10000000,
370 100000000,
371 1000000000,
372 10000000000,
373 100000000000,
374 1000000000000,
375 10000000000000,
376 100000000000000,
377 1000000000000000,
378 10000000000000000,
379 100000000000000000,
380 1000000000000000000,
381 };
382 powOf10 = std::clamp(powOf10, 0, int(std::size(powers) - 1));
383 return n * powers[powOf10] + (n >= 0 ? 0.5 : -0.5);
384 }
385
386 /**
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)
390 */
toNumericWithoutDecimalPlace(const Any & x,sal_Int32 scale)391 sal_Int64 toNumericWithoutDecimalPlace(const Any& x, sal_Int32 scale)
392 {
393 if (double value = 0; x >>= value)
394 return toPowOf10AndRound(value, scale);
395
396 // Can't use conversion of string to double, because it could be not representable in double
397
398 OUString s;
399 x >>= s;
400 std::u16string_view num(o3tl::trim(s));
401 size_t end = num.starts_with('-') ? 1 : 0;
402 for (bool seenDot = false; end < num.size(); ++end)
403 {
404 if (num[end] == '.')
405 {
406 if (seenDot)
407 break;
408 seenDot = true;
409 }
410 else if (!rtl::isAsciiDigit(num[end]))
411 break;
412 }
413 num = num.substr(0, end);
414
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
420 {
421 scale -= num.substr(dotPos + 1).size();
422 buffer.remove(dotPos, 1);
423 if (scale < 0)
424 {
425 const sal_Int32 n = std::min(buffer.getLength(), -scale);
426 buffer.truncate(buffer.getLength() - n);
427 scale = 0;
428 }
429 }
430 for (sal_Int32 i = 0; i < scale; ++i)
431 buffer.append('0');
432
433 return OUString::unacquired(buffer).toInt64();
434 }
435
toDouble(const Any & x)436 double toDouble(const Any& x)
437 {
438 if (double value = 0; x >>= value)
439 return value;
440 OUString s;
441 x >>= s;
442 return s.toDouble();
443 }
444 }
445
446 //----- XParameters -----------------------------------------------------------
setNull(sal_Int32 nIndex,sal_Int32)447 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
448 {
449 MutexGuard aGuard( m_aMutex );
450 ensurePrepared();
451
452 checkParameterIndex(nIndex);
453 setParameterNull(nIndex);
454 }
455
setBoolean(sal_Int32 nIndex,sal_Bool bValue)456 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
457 {
458 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
459 }
460
461 template <typename T>
setValue(sal_Int32 nIndex,const T & nValue,ISC_SHORT nType)462 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
463 {
464 MutexGuard aGuard( m_aMutex );
465 ensurePrepared();
466
467 checkParameterIndex(nIndex);
468 setParameterNull(nIndex, false);
469
470 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
471
472 if ((pVar->sqltype & ~1) != nType)
473 {
474 ::dbtools::throwSQLException(
475 u"Incorrect type for setValue"_ustr,
476 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
477 *this);
478 }
479
480 memcpy(pVar->sqldata, &nValue, sizeof(nValue));
481 }
482
483 // Integral type setters convert transparently to bigger types
484
setByte(sal_Int32 nIndex,sal_Int8 nValue)485 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
486 {
487 // there's no TINYINT or equivalent on Firebird,
488 // so do the same as setShort
489 setShort(nIndex, nValue);
490 }
491
setShort(sal_Int32 nIndex,sal_Int16 nValue)492 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
493 {
494 MutexGuard aGuard(m_aMutex);
495 ensurePrepared();
496
497 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
498 switch (columnType.getSdbcType())
499 {
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());
508 }
509 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
510 }
511
setInt(sal_Int32 nIndex,sal_Int32 nValue)512 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
513 {
514 MutexGuard aGuard(m_aMutex);
515 ensurePrepared();
516
517 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
518 switch (columnType.getSdbcType())
519 {
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());
524 }
525 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
526 }
527
setLong(sal_Int32 nIndex,sal_Int64 nValue)528 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
529 {
530 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
531 }
532
setFloat(sal_Int32 nIndex,float nValue)533 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
534 {
535 setValue< float >(nIndex, nValue, SQL_FLOAT);
536 }
537
setDouble(sal_Int32 nIndex,double nValue)538 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
539 {
540 MutexGuard aGuard( m_aMutex );
541 ensurePrepared();
542
543 ColumnTypeInfo columnType{ m_pInSqlda, nIndex };
544 // Assume it is a sub type of a number.
545 if (columnType.getSubType() < 0 || columnType.getSubType() > 2)
546 {
547 ::dbtools::throwSQLException(
548 u"Incorrect number sub type"_ustr,
549 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
550 *this);
551 }
552
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())
556 {
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?
567 }
568 setValue<double>(nIndex, nValue, SQL_DOUBLE);
569 }
570
setDate(sal_Int32 nIndex,const Date & rDate)571 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
572 {
573 struct tm aCTime;
574 aCTime.tm_mday = rDate.Day;
575 aCTime.tm_mon = rDate.Month -1;
576 aCTime.tm_year = rDate.Year -1900;
577
578 ISC_DATE aISCDate;
579 isc_encode_sql_date(&aCTime, &aISCDate);
580
581 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
582 }
583
setTime(sal_Int32 nIndex,const css::util::Time & rTime)584 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
585 {
586 struct tm aCTime;
587 aCTime.tm_sec = rTime.Seconds;
588 aCTime.tm_min = rTime.Minutes;
589 aCTime.tm_hour = rTime.Hours;
590
591 ISC_TIME aISCTime;
592 isc_encode_sql_time(&aCTime, &aISCTime);
593
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);
597
598 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
599 }
600
setTimestamp(sal_Int32 nIndex,const DateTime & rTimestamp)601 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
602 {
603 struct tm aCTime;
604 aCTime.tm_sec = rTimestamp.Seconds;
605 aCTime.tm_min = rTimestamp.Minutes;
606 aCTime.tm_hour = rTimestamp.Hours;
607 aCTime.tm_mday = rTimestamp.Day;
608 aCTime.tm_mon = rTimestamp.Month - 1;
609 aCTime.tm_year = rTimestamp.Year - 1900;
610
611 ISC_TIMESTAMP aISCTimestamp;
612 isc_encode_timestamp(&aCTime, &aISCTimestamp);
613
614 // As in previous function
615 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
616
617 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
618 }
619
620
621 // void OPreparedStatement::set
openBlobForWriting(isc_blob_handle & rBlobHandle,ISC_QUAD & rBlobId)622 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
623 {
624 ISC_STATUS aErr;
625
626 aErr = isc_create_blob2(m_statusVector,
627 &m_pConnection->getDBHandle(),
628 &m_pConnection->getTransaction(),
629 &rBlobHandle,
630 &rBlobId,
631 0, // Blob parameter buffer length
632 nullptr); // Blob parameter buffer handle
633
634 if (aErr)
635 {
636 evaluateStatusVector(m_statusVector,
637 Concat2View("setBlob failed on " + m_sSqlStatement),
638 *this);
639 assert(false);
640 }
641 }
642
closeBlobAfterWriting(isc_blob_handle & rBlobHandle)643 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
644 {
645 ISC_STATUS aErr;
646
647 aErr = isc_close_blob(m_statusVector,
648 &rBlobHandle);
649 if (aErr)
650 {
651 evaluateStatusVector(m_statusVector,
652 u"isc_close_blob failed",
653 *this);
654 assert(false);
655 }
656 }
657
setClob(sal_Int32 nParameterIndex,const Reference<XClob> & xClob)658 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
659 {
660 ::osl::MutexGuard aGuard( m_aMutex );
661 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
662
663 // value-initialization: isc_blob_handle may be either a pointer of an integer
664 isc_blob_handle aBlobHandle{};
665 ISC_QUAD aBlobId;
666
667 openBlobForWriting(aBlobHandle, aBlobId);
668
669
670 // Max segment size is 2^16 == SAL_MAX_UINT16
671 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
672 // TODO apply max segment size to character encoding
673 sal_Int64 nCharWritten = 1; // XClob is indexed from 1
674 ISC_STATUS aErr = 0;
675 sal_Int64 nLen = xClob->length();
676 while ( nLen >= nCharWritten )
677 {
678 sal_Int64 nCharRemain = nLen - nCharWritten + 1;
679 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
680 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
681 OString sData = OUStringToOString(
682 xClob->getSubString(nCharWritten, nWriteSize),
683 RTL_TEXTENCODING_UTF8);
684 aErr = isc_put_segment( m_statusVector,
685 &aBlobHandle,
686 sData.getLength(),
687 sData.getStr() );
688 nCharWritten += nWriteSize;
689
690 if (aErr)
691 break;
692 }
693
694 // We need to make sure we close the Blob even if there are errors, hence evaluate
695 // errors after closing.
696 closeBlobAfterWriting(aBlobHandle);
697
698 if (aErr)
699 {
700 evaluateStatusVector(m_statusVector,
701 u"isc_put_segment failed",
702 *this);
703 assert(false);
704 }
705
706 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
707 }
708
setClob(sal_Int32 nParameterIndex,std::u16string_view rStr)709 void OPreparedStatement::setClob(sal_Int32 nParameterIndex, std::u16string_view rStr)
710 {
711 ::osl::MutexGuard aGuard( m_aMutex );
712 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
713 checkParameterIndex(nParameterIndex);
714
715 // value-initialization: isc_blob_handle may be either a pointer of an integer
716 isc_blob_handle aBlobHandle{};
717 ISC_QUAD aBlobId;
718
719 openBlobForWriting(aBlobHandle, aBlobId);
720
721 OString sData = OUStringToOString(
722 rStr,
723 RTL_TEXTENCODING_UTF8);
724 size_t nDataSize = sData.getLength();
725 ISC_STATUS aErr = 0;
726 // we can't store more than MAX_SIZE_SEGMENT in a segment
727 if (nDataSize <= MAX_SIZE_SEGMENT)
728 {
729 aErr = isc_put_segment( m_statusVector,
730 &aBlobHandle,
731 sData.getLength(),
732 sData.getStr() );
733 }
734 else
735 {
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)
739 {
740 OString strCurrentChunk = sData.copy(i * MAX_SIZE_SEGMENT, MAX_SIZE_SEGMENT);
741 aErr = isc_put_segment( m_statusVector,
742 &aBlobHandle,
743 strCurrentChunk.getLength(),
744 strCurrentChunk.getStr() );
745 if (aErr)
746 break;
747 }
748 size_t nRemainingBytes = nDataSize - (nNbEntireChunks * MAX_SIZE_SEGMENT);
749 if (nRemainingBytes && !aErr)
750 {
751 // then copy the remaining
752 OString strCurrentChunk = sData.copy(nNbEntireChunks * MAX_SIZE_SEGMENT, nRemainingBytes);
753 aErr = isc_put_segment( m_statusVector,
754 &aBlobHandle,
755 strCurrentChunk.getLength(),
756 strCurrentChunk.getStr() );
757 }
758 }
759
760 // We need to make sure we close the Blob even if there are errors, hence evaluate
761 // errors after closing.
762 closeBlobAfterWriting(aBlobHandle);
763
764 if (aErr)
765 {
766 evaluateStatusVector(m_statusVector,
767 u"isc_put_segment failed",
768 *this);
769 assert(false);
770 }
771
772 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
773 }
774
setBlob(sal_Int32 nParameterIndex,const Reference<XBlob> & xBlob)775 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
776 const Reference< XBlob >& xBlob)
777 {
778 ::osl::MutexGuard aGuard(m_aMutex);
779 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
780 checkParameterIndex(nParameterIndex);
781
782 // value-initialization: isc_blob_handle may be either a pointer of an integer
783 isc_blob_handle aBlobHandle{};
784 ISC_QUAD aBlobId;
785
786 openBlobForWriting(aBlobHandle, aBlobId);
787
788 ISC_STATUS aErr = 0;
789 const sal_Int64 nBlobLen = xBlob->length();
790 if (nBlobLen > 0)
791 {
792 // Max write size is 0xFFFF == SAL_MAX_UINT16
793 sal_uInt64 nDataWritten = 0;
794 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
795 {
796 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
797 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
798 aErr = isc_put_segment(m_statusVector,
799 &aBlobHandle,
800 nWriteSize,
801 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
802 nDataWritten += nWriteSize;
803
804 if (aErr)
805 break;
806 }
807 }
808
809 // We need to make sure we close the Blob even if there are errors, hence evaluate
810 // errors after closing.
811 closeBlobAfterWriting(aBlobHandle);
812
813 if (aErr)
814 {
815 evaluateStatusVector(m_statusVector,
816 u"isc_put_segment failed",
817 *this);
818 assert(false);
819 }
820
821 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
822 }
823
824
setArray(sal_Int32 nIndex,const Reference<XArray> &)825 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
826 {
827 ::osl::MutexGuard aGuard( m_aMutex );
828 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
829 checkParameterIndex(nIndex);
830 }
831
832
setRef(sal_Int32 nIndex,const Reference<XRef> &)833 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
834 {
835 ::osl::MutexGuard aGuard( m_aMutex );
836 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
837 checkParameterIndex(nIndex);
838 }
839
840
setObjectWithInfo(sal_Int32 parameterIndex,const Any & x,sal_Int32 sqlType,sal_Int32 scale)841 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
842 {
843 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
844 ::osl::MutexGuard aGuard( m_aMutex );
845 ensurePrepared();
846
847 checkParameterIndex(parameterIndex);
848 setParameterNull(parameterIndex, false);
849
850 ColumnTypeInfo columnType{ m_pInSqlda, parameterIndex };
851 int dType = columnType.getType() & ~1; // drop null flag
852
853 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
854 {
855 switch(dType)
856 {
857 case SQL_SHORT:
858 return setValue(
859 parameterIndex,
860 static_cast<sal_Int16>(toNumericWithoutDecimalPlace(x, columnType.getScale())),
861 dType);
862 case SQL_LONG:
863 return setValue(
864 parameterIndex,
865 static_cast<sal_Int32>(toNumericWithoutDecimalPlace(x, columnType.getScale())),
866 dType);
867 case SQL_INT64:
868 return setValue(parameterIndex,
869 toNumericWithoutDecimalPlace(x, columnType.getScale()), dType);
870 case SQL_DOUBLE:
871 return setValue(parameterIndex, toDouble(x), dType);
872 default:
873 SAL_WARN("connectivity.firebird",
874 "No Firebird sql type found for numeric or decimal types");
875 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
876 }
877 }
878 else
879 {
880 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
881 }
882
883 }
884
885
setObjectNull(sal_Int32 nIndex,sal_Int32,const OUString &)886 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
887 {
888 ::osl::MutexGuard aGuard( m_aMutex );
889 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
890 checkParameterIndex(nIndex);
891 }
892
893
setObject(sal_Int32 nIndex,const Any &)894 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
895 {
896 ::osl::MutexGuard aGuard( m_aMutex );
897 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
898 checkParameterIndex(nIndex);
899 }
900
setBytes(sal_Int32 nParameterIndex,const Sequence<sal_Int8> & xBytes)901 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
902 const Sequence< sal_Int8 >& xBytes)
903 {
904 ::osl::MutexGuard aGuard(m_aMutex);
905 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
906 checkParameterIndex(nParameterIndex);
907
908 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
909 int dType = (pVar->sqltype & ~1); // drop flag bit for now
910
911 if( dType == SQL_BLOB )
912 {
913 // value-initialization: isc_blob_handle may be either a pointer of an integer
914 isc_blob_handle aBlobHandle{};
915 ISC_QUAD aBlobId;
916
917 openBlobForWriting(aBlobHandle, aBlobId);
918
919 ISC_STATUS aErr = 0;
920 const sal_Int32 nBytesLen = xBytes.getLength();
921 if (nBytesLen > 0)
922 {
923 // Max write size is 0xFFFF == SAL_MAX_UINT16
924 sal_uInt32 nDataWritten = 0;
925 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
926 {
927 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
928 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
929 aErr = isc_put_segment(m_statusVector,
930 &aBlobHandle,
931 nWriteSize,
932 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
933 nDataWritten += nWriteSize;
934
935 if (aErr)
936 break;
937 }
938 }
939
940 // We need to make sure we close the Blob even if there are errors, hence evaluate
941 // errors after closing.
942 closeBlobAfterWriting(aBlobHandle);
943
944 if (aErr)
945 {
946 evaluateStatusVector(m_statusVector,
947 u"isc_put_segment failed",
948 *this);
949 assert(false);
950 }
951
952 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
953 }
954 else if( dType == SQL_VARYING )
955 {
956 setParameterNull(nParameterIndex, false);
957 const sal_Int32 nMaxSize = 0xFFFF;
958 const sal_uInt16 nSize = std::min(xBytes.getLength(), nMaxSize);
959 // 8000 corresponds to value from lcl_addDefaultParameters
960 // in dbaccess/source/filter/hsqldb/createparser.cxx
961 if (nSize > 8000)
962 {
963 free(pVar->sqldata);
964 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
965 }
966 static_assert(sizeof(nSize) == 2, "must match dest memcpy len");
967 // First 2 bytes indicate string size
968 memcpy(pVar->sqldata, &nSize, 2);
969 // Actual data
970 memcpy(pVar->sqldata + 2, xBytes.getConstArray(), nSize);
971 }
972 else if( dType == SQL_TEXT )
973 {
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());
981 }
982 else
983 {
984 ::dbtools::throwSQLException(
985 u"Incorrect type for setBytes"_ustr,
986 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
987 *this);
988 }
989 }
990
991
setCharacterStream(sal_Int32 nIndex,const Reference<css::io::XInputStream> &,sal_Int32)992 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
993 {
994 ::osl::MutexGuard aGuard( m_aMutex );
995 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
996 checkParameterIndex(nIndex);
997 }
998
999
setBinaryStream(sal_Int32 nIndex,const Reference<css::io::XInputStream> &,sal_Int32)1000 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
1001 {
1002 ::osl::MutexGuard aGuard( m_aMutex );
1003 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
1004 checkParameterIndex(nIndex);
1005 }
1006
1007
clearParameters()1008 void SAL_CALL OPreparedStatement::clearParameters( )
1009 {
1010 }
1011
1012 // ---- Batch methods -- unsupported -----------------------------------------
clearBatch()1013 void SAL_CALL OPreparedStatement::clearBatch()
1014 {
1015 // Unsupported
1016 }
1017
addBatch()1018 void SAL_CALL OPreparedStatement::addBatch()
1019 {
1020 // Unsupported by firebird
1021 }
1022
executeBatch()1023 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
1024 {
1025 // Unsupported by firebird
1026 return Sequence< sal_Int32 >();
1027 }
1028
setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any & rValue)1029 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
1030 {
1031 switch(nHandle)
1032 {
1033 case PROPERTY_ID_RESULTSETCONCURRENCY:
1034 break;
1035 case PROPERTY_ID_RESULTSETTYPE:
1036 break;
1037 case PROPERTY_ID_FETCHDIRECTION:
1038 break;
1039 case PROPERTY_ID_USEBOOKMARKS:
1040 break;
1041 default:
1042 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
1043 }
1044 }
1045
checkParameterIndex(sal_Int32 nParameterIndex)1046 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
1047 {
1048 ensurePrepared();
1049 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
1050 {
1051 ::dbtools::throwSQLException(
1052 "No column " + OUString::number(nParameterIndex),
1053 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
1054 *this);
1055 }
1056 }
1057
setParameterNull(sal_Int32 nParameterIndex,bool bSetNull)1058 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
1059 bool bSetNull)
1060 {
1061 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
1062 if (bSetNull)
1063 {
1064 pVar->sqltype |= 1;
1065 *pVar->sqlind = -1;
1066 }
1067 else
1068 *pVar->sqlind = 0;
1069 }
1070
1071 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
1072