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 "ResultSet.hxx" 21 #include "ResultSetMetaData.hxx" 22 #include "Util.hxx" 23 24 #include <comphelper/sequence.hxx> 25 #include <cppuhelper/supportsservice.hxx> 26 #include <connectivity/dbexception.hxx> 27 #include <propertyids.hxx> 28 #include <rtl/ustrbuf.hxx> 29 #include <sal/log.hxx> 30 #include <TConnection.hxx> 31 32 #include <com/sun/star/beans/PropertyAttribute.hpp> 33 #include <com/sun/star/sdbc/DataType.hpp> 34 #include <com/sun/star/sdbc/FetchDirection.hpp> 35 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp> 36 #include <com/sun/star/sdbc/ResultSetType.hpp> 37 #include <com/sun/star/sdbc/SQLException.hpp> 38 39 using namespace ::comphelper; 40 using namespace ::connectivity; 41 using namespace ::connectivity::firebird; 42 using namespace ::cppu; 43 using namespace ::dbtools; 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::sdbcx; 52 using namespace ::com::sun::star::container; 53 using namespace ::com::sun::star::io; 54 using namespace ::com::sun::star::util; 55 56 OResultSet::OResultSet(Connection* pConnection, 57 ::osl::Mutex& rMutex, 58 const uno::Reference< XInterface >& xStatement, 59 isc_stmt_handle aStatementHandle, 60 XSQLDA* pSqlda ) 61 : OResultSet_BASE(rMutex) 62 , OPropertyContainer(OResultSet_BASE::rBHelper) 63 , m_bIsBookmarkable(false) 64 , m_nFetchSize(1) 65 , m_nResultSetType(css::sdbc::ResultSetType::FORWARD_ONLY) 66 , m_nFetchDirection(css::sdbc::FetchDirection::FORWARD) 67 , m_nResultSetConcurrency(css::sdbc::ResultSetConcurrency::READ_ONLY) 68 , m_pConnection(pConnection) 69 , m_rMutex(rMutex) 70 , m_xStatement(xStatement) 71 , m_pSqlda(pSqlda) 72 , m_statementHandle(aStatementHandle) 73 , m_bWasNull(false) 74 , m_currentRow(0) 75 , m_bIsAfterLastRow(false) 76 , m_fieldCount(pSqlda? pSqlda->sqld : 0) 77 { 78 SAL_INFO("connectivity.firebird", "OResultSet()."); 79 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), 80 PROPERTY_ID_ISBOOKMARKABLE, 81 PropertyAttribute::READONLY, 82 &m_bIsBookmarkable, 83 cppu::UnoType<decltype(m_bIsBookmarkable)>::get()); 84 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), 85 PROPERTY_ID_FETCHSIZE, 86 PropertyAttribute::READONLY, 87 &m_nFetchSize, 88 cppu::UnoType<decltype(m_nFetchSize)>::get()); 89 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), 90 PROPERTY_ID_RESULTSETTYPE, 91 PropertyAttribute::READONLY, 92 &m_nResultSetType, 93 cppu::UnoType<decltype(m_nResultSetType)>::get()); 94 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), 95 PROPERTY_ID_FETCHDIRECTION, 96 PropertyAttribute::READONLY, 97 &m_nFetchDirection, 98 cppu::UnoType<decltype(m_nFetchDirection)>::get()); 99 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), 100 PROPERTY_ID_RESULTSETCONCURRENCY, 101 PropertyAttribute::READONLY, 102 &m_nResultSetConcurrency, 103 cppu::UnoType<decltype(m_nResultSetConcurrency)>::get()); 104 105 if (!pSqlda) 106 return; // TODO: what? 107 108 } 109 110 OResultSet::~OResultSet() 111 { 112 } 113 114 // ---- XResultSet -- Row retrieval methods ------------------------------------ 115 sal_Int32 SAL_CALL OResultSet::getRow() 116 { 117 MutexGuard aGuard(m_rMutex); 118 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 119 120 return m_currentRow; 121 } 122 123 sal_Bool SAL_CALL OResultSet::next() 124 { 125 MutexGuard aGuard(m_rMutex); 126 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 127 128 m_currentRow++; 129 130 ISC_STATUS fetchStat = isc_dsql_fetch(m_statusVector, 131 &m_statementHandle, 132 1, 133 m_pSqlda); 134 if (fetchStat == 0) // SUCCESSFUL 135 { 136 return true; 137 } 138 else if (fetchStat == 100) // END OF DATASET 139 { 140 m_bIsAfterLastRow = true; 141 return false; 142 } 143 else 144 { 145 SAL_WARN("connectivity.firebird", "Error when fetching data"); 146 // Throws sql exception as appropriate 147 evaluateStatusVector(m_statusVector, u"isc_dsql_fetch", *this); 148 return false; 149 } 150 } 151 152 sal_Bool SAL_CALL OResultSet::previous() 153 { 154 ::dbtools::throwFunctionNotSupportedSQLException("previous not supported in firebird", 155 *this); 156 return false; 157 } 158 159 sal_Bool SAL_CALL OResultSet::isLast() 160 { 161 ::dbtools::throwFunctionNotSupportedSQLException("isLast not supported in firebird", 162 *this); 163 return false; 164 } 165 166 sal_Bool SAL_CALL OResultSet::isBeforeFirst() 167 { 168 MutexGuard aGuard(m_rMutex); 169 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 170 171 return m_currentRow == 0; 172 } 173 174 sal_Bool SAL_CALL OResultSet::isAfterLast() 175 { 176 MutexGuard aGuard(m_rMutex); 177 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 178 179 return m_bIsAfterLastRow; 180 } 181 182 sal_Bool SAL_CALL OResultSet::isFirst() 183 { 184 MutexGuard aGuard(m_rMutex); 185 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 186 187 return m_currentRow == 1 && !m_bIsAfterLastRow; 188 } 189 190 void SAL_CALL OResultSet::beforeFirst() 191 { 192 MutexGuard aGuard(m_rMutex); 193 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 194 195 if (m_currentRow != 0) 196 ::dbtools::throwFunctionNotSupportedSQLException("beforeFirst not supported in firebird", 197 *this); 198 } 199 200 void SAL_CALL OResultSet::afterLast() 201 { 202 MutexGuard aGuard(m_rMutex); 203 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 204 205 if (!m_bIsAfterLastRow) 206 ::dbtools::throwFunctionNotSupportedSQLException("afterLast not supported in firebird", 207 *this); 208 } 209 210 sal_Bool SAL_CALL OResultSet::first() 211 { 212 MutexGuard aGuard(m_rMutex); 213 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 214 215 if (m_currentRow == 0) 216 { 217 return next(); 218 } 219 else if (m_currentRow == 1 && !m_bIsAfterLastRow) 220 { 221 return true; 222 } 223 else 224 { 225 ::dbtools::throwFunctionNotSupportedSQLException("first not supported in firebird", 226 *this); 227 return false; 228 } 229 } 230 231 sal_Bool SAL_CALL OResultSet::last() 232 { 233 // We need to iterate past the last row to know when we've passed the last 234 // row, hence we can't actually move to last. 235 ::dbtools::throwFunctionNotSupportedSQLException("last not supported in firebird", 236 *this); 237 return false; 238 } 239 240 sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 aRow) 241 { 242 MutexGuard aGuard(m_rMutex); 243 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 244 245 if (aRow > m_currentRow) 246 { 247 sal_Int32 aIterations = aRow - m_currentRow; 248 return relative(aIterations); 249 } 250 else 251 { 252 ::dbtools::throwFunctionNotSupportedSQLException("absolute not supported in firebird", 253 *this); 254 return false; 255 } 256 } 257 258 sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row) 259 { 260 MutexGuard aGuard(m_rMutex); 261 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 262 263 if (row > 0) 264 { 265 while (row--) 266 { 267 if (!next()) 268 return false; 269 } 270 return true; 271 } 272 else 273 { 274 ::dbtools::throwFunctionNotSupportedSQLException("relative not supported in firebird", 275 *this); 276 return false; 277 } 278 } 279 280 void OResultSet::checkColumnIndex(sal_Int32 nIndex) 281 { 282 MutexGuard aGuard(m_rMutex); 283 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 284 285 if( nIndex < 1 || nIndex > m_fieldCount ) 286 { 287 ::dbtools::throwSQLException( 288 "No column " + OUString::number(nIndex), 289 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND, 290 *this); 291 } 292 } 293 294 void OResultSet::checkRowIndex() 295 { 296 MutexGuard aGuard(m_rMutex); 297 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 298 299 if((m_currentRow < 1) || m_bIsAfterLastRow) 300 { 301 ::dbtools::throwSQLException( 302 "Invalid Row", 303 ::dbtools::StandardSQLState::INVALID_CURSOR_POSITION, 304 *this); 305 } 306 } 307 308 Any SAL_CALL OResultSet::queryInterface( const Type & rType ) 309 { 310 Any aRet = OPropertySetHelper::queryInterface(rType); 311 return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType); 312 } 313 314 Sequence< Type > SAL_CALL OResultSet::getTypes() 315 { 316 return concatSequences(OPropertySetHelper::getTypes(), OResultSet_BASE::getTypes()); 317 } 318 // ---- XColumnLocate --------------------------------------------------------- 319 sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& rColumnName) 320 { 321 MutexGuard aGuard(m_rMutex); 322 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 323 324 uno::Reference< XResultSetMetaData > xMeta = getMetaData(); 325 sal_Int32 nLen = xMeta->getColumnCount(); 326 sal_Int32 i; 327 328 for(i = 1; i<=nLen; ++i) 329 { 330 // We assume case sensitive, otherwise you'd have to test 331 // xMeta->isCaseSensitive and use qualsIgnoreAsciiCase as needed. 332 if (rColumnName == xMeta->getColumnName(i)) 333 return i; 334 } 335 336 ::dbtools::throwInvalidColumnException(rColumnName, *this); 337 assert(false); 338 return 0; // Never reached 339 } 340 341 uno::Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 ) 342 { 343 MutexGuard aGuard(m_rMutex); 344 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 345 346 return nullptr; 347 } 348 349 uno::Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 ) 350 { 351 MutexGuard aGuard(m_rMutex); 352 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 353 354 return nullptr; 355 } 356 357 // ---- Internal Utilities --------------------------------------------------- 358 bool OResultSet::isNull(const sal_Int32 nColumnIndex) 359 { 360 assert(nColumnIndex <= m_fieldCount); 361 XSQLVAR* pVar = m_pSqlda->sqlvar; 362 363 if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null 364 { 365 if (*pVar[nColumnIndex-1].sqlind == -1) 366 return true; 367 } 368 return false; 369 } 370 371 template <typename T> 372 OUString OResultSet::makeNumericString(const sal_Int32 nColumnIndex) 373 { 374 // minus because firebird stores scale as a negative number 375 int nDecimalCount = -(m_pSqlda->sqlvar[nColumnIndex-1].sqlscale); 376 if(nDecimalCount < 0) 377 { 378 // scale should be always positive 379 assert(false); 380 return OUString(); 381 } 382 383 OUStringBuffer sRetBuffer; 384 T nAllDigits = *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 385 sal_Int64 nDecimalCountExp = pow10Integer(nDecimalCount); 386 387 if(nAllDigits < 0) 388 { 389 sRetBuffer.append('-'); 390 nAllDigits = -nAllDigits; // abs 391 } 392 393 sRetBuffer.append(static_cast<sal_Int64>(nAllDigits / nDecimalCountExp) ); 394 if( nDecimalCount > 0) 395 { 396 sRetBuffer.append('.'); 397 398 sal_Int64 nFractionalPart = nAllDigits % nDecimalCountExp; 399 400 int iCount = 0; // digit count 401 sal_Int64 nFracTemp = nFractionalPart; 402 while(nFracTemp>0) 403 { 404 nFracTemp /= 10; 405 iCount++; 406 } 407 408 int nMissingNulls = nDecimalCount - iCount; 409 410 // append nulls after dot and before nFractionalPart 411 for(int i=0; i<nMissingNulls; i++) 412 { 413 sRetBuffer.append('0'); 414 } 415 416 // the rest 417 sRetBuffer.append(nFractionalPart); 418 } 419 420 return sRetBuffer.makeStringAndClear(); 421 } 422 423 template <typename T> 424 T OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) 425 { 426 m_bWasNull = isNull(nColumnIndex); 427 if (m_bWasNull) 428 return T(); 429 430 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == nType) 431 return *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 432 else 433 { 434 ORowSetValue row = retrieveValue< ORowSetValue >(nColumnIndex, 0); 435 if constexpr ( std::is_same_v<sal_Int64, T> ) 436 return row.getLong(); 437 else if constexpr ( std::is_same_v<sal_Int32, T> ) 438 return row.getInt32(); 439 else if constexpr ( std::is_same_v<sal_Int16, T> ) 440 return row.getInt16(); 441 else if constexpr ( std::is_same_v<float, T> ) 442 return row.getFloat(); 443 else if constexpr ( std::is_same_v<double, T> ) 444 return row.getDouble(); 445 else if constexpr ( std::is_same_v<bool, T> ) 446 return row.getBool(); 447 else 448 return row; 449 } 450 } 451 452 template <> 453 ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) 454 { 455 // See http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/Using_the_getXXX_Methods 456 // (bottom of page) for a chart of possible conversions, we should allow all 457 // of these -- Blob/Clob will probably need some specialist handling especially 458 // w.r.t. to generating Strings for them. 459 // 460 // Basically we just have to map to the correct direct request and 461 // ORowSetValue does the rest for us here. 462 int nSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype; 463 464 // TODO Firebird 3.0 does not set subtype (i.e. set to 0) for computed numeric/decimal value. 465 // It may change in the future. 466 // Imply numeric data type when subtype is 0 and scale is negative 467 if( nSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0 ) 468 nSqlSubType = 1; 469 470 switch (m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) 471 { 472 case SQL_TEXT: 473 case SQL_VARYING: 474 return getString(nColumnIndex); 475 case SQL_SHORT: 476 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal 477 return getString(nColumnIndex); 478 return getShort(nColumnIndex); 479 case SQL_LONG: 480 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal 481 return getString(nColumnIndex); 482 return getInt(nColumnIndex); 483 case SQL_FLOAT: 484 return getFloat(nColumnIndex); 485 case SQL_DOUBLE: 486 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal 487 return getString(nColumnIndex); 488 return getDouble(nColumnIndex); 489 case SQL_D_FLOAT: 490 return getFloat(nColumnIndex); 491 case SQL_TIMESTAMP: 492 return getTimestamp(nColumnIndex); 493 case SQL_TYPE_TIME: 494 return getTime(nColumnIndex); 495 case SQL_TYPE_DATE: 496 return getDate(nColumnIndex); 497 case SQL_INT64: 498 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal 499 return getString(nColumnIndex); 500 return getLong(nColumnIndex); 501 case SQL_BOOLEAN: 502 return ORowSetValue(bool(getBoolean(nColumnIndex))); 503 case SQL_BLOB: 504 case SQL_NULL: 505 case SQL_QUAD: 506 case SQL_ARRAY: 507 // TODO: these are all invalid conversions, so maybe we should 508 // throw an exception? 509 return ORowSetValue(); 510 default: 511 assert(false); 512 return ORowSetValue(); 513 } 514 } 515 516 template <> 517 Date OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) 518 { 519 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_DATE) 520 { 521 ISC_DATE aISCDate = *reinterpret_cast<ISC_DATE*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 522 523 struct tm aCTime; 524 isc_decode_sql_date(&aISCDate, &aCTime); 525 526 return Date(aCTime.tm_mday, aCTime.tm_mon + 1, aCTime.tm_year + 1900); 527 } 528 else 529 { 530 return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDate(); 531 } 532 } 533 534 template <> 535 Time OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) 536 { 537 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_TIME) 538 { 539 ISC_TIME aISCTime = *reinterpret_cast<ISC_TIME*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 540 541 struct tm aCTime; 542 isc_decode_sql_time(&aISCTime, &aCTime); 543 544 // First field is nanoseconds. 545 // last field denotes UTC (true) or unknown (false) 546 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION 547 // with no other funkiness, so we can get the fractional seconds easily. 548 return Time((aISCTime % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), 549 aCTime.tm_sec, aCTime.tm_min, aCTime.tm_hour, false); 550 } 551 else 552 { 553 return retrieveValue< ORowSetValue >(nColumnIndex, 0).getTime(); 554 } 555 } 556 557 template <> 558 DateTime OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) 559 { 560 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TIMESTAMP) 561 { 562 ISC_TIMESTAMP aISCTimestamp = *reinterpret_cast<ISC_TIMESTAMP*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 563 564 struct tm aCTime; 565 isc_decode_timestamp(&aISCTimestamp, &aCTime); 566 567 // Ditto here, see comment in previous function about ISC_TIME and ISC_TIME_SECONDS_PRECISION. 568 return DateTime((aISCTimestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), //nanoseconds 569 aCTime.tm_sec, 570 aCTime.tm_min, 571 aCTime.tm_hour, 572 aCTime.tm_mday, 573 aCTime.tm_mon + 1, // tm is from 0 to 11 574 aCTime.tm_year + 1900, //tm_year is the years since 1900 575 false); // denotes UTC (true), or unknown (false) 576 } 577 else 578 { 579 return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDateTime(); 580 } 581 } 582 583 template <> 584 OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) 585 { 586 // &~1 to remove the "can contain NULL" indicator 587 int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1; 588 int aSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype; 589 if (aSqlType == SQL_TEXT ) 590 { 591 return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata, 592 m_pSqlda->sqlvar[nColumnIndex-1].sqllen, 593 RTL_TEXTENCODING_UTF8); 594 } 595 else if (aSqlType == SQL_VARYING) 596 { 597 // First 2 bytes are a short containing the length of the string 598 // Under unclear conditions, it may be wrong and greater than sqllen. 599 sal_uInt16 aLength = *reinterpret_cast<sal_uInt16*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 600 // Use greater signed type sal_Int32 to get the minimum of two 16-bit values 601 return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata + 2, 602 std::min<sal_Int32>(aLength, m_pSqlda->sqlvar[nColumnIndex-1].sqllen), 603 RTL_TEXTENCODING_UTF8); 604 } 605 else if ((aSqlType == SQL_SHORT || aSqlType == SQL_LONG || 606 aSqlType == SQL_DOUBLE || aSqlType == SQL_INT64) 607 && (aSqlSubType == 1 || 608 aSqlSubType == 2 || 609 (aSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0) ) ) 610 { 611 // decimal and numeric types 612 switch(aSqlType) 613 { 614 case SQL_SHORT: 615 return makeNumericString<sal_Int16>(nColumnIndex); 616 case SQL_LONG: 617 return makeNumericString<sal_Int32>(nColumnIndex); 618 case SQL_DOUBLE: 619 // TODO FIXME 64 bits? 620 case SQL_INT64: 621 return makeNumericString<sal_Int64>(nColumnIndex); 622 default: 623 assert(false); 624 return OUString(); // never reached 625 } 626 } 627 else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) ) 628 { 629 uno::Reference<XClob> xClob = getClob(nColumnIndex); 630 return xClob->getSubString( 1, xClob->length() ); 631 } 632 else 633 { 634 return retrieveValue< ORowSetValue >(nColumnIndex, 0).getString(); 635 } 636 } 637 638 template <> 639 ISC_QUAD* OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) 640 { 641 // TODO: this is probably wrong 642 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) != nType) 643 throw SQLException(); // TODO: better exception (can't convert Blob) 644 645 return reinterpret_cast<ISC_QUAD*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); 646 } 647 648 template <typename T> 649 T OResultSet::safelyRetrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) 650 { 651 MutexGuard aGuard(m_rMutex); 652 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 653 654 checkColumnIndex(nColumnIndex); 655 checkRowIndex(); 656 657 m_bWasNull = isNull(nColumnIndex); 658 if (m_bWasNull) 659 return T(); 660 661 return retrieveValue< T >(nColumnIndex, nType); 662 } 663 664 // ---- XRow ----------------------------------------------------------------- 665 sal_Bool SAL_CALL OResultSet::wasNull() 666 { 667 MutexGuard aGuard(m_rMutex); 668 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 669 670 return m_bWasNull; 671 } 672 673 // ---- XRow: Simple Numerical types ------------------------------------------ 674 sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex) 675 { 676 return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN); 677 } 678 679 sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 nColumnIndex) 680 { 681 // Not a native firebird type hence we always have to convert. 682 return safelyRetrieveValue< ORowSetValue >(nColumnIndex).getInt8(); 683 } 684 685 Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes(sal_Int32 nColumnIndex) 686 { 687 // &~1 to remove the "can contain NULL" indicator 688 int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1; 689 if ( aSqlType == SQL_BLOB ) 690 { 691 Reference< XBlob> xBlob = getBlob(nColumnIndex); 692 if (xBlob.is()) 693 { 694 const sal_Int64 aBlobLength = xBlob->length(); 695 if (aBlobLength > SAL_MAX_INT32) 696 { 697 SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32); 698 return xBlob->getBytes(1, SAL_MAX_INT32); 699 } 700 return xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength)); 701 } 702 else 703 return Sequence< sal_Int8 >(); 704 } 705 // TODO implement SQL_VARYING and SQL_TEXT 706 // as it's the counterpart as OPreparedStatement::setBytes 707 else 708 { 709 return Sequence< sal_Int8 >(); // TODO: implement 710 } 711 } 712 713 sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex) 714 { 715 return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT); 716 } 717 718 sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex) 719 { 720 return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG); 721 } 722 723 sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex) 724 { 725 return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64); 726 } 727 728 float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex) 729 { 730 return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT); 731 } 732 733 double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex) 734 { 735 return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE); 736 } 737 738 // ---- XRow: More complex types ---------------------------------------------- 739 OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex) 740 { 741 return safelyRetrieveValue< OUString >(nIndex); 742 } 743 744 Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex) 745 { 746 return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE); 747 } 748 749 Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex) 750 { 751 return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME); 752 } 753 754 DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex) 755 { 756 return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP); 757 } 758 759 760 uno::Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( ) 761 { 762 MutexGuard aGuard(m_rMutex); 763 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 764 765 if(!m_xMetaData.is()) 766 m_xMetaData = new OResultSetMetaData(m_pConnection 767 , m_pSqlda); 768 return m_xMetaData; 769 } 770 771 uno::Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 ) 772 { 773 MutexGuard aGuard(m_rMutex); 774 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 775 776 return nullptr; 777 } 778 779 780 uno::Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 columnIndex ) 781 { 782 MutexGuard aGuard(m_rMutex); 783 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 784 785 int aSqlSubType = m_pSqlda->sqlvar[columnIndex-1].sqlsubtype; 786 787 SAL_WARN_IF(aSqlSubType != 1, 788 "connectivity.firebird", "wrong subtype, not a textual blob"); 789 790 ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB); 791 if (!pBlobID) 792 return nullptr; 793 return m_pConnection->createClob(pBlobID); 794 } 795 796 uno::Reference< XBlob > SAL_CALL OResultSet::getBlob(sal_Int32 columnIndex) 797 { 798 MutexGuard aGuard(m_rMutex); 799 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 800 801 // TODO: CLOB etc. should be valid here too, but we probably want some more 802 // cleverness around this. 803 ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB); 804 if (!pBlobID) 805 return nullptr; 806 return m_pConnection->createBlob(pBlobID); 807 } 808 809 810 uno::Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 ) 811 { 812 MutexGuard aGuard(m_rMutex); 813 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 814 815 return nullptr; 816 } 817 818 819 Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::container::XNameAccess >& ) 820 { 821 MutexGuard aGuard(m_rMutex); 822 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 823 824 return Any(); 825 } 826 827 828 void SAL_CALL OResultSet::close() 829 { 830 SAL_INFO("connectivity.firebird", "close()."); 831 832 { 833 MutexGuard aGuard(m_rMutex); 834 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 835 } 836 dispose(); 837 } 838 839 840 uno::Reference< XInterface > SAL_CALL OResultSet::getStatement() 841 { 842 MutexGuard aGuard(m_rMutex); 843 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 844 845 return m_xStatement; 846 } 847 //----- XResultSet: unsupported change detection methods --------------------- 848 sal_Bool SAL_CALL OResultSet::rowDeleted() 849 { 850 ::dbtools::throwFunctionNotSupportedSQLException("rowDeleted not supported in firebird", 851 *this); 852 return false; 853 } 854 sal_Bool SAL_CALL OResultSet::rowInserted() 855 { 856 ::dbtools::throwFunctionNotSupportedSQLException("rowInserted not supported in firebird", 857 *this); 858 return false; 859 } 860 861 sal_Bool SAL_CALL OResultSet::rowUpdated() 862 { 863 ::dbtools::throwFunctionNotSupportedSQLException("rowUpdated not supported in firebird", 864 *this); 865 return false; 866 } 867 868 void SAL_CALL OResultSet::refreshRow() 869 { 870 ::dbtools::throwFunctionNotSupportedSQLException("refreshRow not supported in firebird", 871 *this); 872 } 873 874 875 void SAL_CALL OResultSet::cancel( ) 876 { 877 MutexGuard aGuard(m_rMutex); 878 checkDisposed(OResultSet_BASE::rBHelper.bDisposed); 879 880 } 881 882 //----- OIdPropertyArrayUsageHelper ------------------------------------------ 883 IPropertyArrayHelper* OResultSet::createArrayHelper() const 884 { 885 Sequence< Property > aProperties; 886 describeProperties(aProperties); 887 return new ::cppu::OPropertyArrayHelper(aProperties); 888 } 889 890 IPropertyArrayHelper & OResultSet::getInfoHelper() 891 { 892 return *getArrayHelper(); 893 } 894 895 void SAL_CALL OResultSet::acquire() noexcept 896 { 897 OResultSet_BASE::acquire(); 898 } 899 900 void SAL_CALL OResultSet::release() noexcept 901 { 902 OResultSet_BASE::release(); 903 } 904 905 uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) 906 { 907 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); 908 } 909 910 // ---- XServiceInfo ----------------------------------------------------------- 911 OUString SAL_CALL OResultSet::getImplementationName() 912 { 913 return "com.sun.star.sdbcx.firebird.ResultSet"; 914 } 915 916 Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames() 917 { 918 return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"}; 919 } 920 921 sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName) 922 { 923 return cppu::supportsService(this, _rServiceName); 924 } 925 926 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ 927
