/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "ResultSet.hxx"
#include "ResultSetMetaData.hxx"
#include "Util.hxx"
#include <comphelper/sequence.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <connectivity/dbexception.hxx>
#include <propertyids.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <TConnection.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/FetchDirection.hpp>
#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
using namespace ::comphelper;
using namespace ::connectivity;
using namespace ::connectivity::firebird;
using namespace ::cppu;
using namespace ::dbtools;
using namespace ::osl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::util;
OResultSet::OResultSet(Connection* pConnection,
::osl::Mutex& rMutex,
const uno::Reference< XInterface >& xStatement,
isc_stmt_handle aStatementHandle,
XSQLDA* pSqlda )
: OResultSet_BASE(rMutex)
, OPropertyContainer(OResultSet_BASE::rBHelper)
, m_bIsBookmarkable(false)
, m_nFetchSize(1)
, m_nResultSetType(css::sdbc::ResultSetType::FORWARD_ONLY)
, m_nFetchDirection(css::sdbc::FetchDirection::FORWARD)
, m_nResultSetConcurrency(css::sdbc::ResultSetConcurrency::READ_ONLY)
, m_pConnection(pConnection)
, m_rMutex(rMutex)
, m_xStatement(xStatement)
, m_pSqlda(pSqlda)
, m_statementHandle(aStatementHandle)
, m_bWasNull(false)
, m_currentRow(0)
, m_bIsAfterLastRow(false)
, m_fieldCount(pSqlda? pSqlda->sqld : 0)
{
SAL_INFO("connectivity.firebird", "OResultSet().");
registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE),
PROPERTY_ID_ISBOOKMARKABLE,
PropertyAttribute::READONLY,
&m_bIsBookmarkable,
cppu::UnoType<decltype(m_bIsBookmarkable)>::get());
registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
PROPERTY_ID_FETCHSIZE,
PropertyAttribute::READONLY,
&m_nFetchSize,
cppu::UnoType<decltype(m_nFetchSize)>::get());
registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
PROPERTY_ID_RESULTSETTYPE,
PropertyAttribute::READONLY,
&m_nResultSetType,
cppu::UnoType<decltype(m_nResultSetType)>::get());
registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
PROPERTY_ID_FETCHDIRECTION,
PropertyAttribute::READONLY,
&m_nFetchDirection,
cppu::UnoType<decltype(m_nFetchDirection)>::get());
registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
PROPERTY_ID_RESULTSETCONCURRENCY,
PropertyAttribute::READONLY,
&m_nResultSetConcurrency,
cppu::UnoType<decltype(m_nResultSetConcurrency)>::get());
if (!pSqlda)
return; // TODO: what?
}
OResultSet::~OResultSet()
{
}
// ---- XResultSet -- Row retrieval methods ------------------------------------
sal_Int32 SAL_CALL OResultSet::getRow()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_currentRow;
}
sal_Bool SAL_CALL OResultSet::next()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
m_currentRow++;
ISC_STATUS fetchStat = isc_dsql_fetch(m_statusVector,
&m_statementHandle,
1,
m_pSqlda);
if (fetchStat == 0) // SUCCESSFUL
{
return true;
}
else if (fetchStat == 100) // END OF DATASET
{
m_bIsAfterLastRow = true;
return false;
}
else
{
SAL_WARN("connectivity.firebird", "Error when fetching data");
// Throws sql exception as appropriate
evaluateStatusVector(m_statusVector, u"isc_dsql_fetch", *this);
return false;
}
}
sal_Bool SAL_CALL OResultSet::previous()
{
::dbtools::throwFunctionNotSupportedSQLException(u"previous not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::isLast()
{
::dbtools::throwFunctionNotSupportedSQLException(u"isLast not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::isBeforeFirst()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_currentRow == 0;
}
sal_Bool SAL_CALL OResultSet::isAfterLast()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_bIsAfterLastRow;
}
sal_Bool SAL_CALL OResultSet::isFirst()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_currentRow == 1 && !m_bIsAfterLastRow;
}
void SAL_CALL OResultSet::beforeFirst()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if (m_currentRow != 0)
::dbtools::throwFunctionNotSupportedSQLException(u"beforeFirst not supported in firebird"_ustr,
*this);
}
void SAL_CALL OResultSet::afterLast()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if (!m_bIsAfterLastRow)
::dbtools::throwFunctionNotSupportedSQLException(u"afterLast not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::first()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if (m_currentRow == 0)
{
return next();
}
else if (m_currentRow == 1 && !m_bIsAfterLastRow)
{
return true;
}
else
{
::dbtools::throwFunctionNotSupportedSQLException(u"first not supported in firebird"_ustr,
*this);
return false;
}
}
sal_Bool SAL_CALL OResultSet::last()
{
// We need to iterate past the last row to know when we've passed the last
// row, hence we can't actually move to last.
::dbtools::throwFunctionNotSupportedSQLException(u"last not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 aRow)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if (aRow > m_currentRow)
{
sal_Int32 aIterations = aRow - m_currentRow;
return relative(aIterations);
}
else
{
::dbtools::throwFunctionNotSupportedSQLException(u"absolute not supported in firebird"_ustr,
*this);
return false;
}
}
sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if (row > 0)
{
while (row--)
{
if (!next())
return false;
}
return true;
}
else
{
::dbtools::throwFunctionNotSupportedSQLException(u"relative not supported in firebird"_ustr,
*this);
return false;
}
}
void OResultSet::checkColumnIndex(sal_Int32 nIndex)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if( nIndex < 1 || nIndex > m_fieldCount )
{
::dbtools::throwSQLException(
"No column " + OUString::number(nIndex),
::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
*this);
}
}
void OResultSet::checkRowIndex()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if((m_currentRow < 1) || m_bIsAfterLastRow)
{
::dbtools::throwSQLException(
u"Invalid Row"_ustr,
::dbtools::StandardSQLState::INVALID_CURSOR_POSITION,
*this);
}
}
Any SAL_CALL OResultSet::queryInterface( const Type & rType )
{
Any aRet = OPropertySetHelper::queryInterface(rType);
return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType);
}
Sequence< Type > SAL_CALL OResultSet::getTypes()
{
return concatSequences(OPropertySetHelper::getTypes(), OResultSet_BASE::getTypes());
}
// ---- XColumnLocate ---------------------------------------------------------
sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& rColumnName)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
uno::Reference< XResultSetMetaData > xMeta = getMetaData();
sal_Int32 nLen = xMeta->getColumnCount();
sal_Int32 i;
for(i = 1; i<=nLen; ++i)
{
// We assume case sensitive, otherwise you'd have to test
// xMeta->isCaseSensitive and use qualsIgnoreAsciiCase as needed.
if (rColumnName == xMeta->getColumnName(i))
return i;
}
::dbtools::throwInvalidColumnException(rColumnName, *this);
}
uno::Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return nullptr;
}
uno::Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return nullptr;
}
// ---- Internal Utilities ---------------------------------------------------
bool OResultSet::isNull(const sal_Int32 nColumnIndex)
{
assert(nColumnIndex <= m_fieldCount);
XSQLVAR* pVar = m_pSqlda->sqlvar;
if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null
{
if (*pVar[nColumnIndex-1].sqlind == -1)
return true;
}
return false;
}
template <typename T> requires std::is_integral_v<T>
OUString OResultSet::makeNumericString(const sal_Int32 nColumnIndex)
{
// minus because firebird stores scale as a negative number
int nDecimalCount = -(m_pSqlda->sqlvar[nColumnIndex-1].sqlscale);
if(nDecimalCount < 0)
{
// scale should be always positive
assert(false);
return OUString();
}
OUStringBuffer sRetBuffer;
T nAllDigits = *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
sRetBuffer.append(static_cast<sal_Int64>(nAllDigits));
sal_Int32 insertionPos = nAllDigits < 0 ? 1 : 0; // consider leading minus
int nMissingNulls = nDecimalCount - (sRetBuffer.getLength() - insertionPos) + 1;
for (int i = 0; i < nMissingNulls; ++i)
sRetBuffer.insert(insertionPos, '0');
if (nDecimalCount)
sRetBuffer.insert(sRetBuffer.getLength() - nDecimalCount, '.');
return sRetBuffer.makeStringAndClear();
}
template <typename T>
T OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
{
m_bWasNull = isNull(nColumnIndex);
if (m_bWasNull)
return T();
if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == nType)
return *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
else
{
ORowSetValue row = retrieveValue< ORowSetValue >(nColumnIndex, 0);
if constexpr ( std::is_same_v<sal_Int64, T> )
return row.getLong();
else if constexpr ( std::is_same_v<sal_Int32, T> )
return row.getInt32();
else if constexpr ( std::is_same_v<sal_Int16, T> )
return row.getInt16();
else if constexpr ( std::is_same_v<float, T> )
return row.getFloat();
else if constexpr ( std::is_same_v<double, T> )
return row.getDouble();
else if constexpr ( std::is_same_v<bool, T> )
return row.getBool();
else
return row;
}
}
template <>
ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
{
// See https://wiki.documentfoundation.org/Documentation/DevGuide/Database_Access#Using_the_getXXX_Methods
// (bottom of page) for a chart of possible conversions, we should allow all
// of these -- Blob/Clob will probably need some specialist handling especially
// w.r.t. to generating Strings for them.
//
// Basically we just have to map to the correct direct request and
// ORowSetValue does the rest for us here.
int nSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
// TODO Firebird 3.0 does not set subtype (i.e. set to 0) for computed numeric/decimal value.
// It may change in the future.
// Imply numeric data type when subtype is 0 and scale is negative
if( nSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0 )
nSqlSubType = 1;
switch (m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1)
{
case SQL_TEXT:
case SQL_VARYING:
return getString(nColumnIndex);
case SQL_SHORT:
if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
return getString(nColumnIndex);
return getShort(nColumnIndex);
case SQL_LONG:
if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
return getString(nColumnIndex);
return getInt(nColumnIndex);
case SQL_FLOAT:
return getFloat(nColumnIndex);
case SQL_DOUBLE:
if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
return getString(nColumnIndex);
return getDouble(nColumnIndex);
case SQL_D_FLOAT:
return getFloat(nColumnIndex);
case SQL_TIMESTAMP:
return getTimestamp(nColumnIndex);
case SQL_TYPE_TIME:
return getTime(nColumnIndex);
case SQL_TYPE_DATE:
return getDate(nColumnIndex);
case SQL_INT64:
if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
return getString(nColumnIndex);
return getLong(nColumnIndex);
case SQL_BOOLEAN:
return ORowSetValue(bool(getBoolean(nColumnIndex)));
case SQL_BLOB:
case SQL_NULL:
case SQL_QUAD:
case SQL_ARRAY:
// TODO: these are all invalid conversions, so maybe we should
// throw an exception?
return ORowSetValue();
default:
assert(false);
return ORowSetValue();
}
}
template <>
Date OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
{
if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_DATE)
{
ISC_DATE aISCDate = *reinterpret_cast<ISC_DATE*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
struct tm aCTime;
isc_decode_sql_date(&aISCDate, &aCTime);
return Date(aCTime.tm_mday, aCTime.tm_mon + 1, aCTime.tm_year + 1900);
}
else
{
return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDate();
}
}
template <>
Time OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
{
if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_TIME)
{
ISC_TIME aISCTime = *reinterpret_cast<ISC_TIME*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
struct tm aCTime;
isc_decode_sql_time(&aISCTime, &aCTime);
// First field is nanoseconds.
// last field denotes UTC (true) or unknown (false)
// Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION
// with no other funkiness, so we can get the fractional seconds easily.
return Time((aISCTime % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION),
aCTime.tm_sec, aCTime.tm_min, aCTime.tm_hour, false);
}
else
{
return retrieveValue< ORowSetValue >(nColumnIndex, 0).getTime();
}
}
template <>
DateTime OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
{
if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TIMESTAMP)
{
ISC_TIMESTAMP aISCTimestamp = *reinterpret_cast<ISC_TIMESTAMP*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
struct tm aCTime;
isc_decode_timestamp(&aISCTimestamp, &aCTime);
// Ditto here, see comment in previous function about ISC_TIME and ISC_TIME_SECONDS_PRECISION.
return DateTime((aISCTimestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), //nanoseconds
aCTime.tm_sec,
aCTime.tm_min,
aCTime.tm_hour,
aCTime.tm_mday,
aCTime.tm_mon + 1, // tm is from 0 to 11
aCTime.tm_year + 1900, //tm_year is the years since 1900
false); // denotes UTC (true), or unknown (false)
}
else
{
return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDateTime();
}
}
template <>
OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
{
// &~1 to remove the "can contain NULL" indicator
int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
int aSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
if (aSqlType == SQL_TEXT )
{
return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata,
m_pSqlda->sqlvar[nColumnIndex-1].sqllen,
RTL_TEXTENCODING_UTF8);
}
else if (aSqlType == SQL_VARYING)
{
// First 2 bytes are a short containing the length of the string
// Under unclear conditions, it may be wrong and greater than sqllen.
sal_uInt16 aLength = *reinterpret_cast<sal_uInt16*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
// Use greater signed type sal_Int32 to get the minimum of two 16-bit values
return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata + 2,
std::min<sal_Int32>(aLength, m_pSqlda->sqlvar[nColumnIndex-1].sqllen),
RTL_TEXTENCODING_UTF8);
}
else if ((aSqlType == SQL_SHORT || aSqlType == SQL_LONG ||
aSqlType == SQL_DOUBLE || aSqlType == SQL_INT64)
&& (aSqlSubType == 1 ||
aSqlSubType == 2 ||
(aSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0) ) )
{
// decimal and numeric types
switch(aSqlType)
{
case SQL_SHORT:
return makeNumericString<sal_Int16>(nColumnIndex);
case SQL_LONG:
return makeNumericString<sal_Int32>(nColumnIndex);
case SQL_DOUBLE:
// TODO FIXME 64 bits?
case SQL_INT64:
return makeNumericString<sal_Int64>(nColumnIndex);
default:
assert(false);
return OUString(); // never reached
}
}
else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) )
{
uno::Reference<XClob> xClob = getClob(nColumnIndex);
return xClob->getSubString( 1, xClob->length() );
}
else
{
return retrieveValue< ORowSetValue >(nColumnIndex, 0).getString();
}
}
template <>
ISC_QUAD* OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
{
// TODO: this is probably wrong
if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) != nType)
throw SQLException(); // TODO: better exception (can't convert Blob)
return reinterpret_cast<ISC_QUAD*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
}
template <typename T>
T OResultSet::safelyRetrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
checkColumnIndex(nColumnIndex);
checkRowIndex();
m_bWasNull = isNull(nColumnIndex);
if (m_bWasNull)
return T();
return retrieveValue< T >(nColumnIndex, nType);
}
// ---- XRow -----------------------------------------------------------------
sal_Bool SAL_CALL OResultSet::wasNull()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_bWasNull;
}
// ---- XRow: Simple Numerical types ------------------------------------------
sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex)
{
return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN);
}
sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 nColumnIndex)
{
// Not a native firebird type hence we always have to convert.
return safelyRetrieveValue< ORowSetValue >(nColumnIndex).getInt8();
}
Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes(sal_Int32 nColumnIndex)
{
// &~1 to remove the "can contain NULL" indicator
int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
if ( aSqlType == SQL_BLOB )
{
Reference< XBlob> xBlob = getBlob(nColumnIndex);
if (xBlob.is())
{
const sal_Int64 aBlobLength = xBlob->length();
if (aBlobLength > SAL_MAX_INT32)
{
SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32);
return xBlob->getBytes(1, SAL_MAX_INT32);
}
return xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength));
}
else
return Sequence< sal_Int8 >();
}
// TODO implement SQL_VARYING and SQL_TEXT
// as it's the counterpart as OPreparedStatement::setBytes
else
{
return Sequence< sal_Int8 >(); // TODO: implement
}
}
sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex)
{
return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT);
}
sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex)
{
return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG);
}
sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex)
{
return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64);
}
float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex)
{
return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT);
}
double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex)
{
return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE);
}
// ---- XRow: More complex types ----------------------------------------------
OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex)
{
return safelyRetrieveValue< OUString >(nIndex);
}
Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex)
{
return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE);
}
Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex)
{
return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME);
}
DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex)
{
return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP);
}
uno::Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
if(!m_xMetaData.is())
m_xMetaData = new OResultSetMetaData(m_pConnection
, m_pSqlda);
return m_xMetaData;
}
uno::Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return nullptr;
}
uno::Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 columnIndex )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
int aSqlSubType = m_pSqlda->sqlvar[columnIndex-1].sqlsubtype;
SAL_WARN_IF(aSqlSubType != 1,
"connectivity.firebird", "wrong subtype, not a textual blob");
ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
if (!pBlobID)
return nullptr;
return m_pConnection->createClob(pBlobID);
}
uno::Reference< XBlob > SAL_CALL OResultSet::getBlob(sal_Int32 columnIndex)
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
// TODO: CLOB etc. should be valid here too, but we probably want some more
// cleverness around this.
ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
if (!pBlobID)
return nullptr;
return m_pConnection->createBlob(pBlobID);
}
uno::Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return nullptr;
}
Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::container::XNameAccess >& )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return Any();
}
void SAL_CALL OResultSet::close()
{
SAL_INFO("connectivity.firebird", "close().");
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
}
dispose();
}
uno::Reference< XInterface > SAL_CALL OResultSet::getStatement()
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
return m_xStatement;
}
//----- XResultSet: unsupported change detection methods ---------------------
sal_Bool SAL_CALL OResultSet::rowDeleted()
{
::dbtools::throwFunctionNotSupportedSQLException(u"rowDeleted not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::rowInserted()
{
::dbtools::throwFunctionNotSupportedSQLException(u"rowInserted not supported in firebird"_ustr,
*this);
}
sal_Bool SAL_CALL OResultSet::rowUpdated()
{
::dbtools::throwFunctionNotSupportedSQLException(u"rowUpdated not supported in firebird"_ustr,
*this);
}
void SAL_CALL OResultSet::refreshRow()
{
::dbtools::throwFunctionNotSupportedSQLException(u"refreshRow not supported in firebird"_ustr,
*this);
}
void SAL_CALL OResultSet::cancel( )
{
MutexGuard aGuard(m_rMutex);
checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
}
//----- OIdPropertyArrayUsageHelper ------------------------------------------
IPropertyArrayHelper* OResultSet::createArrayHelper() const
{
Sequence< Property > aProperties;
describeProperties(aProperties);
return new ::cppu::OPropertyArrayHelper(aProperties);
}
IPropertyArrayHelper & OResultSet::getInfoHelper()
{
return *getArrayHelper();
}
void SAL_CALL OResultSet::acquire() noexcept
{
OResultSet_BASE::acquire();
}
void SAL_CALL OResultSet::release() noexcept
{
OResultSet_BASE::release();
}
uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( )
{
return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
}
// ---- XServiceInfo -----------------------------------------------------------
OUString SAL_CALL OResultSet::getImplementationName()
{
return u"com.sun.star.sdbcx.firebird.ResultSet"_ustr;
}
Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames()
{
return {u"com.sun.star.sdbc.ResultSet"_ustr,u"com.sun.star.sdbcx.ResultSet"_ustr};
}
sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName)
{
return cppu::supportsService(this, _rServiceName);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'insert' is required to be utilized.
↑ V530 The return value of function 'insert' is required to be utilized.