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