bump product version to 7.2.5.1
[LibreOffice.git] / connectivity / source / drivers / firebird / ResultSet.cxx
blob17e87cf8a55df7dfb9ae814ccf48f66077ce8464
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.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "ResultSet.hxx"
21 #include "ResultSetMetaData.hxx"
22 #include "Util.hxx"
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>
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>
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;
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;
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)
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());
105 if (!pSqlda)
106 return; // TODO: what?
110 OResultSet::~OResultSet()
114 // ---- XResultSet -- Row retrieval methods ------------------------------------
115 sal_Int32 SAL_CALL OResultSet::getRow()
117 MutexGuard aGuard(m_rMutex);
118 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
120 return m_currentRow;
123 sal_Bool SAL_CALL OResultSet::next()
125 MutexGuard aGuard(m_rMutex);
126 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
128 m_currentRow++;
130 ISC_STATUS fetchStat = isc_dsql_fetch(m_statusVector,
131 &m_statementHandle,
133 m_pSqlda);
134 if (fetchStat == 0) // SUCCESSFUL
136 return true;
138 else if (fetchStat == 100) // END OF DATASET
140 m_bIsAfterLastRow = true;
141 return false;
143 else
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;
152 sal_Bool SAL_CALL OResultSet::previous()
154 ::dbtools::throwFunctionNotSupportedSQLException("previous not supported in firebird",
155 *this);
156 return false;
159 sal_Bool SAL_CALL OResultSet::isLast()
161 ::dbtools::throwFunctionNotSupportedSQLException("isLast not supported in firebird",
162 *this);
163 return false;
166 sal_Bool SAL_CALL OResultSet::isBeforeFirst()
168 MutexGuard aGuard(m_rMutex);
169 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
171 return m_currentRow == 0;
174 sal_Bool SAL_CALL OResultSet::isAfterLast()
176 MutexGuard aGuard(m_rMutex);
177 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
179 return m_bIsAfterLastRow;
182 sal_Bool SAL_CALL OResultSet::isFirst()
184 MutexGuard aGuard(m_rMutex);
185 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
187 return m_currentRow == 1 && !m_bIsAfterLastRow;
190 void SAL_CALL OResultSet::beforeFirst()
192 MutexGuard aGuard(m_rMutex);
193 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
195 if (m_currentRow != 0)
196 ::dbtools::throwFunctionNotSupportedSQLException("beforeFirst not supported in firebird",
197 *this);
200 void SAL_CALL OResultSet::afterLast()
202 MutexGuard aGuard(m_rMutex);
203 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
205 if (!m_bIsAfterLastRow)
206 ::dbtools::throwFunctionNotSupportedSQLException("afterLast not supported in firebird",
207 *this);
210 sal_Bool SAL_CALL OResultSet::first()
212 MutexGuard aGuard(m_rMutex);
213 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
215 if (m_currentRow == 0)
217 return next();
219 else if (m_currentRow == 1 && !m_bIsAfterLastRow)
221 return true;
223 else
225 ::dbtools::throwFunctionNotSupportedSQLException("first not supported in firebird",
226 *this);
227 return false;
231 sal_Bool SAL_CALL OResultSet::last()
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;
240 sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 aRow)
242 MutexGuard aGuard(m_rMutex);
243 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
245 if (aRow > m_currentRow)
247 sal_Int32 aIterations = aRow - m_currentRow;
248 return relative(aIterations);
250 else
252 ::dbtools::throwFunctionNotSupportedSQLException("absolute not supported in firebird",
253 *this);
254 return false;
258 sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row)
260 MutexGuard aGuard(m_rMutex);
261 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
263 if (row > 0)
265 while (row--)
267 if (!next())
268 return false;
270 return true;
272 else
274 ::dbtools::throwFunctionNotSupportedSQLException("relative not supported in firebird",
275 *this);
276 return false;
280 void OResultSet::checkColumnIndex(sal_Int32 nIndex)
282 MutexGuard aGuard(m_rMutex);
283 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
285 if( nIndex < 1 || nIndex > m_fieldCount )
287 ::dbtools::throwSQLException(
288 "No column " + OUString::number(nIndex),
289 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
290 *this);
294 void OResultSet::checkRowIndex()
296 MutexGuard aGuard(m_rMutex);
297 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
299 if((m_currentRow < 1) || m_bIsAfterLastRow)
301 ::dbtools::throwSQLException(
302 "Invalid Row",
303 ::dbtools::StandardSQLState::INVALID_CURSOR_POSITION,
304 *this);
308 Any SAL_CALL OResultSet::queryInterface( const Type & rType )
310 Any aRet = OPropertySetHelper::queryInterface(rType);
311 return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType);
314 Sequence< Type > SAL_CALL OResultSet::getTypes()
316 return concatSequences(OPropertySetHelper::getTypes(), OResultSet_BASE::getTypes());
318 // ---- XColumnLocate ---------------------------------------------------------
319 sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& rColumnName)
321 MutexGuard aGuard(m_rMutex);
322 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
324 uno::Reference< XResultSetMetaData > xMeta = getMetaData();
325 sal_Int32 nLen = xMeta->getColumnCount();
326 sal_Int32 i;
328 for(i = 1; i<=nLen; ++i)
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;
336 ::dbtools::throwInvalidColumnException(rColumnName, *this);
337 assert(false);
338 return 0; // Never reached
341 uno::Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 )
343 MutexGuard aGuard(m_rMutex);
344 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
346 return nullptr;
349 uno::Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 )
351 MutexGuard aGuard(m_rMutex);
352 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
354 return nullptr;
357 // ---- Internal Utilities ---------------------------------------------------
358 bool OResultSet::isNull(const sal_Int32 nColumnIndex)
360 assert(nColumnIndex <= m_fieldCount);
361 XSQLVAR* pVar = m_pSqlda->sqlvar;
363 if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null
365 if (*pVar[nColumnIndex-1].sqlind == -1)
366 return true;
368 return false;
371 template <typename T>
372 OUString OResultSet::makeNumericString(const sal_Int32 nColumnIndex)
374 // minus because firebird stores scale as a negative number
375 int nDecimalCount = -(m_pSqlda->sqlvar[nColumnIndex-1].sqlscale);
376 if(nDecimalCount < 0)
378 // scale should be always positive
379 assert(false);
380 return OUString();
383 OUStringBuffer sRetBuffer;
384 T nAllDigits = *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
385 sal_Int64 nDecimalCountExp = pow10Integer(nDecimalCount);
387 if(nAllDigits < 0)
389 sRetBuffer.append('-');
390 nAllDigits = -nAllDigits; // abs
393 sRetBuffer.append(static_cast<sal_Int64>(nAllDigits / nDecimalCountExp) );
394 if( nDecimalCount > 0)
396 sRetBuffer.append('.');
398 sal_Int64 nFractionalPart = nAllDigits % nDecimalCountExp;
400 int iCount = 0; // digit count
401 sal_Int64 nFracTemp = nFractionalPart;
402 while(nFracTemp>0)
404 nFracTemp /= 10;
405 iCount++;
408 int nMissingNulls = nDecimalCount - iCount;
410 // append nulls after dot and before nFractionalPart
411 for(int i=0; i<nMissingNulls; i++)
413 sRetBuffer.append('0');
416 // the rest
417 sRetBuffer.append(nFractionalPart);
420 return sRetBuffer.makeStringAndClear();
423 template <typename T>
424 T OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
426 m_bWasNull = isNull(nColumnIndex);
427 if (m_bWasNull)
428 return T();
430 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == nType)
431 return *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
432 else
433 return retrieveValue< ORowSetValue >(nColumnIndex, 0);
436 template <>
437 ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
439 // See http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/Using_the_getXXX_Methods
440 // (bottom of page) for a chart of possible conversions, we should allow all
441 // of these -- Blob/Clob will probably need some specialist handling especially
442 // w.r.t. to generating Strings for them.
444 // Basically we just have to map to the correct direct request and
445 // ORowSetValue does the rest for us here.
446 int nSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
448 // TODO Firebird 3.0 does not set subtype (i.e. set to 0) for computed numeric/decimal value.
449 // It may change in the future.
450 // Imply numeric data type when subtype is 0 and scale is negative
451 if( nSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0 )
452 nSqlSubType = 1;
454 switch (m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1)
456 case SQL_TEXT:
457 case SQL_VARYING:
458 return getString(nColumnIndex);
459 case SQL_SHORT:
460 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
461 return getString(nColumnIndex);
462 return getShort(nColumnIndex);
463 case SQL_LONG:
464 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
465 return getString(nColumnIndex);
466 return getInt(nColumnIndex);
467 case SQL_FLOAT:
468 return getFloat(nColumnIndex);
469 case SQL_DOUBLE:
470 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
471 return getString(nColumnIndex);
472 return getDouble(nColumnIndex);
473 case SQL_D_FLOAT:
474 return getFloat(nColumnIndex);
475 case SQL_TIMESTAMP:
476 return getTimestamp(nColumnIndex);
477 case SQL_TYPE_TIME:
478 return getTime(nColumnIndex);
479 case SQL_TYPE_DATE:
480 return getDate(nColumnIndex);
481 case SQL_INT64:
482 if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
483 return getString(nColumnIndex);
484 return getLong(nColumnIndex);
485 case SQL_BOOLEAN:
486 return ORowSetValue(bool(getBoolean(nColumnIndex)));
487 case SQL_BLOB:
488 case SQL_NULL:
489 case SQL_QUAD:
490 case SQL_ARRAY:
491 // TODO: these are all invalid conversions, so maybe we should
492 // throw an exception?
493 return ORowSetValue();
494 default:
495 assert(false);
496 return ORowSetValue();
500 template <>
501 Date OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
503 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_DATE)
505 ISC_DATE aISCDate = *reinterpret_cast<ISC_DATE*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
507 struct tm aCTime;
508 isc_decode_sql_date(&aISCDate, &aCTime);
510 return Date(aCTime.tm_mday, aCTime.tm_mon + 1, aCTime.tm_year + 1900);
512 else
514 return retrieveValue< ORowSetValue >(nColumnIndex, 0);
518 template <>
519 Time OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
521 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_TIME)
523 ISC_TIME aISCTime = *reinterpret_cast<ISC_TIME*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
525 struct tm aCTime;
526 isc_decode_sql_time(&aISCTime, &aCTime);
528 // First field is nanoseconds.
529 // last field denotes UTC (true) or unknown (false)
530 // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION
531 // with no other funkiness, so we can get the fractional seconds easily.
532 return Time((aISCTime % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION),
533 aCTime.tm_sec, aCTime.tm_min, aCTime.tm_hour, false);
535 else
537 return retrieveValue< ORowSetValue >(nColumnIndex, 0);
541 template <>
542 DateTime OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
544 if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TIMESTAMP)
546 ISC_TIMESTAMP aISCTimestamp = *reinterpret_cast<ISC_TIMESTAMP*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
548 struct tm aCTime;
549 isc_decode_timestamp(&aISCTimestamp, &aCTime);
551 // Ditto here, see comment in previous function about ISC_TIME and ISC_TIME_SECONDS_PRECISION.
552 return DateTime((aISCTimestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), //nanoseconds
553 aCTime.tm_sec,
554 aCTime.tm_min,
555 aCTime.tm_hour,
556 aCTime.tm_mday,
557 aCTime.tm_mon + 1, // tm is from 0 to 11
558 aCTime.tm_year + 1900, //tm_year is the years since 1900
559 false); // denotes UTC (true), or unknown (false)
561 else
563 return retrieveValue< ORowSetValue >(nColumnIndex, 0);
567 template <>
568 OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
570 // &~1 to remove the "can contain NULL" indicator
571 int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
572 int aSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
573 if (aSqlType == SQL_TEXT )
575 return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata,
576 m_pSqlda->sqlvar[nColumnIndex-1].sqllen,
577 RTL_TEXTENCODING_UTF8);
579 else if (aSqlType == SQL_VARYING)
581 // First 2 bytes are a short containing the length of the string
582 // Under unclear conditions, it may be wrong and greater than sqllen.
583 sal_uInt16 aLength = *reinterpret_cast<sal_uInt16*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
584 // Use greater signed type sal_Int32 to get the minimum of two 16-bit values
585 return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata + 2,
586 std::min<sal_Int32>(aLength, m_pSqlda->sqlvar[nColumnIndex-1].sqllen),
587 RTL_TEXTENCODING_UTF8);
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) ) )
595 // decimal and numeric types
596 switch(aSqlType)
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
611 else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) )
613 uno::Reference<XClob> xClob = getClob(nColumnIndex);
614 return xClob->getSubString( 0, xClob->length() );
616 else
618 return retrieveValue< ORowSetValue >(nColumnIndex, 0);
622 template <>
623 ISC_QUAD* OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
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)
629 return reinterpret_cast<ISC_QUAD*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
632 template <typename T>
633 T OResultSet::safelyRetrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
635 MutexGuard aGuard(m_rMutex);
636 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
638 checkColumnIndex(nColumnIndex);
639 checkRowIndex();
641 m_bWasNull = isNull(nColumnIndex);
642 if (m_bWasNull)
643 return T();
645 return retrieveValue< T >(nColumnIndex, nType);
648 // ---- XRow -----------------------------------------------------------------
649 sal_Bool SAL_CALL OResultSet::wasNull()
651 MutexGuard aGuard(m_rMutex);
652 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
654 return m_bWasNull;
657 // ---- XRow: Simple Numerical types ------------------------------------------
658 sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex)
660 return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN);
663 sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 nColumnIndex)
665 // Not a native firebird type hence we always have to convert.
666 return safelyRetrieveValue< ORowSetValue >(nColumnIndex);
669 Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes(sal_Int32 nColumnIndex)
671 // &~1 to remove the "can contain NULL" indicator
672 int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
673 if ( aSqlType == SQL_BLOB )
675 Reference< XBlob> xBlob = getBlob(nColumnIndex);
676 if (xBlob.is())
678 const sal_Int64 aBlobLength = xBlob->length();
679 if (aBlobLength > SAL_MAX_INT32)
681 SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32);
682 return xBlob->getBytes(1, SAL_MAX_INT32);
684 return xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength));
686 else
687 return Sequence< sal_Int8 >();
689 // TODO implement SQL_VARYING and SQL_TEXT
690 // as it's the counterpart as OPreparedStatement::setBytes
691 else
693 return Sequence< sal_Int8 >(); // TODO: implement
697 sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex)
699 return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT);
702 sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex)
704 return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG);
707 sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex)
709 return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64);
712 float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex)
714 return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT);
717 double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex)
719 return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE);
722 // ---- XRow: More complex types ----------------------------------------------
723 OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex)
725 return safelyRetrieveValue< OUString >(nIndex);
728 Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex)
730 return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE);
733 Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex)
735 return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME);
738 DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex)
740 return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP);
744 uno::Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( )
746 MutexGuard aGuard(m_rMutex);
747 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
749 if(!m_xMetaData.is())
750 m_xMetaData = new OResultSetMetaData(m_pConnection
751 , m_pSqlda);
752 return m_xMetaData;
755 uno::Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 )
757 MutexGuard aGuard(m_rMutex);
758 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
760 return nullptr;
764 uno::Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 columnIndex )
766 MutexGuard aGuard(m_rMutex);
767 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
769 int aSqlSubType = m_pSqlda->sqlvar[columnIndex-1].sqlsubtype;
771 SAL_WARN_IF(aSqlSubType != 1,
772 "connectivity.firebird", "wrong subtype, not a textual blob");
774 ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
775 if (!pBlobID)
776 return nullptr;
777 return m_pConnection->createClob(pBlobID);
780 uno::Reference< XBlob > SAL_CALL OResultSet::getBlob(sal_Int32 columnIndex)
782 MutexGuard aGuard(m_rMutex);
783 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
785 // TODO: CLOB etc. should be valid here too, but we probably want some more
786 // cleverness around this.
787 ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
788 if (!pBlobID)
789 return nullptr;
790 return m_pConnection->createBlob(pBlobID);
794 uno::Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 )
796 MutexGuard aGuard(m_rMutex);
797 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
799 return nullptr;
803 Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::container::XNameAccess >& )
805 MutexGuard aGuard(m_rMutex);
806 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
808 return Any();
812 void SAL_CALL OResultSet::close()
814 SAL_INFO("connectivity.firebird", "close().");
817 MutexGuard aGuard(m_rMutex);
818 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
820 dispose();
824 uno::Reference< XInterface > SAL_CALL OResultSet::getStatement()
826 MutexGuard aGuard(m_rMutex);
827 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
829 return m_xStatement;
831 //----- XResultSet: unsupported change detection methods ---------------------
832 sal_Bool SAL_CALL OResultSet::rowDeleted()
834 ::dbtools::throwFunctionNotSupportedSQLException("rowDeleted not supported in firebird",
835 *this);
836 return false;
838 sal_Bool SAL_CALL OResultSet::rowInserted()
840 ::dbtools::throwFunctionNotSupportedSQLException("rowInserted not supported in firebird",
841 *this);
842 return false;
845 sal_Bool SAL_CALL OResultSet::rowUpdated()
847 ::dbtools::throwFunctionNotSupportedSQLException("rowUpdated not supported in firebird",
848 *this);
849 return false;
852 void SAL_CALL OResultSet::refreshRow()
854 ::dbtools::throwFunctionNotSupportedSQLException("refreshRow not supported in firebird",
855 *this);
859 void SAL_CALL OResultSet::cancel( )
861 MutexGuard aGuard(m_rMutex);
862 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
866 //----- OIdPropertyArrayUsageHelper ------------------------------------------
867 IPropertyArrayHelper* OResultSet::createArrayHelper() const
869 Sequence< Property > aProperties;
870 describeProperties(aProperties);
871 return new ::cppu::OPropertyArrayHelper(aProperties);
874 IPropertyArrayHelper & OResultSet::getInfoHelper()
876 return *getArrayHelper();
879 void SAL_CALL OResultSet::acquire() noexcept
881 OResultSet_BASE::acquire();
884 void SAL_CALL OResultSet::release() noexcept
886 OResultSet_BASE::release();
889 uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( )
891 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
894 // ---- XServiceInfo -----------------------------------------------------------
895 OUString SAL_CALL OResultSet::getImplementationName()
897 return "com.sun.star.sdbcx.firebird.ResultSet";
900 Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames()
902 return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"};
905 sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName)
907 return cppu::supportsService(this, _rServiceName);
910 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */