/* -*- 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 .
*/
if (m_currentRow == 0)
{ return next();
} elseif (m_currentRow == 1 && !m_bIsAfterLastRow)
{ returntrue;
} else
{
::dbtools::throwFunctionNotSupportedSQLException(u"first not supported in firebird"_ustr,
*this); returnfalse;
}
}
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);
}
if (row > 0)
{ while (row--)
{ if (!next()) returnfalse;
} returntrue;
} else
{
::dbtools::throwFunctionNotSupportedSQLException(u"relative not supported in firebird"_ustr,
*this); returnfalse;
}
}
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;
}
if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null
{ if (*pVar[nColumnIndex-1].sqlind == -1) returntrue;
} returnfalse;
}
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();
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();
}
}
// 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();
}
}
// 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);
} elseif (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);
} elseif ((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
}
} elseif(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();
}
}
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
}
}
// 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);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.