1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "ado/AConnection.hxx"
21 #include "ado/ADatabaseMetaData.hxx"
22 #include "ado/ADriver.hxx"
23 #include "ado/AStatement.hxx"
24 #include "ado/ACallableStatement.hxx"
25 #include "ado/APreparedStatement.hxx"
26 #include "ado/ACatalog.hxx"
27 #include <com/sun/star/sdbc/ColumnValue.hpp>
28 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
29 #include <com/sun/star/sdbc/XRow.hpp>
30 #include <com/sun/star/lang/DisposedException.hpp>
31 #include <cppuhelper/typeprovider.hxx>
32 #include "connectivity/dbexception.hxx"
33 #include <osl/file.hxx>
34 #include "resource/ado_res.hrc"
36 #include <o3tl/compat_functional.hxx>
38 using namespace dbtools
;
39 using namespace connectivity::ado
;
40 using namespace com::sun::star::uno
;
41 using namespace com::sun::star::lang
;
42 using namespace com::sun::star::beans
;
43 using namespace com::sun::star::sdbc
;
44 using namespace com::sun::star::sdbcx
;
47 IMPLEMENT_SERVICE_INFO(OConnection
,"com.sun.star.sdbcx.AConnection","com.sun.star.sdbc.Connection");
49 OConnection::OConnection(ODriver
* _pDriver
) throw(SQLException
, RuntimeException
)
50 : OSubComponent
<OConnection
, OConnection_BASE
>((::cppu::OWeakObject
*)_pDriver
, this),
53 m_pAdoConnection(NULL
),
57 m_bAutocommit(sal_True
)
59 osl_atomic_increment( &m_refCount
);
61 IClassFactory2
* pIUnknown
= NULL
;
63 hr
= CoGetClassObject( ADOS::CLSID_ADOCONNECTION_21
,
71 ADOConnection
*pCon
= NULL
;
72 IUnknown
*pOuter
= NULL
;
73 hr
= pIUnknown
->CreateInstanceLic( pOuter
,
75 ADOS::IID_ADOCONNECTION_21
,
81 OSL_ENSURE( pCon
, "OConnection::OConnection: invalid ADO object!" );
83 m_pAdoConnection
= new WpADOConnection( pCon
);
84 // CreateInstanceLic returned an object which was already acquired
89 // Class Factory is no longer needed
93 osl_atomic_decrement( &m_refCount
);
96 OConnection::~OConnection()
100 void OConnection::construct(const OUString
& url
,const Sequence
< PropertyValue
>& info
)
102 osl_atomic_increment( &m_refCount
);
104 setConnectionInfo(info
);
106 sal_Int32 nLen
= url
.indexOf(':');
107 nLen
= url
.indexOf(':',nLen
+1);
108 OUString
aDSN(url
.copy(nLen
+1)),aUID
,aPWD
;
109 if ( aDSN
.startsWith("access:") )
112 sal_Int32 nTimeout
= 20;
113 sal_Bool bSilent
= sal_True
;
114 const PropertyValue
*pIter
= info
.getConstArray();
115 const PropertyValue
*pEnd
= pIter
+ info
.getLength();
116 for(;pIter
!= pEnd
;++pIter
)
118 if(pIter
->Name
.equalsAscii("Timeout"))
119 pIter
->Value
>>= nTimeout
;
120 else if(pIter
->Name
.equalsAscii("Silent"))
121 pIter
->Value
>>= bSilent
;
122 else if(pIter
->Name
.equalsAscii("user"))
123 pIter
->Value
>>= aUID
;
124 else if(pIter
->Name
.equalsAscii("password"))
125 pIter
->Value
>>= aPWD
;
131 if(m_pAdoConnection
->Open(aDSN
,aUID
,aPWD
,adConnectUnspecified
))
132 m_pAdoConnection
->PutCommandTimeout(nTimeout
);
134 ADOS::ThrowException(*m_pAdoConnection
,*this);
135 if(m_pAdoConnection
->get_State() != adStateOpen
)
136 throwGenericSQLException( STR_NO_CONNECTION
,*this );
138 WpADOProperties aProps
= m_pAdoConnection
->get_Properties();
141 OTools::putValue(aProps
,OUString("Jet OLEDB:ODBC Parsing"),true);
142 OLEVariant
aVar(OTools::getValue(aProps
,OUString("Jet OLEDB:Engine Type")));
143 if(!aVar
.isNull() && !aVar
.isEmpty())
144 m_nEngineType
= aVar
;
150 ::dbtools::throwFunctionSequenceException(*this);
153 catch(const Exception
& )
155 osl_atomic_decrement( &m_refCount
);
158 osl_atomic_decrement( &m_refCount
);
161 void SAL_CALL
OConnection::release() throw()
166 Reference
< XStatement
> SAL_CALL
OConnection::createStatement( ) throw(SQLException
, RuntimeException
)
168 ::osl::MutexGuard
aGuard( m_aMutex
);
169 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
171 OStatement
* pStmt
= new OStatement(this);
172 Reference
< XStatement
> xStmt
= pStmt
;
173 m_aStatements
.push_back(WeakReferenceHelper(*pStmt
));
177 Reference
< XPreparedStatement
> SAL_CALL
OConnection::prepareStatement( const OUString
& sql
) throw(SQLException
, RuntimeException
)
179 ::osl::MutexGuard
aGuard( m_aMutex
);
180 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
183 OPreparedStatement
* pStmt
= new OPreparedStatement(this,m_aTypeInfo
,sql
);
184 Reference
< XPreparedStatement
> xPStmt
= pStmt
;
185 m_aStatements
.push_back(WeakReferenceHelper(*pStmt
));
189 Reference
< XPreparedStatement
> SAL_CALL
OConnection::prepareCall( const OUString
& sql
) throw(SQLException
, RuntimeException
)
191 ::osl::MutexGuard
aGuard( m_aMutex
);
192 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
195 OCallableStatement
* pStmt
= new OCallableStatement(this,m_aTypeInfo
,sql
);
196 Reference
< XPreparedStatement
> xPStmt
= pStmt
;
197 m_aStatements
.push_back(WeakReferenceHelper(*pStmt
));
201 OUString SAL_CALL
OConnection::nativeSQL( const OUString
& _sql
) throw(SQLException
, RuntimeException
)
203 ::osl::MutexGuard
aGuard( m_aMutex
);
204 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
208 WpADOProperties aProps
= m_pAdoConnection
->get_Properties();
211 OTools::putValue(aProps
,OUString("Jet OLEDB:ODBC Parsing"),true);
212 WpADOCommand aCommand
;
214 aCommand
.put_ActiveConnection((IDispatch
*)*m_pAdoConnection
);
215 aCommand
.put_CommandText(sql
);
216 sql
= aCommand
.get_CommandText();
222 void SAL_CALL
OConnection::setAutoCommit( sal_Bool autoCommit
) throw(SQLException
, RuntimeException
)
224 ::osl::MutexGuard
aGuard( m_aMutex
);
225 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
228 m_bAutocommit
= autoCommit
;
230 m_pAdoConnection
->BeginTrans();
232 m_pAdoConnection
->RollbackTrans();
235 sal_Bool SAL_CALL
OConnection::getAutoCommit( ) throw(SQLException
, RuntimeException
)
237 ::osl::MutexGuard
aGuard( m_aMutex
);
238 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
241 return m_bAutocommit
;
244 void SAL_CALL
OConnection::commit( ) throw(SQLException
, RuntimeException
)
246 ::osl::MutexGuard
aGuard( m_aMutex
);
247 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
250 m_pAdoConnection
->CommitTrans();
253 void SAL_CALL
OConnection::rollback( ) throw(SQLException
, RuntimeException
)
255 ::osl::MutexGuard
aGuard( m_aMutex
);
256 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
259 m_pAdoConnection
->RollbackTrans();
262 sal_Bool SAL_CALL
OConnection::isClosed( ) throw(SQLException
, RuntimeException
)
264 ::osl::MutexGuard
aGuard( m_aMutex
);
266 return OConnection_BASE::rBHelper
.bDisposed
&& !m_pAdoConnection
->get_State();
269 Reference
< XDatabaseMetaData
> SAL_CALL
OConnection::getMetaData( ) throw(SQLException
, RuntimeException
)
271 ::osl::MutexGuard
aGuard( m_aMutex
);
272 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
275 Reference
< XDatabaseMetaData
> xMetaData
= m_xMetaData
;
278 xMetaData
= new ODatabaseMetaData(this);
279 m_xMetaData
= xMetaData
;
285 void SAL_CALL
OConnection::setReadOnly( sal_Bool readOnly
) throw(SQLException
, RuntimeException
)
287 ::osl::MutexGuard
aGuard( m_aMutex
);
288 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
292 m_pAdoConnection
->put_Mode(readOnly
? adModeRead
: adModeReadWrite
);
293 ADOS::ThrowException(*m_pAdoConnection
,*this);
296 sal_Bool SAL_CALL
OConnection::isReadOnly( ) throw(SQLException
, RuntimeException
)
298 ::osl::MutexGuard
aGuard( m_aMutex
);
299 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
302 return m_pAdoConnection
->get_Mode() == adModeRead
;
305 void SAL_CALL
OConnection::setCatalog( const OUString
& catalog
) throw(SQLException
, RuntimeException
)
307 ::osl::MutexGuard
aGuard( m_aMutex
);
308 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
310 m_pAdoConnection
->PutDefaultDatabase(catalog
);
311 ADOS::ThrowException(*m_pAdoConnection
,*this);
314 OUString SAL_CALL
OConnection::getCatalog( ) throw(SQLException
, RuntimeException
)
316 ::osl::MutexGuard
aGuard( m_aMutex
);
317 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
319 return m_pAdoConnection
->GetDefaultDatabase();
322 void SAL_CALL
OConnection::setTransactionIsolation( sal_Int32 level
) throw(SQLException
, RuntimeException
)
324 ::osl::MutexGuard
aGuard( m_aMutex
);
325 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
328 IsolationLevelEnum eIso
;
331 case TransactionIsolation::NONE
:
332 eIso
= adXactUnspecified
;
334 case TransactionIsolation::READ_UNCOMMITTED
:
335 eIso
= adXactReadUncommitted
;
337 case TransactionIsolation::READ_COMMITTED
:
338 eIso
= adXactReadCommitted
;
340 case TransactionIsolation::REPEATABLE_READ
:
341 eIso
= adXactRepeatableRead
;
343 case TransactionIsolation::SERIALIZABLE
:
344 eIso
= adXactSerializable
;
347 OSL_FAIL("OConnection::setTransactionIsolation invalid level");
350 m_pAdoConnection
->put_IsolationLevel(eIso
);
351 ADOS::ThrowException(*m_pAdoConnection
,*this);
354 sal_Int32 SAL_CALL
OConnection::getTransactionIsolation( ) throw(SQLException
, RuntimeException
)
356 ::osl::MutexGuard
aGuard( m_aMutex
);
357 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
361 switch(m_pAdoConnection
->get_IsolationLevel())
363 case adXactUnspecified
:
364 nRet
= TransactionIsolation::NONE
;
366 case adXactReadUncommitted
:
367 nRet
= TransactionIsolation::READ_UNCOMMITTED
;
369 case adXactReadCommitted
:
370 nRet
= TransactionIsolation::READ_COMMITTED
;
372 case adXactRepeatableRead
:
373 nRet
= TransactionIsolation::REPEATABLE_READ
;
375 case adXactSerializable
:
376 nRet
= TransactionIsolation::SERIALIZABLE
;
379 OSL_FAIL("OConnection::setTransactionIsolation invalid level");
381 ADOS::ThrowException(*m_pAdoConnection
,*this);
385 Reference
< ::com::sun::star::container::XNameAccess
> SAL_CALL
OConnection::getTypeMap( ) throw(SQLException
, RuntimeException
)
387 ::osl::MutexGuard
aGuard( m_aMutex
);
388 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
394 void SAL_CALL
OConnection::setTypeMap( const Reference
< ::com::sun::star::container::XNameAccess
>& /*typeMap*/ ) throw(SQLException
, RuntimeException
)
396 ::dbtools::throwFeatureNotImplementedException( "XConnection::setTypeMap", *this );
400 void SAL_CALL
OConnection::close( ) throw(SQLException
, RuntimeException
)
403 ::osl::MutexGuard
aGuard( m_aMutex
);
404 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
411 Any SAL_CALL
OConnection::getWarnings( ) throw(SQLException
, RuntimeException
)
416 void SAL_CALL
OConnection::clearWarnings( ) throw(SQLException
, RuntimeException
)
420 void OConnection::buildTypeInfo() throw( SQLException
)
422 ::osl::MutexGuard
aGuard( m_aMutex
);
424 ADORecordset
*pRecordset
= m_pAdoConnection
->getTypeInfo();
427 pRecordset
->AddRef();
428 VARIANT_BOOL bIsAtBOF
;
429 pRecordset
->get_BOF(&bIsAtBOF
);
431 sal_Bool bOk
= sal_True
;
432 if ( bIsAtBOF
== VARIANT_TRUE
)
433 bOk
= SUCCEEDED(pRecordset
->MoveNext());
438 static const OUString
s_sVarChar("VarChar");
442 OExtendedTypeInfo
* aInfo
= new OExtendedTypeInfo();
443 aInfo
->aSimpleType
.aTypeName
= ADOS::getField(pRecordset
,nPos
++).get_Value();
444 aInfo
->eType
= (DataTypeEnum
)(sal_Int32
)ADOS::getField(pRecordset
,nPos
++).get_Value();
445 if ( aInfo
->eType
== adWChar
&& aInfo
->aSimpleType
.aTypeName
== s_sVarChar
)
446 aInfo
->eType
= adVarWChar
;
447 aInfo
->aSimpleType
.nType
= (sal_Int16
)ADOS::MapADOType2Jdbc(static_cast<DataTypeEnum
>(aInfo
->eType
));
448 aInfo
->aSimpleType
.nPrecision
= ADOS::getField(pRecordset
,nPos
++).get_Value();
449 aInfo
->aSimpleType
.aLiteralPrefix
= ADOS::getField(pRecordset
,nPos
++).get_Value();
450 aInfo
->aSimpleType
.aLiteralSuffix
= ADOS::getField(pRecordset
,nPos
++).get_Value();
451 aInfo
->aSimpleType
.aCreateParams
= ADOS::getField(pRecordset
,nPos
++).get_Value();
452 aInfo
->aSimpleType
.bNullable
= ADOS::getField(pRecordset
,nPos
++).get_Value();
453 aInfo
->aSimpleType
.bCaseSensitive
= ADOS::getField(pRecordset
,nPos
++).get_Value();
454 aInfo
->aSimpleType
.nSearchType
= ADOS::getField(pRecordset
,nPos
++).get_Value();
455 aInfo
->aSimpleType
.bUnsigned
= ADOS::getField(pRecordset
,nPos
++).get_Value();
456 aInfo
->aSimpleType
.bCurrency
= ADOS::getField(pRecordset
,nPos
++).get_Value();
457 aInfo
->aSimpleType
.bAutoIncrement
= ADOS::getField(pRecordset
,nPos
++).get_Value();
458 aInfo
->aSimpleType
.aLocalTypeName
= ADOS::getField(pRecordset
,nPos
++).get_Value();
459 aInfo
->aSimpleType
.nMinimumScale
= ADOS::getField(pRecordset
,nPos
++).get_Value();
460 aInfo
->aSimpleType
.nMaximumScale
= ADOS::getField(pRecordset
,nPos
++).get_Value();
461 if ( adCurrency
== aInfo
->eType
&& !aInfo
->aSimpleType
.nMaximumScale
)
463 aInfo
->aSimpleType
.nMinimumScale
= 4;
464 aInfo
->aSimpleType
.nMaximumScale
= 4;
466 aInfo
->aSimpleType
.nNumPrecRadix
= ADOS::getField(pRecordset
,nPos
++).get_Value();
467 // Now that we have the type info, save it
468 // in the Hashtable if we don't already have an
469 // entry for this SQL type.
471 m_aTypeInfo
.insert(OTypeInfoMap::value_type(aInfo
->eType
,aInfo
));
473 while ( SUCCEEDED(pRecordset
->MoveNext()) );
475 pRecordset
->Release();
479 void OConnection::disposing()
481 ::osl::MutexGuard
aGuard(m_aMutex
);
483 OConnection_BASE::disposing();
485 m_bClosed
= sal_True
;
486 m_xMetaData
= ::com::sun::star::uno::WeakReference
< ::com::sun::star::sdbc::XDatabaseMetaData
>();
487 m_xCatalog
= ::com::sun::star::uno::WeakReference
< ::com::sun::star::sdbcx::XTablesSupplier
>();
490 m_pAdoConnection
->Close();
492 OTypeInfoMap::iterator aIter
= m_aTypeInfo
.begin();
493 for (; aIter
!= m_aTypeInfo
.end(); ++aIter
)
494 delete aIter
->second
;
498 delete m_pAdoConnection
;
499 m_pAdoConnection
= NULL
;
504 sal_Int64 SAL_CALL
OConnection::getSomething( const ::com::sun::star::uno::Sequence
< sal_Int8
>& rId
) throw (::com::sun::star::uno::RuntimeException
)
506 return (rId
.getLength() == 16 && 0 == memcmp(getUnoTunnelImplementationId().getConstArray(), rId
.getConstArray(), 16 ) )
508 reinterpret_cast< sal_Int64
>( this )
510 OConnection_BASE::getSomething(rId
);
513 Sequence
< sal_Int8
> OConnection::getUnoTunnelImplementationId()
515 static ::cppu::OImplementationId
* pId
= 0;
518 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
521 static ::cppu::OImplementationId aId
;
525 return pId
->getImplementationId();
528 const OExtendedTypeInfo
* OConnection::getTypeInfoFromType(const OTypeInfoMap
& _rTypeInfo
,
530 const OUString
& _sTypeName
,
531 sal_Int32 _nPrecision
,
533 sal_Bool
& _brForceToType
)
535 const OExtendedTypeInfo
* pTypeInfo
= NULL
;
536 _brForceToType
= sal_False
;
538 ::std::pair
<OTypeInfoMap::const_iterator
, OTypeInfoMap::const_iterator
> aPair
= _rTypeInfo
.equal_range(_nType
);
539 OTypeInfoMap::const_iterator aIter
= aPair
.first
;
540 if(aIter
!= _rTypeInfo
.end()) // compare with end is correct here
542 for(;aIter
!= aPair
.second
;++aIter
)
544 // search the best matching type
545 OExtendedTypeInfo
* pInfo
= aIter
->second
;
547 OUString sDBTypeName
= pInfo
->aSimpleType
.aTypeName
;
548 sal_Int32 nDBTypePrecision
= pInfo
->aSimpleType
.nPrecision
; (void)nDBTypePrecision
;
549 sal_Int32 nDBTypeScale
= pInfo
->aSimpleType
.nMaximumScale
; (void)nDBTypeScale
;
550 sal_Int32 nAdoType
= pInfo
->eType
; (void)nAdoType
;
552 if ( ( !_sTypeName
.getLength()
553 || (pInfo
->aSimpleType
.aTypeName
.equalsIgnoreAsciiCase(_sTypeName
))
555 && (pInfo
->aSimpleType
.nPrecision
>= _nPrecision
)
556 && (pInfo
->aSimpleType
.nMaximumScale
>= _nScale
)
562 if (aIter
== aPair
.second
)
564 for(aIter
= aPair
.first
; aIter
!= aPair
.second
; ++aIter
)
566 // search the best matching type (now comparing the local names)
567 if ( (aIter
->second
->aSimpleType
.aLocalTypeName
.equalsIgnoreAsciiCase(_sTypeName
))
568 && (aIter
->second
->aSimpleType
.nPrecision
>= _nPrecision
)
569 && (aIter
->second
->aSimpleType
.nMaximumScale
>= _nScale
)
572 // we can not assert here because we could be in d&d
574 OSL_FAIL(( OString("getTypeInfoFromType: assuming column type ")
575 += OString(aIter->second->aTypeName.getStr(), aIter->second->aTypeName.getLength(), osl_getThreadTextEncoding())
576 += OString("\" (expected type name ")
577 += OString(_sTypeName.getStr(), _sTypeName.getLength(), osl_getThreadTextEncoding())
578 += OString(" matches the type's local name).")).getStr());
585 if (aIter
== aPair
.second
)
586 { // no match for the names, no match for the local names
587 // -> drop the precision and the scale restriction, accept any type with the property
590 // we can not assert here because we could be in d&d
591 pTypeInfo
= aPair
.first
->second
;
592 _brForceToType
= sal_True
;
595 pTypeInfo
= aIter
->second
;
597 else if ( _sTypeName
.getLength() )
599 ::comphelper::UStringMixEqual
aCase(sal_False
);
600 // search for typeinfo where the typename is equal _sTypeName
601 OTypeInfoMap::const_iterator aFind
= ::std::find_if(_rTypeInfo
.begin(),
604 ::std::bind2nd(aCase
, _sTypeName
),
606 ::std::mem_fun(&OExtendedTypeInfo::getDBName
),
607 ::o3tl::select2nd
<OTypeInfoMap::value_type
>())
610 if(aFind
!= _rTypeInfo
.end())
611 pTypeInfo
= aFind
->second
;
614 // we can not assert here because we could be in d&d
615 // OSL_ENSURE(pTypeInfo, "getTypeInfoFromType: no type info found for this type!");
622 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */