bump product version to 6.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / odbc / OResultSet.cxx
blob8b5d7b82e4776d9eed6cdee7fd449fb7dbe62946
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <odbc/OResultSet.hxx>
21 #include <odbc/OTools.hxx>
22 #include <odbc/OResultSetMetaData.hxx>
23 #include <com/sun/star/sdbc/DataType.hpp>
24 #include <com/sun/star/beans/PropertyAttribute.hpp>
25 #include <com/sun/star/beans/PropertyVetoException.hpp>
26 #include <com/sun/star/sdbcx/CompareBookmark.hpp>
27 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
28 #include <com/sun/star/sdbc/FetchDirection.hpp>
29 #include <com/sun/star/sdbc/ResultSetType.hpp>
30 #include <comphelper/property.hxx>
31 #include <comphelper/sequence.hxx>
32 #include <cppuhelper/typeprovider.hxx>
33 #include <cppuhelper/supportsservice.hxx>
34 #include <com/sun/star/lang/DisposedException.hpp>
35 #include <comphelper/types.hxx>
36 #include <connectivity/dbtools.hxx>
37 #include <connectivity/dbexception.hxx>
38 #include <sal/log.hxx>
40 using namespace ::comphelper;
41 using namespace connectivity;
42 using namespace connectivity::odbc;
43 using namespace cppu;
44 using namespace com::sun::star::uno;
45 using namespace com::sun::star::lang;
46 using namespace com::sun::star::beans;
47 using namespace com::sun::star::sdbc;
48 using namespace com::sun::star::sdbcx;
49 using namespace com::sun::star::container;
50 using namespace com::sun::star::io;
51 using namespace com::sun::star::util;
53 #define ODBC_SQL_NOT_DEFINED 99UL
54 static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_OFF, "ODBC_SQL_NOT_DEFINED must be unique");
55 static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_ON, "ODBC_SQL_NOT_DEFINED must be unique");
56 static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_FIXED, "ODBC_SQL_NOT_DEFINED must be unique");
57 static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_VARIABLE, "ODBC_SQL_NOT_DEFINED must be unique");
59 namespace
61 const SQLLEN nMaxBookmarkLen = 20;
65 // IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet");
66 OUString SAL_CALL OResultSet::getImplementationName( )
68 return OUString("com.sun.star.sdbcx.odbc.ResultSet");
71 Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames( )
73 Sequence< OUString > aSupported(2);
74 aSupported[0] = "com.sun.star.sdbc.ResultSet";
75 aSupported[1] = "com.sun.star.sdbcx.ResultSet";
76 return aSupported;
79 sal_Bool SAL_CALL OResultSet::supportsService( const OUString& _rServiceName )
81 return cppu::supportsService(this, _rServiceName);
85 OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) : OResultSet_BASE(m_aMutex)
86 ,OPropertySetHelper(OResultSet_BASE::rBHelper)
87 ,m_bFetchDataInOrder(true)
88 ,m_aStatementHandle(_pStatementHandle)
89 ,m_aConnectionHandle(pStmt->getConnectionHandle())
90 ,m_pStatement(pStmt)
91 ,m_xStatement(*pStmt)
92 ,m_nTextEncoding(pStmt->getOwnConnection()->getTextEncoding())
93 ,m_nRowPos(0)
94 ,m_nUseBookmarks(ODBC_SQL_NOT_DEFINED)
95 ,m_nCurrentFetchState(0)
96 ,m_bWasNull(true)
97 ,m_bEOF(true)
98 ,m_bRowInserted(false)
99 ,m_bRowDeleted(false)
100 ,m_bUseFetchScroll(false)
102 osl_atomic_increment( &m_refCount );
105 m_pRowStatusArray.reset( new SQLUSMALLINT[1] ); // the default value
106 setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get());
108 catch(const Exception&)
109 { // we don't want our result destroy here
111 SQLULEN nCurType = 0;
114 nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);
115 SQLUINTEGER nValueLen = m_pStatement->getCursorProperties(nCurType,false);
116 if( (nValueLen & SQL_CA2_SENSITIVITY_DELETIONS) != SQL_CA2_SENSITIVITY_DELETIONS ||
117 (nValueLen & SQL_CA2_CRC_EXACT) != SQL_CA2_CRC_EXACT)
118 m_pSkipDeletedSet.reset( new OSkipDeletedSet(this) );
120 catch(const Exception&)
121 { // we don't want our result destroy here
125 SQLUINTEGER nValueLen = 0;
126 // Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441%28v=vs.85%29.aspx
127 // LibreOffice ODBC binds columns only on update, so we don't care about SQL_GD_ANY_COLUMN / SQL_GD_BOUND
128 // TODO: maybe a problem if a column is updated, then an earlier column fetched?
129 // an updated column is bound...
130 // TODO: aren't we assuming SQL_GD_OUTPUT_PARAMS?
131 // If yes, we should at least OSL_ENSURE it,
132 // even better throw an exception any OUT parameter registration if !SQL_GD_OUTPUT_PARAMS.
133 // If !SQL_GD_ANY_ORDER, cache the whole row so that callers can access columns in any order.
134 // In other words, isolate them from ODBC restrictions.
135 // TODO: we assume SQL_GD_BLOCK, unless fetchSize is 1
136 OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,nullptr);
137 m_bFetchDataInOrder = ((SQL_GD_ANY_ORDER & nValueLen) != SQL_GD_ANY_ORDER);
139 catch(const Exception&)
141 m_bFetchDataInOrder = true;
145 // TODO: this does *not* do what it appears.
146 // We use SQLFetchScroll unconditionally in several places
147 // the *only* difference this makes is whether ::next() uses SQLFetchScroll or SQLFetch
148 // so this test seems pointless
149 if ( getOdbcFunction(ODBC3SQLFunctionId::GetFunctions) )
151 SQLUSMALLINT nSupported = 0;
152 m_bUseFetchScroll = ( N3SQLGetFunctions(m_aConnectionHandle,SQL_API_SQLFETCHSCROLL,&nSupported) == SQL_SUCCESS && nSupported == 1 );
155 catch(const Exception&)
157 m_bUseFetchScroll = false;
160 osl_atomic_decrement( &m_refCount );
163 OResultSet::~OResultSet()
167 void OResultSet::construct()
169 osl_atomic_increment( &m_refCount );
170 allocBuffer();
171 osl_atomic_decrement( &m_refCount );
174 void OResultSet::disposing()
176 N3SQLCloseCursor(m_aStatementHandle);
177 OPropertySetHelper::disposing();
179 ::osl::MutexGuard aGuard(m_aMutex);
180 releaseBuffer();
182 setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, nullptr);
183 m_xStatement.clear();
184 m_xMetaData.clear();
187 SQLRETURN OResultSet::unbind(bool _bUnbindHandle)
189 SQLRETURN nRet = 0;
190 if ( _bUnbindHandle )
191 nRet = N3SQLFreeStmt(m_aStatementHandle,SQL_UNBIND);
193 if ( !m_aBindVector.empty() )
195 for(auto& [rPtrAddr, rType] : m_aBindVector)
197 switch (rType)
199 case DataType::CHAR:
200 case DataType::VARCHAR:
201 delete static_cast< OString* >(reinterpret_cast< void * >(rPtrAddr));
202 break;
203 case DataType::BIGINT:
204 delete static_cast< sal_Int64* >(reinterpret_cast< void * >(rPtrAddr));
205 break;
206 case DataType::DECIMAL:
207 case DataType::NUMERIC:
208 delete static_cast< OString* >(reinterpret_cast< void * >(rPtrAddr));
209 break;
210 case DataType::REAL:
211 case DataType::DOUBLE:
212 delete static_cast< double* >(reinterpret_cast< void * >(rPtrAddr));
213 break;
214 case DataType::LONGVARCHAR:
215 case DataType::CLOB:
216 delete [] static_cast< char* >(reinterpret_cast< void * >(rPtrAddr));
217 break;
218 case DataType::LONGVARBINARY:
219 case DataType::BLOB:
220 delete [] static_cast< char* >(reinterpret_cast< void * >(rPtrAddr));
221 break;
222 case DataType::DATE:
223 delete static_cast< DATE_STRUCT* >(reinterpret_cast< void * >(rPtrAddr));
224 break;
225 case DataType::TIME:
226 delete static_cast< TIME_STRUCT* >(reinterpret_cast< void * >(rPtrAddr));
227 break;
228 case DataType::TIMESTAMP:
229 delete static_cast< TIMESTAMP_STRUCT* >(reinterpret_cast< void * >(rPtrAddr));
230 break;
231 case DataType::BIT:
232 case DataType::TINYINT:
233 delete static_cast< sal_Int8* >(reinterpret_cast< void * >(rPtrAddr));
234 break;
235 case DataType::SMALLINT:
236 delete static_cast< sal_Int16* >(reinterpret_cast< void * >(rPtrAddr));
237 break;
238 case DataType::INTEGER:
239 delete static_cast< sal_Int32* >(reinterpret_cast< void * >(rPtrAddr));
240 break;
241 case DataType::FLOAT:
242 delete static_cast< float* >(reinterpret_cast< void * >(rPtrAddr));
243 break;
244 case DataType::BINARY:
245 case DataType::VARBINARY:
246 delete static_cast< sal_Int8* >(reinterpret_cast< void * >(rPtrAddr));
247 break;
250 m_aBindVector.clear();
252 return nRet;
255 TVoidPtr OResultSet::allocBindColumn(sal_Int32 _nType,sal_Int32 _nColumnIndex)
257 TVoidPtr aPair;
258 switch (_nType)
260 case DataType::CHAR:
261 case DataType::VARCHAR:
262 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType);
263 break;
264 case DataType::BIGINT:
265 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int64(0)),_nType);
266 break;
267 case DataType::DECIMAL:
268 case DataType::NUMERIC:
269 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType);
270 break;
271 case DataType::REAL:
272 case DataType::DOUBLE:
273 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new double(0.0)),_nType);
274 break;
275 case DataType::LONGVARCHAR:
276 case DataType::CLOB:
277 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType); // only for finding
278 break;
279 case DataType::LONGVARBINARY:
280 case DataType::BLOB:
281 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType); // only for finding
282 break;
283 case DataType::DATE:
284 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new DATE_STRUCT),_nType);
285 break;
286 case DataType::TIME:
287 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIME_STRUCT),_nType);
288 break;
289 case DataType::TIMESTAMP:
290 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIMESTAMP_STRUCT),_nType);
291 break;
292 case DataType::BIT:
293 case DataType::TINYINT:
294 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8(0)),_nType);
295 break;
296 case DataType::SMALLINT:
297 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int16(0)),_nType);
298 break;
299 case DataType::INTEGER:
300 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int32(0)),_nType);
301 break;
302 case DataType::FLOAT:
303 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new float(0)),_nType);
304 break;
305 case DataType::BINARY:
306 case DataType::VARBINARY:
307 aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8[m_aRow[_nColumnIndex].getSequence().getLength()]),_nType);
308 break;
309 default:
310 SAL_WARN( "connectivity.odbc", "Unknown type");
311 aPair = TVoidPtr(0,_nType);
313 return aPair;
316 void OResultSet::allocBuffer()
318 Reference< XResultSetMetaData > xMeta = getMetaData();
319 sal_Int32 nLen = xMeta->getColumnCount();
321 m_aBindVector.reserve(nLen);
322 m_aRow.resize(nLen+1);
324 m_aRow[0].setTypeKind(DataType::VARBINARY);
325 m_aRow[0].setBound( false );
327 for(sal_Int32 i = 1;i<=nLen;++i)
329 sal_Int32 nType = xMeta->getColumnType(i);
330 m_aRow[i].setTypeKind( nType );
331 m_aRow[i].setBound( false );
333 m_aLengthVector.resize(nLen + 1);
336 void OResultSet::releaseBuffer()
338 unbind(false);
339 m_aLengthVector.clear();
342 Any SAL_CALL OResultSet::queryInterface( const Type & rType )
344 Any aRet = OPropertySetHelper::queryInterface(rType);
345 return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType);
348 Sequence< Type > SAL_CALL OResultSet::getTypes( )
350 OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(),
351 cppu::UnoType<css::beans::XFastPropertySet>::get(),
352 cppu::UnoType<css::beans::XPropertySet>::get());
354 return ::comphelper::concatSequences(aTypes.getTypes(),OResultSet_BASE::getTypes());
358 sal_Int32 SAL_CALL OResultSet::findColumn( const OUString& columnName )
360 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
363 ::osl::MutexGuard aGuard( m_aMutex );
365 Reference< XResultSetMetaData > xMeta = getMetaData();
366 sal_Int32 nLen = xMeta->getColumnCount();
367 sal_Int32 i = 1;
368 for(;i<=nLen;++i)
370 if(xMeta->isCaseSensitive(i) ? columnName == xMeta->getColumnName(i) :
371 columnName.equalsIgnoreAsciiCase(xMeta->getColumnName(i)))
372 return i;
375 ::dbtools::throwInvalidColumnException( columnName, *this );
376 assert(false);
377 return 0; // Never reached
380 void OResultSet::ensureCacheForColumn(sal_Int32 columnIndex)
382 SAL_INFO( "connectivity.odbc", "odbc lionel@mamane.lu OResultSet::ensureCacheForColumn" );
384 assert(columnIndex >= 0);
386 const TDataRow::size_type oldCacheSize = m_aRow.size();
387 const TDataRow::size_type uColumnIndex = static_cast<TDataRow::size_type>(columnIndex);
389 if (oldCacheSize > uColumnIndex)
390 // nothing to do
391 return;
393 m_aRow.resize(columnIndex + 1);
394 TDataRow::iterator i (m_aRow.begin() + oldCacheSize);
395 const TDataRow::const_iterator end(m_aRow.end());
396 for (; i != end; ++i)
398 i->setBound(false);
401 void OResultSet::invalidateCache()
403 for(auto& rItem : m_aRow)
405 rItem.setBound(false);
409 Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ )
411 ::osl::MutexGuard aGuard( m_aMutex );
412 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
414 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this );
416 return nullptr;
419 Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ )
421 ::osl::MutexGuard aGuard( m_aMutex );
422 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
424 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this );
426 return nullptr;
429 template < typename T > T OResultSet::impl_getValue( const sal_Int32 _nColumnIndex, SQLSMALLINT nType )
431 T val;
433 OTools::getValue(m_pStatement->getOwnConnection(), m_aStatementHandle, _nColumnIndex, nType, m_bWasNull, **this, &val, sizeof(val));
435 return val;
438 // this function exists for the implicit conversion to sal_Bool (compared to a direct call to impl_getValue)
439 bool OResultSet::impl_getBoolean( sal_Int32 columnIndex )
441 return impl_getValue<sal_Int8>(columnIndex, SQL_C_BIT);
444 template < typename T > T OResultSet::getValue( sal_Int32 columnIndex )
446 ::osl::MutexGuard aGuard( m_aMutex );
447 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
448 fillColumn(columnIndex);
449 m_bWasNull = m_aRow[columnIndex].isNull();
450 return m_aRow[columnIndex];
452 sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 columnIndex )
454 return getValue<bool>( columnIndex );
457 sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex )
459 return getValue<sal_Int8>( columnIndex );
463 Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex )
465 ::osl::MutexGuard aGuard( m_aMutex );
466 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
467 fillColumn(columnIndex);
468 m_bWasNull = m_aRow[columnIndex].isNull();
470 Sequence< sal_Int8 > nRet;
471 switch(m_aRow[columnIndex].getTypeKind())
473 case DataType::BINARY:
474 case DataType::VARBINARY:
475 case DataType::LONGVARBINARY:
476 nRet = m_aRow[columnIndex];
477 break;
478 default:
480 OUString const & sRet = m_aRow[columnIndex].getString();
481 nRet = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(sRet.getStr()),sizeof(sal_Unicode)*sRet.getLength());
484 return nRet;
486 Sequence< sal_Int8 > OResultSet::impl_getBytes( sal_Int32 columnIndex )
488 const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex);
490 switch(nColumnType)
492 case SQL_WVARCHAR:
493 case SQL_WCHAR:
494 case SQL_WLONGVARCHAR:
495 case SQL_VARCHAR:
496 case SQL_CHAR:
497 case SQL_LONGVARCHAR:
499 OUString const & aRet = OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding);
500 return Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),sizeof(sal_Unicode)*aRet.getLength());
502 default:
503 return OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this);
507 Date OResultSet::impl_getDate( sal_Int32 columnIndex )
509 DATE_STRUCT aDate = impl_getValue< DATE_STRUCT> ( columnIndex,
510 m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_DATE : SQL_C_TYPE_DATE );
512 return Date(aDate.day, aDate.month, aDate.year);
515 Date SAL_CALL OResultSet::getDate( sal_Int32 columnIndex )
517 return getValue<Date>( columnIndex );
521 double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex )
523 return getValue<double>( columnIndex );
527 float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex )
529 return getValue<float>( columnIndex );
532 sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex )
534 return getValue<sal_Int16>( columnIndex );
537 sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex )
539 return getValue<sal_Int32>( columnIndex );
542 sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex )
544 return getValue<sal_Int64>( columnIndex );
546 sal_Int64 OResultSet::impl_getLong( sal_Int32 columnIndex )
550 return impl_getValue<sal_Int64>(columnIndex, SQL_C_SBIGINT);
552 catch(const SQLException&)
554 return getString(columnIndex).toInt64();
558 sal_Int32 SAL_CALL OResultSet::getRow( )
560 ::osl::MutexGuard aGuard( m_aMutex );
561 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
563 return m_pSkipDeletedSet ? m_pSkipDeletedSet->getMappedPosition(getDriverPos()) : getDriverPos();
566 Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( )
568 ::osl::MutexGuard aGuard( m_aMutex );
569 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
572 if(!m_xMetaData.is())
573 m_xMetaData = new OResultSetMetaData(m_pStatement->getOwnConnection(),m_aStatementHandle);
574 return m_xMetaData;
577 Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 /*columnIndex*/ )
579 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getArray", *this );
580 return nullptr;
584 Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 /*columnIndex*/ )
586 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getClob", *this );
587 return nullptr;
590 Reference< XBlob > SAL_CALL OResultSet::getBlob( sal_Int32 /*columnIndex*/ )
592 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBlob", *this );
593 return nullptr;
597 Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ )
599 ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getRef", *this );
600 return nullptr;
604 Any SAL_CALL OResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ )
606 return getValue<ORowSetValue>( columnIndex ).makeAny();
609 OUString OResultSet::impl_getString( sal_Int32 columnIndex )
611 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
612 const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex);
613 return OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding);
615 OUString OResultSet::getString( sal_Int32 columnIndex )
617 return getValue<OUString>( columnIndex );
620 Time OResultSet::impl_getTime( sal_Int32 columnIndex )
622 TIME_STRUCT aTime = impl_getValue< TIME_STRUCT > ( columnIndex,
623 m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIME : SQL_C_TYPE_TIME );
625 return Time(0, aTime.second,aTime.minute,aTime.hour, false);
627 Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex )
629 return getValue<Time>( columnIndex );
632 DateTime OResultSet::impl_getTimestamp( sal_Int32 columnIndex )
634 TIMESTAMP_STRUCT aTime = impl_getValue< TIMESTAMP_STRUCT > ( columnIndex,
635 m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP );
637 return DateTime(aTime.fraction,
638 aTime.second,
639 aTime.minute,
640 aTime.hour,
641 aTime.day,
642 aTime.month,
643 aTime.year,
644 false);
646 DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex )
648 return getValue<DateTime>( columnIndex );
651 sal_Bool SAL_CALL OResultSet::isBeforeFirst( )
653 ::osl::MutexGuard aGuard( m_aMutex );
654 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
655 return m_nRowPos == 0;
658 sal_Bool SAL_CALL OResultSet::isAfterLast( )
660 ::osl::MutexGuard aGuard( m_aMutex );
661 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
663 return m_nRowPos != 0 && m_nCurrentFetchState == SQL_NO_DATA;
666 sal_Bool SAL_CALL OResultSet::isFirst( )
668 ::osl::MutexGuard aGuard( m_aMutex );
669 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
671 return m_nRowPos == 1;
674 sal_Bool SAL_CALL OResultSet::isLast( )
676 ::osl::MutexGuard aGuard( m_aMutex );
677 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
680 return m_bEOF && m_nCurrentFetchState != SQL_NO_DATA;
683 void SAL_CALL OResultSet::beforeFirst( )
685 ::osl::MutexGuard aGuard( m_aMutex );
686 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
689 if(first())
690 previous();
691 m_nCurrentFetchState = SQL_SUCCESS;
694 void SAL_CALL OResultSet::afterLast( )
696 ::osl::MutexGuard aGuard( m_aMutex );
697 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
699 if(last())
700 next();
701 m_bEOF = true;
705 void SAL_CALL OResultSet::close( )
708 ::osl::MutexGuard aGuard( m_aMutex );
709 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
712 dispose();
716 sal_Bool SAL_CALL OResultSet::first( )
718 return moveImpl(IResultSetHelper::FIRST,0);
722 sal_Bool SAL_CALL OResultSet::last( )
724 return moveImpl(IResultSetHelper::LAST,0);
727 sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row )
729 return moveImpl(IResultSetHelper::ABSOLUTE1,row);
732 sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row )
734 return moveImpl(IResultSetHelper::RELATIVE1,row);
737 sal_Bool SAL_CALL OResultSet::previous( )
739 return moveImpl(IResultSetHelper::PRIOR,0);
742 Reference< XInterface > SAL_CALL OResultSet::getStatement( )
744 ::osl::MutexGuard aGuard( m_aMutex );
745 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
746 return m_xStatement;
750 sal_Bool SAL_CALL OResultSet::rowDeleted()
752 ::osl::MutexGuard aGuard( m_aMutex );
753 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
755 bool bRet = m_bRowDeleted;
756 m_bRowDeleted = false;
758 return bRet;
761 sal_Bool SAL_CALL OResultSet::rowInserted( )
763 ::osl::MutexGuard aGuard( m_aMutex );
764 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
766 bool bInserted = m_bRowInserted;
767 m_bRowInserted = false;
769 return bInserted;
772 sal_Bool SAL_CALL OResultSet::rowUpdated( )
774 ::osl::MutexGuard aGuard( m_aMutex );
775 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
778 return m_pRowStatusArray[0] == SQL_ROW_UPDATED;
782 sal_Bool SAL_CALL OResultSet::next( )
784 return moveImpl(IResultSetHelper::NEXT,1);
788 sal_Bool SAL_CALL OResultSet::wasNull( )
790 ::osl::MutexGuard aGuard( m_aMutex );
791 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
793 return m_bWasNull;
797 void SAL_CALL OResultSet::cancel( )
799 ::osl::MutexGuard aGuard( m_aMutex );
800 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
803 N3SQLCancel(m_aStatementHandle);
806 void SAL_CALL OResultSet::clearWarnings( )
810 Any SAL_CALL OResultSet::getWarnings( )
812 return Any();
815 void SAL_CALL OResultSet::insertRow( )
817 ::osl::MutexGuard aGuard( m_aMutex );
818 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
821 SQLLEN nRealLen = 0;
822 Sequence<sal_Int8> aBookmark(nMaxBookmarkLen);
823 static_assert(static_cast<size_t>(nMaxBookmarkLen) >= sizeof(SQLLEN), "must be larger");
825 SQLRETURN nRet = N3SQLBindCol(m_aStatementHandle,
827 SQL_C_VARBOOKMARK,
828 aBookmark.getArray(),
829 nMaxBookmarkLen,
830 &nRealLen
833 bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) );
834 if ( bPositionByBookmark )
836 nRet = N3SQLBulkOperations( m_aStatementHandle, SQL_ADD );
837 fillNeededData( nRet );
839 else
841 if(isBeforeFirst())
842 next(); // must be done
843 nRet = N3SQLSetPos( m_aStatementHandle, 1, SQL_ADD, SQL_LOCK_NO_CHANGE );
844 fillNeededData( nRet );
846 aBookmark.realloc(nRealLen);
849 OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
851 catch(const SQLException&)
853 nRet = unbind();
854 throw;
857 nRet = unbind();
858 OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
860 if ( bPositionByBookmark )
862 setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));
864 nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0);
866 else
867 nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0); // OJ 06.03.2004
868 // sometimes we got an error but we are not interested in anymore #106047# OJ
869 // OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
871 if(m_pSkipDeletedSet)
873 if(moveToBookmark(makeAny(aBookmark)))
875 sal_Int32 nRowPos = getDriverPos();
876 if ( -1 == m_nRowPos )
878 nRowPos = m_aPosToBookmarks.size() + 1;
880 if ( nRowPos == m_nRowPos )
881 ++nRowPos;
882 m_nRowPos = nRowPos;
883 m_pSkipDeletedSet->insertNewPosition(nRowPos);
884 m_aPosToBookmarks[aBookmark] = nRowPos;
887 m_bRowInserted = true;
891 void SAL_CALL OResultSet::updateRow( )
893 ::osl::MutexGuard aGuard( m_aMutex );
894 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
896 SQLRETURN nRet;
900 bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) );
901 if ( bPositionByBookmark )
903 getBookmark();
904 assert(m_aRow[0].isBound());
905 Sequence<sal_Int8> aBookmark(m_aRow[0].getSequence());
906 SQLLEN nRealLen = aBookmark.getLength();
907 nRet = N3SQLBindCol(m_aStatementHandle,
909 SQL_C_VARBOOKMARK,
910 aBookmark.getArray(),
911 aBookmark.getLength(),
912 &nRealLen
914 OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
915 nRet = N3SQLBulkOperations(m_aStatementHandle, SQL_UPDATE_BY_BOOKMARK);
916 fillNeededData(nRet);
917 // the driver should not have touched this
918 // (neither the contents of aBookmark FWIW)
919 assert(nRealLen == aBookmark.getLength());
921 else
923 nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE);
924 fillNeededData(nRet);
926 OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
927 // unbind all columns so we can fetch all columns again with SQLGetData
928 // (and also so that our buffers don't clobber anything, and
929 // so that a subsequent fetch does not overwrite m_aRow[0])
930 invalidateCache();
931 nRet = unbind();
932 OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after success");
934 catch(...)
936 // unbind all columns so that a subsequent fetch does not overwrite m_aRow[0]
937 nRet = unbind();
938 OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after failure");
939 throw;
943 void SAL_CALL OResultSet::deleteRow( )
945 SQLRETURN nRet = SQL_SUCCESS;
946 sal_Int32 nPos = getDriverPos();
947 nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_DELETE,SQL_LOCK_NO_CHANGE);
948 OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
950 m_bRowDeleted = ( m_pRowStatusArray[0] == SQL_ROW_DELETED );
951 if ( m_bRowDeleted )
953 TBookmarkPosMap::iterator aIter = std::find_if(m_aPosToBookmarks.begin(), m_aPosToBookmarks.end(),
954 [&nPos](const TBookmarkPosMap::value_type& rEntry) { return rEntry.second == nPos; });
955 if (aIter != m_aPosToBookmarks.end())
956 m_aPosToBookmarks.erase(aIter);
958 if ( m_pSkipDeletedSet )
959 m_pSkipDeletedSet->deletePosition(nPos);
963 void SAL_CALL OResultSet::cancelRowUpdates( )
968 void SAL_CALL OResultSet::moveToInsertRow( )
970 ::osl::MutexGuard aGuard( m_aMutex );
971 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
974 invalidateCache();
975 // first unbound all columns
976 OSL_VERIFY( unbind() == SQL_SUCCESS );
977 // SQLRETURN nRet = N3SQLSetStmtAttr(m_aStatementHandle,SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1,SQL_IS_INTEGER);
981 void SAL_CALL OResultSet::moveToCurrentRow( )
983 invalidateCache();
986 void OResultSet::updateValue(sal_Int32 columnIndex, SQLSMALLINT _nType, void const * _pValue)
988 ::osl::MutexGuard aGuard( m_aMutex );
989 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
991 m_aBindVector.push_back(allocBindColumn(OTools::MapOdbcType2Jdbc(_nType),columnIndex));
992 void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first);
993 OSL_ENSURE(pData != nullptr,"Data for update is NULL!");
994 OTools::bindValue( m_pStatement->getOwnConnection(),
995 m_aStatementHandle,
996 columnIndex,
997 _nType,
999 _pValue,
1000 pData,
1001 &m_aLengthVector[columnIndex],
1002 **this,
1003 m_nTextEncoding,
1004 m_pStatement->getOwnConnection()->useOldDateFormat());
1007 void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex )
1009 ::osl::MutexGuard aGuard( m_aMutex );
1010 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1012 m_aBindVector.push_back(allocBindColumn(DataType::CHAR,columnIndex));
1013 void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first);
1014 OTools::bindValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_CHAR,0,nullptr,pData,&m_aLengthVector[columnIndex],**this,m_nTextEncoding,m_pStatement->getOwnConnection()->useOldDateFormat());
1018 void SAL_CALL OResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x )
1020 updateValue(columnIndex,SQL_BIT,&x);
1023 void SAL_CALL OResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x )
1025 updateValue(columnIndex,SQL_CHAR,&x);
1029 void SAL_CALL OResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x )
1031 updateValue(columnIndex,SQL_TINYINT,&x);
1034 void SAL_CALL OResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x )
1036 updateValue(columnIndex,SQL_INTEGER,&x);
1039 void SAL_CALL OResultSet::updateLong( sal_Int32 /*columnIndex*/, sal_Int64 /*x*/ )
1041 ::dbtools::throwFunctionNotSupportedSQLException( "XRowUpdate::updateLong", *this );
1044 void SAL_CALL OResultSet::updateFloat( sal_Int32 columnIndex, float x )
1046 updateValue(columnIndex,SQL_REAL,&x);
1050 void SAL_CALL OResultSet::updateDouble( sal_Int32 columnIndex, double x )
1052 updateValue(columnIndex,SQL_DOUBLE,&x);
1055 void SAL_CALL OResultSet::updateString( sal_Int32 columnIndex, const OUString& x )
1057 sal_Int32 nType = m_aRow[columnIndex].getTypeKind();
1058 SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType);
1059 m_aRow[columnIndex] = x;
1060 m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarchar will be recognized by fillNeededData
1061 m_aRow[columnIndex].setBound(true);
1062 updateValue(columnIndex,nOdbcType, &x);
1065 void SAL_CALL OResultSet::updateBytes( sal_Int32 columnIndex, const Sequence< sal_Int8 >& x )
1067 sal_Int32 nType = m_aRow[columnIndex].getTypeKind();
1068 SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType);
1069 m_aRow[columnIndex] = x;
1070 m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarbinary will be recognized by fillNeededData
1071 m_aRow[columnIndex].setBound(true);
1072 updateValue(columnIndex,nOdbcType, &x);
1075 void SAL_CALL OResultSet::updateDate( sal_Int32 columnIndex, const Date& x )
1077 DATE_STRUCT aVal = OTools::DateToOdbcDate(x);
1078 updateValue(columnIndex,SQL_DATE,&aVal);
1082 void SAL_CALL OResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x )
1084 TIME_STRUCT aVal = OTools::TimeToOdbcTime(x);
1085 updateValue(columnIndex,SQL_TIME,&aVal);
1089 void SAL_CALL OResultSet::updateTimestamp( sal_Int32 columnIndex, const DateTime& x )
1091 TIMESTAMP_STRUCT aVal = OTools::DateTimeToTimestamp(x);
1092 updateValue(columnIndex,SQL_TIMESTAMP,&aVal);
1096 void SAL_CALL OResultSet::updateBinaryStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length )
1098 if(!x.is())
1099 ::dbtools::throwFunctionSequenceException(*this);
1101 Sequence<sal_Int8> aSeq;
1102 x->readBytes(aSeq,length);
1103 updateBytes(columnIndex,aSeq);
1106 void SAL_CALL OResultSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length )
1108 updateBinaryStream(columnIndex,x,length);
1111 void SAL_CALL OResultSet::refreshRow( )
1113 ::osl::MutexGuard aGuard( m_aMutex );
1114 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1117 // SQLRETURN nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_REFRESH,SQL_LOCK_NO_CHANGE);
1118 m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0);
1119 OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
1122 void SAL_CALL OResultSet::updateObject( sal_Int32 columnIndex, const Any& x )
1124 if (!::dbtools::implUpdateObject(this, columnIndex, x))
1125 throw SQLException();
1129 void SAL_CALL OResultSet::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/ )
1131 if (!::dbtools::implUpdateObject(this, columnIndex, x))
1132 throw SQLException();
1135 // XRowLocate
1136 Any SAL_CALL OResultSet::getBookmark( )
1138 fillColumn(0);
1139 if(m_aRow[0].isNull())
1140 throw SQLException();
1141 return m_aRow[0].makeAny();
1143 Sequence<sal_Int8> OResultSet::impl_getBookmark( )
1145 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1147 TBookmarkPosMap::const_iterator aFind = std::find_if(m_aPosToBookmarks.begin(),m_aPosToBookmarks.end(),
1148 [this] (const TBookmarkPosMap::value_type& bookmarkPos) {
1149 return bookmarkPos.second == m_nRowPos;
1152 if ( aFind == m_aPosToBookmarks.end() )
1154 if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
1156 m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
1158 if(m_nUseBookmarks == SQL_UB_OFF)
1159 throw SQLException();
1161 Sequence<sal_Int8> bookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
1162 m_aPosToBookmarks[bookmark] = m_nRowPos;
1163 OSL_ENSURE(bookmark.getLength(),"Invalid bookmark from length 0!");
1164 return bookmark;
1166 else
1168 return aFind->first;
1172 sal_Bool SAL_CALL OResultSet::moveToBookmark( const Any& bookmark )
1174 ::osl::MutexGuard aGuard( m_aMutex );
1175 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1177 invalidateCache();
1178 Sequence<sal_Int8> aBookmark;
1179 bookmark >>= aBookmark;
1180 OSL_ENSURE(aBookmark.getLength(),"Invalid bookmark from length 0!");
1181 if(aBookmark.getLength())
1183 SQLRETURN nReturn = setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));
1185 if ( SQL_INVALID_HANDLE != nReturn && SQL_ERROR != nReturn )
1187 m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0);
1188 OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
1189 TBookmarkPosMap::const_iterator aFind = m_aPosToBookmarks.find(aBookmark);
1190 if(aFind != m_aPosToBookmarks.end())
1191 m_nRowPos = aFind->second;
1192 else
1193 m_nRowPos = -1;
1194 return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
1197 return false;
1200 sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
1202 ::osl::MutexGuard aGuard( m_aMutex );
1203 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1206 invalidateCache();
1207 Sequence<sal_Int8> aBookmark;
1208 bookmark >>= aBookmark;
1209 setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));
1211 m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,rows);
1212 OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
1213 return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
1216 sal_Int32 SAL_CALL OResultSet::compareBookmarks( const Any& lhs, const Any& rhs )
1218 ::osl::MutexGuard aGuard( m_aMutex );
1219 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1221 return (lhs == rhs) ? CompareBookmark::EQUAL : CompareBookmark::NOT_EQUAL;
1224 sal_Bool SAL_CALL OResultSet::hasOrderedBookmarks( )
1226 return false;
1229 sal_Int32 SAL_CALL OResultSet::hashBookmark( const Any& /*bookmark*/ )
1231 ::dbtools::throwFunctionNotSupportedSQLException( "XRowLocate::hashBookmark", *this );
1232 return 0;
1235 // XDeleteRows
1236 Sequence< sal_Int32 > SAL_CALL OResultSet::deleteRows( const Sequence< Any >& rows )
1238 Sequence< sal_Int32 > aRet(rows.getLength());
1239 sal_Int32 *pRet = aRet.getArray();
1241 const Any *pBegin = rows.getConstArray();
1242 const Any *pEnd = pBegin + rows.getLength();
1244 for(;pBegin != pEnd;++pBegin,++pRet)
1248 if(moveToBookmark(*pBegin))
1250 deleteRow();
1251 *pRet = 1;
1254 catch(const SQLException&)
1256 *pRet = 0;
1259 return aRet;
1262 template < typename T, SQLINTEGER BufferLength > T OResultSet::getStmtOption (SQLINTEGER fOption) const
1264 T result (0);
1265 OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!");
1266 N3SQLGetStmtAttr(m_aStatementHandle, fOption, &result, BufferLength, nullptr);
1267 return result;
1269 template < typename T, SQLINTEGER BufferLength > SQLRETURN OResultSet::setStmtOption (SQLINTEGER fOption, T value) const
1271 OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!");
1272 SQLPOINTER sv = reinterpret_cast<SQLPOINTER>(value);
1273 return N3SQLSetStmtAttr(m_aStatementHandle, fOption, sv, BufferLength);
1276 sal_Int32 OResultSet::getResultSetConcurrency() const
1278 sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY);
1279 if(SQL_CONCUR_READ_ONLY == nValue)
1280 nValue = ResultSetConcurrency::READ_ONLY;
1281 else
1282 nValue = ResultSetConcurrency::UPDATABLE;
1284 return nValue;
1287 sal_Int32 OResultSet::getResultSetType() const
1289 sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SENSITIVITY);
1290 if(SQL_SENSITIVE == nValue)
1291 nValue = ResultSetType::SCROLL_SENSITIVE;
1292 else if(SQL_INSENSITIVE == nValue)
1293 nValue = ResultSetType::SCROLL_INSENSITIVE;
1294 else
1296 SQLULEN nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);
1297 if(SQL_CURSOR_KEYSET_DRIVEN == nCurType)
1298 nValue = ResultSetType::SCROLL_SENSITIVE;
1299 else if(SQL_CURSOR_STATIC == nCurType)
1300 nValue = ResultSetType::SCROLL_INSENSITIVE;
1301 else if(SQL_CURSOR_FORWARD_ONLY == nCurType)
1302 nValue = ResultSetType::FORWARD_ONLY;
1303 else if(SQL_CURSOR_DYNAMIC == nCurType)
1304 nValue = ResultSetType::SCROLL_SENSITIVE;
1306 return nValue;
1309 sal_Int32 OResultSet::getFetchSize() const
1311 return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE);
1314 OUString OResultSet::getCursorName() const
1316 SQLCHAR pName[258];
1317 SQLSMALLINT nRealLen = 0;
1318 N3SQLGetCursorName(m_aStatementHandle,pName,256,&nRealLen);
1319 return OUString::createFromAscii(reinterpret_cast<char*>(pName));
1322 bool OResultSet::isBookmarkable() const
1324 if(!m_aConnectionHandle)
1325 return false;
1327 const SQLULEN nCursorType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);
1329 sal_Int32 nAttr = 0;
1332 switch(nCursorType)
1334 case SQL_CURSOR_FORWARD_ONLY:
1335 return false;
1336 case SQL_CURSOR_STATIC:
1337 OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_STATIC_CURSOR_ATTRIBUTES1,nAttr,nullptr);
1338 break;
1339 case SQL_CURSOR_KEYSET_DRIVEN:
1340 OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_KEYSET_CURSOR_ATTRIBUTES1,nAttr,nullptr);
1341 break;
1342 case SQL_CURSOR_DYNAMIC:
1343 OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nAttr,nullptr);
1344 break;
1347 catch(const Exception&)
1349 return false;
1352 if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
1354 m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
1357 return (m_nUseBookmarks != SQL_UB_OFF) && (nAttr & SQL_CA1_BOOKMARK) == SQL_CA1_BOOKMARK;
1360 void OResultSet::setFetchDirection(sal_Int32 _par0)
1362 ::dbtools::throwFunctionNotSupportedSQLException( "setFetchDirection", *this );
1364 OSL_ENSURE(_par0>0,"Illegal fetch direction!");
1365 if ( _par0 > 0 )
1367 setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, _par0);
1371 void OResultSet::setFetchSize(sal_Int32 _par0)
1373 OSL_ENSURE(_par0>0,"Illegal fetch size!");
1374 if ( _par0 != 1 )
1376 throw css::beans::PropertyVetoException("SDBC/ODBC layer not prepared for fetchSize > 1", *this);
1378 setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE, _par0);
1379 m_pRowStatusArray.reset( new SQLUSMALLINT[_par0] );
1380 setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get());
1383 IPropertyArrayHelper* OResultSet::createArrayHelper( ) const
1385 Sequence< Property > aProps(6);
1386 Property* pProperties = aProps.getArray();
1387 sal_Int32 nPos = 0;
1388 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME),
1389 PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY);
1391 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
1392 PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0);
1394 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
1395 PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0);
1397 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE),
1398 PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY);
1400 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
1401 PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY);
1403 pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
1404 PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY);
1406 return new OPropertyArrayHelper(aProps);
1409 IPropertyArrayHelper & OResultSet::getInfoHelper()
1411 return *getArrayHelper();
1414 sal_Bool OResultSet::convertFastPropertyValue(
1415 Any & rConvertedValue,
1416 Any & rOldValue,
1417 sal_Int32 nHandle,
1418 const Any& rValue )
1420 switch(nHandle)
1422 case PROPERTY_ID_ISBOOKMARKABLE:
1423 case PROPERTY_ID_CURSORNAME:
1424 case PROPERTY_ID_RESULTSETCONCURRENCY:
1425 case PROPERTY_ID_RESULTSETTYPE:
1426 throw css::lang::IllegalArgumentException();
1427 case PROPERTY_ID_FETCHDIRECTION:
1428 return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection());
1429 case PROPERTY_ID_FETCHSIZE:
1430 return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize());
1431 default:
1434 return false;
1437 void OResultSet::setFastPropertyValue_NoBroadcast(
1438 sal_Int32 nHandle,
1439 const Any& rValue
1442 switch(nHandle)
1444 case PROPERTY_ID_ISBOOKMARKABLE:
1445 case PROPERTY_ID_CURSORNAME:
1446 case PROPERTY_ID_RESULTSETCONCURRENCY:
1447 case PROPERTY_ID_RESULTSETTYPE:
1448 throw Exception("cannot set prop " + OUString::number(nHandle), nullptr);
1449 case PROPERTY_ID_FETCHDIRECTION:
1450 setFetchDirection(getINT32(rValue));
1451 break;
1452 case PROPERTY_ID_FETCHSIZE:
1453 setFetchSize(getINT32(rValue));
1454 break;
1455 default:
1460 void OResultSet::getFastPropertyValue(
1461 Any& rValue,
1462 sal_Int32 nHandle
1463 ) const
1465 switch(nHandle)
1467 case PROPERTY_ID_ISBOOKMARKABLE:
1468 rValue <<= isBookmarkable();
1469 break;
1470 case PROPERTY_ID_CURSORNAME:
1471 rValue <<= getCursorName();
1472 break;
1473 case PROPERTY_ID_RESULTSETCONCURRENCY:
1474 rValue <<= getResultSetConcurrency();
1475 break;
1476 case PROPERTY_ID_RESULTSETTYPE:
1477 rValue <<= getResultSetType();
1478 break;
1479 case PROPERTY_ID_FETCHDIRECTION:
1480 rValue <<= getFetchDirection();
1481 break;
1482 case PROPERTY_ID_FETCHSIZE:
1483 rValue <<= getFetchSize();
1484 break;
1488 void OResultSet::fillColumn(const sal_Int32 _nColumn)
1490 ensureCacheForColumn(_nColumn);
1492 if (m_aRow[_nColumn].isBound())
1493 return;
1495 sal_Int32 curCol;
1496 if(m_bFetchDataInOrder)
1498 // m_aRow necessarily has a prefix of bound values, then all unbound values
1499 // EXCEPT for column 0
1500 // so use binary search to find the earliest unbound value before or at _nColumn
1501 sal_Int32 lower=0;
1502 sal_Int32 upper=_nColumn;
1504 while (lower < upper)
1506 const sal_Int32 middle=(upper-lower)/2 + lower;
1507 if(m_aRow[middle].isBound())
1509 lower=middle+1;
1511 else
1513 upper=middle;
1517 curCol = upper;
1519 else
1521 curCol = _nColumn;
1524 TDataRow::iterator pColumn = m_aRow.begin() + curCol;
1525 const TDataRow::const_iterator pColumnEnd = m_aRow.begin() + _nColumn + 1;
1527 if(curCol==0)
1531 *pColumn=impl_getBookmark();
1533 catch (SQLException &)
1535 pColumn->setNull();
1537 pColumn->setBound(true);
1538 ++curCol;
1539 ++pColumn;
1542 for (; pColumn != pColumnEnd; ++curCol, ++pColumn)
1544 const sal_Int32 nType = pColumn->getTypeKind();
1545 switch (nType)
1547 case DataType::CHAR:
1548 case DataType::VARCHAR:
1549 case DataType::DECIMAL:
1550 case DataType::NUMERIC:
1551 case DataType::LONGVARCHAR:
1552 case DataType::CLOB:
1553 *pColumn=impl_getString(curCol);
1554 break;
1555 case DataType::FLOAT:
1556 *pColumn = impl_getValue<float>(curCol, SQL_C_FLOAT);
1557 break;
1558 case DataType::REAL:
1559 case DataType::DOUBLE:
1560 *pColumn = impl_getValue<double>(curCol, SQL_C_DOUBLE);
1561 break;
1562 case DataType::BINARY:
1563 case DataType::VARBINARY:
1564 case DataType::LONGVARBINARY:
1565 case DataType::BLOB:
1566 *pColumn = impl_getBytes(curCol);
1567 break;
1568 case DataType::DATE:
1569 *pColumn = impl_getDate(curCol);
1570 break;
1571 case DataType::TIME:
1572 *pColumn = impl_getTime(curCol);
1573 break;
1574 case DataType::TIMESTAMP:
1575 *pColumn = impl_getTimestamp(curCol);
1576 break;
1577 case DataType::BIT:
1578 *pColumn = impl_getBoolean(curCol);
1579 break;
1580 case DataType::TINYINT:
1581 *pColumn = impl_getValue<sal_Int8>(curCol, SQL_C_TINYINT);
1582 break;
1583 case DataType::SMALLINT:
1584 *pColumn = impl_getValue<sal_Int16>(curCol, SQL_C_SHORT);
1585 break;
1586 case DataType::INTEGER:
1587 *pColumn = impl_getValue<sal_Int32>(curCol, SQL_C_LONG);
1588 break;
1589 case DataType::BIGINT:
1590 *pColumn = impl_getLong(curCol);
1591 break;
1592 default:
1593 SAL_WARN( "connectivity.odbc","Unknown DataType");
1596 if ( m_bWasNull )
1597 pColumn->setNull();
1598 pColumn->setBound(true);
1599 if(nType != pColumn->getTypeKind())
1601 pColumn->setTypeKind(nType);
1606 void SAL_CALL OResultSet::acquire() throw()
1608 OResultSet_BASE::acquire();
1611 void SAL_CALL OResultSet::release() throw()
1613 OResultSet_BASE::release();
1616 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( )
1618 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
1621 bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, bool /*_bRetrieveData*/)
1623 SQLSMALLINT nFetchOrientation = SQL_FETCH_NEXT;
1624 switch(_eCursorPosition)
1626 case IResultSetHelper::NEXT:
1627 nFetchOrientation = SQL_FETCH_NEXT;
1628 break;
1629 case IResultSetHelper::PRIOR:
1630 nFetchOrientation = SQL_FETCH_PRIOR;
1631 break;
1632 case IResultSetHelper::FIRST:
1633 nFetchOrientation = SQL_FETCH_FIRST;
1634 break;
1635 case IResultSetHelper::LAST:
1636 nFetchOrientation = SQL_FETCH_LAST;
1637 break;
1638 case IResultSetHelper::RELATIVE1:
1639 nFetchOrientation = SQL_FETCH_RELATIVE;
1640 break;
1641 case IResultSetHelper::ABSOLUTE1:
1642 nFetchOrientation = SQL_FETCH_ABSOLUTE;
1643 break;
1644 case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers
1646 TBookmarkPosMap::const_iterator aIter = std::find_if(m_aPosToBookmarks.begin(), m_aPosToBookmarks.end(),
1647 [&_nOffset](const TBookmarkPosMap::value_type& rEntry) { return rEntry.second == _nOffset; });
1648 if (aIter != m_aPosToBookmarks.end())
1649 return moveToBookmark(makeAny(aIter->first));
1650 SAL_WARN( "connectivity.odbc", "Bookmark not found!");
1652 return false;
1655 m_bEOF = false;
1656 invalidateCache();
1658 SQLRETURN nOldFetchStatus = m_nCurrentFetchState;
1659 // TODO FIXME: both of these will misbehave for
1660 // _eCursorPosition == IResultSetHelper::NEXT/PREVIOUS
1661 // when fetchSize > 1
1662 if ( !m_bUseFetchScroll && _eCursorPosition == IResultSetHelper::NEXT )
1663 m_nCurrentFetchState = N3SQLFetch(m_aStatementHandle);
1664 else
1665 m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,nFetchOrientation,_nOffset);
1667 SAL_INFO(
1668 "connectivity.odbc",
1669 "move(" << nFetchOrientation << "," << _nOffset << "), FetchState = "
1670 << m_nCurrentFetchState);
1671 OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
1673 const bool bSuccess = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
1674 if ( bSuccess )
1676 switch(_eCursorPosition)
1678 case IResultSetHelper::NEXT:
1679 ++m_nRowPos;
1680 break;
1681 case IResultSetHelper::PRIOR:
1682 --m_nRowPos;
1683 break;
1684 case IResultSetHelper::FIRST:
1685 m_nRowPos = 1;
1686 break;
1687 case IResultSetHelper::LAST:
1688 m_bEOF = true;
1689 break;
1690 case IResultSetHelper::RELATIVE1:
1691 m_nRowPos += _nOffset;
1692 break;
1693 case IResultSetHelper::ABSOLUTE1:
1694 case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers
1695 m_nRowPos = _nOffset;
1696 break;
1697 } // switch(_eCursorPosition)
1698 if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
1700 m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
1702 if ( m_nUseBookmarks == SQL_UB_OFF )
1704 m_aRow[0].setNull();
1706 else
1708 ensureCacheForColumn(0);
1709 Sequence<sal_Int8> bookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
1710 m_aPosToBookmarks[bookmark] = m_nRowPos;
1711 OSL_ENSURE(bookmark.getLength(),"Invalid bookmark from length 0!");
1712 m_aRow[0] = bookmark;
1714 m_aRow[0].setBound(true);
1716 else if ( IResultSetHelper::PRIOR == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA )
1717 // we went beforeFirst
1718 m_nRowPos = 0;
1719 else if(IResultSetHelper::NEXT == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA && nOldFetchStatus != SQL_NO_DATA)
1720 // we went afterLast
1721 ++m_nRowPos;
1723 return bSuccess;
1726 sal_Int32 OResultSet::getDriverPos() const
1728 sal_Int32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_NUMBER);
1729 SAL_INFO(
1730 "connectivity.odbc",
1731 "RowNum = " << nValue << ", RowPos = " << m_nRowPos);
1732 return nValue ? nValue : m_nRowPos;
1735 bool OResultSet::isRowDeleted() const
1737 return m_pRowStatusArray[0] == SQL_ROW_DELETED;
1740 bool OResultSet::moveImpl(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset)
1742 ::osl::MutexGuard aGuard( m_aMutex );
1743 checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
1744 return (m_pSkipDeletedSet != nullptr)
1745 ? m_pSkipDeletedSet->skipDeleted(_eCursorPosition,_nOffset,true/*_bRetrieveData*/)
1746 : move(_eCursorPosition,_nOffset,true/*_bRetrieveData*/);
1749 void OResultSet::fillNeededData(SQLRETURN _nRet)
1751 SQLRETURN nRet = _nRet;
1752 if( nRet == SQL_NEED_DATA)
1754 void* pColumnIndex = nullptr;
1755 nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex);
1759 if (nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO && nRet != SQL_NEED_DATA)
1760 break;
1762 sal_IntPtr nColumnIndex ( reinterpret_cast<sal_IntPtr>(pColumnIndex));
1763 Sequence< sal_Int8 > aSeq;
1764 switch(m_aRow[nColumnIndex].getTypeKind())
1766 case DataType::BINARY:
1767 case DataType::VARBINARY:
1768 case DataType::LONGVARBINARY:
1769 case DataType::BLOB:
1770 aSeq = m_aRow[nColumnIndex];
1771 N3SQLPutData (m_aStatementHandle, aSeq.getArray(), aSeq.getLength());
1772 break;
1773 case SQL_WLONGVARCHAR:
1775 OUString const & sRet = m_aRow[nColumnIndex].getString();
1776 N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<sal_Unicode *>(sRet.getStr())), sizeof(sal_Unicode)*sRet.getLength());
1777 break;
1779 case DataType::LONGVARCHAR:
1780 case DataType::CLOB:
1782 OUString sRet = m_aRow[nColumnIndex].getString();
1783 OString aString(OUStringToOString(sRet,m_nTextEncoding));
1784 N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<char *>(aString.getStr())), aString.getLength());
1785 break;
1787 default:
1788 SAL_WARN( "connectivity.odbc", "Not supported at the moment!");
1790 nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex);
1792 while (nRet == SQL_NEED_DATA);
1796 SWORD OResultSet::impl_getColumnType_nothrow(sal_Int32 columnIndex)
1798 std::map<sal_Int32,SWORD>::const_iterator aFind = m_aODBCColumnTypes.find(columnIndex);
1799 if ( aFind == m_aODBCColumnTypes.end() )
1800 aFind = m_aODBCColumnTypes.emplace(
1801 columnIndex,
1802 OResultSetMetaData::getColumnODBCType(m_pStatement->getOwnConnection(),m_aStatementHandle,*this,columnIndex)
1803 ).first;
1804 return aFind->second;
1807 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */