LibreOffice Module connectivity (master) 1
firebird/ResultSet.cxx
Go to the documentation of this file.
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
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
39using namespace ::comphelper;
40using namespace ::connectivity;
41using namespace ::connectivity::firebird;
42using namespace ::cppu;
43using namespace ::dbtools;
44using namespace ::osl;
45
46using namespace ::com::sun::star;
47using namespace ::com::sun::star::uno;
48using namespace ::com::sun::star::lang;
49using namespace ::com::sun::star::beans;
50using namespace ::com::sun::star::sdbc;
51using namespace ::com::sun::star::sdbcx;
52using namespace ::com::sun::star::container;
53using namespace ::com::sun::star::io;
54using namespace ::com::sun::star::util;
55
56OResultSet::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),
81 PropertyAttribute::READONLY,
82 &m_bIsBookmarkable,
83 cppu::UnoType<decltype(m_bIsBookmarkable)>::get());
84 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
86 PropertyAttribute::READONLY,
87 &m_nFetchSize,
88 cppu::UnoType<decltype(m_nFetchSize)>::get());
89 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
91 PropertyAttribute::READONLY,
92 &m_nResultSetType,
93 cppu::UnoType<decltype(m_nResultSetType)>::get());
94 registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
96 PropertyAttribute::READONLY,
97 &m_nFetchDirection,
98 cppu::UnoType<decltype(m_nFetchDirection)>::get());
99 registerProperty(OMetaConnection::getPropMap().getNameByIndex(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
110OResultSet::~OResultSet()
111{
112}
113
114// ---- XResultSet -- Row retrieval methods ------------------------------------
115sal_Int32 SAL_CALL OResultSet::getRow()
116{
117 MutexGuard aGuard(m_rMutex);
118 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
119
120 return m_currentRow;
121}
122
123sal_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
152sal_Bool SAL_CALL OResultSet::previous()
153{
154 ::dbtools::throwFunctionNotSupportedSQLException("previous not supported in firebird",
155 *this);
156 return false;
157}
158
159sal_Bool SAL_CALL OResultSet::isLast()
160{
161 ::dbtools::throwFunctionNotSupportedSQLException("isLast not supported in firebird",
162 *this);
163 return false;
164}
165
166sal_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
174sal_Bool SAL_CALL OResultSet::isAfterLast()
175{
176 MutexGuard aGuard(m_rMutex);
177 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
178
179 return m_bIsAfterLastRow;
180}
181
182sal_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
190void 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
200void 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
210sal_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
231sal_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
240sal_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
258sal_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
280void 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 {
288 "No column " + OUString::number(nIndex),
289 ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
290 *this);
291 }
292}
293
294void OResultSet::checkRowIndex()
295{
296 MutexGuard aGuard(m_rMutex);
297 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
298
299 if((m_currentRow < 1) || m_bIsAfterLastRow)
300 {
302 "Invalid Row",
303 ::dbtools::StandardSQLState::INVALID_CURSOR_POSITION,
304 *this);
305 }
306}
307
308Any 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 ---------------------------------------------------------
319sal_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
341uno::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
349uno::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 ---------------------------------------------------
358bool 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
371template <typename T>
372OUString 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
423template <typename T>
424T 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
452template <>
453ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
454{
455 // See https://wiki.documentfoundation.org/Documentation/DevGuide/Database_Access#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
516template <>
517Date 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
534template <>
535Time 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
557template <>
558DateTime 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
583template <>
584OUString 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
638template <>
639ISC_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
648template <typename T>
649T 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 -----------------------------------------------------------------
665sal_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 ------------------------------------------
674sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex)
675{
676 return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN);
677}
678
679sal_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
685Sequence< 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
713sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex)
714{
715 return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT);
716}
717
718sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex)
719{
720 return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG);
721}
722
723sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex)
724{
725 return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64);
726}
727
728float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex)
729{
730 return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT);
731}
732
733double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex)
734{
735 return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE);
736}
737
738// ---- XRow: More complex types ----------------------------------------------
739OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex)
740{
741 return safelyRetrieveValue< OUString >(nIndex);
742}
743
744Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex)
745{
746 return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE);
747}
748
749Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex)
750{
751 return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME);
752}
753
754DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex)
755{
756 return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP);
757}
758
759
760uno::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
771uno::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
780uno::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
796uno::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
810uno::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
819Any 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
828void 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
840uno::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 ---------------------
848sal_Bool SAL_CALL OResultSet::rowDeleted()
849{
850 ::dbtools::throwFunctionNotSupportedSQLException("rowDeleted not supported in firebird",
851 *this);
852 return false;
853}
854sal_Bool SAL_CALL OResultSet::rowInserted()
855{
856 ::dbtools::throwFunctionNotSupportedSQLException("rowInserted not supported in firebird",
857 *this);
858 return false;
859}
860
861sal_Bool SAL_CALL OResultSet::rowUpdated()
862{
863 ::dbtools::throwFunctionNotSupportedSQLException("rowUpdated not supported in firebird",
864 *this);
865 return false;
866}
867
868void SAL_CALL OResultSet::refreshRow()
869{
870 ::dbtools::throwFunctionNotSupportedSQLException("refreshRow not supported in firebird",
871 *this);
872}
873
874
875void SAL_CALL OResultSet::cancel( )
876{
877 MutexGuard aGuard(m_rMutex);
878 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
879
880}
881
882//----- OIdPropertyArrayUsageHelper ------------------------------------------
883IPropertyArrayHelper* OResultSet::createArrayHelper() const
884{
885 Sequence< Property > aProperties;
886 describeProperties(aProperties);
887 return new ::cppu::OPropertyArrayHelper(aProperties);
888}
889
890IPropertyArrayHelper & OResultSet::getInfoHelper()
891{
892 return *getArrayHelper();
893}
894
895void SAL_CALL OResultSet::acquire() noexcept
896{
897 OResultSet_BASE::acquire();
898}
899
900void SAL_CALL OResultSet::release() noexcept
901{
902 OResultSet_BASE::release();
903}
904
905uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( )
906{
907 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
908}
909
910// ---- XServiceInfo -----------------------------------------------------------
911OUString SAL_CALL OResultSet::getImplementationName()
912{
913 return "com.sun.star.sdbcx.firebird.ResultSet";
914}
915
916Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames()
917{
918 return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"};
919}
920
921sal_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: */
PropertiesInfo aProperties
sal_Int32 getInt32() const
Definition: FValue.cxx:1377
float getFloat() const
Definition: FValue.cxx:1669
sal_Int16 getInt16() const
Definition: FValue.cxx:1235
sal_Int64 getLong() const
Definition: FValue.cxx:1523
double getDouble() const
Definition: FValue.cxx:1745
::osl::Mutex & m_rMutex
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
css::uno::Sequence< T > concatSequences(const css::uno::Sequence< T > &rS1, const Ss &... rSn)
double getDouble(const Any &_rAny)
float getFloat(const Any &_rAny)
OUString getString(const Any &_rAny)
Type
::cppu::WeakComponentImplHelper< css::sdbc::XResultSet, css::sdbc::XRow, css::sdbc::XResultSetMetaDataSupplier, css::util::XCancellable, css::sdbc::XWarningsSupplier, css::sdbc::XCloseable, css::sdbc::XColumnLocate, css::lang::XServiceInfo > OResultSet_BASE
Definition: NResultSet.hxx:72
sal_Int64 pow10Integer(int nDecimalCount)
void evaluateStatusVector(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view aCause, const css::uno::Reference< css::uno::XInterface > &_rxContext)
Evaluate a firebird status vector and throw exceptions as necessary.
void checkDisposed(bool _bThrow)
Definition: dbtools.cxx:1951
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
void throwFunctionNotSupportedSQLException(const OUString &_rFunctionName, const css::uno::Reference< css::uno::XInterface > &_rxContext)
throws an exception with SQL state IM001, saying that a certain function is not supported
void throwSQLException(const OUString &_rMessage, const OUString &_rSQLState, const Reference< XInterface > &_rxContext, const sal_Int32 _nErrorCode)
void throwInvalidColumnException(const OUString &_rColumnName, const Reference< XInterface > &_rxContext)
int i
void dispose()
#define PROPERTY_ID_ISBOOKMARKABLE
Definition: propertyids.hxx:82
#define PROPERTY_ID_RESULTSETTYPE
Definition: propertyids.hxx:44
#define PROPERTY_ID_RESULTSETCONCURRENCY
Definition: propertyids.hxx:43
#define PROPERTY_ID_FETCHSIZE
Definition: propertyids.hxx:46
#define PROPERTY_ID_FETCHDIRECTION
Definition: propertyids.hxx:45
QPRO_FUNC_TYPE nType
TransliterationModules tm
unsigned char sal_Bool
signed char sal_Int8