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 22 #include <string_view> 23 24 #include "Connection.hxx" 25 #include "PreparedStatement.hxx" 26 #include "ResultSet.hxx" 27 #include "ResultSetMetaData.hxx" 28 #include "Util.hxx" 29 30 #include <comphelper/sequence.hxx> 31 #include <connectivity/dbexception.hxx> 32 #include <cppuhelper/typeprovider.hxx> 33 #include <propertyids.hxx> 34 #include <time.h> 35 #include <connectivity/dbtools.hxx> 36 #include <sal/log.hxx> 37 38 #include <com/sun/star/sdbc/DataType.hpp> 39 #include <com/sun/star/lang/DisposedException.hpp> 40 41 using namespace connectivity::firebird; 42 43 using namespace ::comphelper; 44 using namespace ::osl; 45 46 using namespace com::sun::star; 47 using namespace com::sun::star::uno; 48 using namespace com::sun::star::lang; 49 using namespace com::sun::star::beans; 50 using namespace com::sun::star::sdbc; 51 using namespace com::sun::star::container; 52 using namespace com::sun::star::io; 53 using namespace com::sun::star::util; 54 55 IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); 56 57 58 OPreparedStatement::OPreparedStatement( Connection* _pConnection, 59 const OUString& sql) 60 :OStatementCommonBase(_pConnection) 61 ,m_sSqlStatement(sql) 62 ,m_pOutSqlda(nullptr) 63 ,m_pInSqlda(nullptr) 64 { 65 SAL_INFO("connectivity.firebird", "OPreparedStatement(). " 66 "sql: " << sql); 67 } 68 69 void OPreparedStatement::ensurePrepared() 70 { 71 MutexGuard aGuard(m_aMutex); 72 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 73 74 if (m_aStatementHandle) 75 return; 76 77 ISC_STATUS aErr = 0; 78 79 if (!m_pInSqlda) 80 { 81 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10))); 82 m_pInSqlda->version = SQLDA_VERSION1; 83 m_pInSqlda->sqln = 10; 84 } 85 86 prepareAndDescribeStatement(m_sSqlStatement, 87 m_pOutSqlda, 88 m_pInSqlda); 89 90 aErr = isc_dsql_describe_bind(m_statusVector, 91 &m_aStatementHandle, 92 1, 93 m_pInSqlda); 94 95 if (aErr) 96 { 97 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed"); 98 } 99 else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough 100 { 101 short nItems = m_pInSqlda->sqld; 102 free(m_pInSqlda); 103 m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems))); 104 m_pInSqlda->version = SQLDA_VERSION1; 105 m_pInSqlda->sqln = nItems; 106 aErr = isc_dsql_describe_bind(m_statusVector, 107 &m_aStatementHandle, 108 1, 109 m_pInSqlda); 110 SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed"); 111 } 112 113 if (!aErr) 114 mallocSQLVAR(m_pInSqlda); 115 else 116 evaluateStatusVector(m_statusVector, m_sSqlStatement, *this); 117 } 118 119 OPreparedStatement::~OPreparedStatement() 120 { 121 } 122 123 void SAL_CALL OPreparedStatement::acquire() throw() 124 { 125 OStatementCommonBase::acquire(); 126 } 127 128 void SAL_CALL OPreparedStatement::release() throw() 129 { 130 OStatementCommonBase::release(); 131 } 132 133 Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType) 134 { 135 Any aRet = OStatementCommonBase::queryInterface(rType); 136 if(!aRet.hasValue()) 137 aRet = OPreparedStatement_Base::queryInterface(rType); 138 return aRet; 139 } 140 141 uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes() 142 { 143 return concatSequences(OPreparedStatement_Base::getTypes(), 144 OStatementCommonBase::getTypes()); 145 } 146 147 Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData() 148 { 149 ::osl::MutexGuard aGuard( m_aMutex ); 150 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 151 ensurePrepared(); 152 153 if(!m_xMetaData.is()) 154 m_xMetaData = new OResultSetMetaData(m_pConnection.get() 155 , m_pOutSqlda); 156 157 return m_xMetaData; 158 } 159 160 void SAL_CALL OPreparedStatement::close() 161 { 162 MutexGuard aGuard( m_aMutex ); 163 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 164 165 OStatementCommonBase::close(); 166 if (m_pInSqlda) 167 { 168 freeSQLVAR(m_pInSqlda); 169 free(m_pInSqlda); 170 m_pInSqlda = nullptr; 171 } 172 if (m_pOutSqlda) 173 { 174 freeSQLVAR(m_pOutSqlda); 175 free(m_pOutSqlda); 176 m_pOutSqlda = nullptr; 177 } 178 } 179 180 void SAL_CALL OPreparedStatement::disposing() 181 { 182 close(); 183 } 184 185 void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex, 186 const OUString& sInput) 187 { 188 SAL_INFO("connectivity.firebird", 189 "setString(" << nParameterIndex << " , " << sInput << ")"); 190 191 MutexGuard aGuard( m_aMutex ); 192 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 193 ensurePrepared(); 194 195 checkParameterIndex(nParameterIndex); 196 setParameterNull(nParameterIndex, false); 197 198 OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 ); 199 200 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); 201 202 int dtype = (pVar->sqltype & ~1); // drop flag bit for now 203 204 if (str.getLength() > pVar->sqllen) 205 str = str.copy(0, pVar->sqllen); 206 207 switch (dtype) { 208 case SQL_VARYING: 209 { 210 const sal_Int32 max_varchar_len = 0xFFFF; 211 // First 2 bytes indicate string size 212 if (str.getLength() > max_varchar_len) 213 { 214 str = str.copy(0, max_varchar_len); 215 } 216 const auto nLength = str.getLength(); 217 memcpy(pVar->sqldata, &nLength, 2); 218 // Actual data 219 memcpy(pVar->sqldata + 2, str.getStr(), str.getLength()); 220 break; 221 } 222 case SQL_TEXT: 223 memcpy(pVar->sqldata, str.getStr(), str.getLength()); 224 // Fill remainder with spaces 225 memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength()); 226 break; 227 case SQL_BLOB: // Clob 228 assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) ); 229 setClob(nParameterIndex, sInput ); 230 break; 231 default: 232 ::dbtools::throwSQLException( 233 "Incorrect type for setString", 234 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, 235 *this); 236 } 237 } 238 239 Reference< XConnection > SAL_CALL OPreparedStatement::getConnection() 240 { 241 MutexGuard aGuard( m_aMutex ); 242 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 243 244 return Reference<XConnection>(m_pConnection.get()); 245 } 246 247 sal_Bool SAL_CALL OPreparedStatement::execute() 248 { 249 SAL_INFO("connectivity.firebird", "executeQuery(). " 250 "Got called with sql: " << m_sSqlStatement); 251 252 MutexGuard aGuard( m_aMutex ); 253 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 254 255 ensurePrepared(); 256 257 ISC_STATUS aErr; 258 259 if (m_xResultSet.is()) // Checks whether we have already run the statement. 260 { 261 disposeResultSet(); 262 // Closes the cursor from the last run. 263 // This doesn't actually free the statement -- using DSQL_close closes 264 // the cursor and keeps the statement, using DSQL_drop frees the statement 265 // (and associated cursors). 266 aErr = isc_dsql_free_statement(m_statusVector, 267 &m_aStatementHandle, 268 DSQL_close); 269 if (aErr) 270 { 271 // Do not throw error. Trying to close a closed cursor is not a 272 // critical mistake. 273 OUString sErrMsg = StatusVectorToString(m_statusVector, 274 "isc_dsql_free_statement: close cursor"); 275 SAL_WARN("connectivity.firebird", sErrMsg); 276 } 277 } 278 279 aErr = isc_dsql_execute(m_statusVector, 280 &m_pConnection->getTransaction(), 281 &m_aStatementHandle, 282 1, 283 m_pInSqlda); 284 if (aErr) 285 { 286 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" ); 287 evaluateStatusVector(m_statusVector, "isc_dsql_execute", *this); 288 } 289 290 m_xResultSet = new OResultSet(m_pConnection.get(), 291 m_aMutex, 292 uno::Reference< XInterface >(*this), 293 m_aStatementHandle, 294 m_pOutSqlda); 295 296 if (getStatementChangeCount() > 0) 297 m_pConnection->notifyDatabaseModified(); 298 299 return m_xResultSet.is(); 300 // TODO: implement handling of multiple ResultSets. 301 } 302 303 sal_Int32 SAL_CALL OPreparedStatement::executeUpdate() 304 { 305 execute(); 306 return getStatementChangeCount(); 307 } 308 309 Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery() 310 { 311 execute(); 312 return m_xResultSet; 313 } 314 315 namespace { 316 317 /** 318 * Take out the number part of a fix point decimal without 319 * the information of where is the fractional part from a 320 * string representation of a number. (e.g. 54.654 -> 54654) 321 */ 322 sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource) 323 { 324 OUString sNumber(sSource); 325 326 // cut off leading 0 eventually ( eg. 0.567 -> .567) 327 (void)sSource.startsWith("0", &sNumber); 328 329 sal_Int32 nDotIndex = sNumber.indexOf('.'); 330 331 if( nDotIndex < 0) 332 { 333 return sNumber.toInt64(); // no dot -> it's an integer 334 } 335 else 336 { 337 // remove dot 338 OUStringBuffer sBuffer(15); 339 if(nDotIndex > 0) 340 { 341 sBuffer.append(std::u16string_view(sNumber).substr(0, nDotIndex)); 342 } 343 sBuffer.append(std::u16string_view(sNumber).substr(nDotIndex + 1)); 344 return sBuffer.makeStringAndClear().toInt64(); 345 } 346 } 347 348 } 349 350 //----- XParameters ----------------------------------------------------------- 351 void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/) 352 { 353 MutexGuard aGuard( m_aMutex ); 354 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 355 ensurePrepared(); 356 357 checkParameterIndex(nIndex); 358 setParameterNull(nIndex); 359 } 360 361 void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue) 362 { 363 setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN); 364 } 365 366 template <typename T> 367 void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType) 368 { 369 MutexGuard aGuard( m_aMutex ); 370 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 371 ensurePrepared(); 372 373 checkParameterIndex(nIndex); 374 setParameterNull(nIndex, false); 375 376 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1); 377 378 if ((pVar->sqltype & ~1) != nType) 379 { 380 ::dbtools::throwSQLException( 381 "Incorrect type for setValue", 382 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, 383 *this); 384 } 385 386 memcpy(pVar->sqldata, &nValue, sizeof(nValue)); 387 } 388 389 void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue) 390 { 391 // there's no TINYINT or equivalent on Firebird, 392 // so do the same as setShort 393 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT); 394 } 395 396 void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue) 397 { 398 setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT); 399 } 400 401 void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue) 402 { 403 setValue< sal_Int32 >(nIndex, nValue, SQL_LONG); 404 } 405 406 void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue) 407 { 408 setValue< sal_Int64 >(nIndex, nValue, SQL_INT64); 409 } 410 411 void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue) 412 { 413 setValue< float >(nIndex, nValue, SQL_FLOAT); 414 } 415 416 void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue) 417 { 418 MutexGuard aGuard( m_aMutex ); 419 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 420 ensurePrepared(); 421 422 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1); 423 int dType = (pVar->sqltype & ~1); // drop flag bit for now 424 425 // Caller might try to set an integer type here. It makes sense to convert 426 // it instead of throwing an error. 427 switch(dType) 428 { 429 case SQL_SHORT: 430 setValue< sal_Int16 >(nIndex, 431 static_cast<sal_Int16>(nValue), 432 dType); 433 break; 434 case SQL_LONG: 435 setValue< sal_Int32 >(nIndex, 436 static_cast<sal_Int32>(nValue), 437 dType); 438 break; 439 case SQL_INT64: 440 setValue< sal_Int64 >(nIndex, 441 static_cast<sal_Int64>(nValue), 442 dType); 443 break; 444 default: 445 setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT? 446 } 447 } 448 449 void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate) 450 { 451 struct tm aCTime; 452 aCTime.tm_mday = rDate.Day; 453 aCTime.tm_mon = rDate.Month -1; 454 aCTime.tm_year = rDate.Year -1900; 455 456 ISC_DATE aISCDate; 457 isc_encode_sql_date(&aCTime, &aISCDate); 458 459 setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE); 460 } 461 462 void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime) 463 { 464 struct tm aCTime; 465 aCTime.tm_sec = rTime.Seconds; 466 aCTime.tm_min = rTime.Minutes; 467 aCTime.tm_hour = rTime.Hours; 468 469 ISC_TIME aISCTime; 470 isc_encode_sql_time(&aCTime, &aISCTime); 471 472 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no 473 // other funkiness, so we can simply add the fraction of a second. 474 aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); 475 476 setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME); 477 } 478 479 void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp) 480 { 481 struct tm aCTime; 482 aCTime.tm_sec = rTimestamp.Seconds; 483 aCTime.tm_min = rTimestamp.Minutes; 484 aCTime.tm_hour = rTimestamp.Hours; 485 aCTime.tm_mday = rTimestamp.Day; 486 aCTime.tm_mon = rTimestamp.Month - 1; 487 aCTime.tm_year = rTimestamp.Year - 1900; 488 489 ISC_TIMESTAMP aISCTimestamp; 490 isc_encode_timestamp(&aCTime, &aISCTimestamp); 491 492 // As in previous function 493 aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); 494 495 setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP); 496 } 497 498 499 // void OPreaparedStatement::set 500 void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId) 501 { 502 ISC_STATUS aErr; 503 504 aErr = isc_create_blob2(m_statusVector, 505 &m_pConnection->getDBHandle(), 506 &m_pConnection->getTransaction(), 507 &rBlobHandle, 508 &rBlobId, 509 0, // Blob parameter buffer length 510 nullptr); // Blob parameter buffer handle 511 512 if (aErr) 513 { 514 evaluateStatusVector(m_statusVector, 515 "setBlob failed on " + m_sSqlStatement, 516 *this); 517 assert(false); 518 } 519 } 520 521 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle) 522 { 523 ISC_STATUS aErr; 524 525 aErr = isc_close_blob(m_statusVector, 526 &rBlobHandle); 527 if (aErr) 528 { 529 evaluateStatusVector(m_statusVector, 530 "isc_close_blob failed", 531 *this); 532 assert(false); 533 } 534 } 535 536 void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob ) 537 { 538 ::osl::MutexGuard aGuard( m_aMutex ); 539 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 540 541 #if SAL_TYPES_SIZEOFPOINTER == 8 542 isc_blob_handle aBlobHandle = 0; 543 #else 544 isc_blob_handle aBlobHandle = nullptr; 545 #endif 546 ISC_QUAD aBlobId; 547 548 openBlobForWriting(aBlobHandle, aBlobId); 549 550 551 // Max segment size is 2^16 == SAL_MAX_UINT16 552 // SAL_MAX_UINT16 / 4 is surely enough for UTF-8 553 // TODO apply max segment size to character encoding 554 sal_Int64 nCharWritten = 1; // XClob is indexed from 1 555 ISC_STATUS aErr = 0; 556 sal_Int64 nLen = xClob->length(); 557 while ( nLen > nCharWritten ) 558 { 559 sal_Int64 nCharRemain = nLen - nCharWritten; 560 constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4; 561 sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE); 562 OString sData = OUStringToOString( 563 xClob->getSubString(nCharWritten, nWriteSize), 564 RTL_TEXTENCODING_UTF8); 565 aErr = isc_put_segment( m_statusVector, 566 &aBlobHandle, 567 sData.getLength(), 568 sData.getStr() ); 569 nCharWritten += nWriteSize; 570 571 if (aErr) 572 break; 573 } 574 575 // We need to make sure we close the Blob even if there are errors, hence evaluate 576 // errors after closing. 577 closeBlobAfterWriting(aBlobHandle); 578 579 if (aErr) 580 { 581 evaluateStatusVector(m_statusVector, 582 "isc_put_segment failed", 583 *this); 584 assert(false); 585 } 586 587 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); 588 } 589 590 void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr ) 591 { 592 ::osl::MutexGuard aGuard( m_aMutex ); 593 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 594 checkParameterIndex(nParameterIndex); 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 OString sData = OUStringToOString( 606 rStr, 607 RTL_TEXTENCODING_UTF8); 608 ISC_STATUS aErr = isc_put_segment( m_statusVector, 609 &aBlobHandle, 610 sData.getLength(), 611 sData.getStr() ); 612 613 // We need to make sure we close the Blob even if there are errors, hence evaluate 614 // errors after closing. 615 closeBlobAfterWriting(aBlobHandle); 616 617 if (aErr) 618 { 619 evaluateStatusVector(m_statusVector, 620 "isc_put_segment failed", 621 *this); 622 assert(false); 623 } 624 625 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); 626 } 627 628 void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex, 629 const Reference< XBlob >& xBlob) 630 { 631 ::osl::MutexGuard aGuard(m_aMutex); 632 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 633 checkParameterIndex(nParameterIndex); 634 635 #if SAL_TYPES_SIZEOFPOINTER == 8 636 isc_blob_handle aBlobHandle = 0; 637 #else 638 isc_blob_handle aBlobHandle = nullptr; 639 #endif 640 ISC_QUAD aBlobId; 641 642 openBlobForWriting(aBlobHandle, aBlobId); 643 644 ISC_STATUS aErr = 0; 645 const sal_Int64 nBlobLen = xBlob->length(); 646 if (nBlobLen > 0) 647 { 648 // Max write size is 0xFFFF == SAL_MAX_UINT16 649 sal_uInt64 nDataWritten = 0; 650 while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten) 651 { 652 sal_uInt64 nDataRemaining = nBlobLen - nDataWritten; 653 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16)); 654 aErr = isc_put_segment(m_statusVector, 655 &aBlobHandle, 656 nWriteSize, 657 reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray())); 658 nDataWritten += nWriteSize; 659 660 if (aErr) 661 break; 662 } 663 } 664 665 // We need to make sure we close the Blob even if there are errors, hence evaluate 666 // errors after closing. 667 closeBlobAfterWriting(aBlobHandle); 668 669 if (aErr) 670 { 671 evaluateStatusVector(m_statusVector, 672 "isc_put_segment failed", 673 *this); 674 assert(false); 675 } 676 677 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); 678 } 679 680 681 void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& ) 682 { 683 ::osl::MutexGuard aGuard( m_aMutex ); 684 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 685 checkParameterIndex(nIndex); 686 } 687 688 689 void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& ) 690 { 691 ::osl::MutexGuard aGuard( m_aMutex ); 692 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 693 checkParameterIndex(nIndex); 694 } 695 696 697 void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale ) 698 { 699 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 700 ::osl::MutexGuard aGuard( m_aMutex ); 701 ensurePrepared(); 702 703 checkParameterIndex(parameterIndex); 704 setParameterNull(parameterIndex, false); 705 706 XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1); 707 int dType = (pVar->sqltype & ~1); // drop null flag 708 709 if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC) 710 { 711 double dbValue =0.0; 712 OUString sValue; 713 if( x >>= dbValue ) 714 { 715 sValue = OUString::number( dbValue ); 716 } 717 else 718 { 719 x >>= sValue; 720 } 721 722 // fill in the number with nulls in fractional part. 723 // We need this because e.g. 0.450 != 0.045 despite 724 // their scale is equal 725 OUStringBuffer sBuffer(15); 726 sBuffer.append(sValue); 727 if(sValue.indexOf('.') != -1) // there is a dot 728 { 729 for(sal_Int32 i=sValue.copy(sValue.indexOf('.')+1).getLength(); i<scale;i++) 730 { 731 sBuffer.append('0'); 732 } 733 } 734 else 735 { 736 for (sal_Int32 i=0; i<scale; i++) 737 { 738 sBuffer.append('0'); 739 } 740 } 741 742 sValue = sBuffer.makeStringAndClear(); 743 switch(dType) 744 { 745 case SQL_SHORT: 746 setValue< sal_Int16 >(parameterIndex, 747 static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ), 748 dType); 749 break; 750 case SQL_LONG: 751 case SQL_DOUBLE: 752 setValue< sal_Int32 >(parameterIndex, 753 static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ), 754 dType); 755 break; 756 case SQL_INT64: 757 setValue< sal_Int64 >(parameterIndex, 758 toNumericWithoutDecimalPlace(sValue), 759 dType); 760 break; 761 default: 762 SAL_WARN("connectivity.firebird", 763 "No Firebird sql type found for numeric or decimal types"); 764 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); 765 } 766 } 767 else 768 { 769 ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); 770 } 771 772 } 773 774 775 void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& ) 776 { 777 ::osl::MutexGuard aGuard( m_aMutex ); 778 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 779 checkParameterIndex(nIndex); 780 } 781 782 783 void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& ) 784 { 785 ::osl::MutexGuard aGuard( m_aMutex ); 786 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 787 checkParameterIndex(nIndex); 788 } 789 790 void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex, 791 const Sequence< sal_Int8 >& xBytes) 792 { 793 ::osl::MutexGuard aGuard(m_aMutex); 794 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 795 checkParameterIndex(nParameterIndex); 796 797 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); 798 int dType = (pVar->sqltype & ~1); // drop flag bit for now 799 800 if( dType == SQL_BLOB ) 801 { 802 #if SAL_TYPES_SIZEOFPOINTER == 8 803 isc_blob_handle aBlobHandle = 0; 804 #else 805 isc_blob_handle aBlobHandle = nullptr; 806 #endif 807 ISC_QUAD aBlobId; 808 809 openBlobForWriting(aBlobHandle, aBlobId); 810 811 ISC_STATUS aErr = 0; 812 const sal_Int32 nBytesLen = xBytes.getLength(); 813 if (nBytesLen > 0) 814 { 815 // Max write size is 0xFFFF == SAL_MAX_UINT16 816 sal_uInt32 nDataWritten = 0; 817 while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten) 818 { 819 sal_uInt32 nDataRemaining = nBytesLen - nDataWritten; 820 sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16)); 821 aErr = isc_put_segment(m_statusVector, 822 &aBlobHandle, 823 nWriteSize, 824 reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten); 825 nDataWritten += nWriteSize; 826 827 if (aErr) 828 break; 829 } 830 } 831 832 // We need to make sure we close the Blob even if there are errors, hence evaluate 833 // errors after closing. 834 closeBlobAfterWriting(aBlobHandle); 835 836 if (aErr) 837 { 838 evaluateStatusVector(m_statusVector, 839 "isc_put_segment failed", 840 *this); 841 assert(false); 842 } 843 844 setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); 845 } 846 else if( dType == SQL_VARYING ) 847 { 848 setParameterNull(nParameterIndex, false); 849 const sal_Int32 nMaxSize = 0xFFFF; 850 Sequence<sal_Int8> xBytesCopy(xBytes); 851 if (xBytesCopy.getLength() > nMaxSize) 852 { 853 xBytesCopy.realloc( nMaxSize ); 854 } 855 const auto nSize = xBytesCopy.getLength(); 856 // 8000 corresponds to value from lcl_addDefaultParameters 857 // in dbaccess/source/filter/hsqldb/createparser.cxx 858 if (nSize > 8000) 859 { 860 free(pVar->sqldata); 861 pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2)); 862 } 863 // First 2 bytes indicate string size 864 memcpy(pVar->sqldata, &nSize, 2); 865 // Actual data 866 memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize); 867 } 868 else if( dType == SQL_TEXT ) 869 { 870 setParameterNull(nParameterIndex, false); 871 memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() ); 872 // Fill remainder with spaces 873 memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength()); 874 } 875 else 876 { 877 ::dbtools::throwSQLException( 878 "Incorrect type for setBytes", 879 ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, 880 *this); 881 } 882 } 883 884 885 void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 ) 886 { 887 ::osl::MutexGuard aGuard( m_aMutex ); 888 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 889 checkParameterIndex(nIndex); 890 } 891 892 893 void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 ) 894 { 895 ::osl::MutexGuard aGuard( m_aMutex ); 896 checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); 897 checkParameterIndex(nIndex); 898 } 899 900 901 void SAL_CALL OPreparedStatement::clearParameters( ) 902 { 903 } 904 905 // ---- Batch methods -- unsupported ----------------------------------------- 906 void SAL_CALL OPreparedStatement::clearBatch() 907 { 908 // Unsupported 909 } 910 911 void SAL_CALL OPreparedStatement::addBatch() 912 { 913 // Unsupported by firebird 914 } 915 916 Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch() 917 { 918 // Unsupported by firebird 919 return Sequence< sal_Int32 >(); 920 } 921 922 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) 923 { 924 switch(nHandle) 925 { 926 case PROPERTY_ID_RESULTSETCONCURRENCY: 927 break; 928 case PROPERTY_ID_RESULTSETTYPE: 929 break; 930 case PROPERTY_ID_FETCHDIRECTION: 931 break; 932 case PROPERTY_ID_USEBOOKMARKS: 933 break; 934 default: 935 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue); 936 } 937 } 938 939 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex) 940 { 941 ensurePrepared(); 942 if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld)) 943 { 944 ::dbtools::throwSQLException( 945 "No column " + OUString::number(nParameterIndex), 946 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND, 947 *this); 948 } 949 } 950 951 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex, 952 bool bSetNull) 953 { 954 XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); 955 if (bSetNull) 956 { 957 pVar->sqltype |= 1; 958 *pVar->sqlind = -1; 959 } 960 else 961 *pVar->sqlind = 0; 962 } 963 964 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 965
