1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <ado/AConnection.hxx>
25 #include <ado/ADatabaseMetaData.hxx>
26 #include <ado/ADriver.hxx>
27 #include <ado/AStatement.hxx>
28 #include <ado/ACallableStatement.hxx>
29 #include <ado/APreparedStatement.hxx>
30 #include <ado/ACatalog.hxx>
31 #include <com/sun/star/sdbc/ColumnValue.hpp>
32 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
33 #include <com/sun/star/sdbc/XRow.hpp>
34 #include <com/sun/star/lang/DisposedException.hpp>
35 #include <comphelper/servicehelper.hxx>
36 #include <cppuhelper/typeprovider.hxx>
37 #include <connectivity/dbexception.hxx>
38 #include <osl/file.hxx>
39 #include <strings.hrc>
40 
41 using namespace dbtools;
42 using namespace connectivity::ado;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::lang;
45 using namespace com::sun::star::beans;
46 using namespace com::sun::star::sdbc;
47 using namespace com::sun::star::sdbcx;
48 
49 
50 IMPLEMENT_SERVICE_INFO(OConnection,"com.sun.star.sdbcx.AConnection","com.sun.star.sdbc.Connection");
51 
OConnection(ODriver * _pDriver)52 OConnection::OConnection(ODriver*   _pDriver)
53                          : m_xCatalog(nullptr),
54                          m_pDriver(_pDriver),
55                          m_pAdoConnection(nullptr),
56                          m_pCatalog(nullptr),
57                          m_nEngineType(0),
58                          m_bClosed(false),
59                          m_bAutocommit(true)
60 {
61     osl_atomic_increment( &m_refCount );
62 
63     IClassFactory2* pIUnknown   = nullptr;
64     HRESULT         hr;
65     hr = CoGetClassObject( ADOS::CLSID_ADOCONNECTION_21,
66                           CLSCTX_INPROC_SERVER,
67                           nullptr,
68                           IID_IClassFactory2,
69                           reinterpret_cast<void**>(&pIUnknown) );
70 
71     if( !FAILED( hr ) )
72     {
73         ADOConnection *pCon         = nullptr;
74         IUnknown *pOuter     = nullptr;
75         hr = pIUnknown->CreateInstanceLic(  pOuter,
76                                             nullptr,
77                                             ADOS::IID_ADOCONNECTION_21,
78                                             ADOS::GetKeyStr().asBSTR(),
79                                             reinterpret_cast<void**>(&pCon));
80 
81         if( !FAILED( hr ) )
82         {
83             OSL_ENSURE( pCon, "OConnection::OConnection: invalid ADO object!" );
84 
85             m_pAdoConnection = new WpADOConnection( pCon );
86             // CreateInstanceLic returned an object which was already acquired
87             pCon->Release( );
88 
89         }
90 
91         // Class Factory is no longer needed
92         pIUnknown->Release();
93     }
94 
95     osl_atomic_decrement( &m_refCount );
96 }
97 
~OConnection()98 OConnection::~OConnection()
99 {
100 }
101 
construct(const OUString & url,const Sequence<PropertyValue> & info)102 void OConnection::construct(const OUString& url,const Sequence< PropertyValue >& info)
103 {
104     osl_atomic_increment( &m_refCount );
105 
106     setConnectionInfo(info);
107 
108     sal_Int32 nLen = url.indexOf(':');
109     nLen = url.indexOf(':',nLen+1);
110     OUString aDSN(url.copy(nLen+1)),aUID,aPWD;
111     if ( aDSN.startsWith("access:") )
112         aDSN = aDSN.copy(7);
113 
114     sal_Int32 nTimeout = 20;
115     const PropertyValue *pIter  = info.getConstArray();
116     const PropertyValue *pEnd   = pIter + info.getLength();
117     for(;pIter != pEnd;++pIter)
118     {
119         if(pIter->Name == "Timeout")
120             pIter->Value >>= nTimeout;
121         else if(pIter->Name == "user")
122             pIter->Value >>= aUID;
123         else if(pIter->Name == "password")
124             pIter->Value >>= aPWD;
125     }
126     try
127     {
128         if(m_pAdoConnection)
129         {
130             if(m_pAdoConnection->Open(aDSN,aUID,aPWD,adConnectUnspecified))
131                 m_pAdoConnection->PutCommandTimeout(nTimeout);
132             else
133                 ADOS::ThrowException(*m_pAdoConnection,*this);
134             if(m_pAdoConnection->get_State() != adStateOpen)
135                 throwGenericSQLException( STR_NO_CONNECTION,*this );
136 
137             WpADOProperties aProps = m_pAdoConnection->get_Properties();
138             if(aProps.IsValid())
139             {
140                 OTools::putValue(aProps, std::u16string_view(u"Jet OLEDB:ODBC Parsing"), true);
141                 OLEVariant aVar(
142                     OTools::getValue(aProps, std::u16string_view(u"Jet OLEDB:Engine Type")));
143                 if(!aVar.isNull() && !aVar.isEmpty())
144                     m_nEngineType = aVar.getInt32();
145             }
146             buildTypeInfo();
147             //bErg = TRUE;
148         }
149         else
150             ::dbtools::throwFunctionSequenceException(*this);
151 
152     }
153     catch(const Exception& )
154     {
155         osl_atomic_decrement( &m_refCount );
156         throw;
157     }
158     osl_atomic_decrement( &m_refCount );
159 }
160 
createStatement()161 Reference< XStatement > SAL_CALL OConnection::createStatement(  )
162 {
163     ::osl::MutexGuard aGuard( m_aMutex );
164     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
165 
166     Reference< XStatement > xStmt = new OStatement(this);
167     m_aStatements.push_back(WeakReferenceHelper(xStmt));
168     return xStmt;
169 }
170 
prepareStatement(const OUString & sql)171 Reference< XPreparedStatement > SAL_CALL OConnection::prepareStatement( const OUString& sql )
172 {
173     ::osl::MutexGuard aGuard( m_aMutex );
174     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
175 
176 
177     Reference< XPreparedStatement > xPStmt = new OPreparedStatement(this, sql);
178     m_aStatements.push_back(WeakReferenceHelper(xPStmt));
179     return xPStmt;
180 }
181 
prepareCall(const OUString & sql)182 Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& sql )
183 {
184     ::osl::MutexGuard aGuard( m_aMutex );
185     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
186 
187 
188     Reference< XPreparedStatement > xPStmt = new OCallableStatement(this, sql);
189     m_aStatements.push_back(WeakReferenceHelper(xPStmt));
190     return xPStmt;
191 }
192 
nativeSQL(const OUString & _sql)193 OUString SAL_CALL OConnection::nativeSQL( const OUString& _sql )
194 {
195     ::osl::MutexGuard aGuard( m_aMutex );
196     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
197 
198 
199     OUString sql = _sql;
200     WpADOProperties aProps = m_pAdoConnection->get_Properties();
201     if(aProps.IsValid())
202     {
203         OTools::putValue(aProps, std::u16string_view(u"Jet OLEDB:ODBC Parsing"), true);
204         WpADOCommand aCommand;
205         aCommand.Create();
206         aCommand.put_ActiveConnection(static_cast<IDispatch*>(*m_pAdoConnection));
207         aCommand.put_CommandText(sql);
208         sql = aCommand.get_CommandText();
209     }
210 
211     return sql;
212 }
213 
setAutoCommit(sal_Bool autoCommit)214 void SAL_CALL OConnection::setAutoCommit( sal_Bool autoCommit )
215 {
216     ::osl::MutexGuard aGuard( m_aMutex );
217     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
218 
219 
220     m_bAutocommit = autoCommit;
221     if(!autoCommit)
222         m_pAdoConnection->BeginTrans();
223     else
224         m_pAdoConnection->RollbackTrans();
225 }
226 
getAutoCommit()227 sal_Bool SAL_CALL OConnection::getAutoCommit(  )
228 {
229     ::osl::MutexGuard aGuard( m_aMutex );
230     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
231 
232 
233     return m_bAutocommit;
234 }
235 
commit()236 void SAL_CALL OConnection::commit(  )
237 {
238     ::osl::MutexGuard aGuard( m_aMutex );
239     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
240 
241 
242     m_pAdoConnection->CommitTrans();
243 }
244 
rollback()245 void SAL_CALL OConnection::rollback(  )
246 {
247     ::osl::MutexGuard aGuard( m_aMutex );
248     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
249 
250 
251     m_pAdoConnection->RollbackTrans();
252 }
253 
isClosed()254 sal_Bool SAL_CALL OConnection::isClosed(  )
255 {
256     ::osl::MutexGuard aGuard( m_aMutex );
257 
258     return OConnection_BASE::rBHelper.bDisposed && !m_pAdoConnection->get_State();
259 }
260 
getMetaData()261 Reference< XDatabaseMetaData > SAL_CALL OConnection::getMetaData(  )
262 {
263     ::osl::MutexGuard aGuard( m_aMutex );
264     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
265 
266 
267     Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
268     if(!xMetaData.is())
269     {
270         xMetaData = new ODatabaseMetaData(this);
271         m_xMetaData = xMetaData;
272     }
273 
274     return xMetaData;
275 }
276 
setReadOnly(sal_Bool readOnly)277 void SAL_CALL OConnection::setReadOnly( sal_Bool readOnly )
278 {
279     ::osl::MutexGuard aGuard( m_aMutex );
280     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
281 
282 
283     m_pAdoConnection->put_Mode(readOnly ? adModeRead : adModeReadWrite);
284     ADOS::ThrowException(*m_pAdoConnection,*this);
285 }
286 
isReadOnly()287 sal_Bool SAL_CALL OConnection::isReadOnly(  )
288 {
289     ::osl::MutexGuard aGuard( m_aMutex );
290     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
291 
292 
293     return m_pAdoConnection->get_Mode() == adModeRead;
294 }
295 
setCatalog(const OUString & catalog)296 void SAL_CALL OConnection::setCatalog( const OUString& catalog )
297 {
298     ::osl::MutexGuard aGuard( m_aMutex );
299     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
300 
301     m_pAdoConnection->PutDefaultDatabase(catalog);
302     ADOS::ThrowException(*m_pAdoConnection,*this);
303 }
304 
getCatalog()305 OUString SAL_CALL OConnection::getCatalog(  )
306 {
307     ::osl::MutexGuard aGuard( m_aMutex );
308     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
309 
310     return m_pAdoConnection->GetDefaultDatabase();
311 }
312 
setTransactionIsolation(sal_Int32 level)313 void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 level )
314 {
315     ::osl::MutexGuard aGuard( m_aMutex );
316     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
317 
318 
319     IsolationLevelEnum eIso;
320     switch(level)
321     {
322         case TransactionIsolation::NONE:
323             eIso = adXactUnspecified;
324             break;
325         case TransactionIsolation::READ_UNCOMMITTED:
326             eIso = adXactReadUncommitted;
327             break;
328         case TransactionIsolation::READ_COMMITTED:
329             eIso = adXactReadCommitted;
330             break;
331         case TransactionIsolation::REPEATABLE_READ:
332             eIso = adXactRepeatableRead;
333             break;
334         case TransactionIsolation::SERIALIZABLE:
335             eIso = adXactSerializable;
336             break;
337         default:
338             OSL_FAIL("OConnection::setTransactionIsolation invalid level");
339             return;
340     }
341     m_pAdoConnection->put_IsolationLevel(eIso);
342     ADOS::ThrowException(*m_pAdoConnection,*this);
343 }
344 
getTransactionIsolation()345 sal_Int32 SAL_CALL OConnection::getTransactionIsolation(  )
346 {
347     ::osl::MutexGuard aGuard( m_aMutex );
348     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
349 
350 
351     sal_Int32 nRet = 0;
352     switch(m_pAdoConnection->get_IsolationLevel())
353     {
354         case adXactUnspecified:
355             nRet = TransactionIsolation::NONE;
356             break;
357         case adXactReadUncommitted:
358             nRet = TransactionIsolation::READ_UNCOMMITTED;
359             break;
360         case adXactReadCommitted:
361             nRet = TransactionIsolation::READ_COMMITTED;
362             break;
363         case adXactRepeatableRead:
364             nRet = TransactionIsolation::REPEATABLE_READ;
365             break;
366         case adXactSerializable:
367             nRet = TransactionIsolation::SERIALIZABLE;
368             break;
369         default:
370             OSL_FAIL("OConnection::setTransactionIsolation invalid level");
371     }
372     ADOS::ThrowException(*m_pAdoConnection,*this);
373     return nRet;
374 }
375 
getTypeMap()376 Reference< css::container::XNameAccess > SAL_CALL OConnection::getTypeMap(  )
377 {
378     ::osl::MutexGuard aGuard( m_aMutex );
379     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
380 
381 
382     return nullptr;
383 }
384 
setTypeMap(const Reference<css::container::XNameAccess> &)385 void SAL_CALL OConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ )
386 {
387     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
388 }
389 
390 // XCloseable
close()391 void SAL_CALL OConnection::close(  )
392 {
393     {
394         ::osl::MutexGuard aGuard( m_aMutex );
395         checkDisposed(OConnection_BASE::rBHelper.bDisposed);
396 
397     }
398     dispose();
399 }
400 
401 // XWarningsSupplier
getWarnings()402 Any SAL_CALL OConnection::getWarnings(  )
403 {
404     return Any();
405 }
406 
clearWarnings()407 void SAL_CALL OConnection::clearWarnings(  )
408 {
409 }
410 
buildTypeInfo()411 void OConnection::buildTypeInfo()
412 {
413     ::osl::MutexGuard aGuard( m_aMutex );
414 
415     ADORecordset *pRecordset = m_pAdoConnection->getTypeInfo();
416     if ( pRecordset )
417     {
418         pRecordset->AddRef();
419         VARIANT_BOOL bIsAtBOF;
420         pRecordset->get_BOF(&bIsAtBOF);
421 
422         bool bOk = true;
423         if ( bIsAtBOF == VARIANT_TRUE )
424             bOk = SUCCEEDED(pRecordset->MoveNext());
425 
426         if ( bOk )
427         {
428             // HACK for access
429             static const char s_sVarChar[] = "VarChar";
430             do
431             {
432                 sal_Int32 nPos = 1;
433                 OExtendedTypeInfo* aInfo            = new OExtendedTypeInfo;
434                 aInfo->aSimpleType.aTypeName        = ADOS::getField(pRecordset,nPos++).get_Value().getString();
435                 aInfo->eType                        = static_cast<DataTypeEnum>(ADOS::getField(pRecordset,nPos++).get_Value().getInt32());
436                 if ( aInfo->eType == adWChar && aInfo->aSimpleType.aTypeName == s_sVarChar )
437                     aInfo->eType = adVarWChar;
438                 aInfo->aSimpleType.nType            = static_cast<sal_Int16>(ADOS::MapADOType2Jdbc(aInfo->eType));
439                 aInfo->aSimpleType.nPrecision       = ADOS::getField(pRecordset,nPos++).get_Value().getInt32();
440                 nPos++; // aLiteralPrefix
441                 nPos++; // aLiteralSuffix
442                 nPos++; // aCreateParams
443                 nPos++; // bNullable
444                 nPos++; // bCaseSensitive
445                 nPos++; // nSearchType
446                 nPos++; // bUnsigned
447                 nPos++; // bCurrency
448                 nPos++; // bAutoIncrement
449                 aInfo->aSimpleType.aLocalTypeName   = ADOS::getField(pRecordset,nPos++).get_Value().getString();
450                 nPos++; // nMinimumScale
451                 aInfo->aSimpleType.nMaximumScale    = ADOS::getField(pRecordset,nPos++).get_Value().getInt16();
452                 if ( adCurrency == aInfo->eType && !aInfo->aSimpleType.nMaximumScale)
453                 {
454                     aInfo->aSimpleType.nMaximumScale = 4;
455                 }
456                 nPos++; // nNumPrecRadix
457                 // Now that we have the type info, save it
458                 // in the Hashtable if we don't already have an
459                 // entry for this SQL type.
460 
461                 m_aTypeInfo.emplace(aInfo->eType,aInfo);
462             }
463             while ( SUCCEEDED(pRecordset->MoveNext()) );
464         }
465         pRecordset->Release();
466     }
467 }
468 
disposing()469 void OConnection::disposing()
470 {
471     ::osl::MutexGuard aGuard(m_aMutex);
472 
473     OConnection_BASE::disposing();
474 
475     m_bClosed   = true;
476     m_xMetaData = css::uno::WeakReference< css::sdbc::XDatabaseMetaData>();
477     m_xCatalog  = css::uno::WeakReference< css::sdbcx::XTablesSupplier>();
478     m_pDriver   = nullptr;
479 
480     m_pAdoConnection->Close();
481 
482     for (auto& rEntry : m_aTypeInfo)
483         delete rEntry.second;
484 
485     m_aTypeInfo.clear();
486 
487     delete m_pAdoConnection;
488     m_pAdoConnection = nullptr;
489 }
490 
getSomething(const css::uno::Sequence<sal_Int8> & rId)491 sal_Int64 SAL_CALL OConnection::getSomething( const css::uno::Sequence< sal_Int8 >& rId )
492 {
493     return isUnoTunnelId<OConnection>(rId)
494                 ?
495             reinterpret_cast< sal_Int64 >( this )
496                 :
497             OConnection_BASE::getSomething(rId);
498 }
499 
getUnoTunnelId()500 Sequence< sal_Int8 > OConnection::getUnoTunnelId()
501 {
502     static ::cppu::OImplementationId implId;
503 
504     return implId.getImplementationId();
505 }
506 
getTypeInfoFromType(const OTypeInfoMap & _rTypeInfo,DataTypeEnum _nType,const OUString & _sTypeName,sal_Int32 _nPrecision,sal_Int32 _nScale,bool & _brForceToType)507 const OExtendedTypeInfo* OConnection::getTypeInfoFromType(const OTypeInfoMap& _rTypeInfo,
508                            DataTypeEnum _nType,
509                            const OUString& _sTypeName,
510                            sal_Int32 _nPrecision,
511                            sal_Int32 _nScale,
512                            bool& _brForceToType)
513 {
514     const OExtendedTypeInfo* pTypeInfo = nullptr;
515     _brForceToType = false;
516     // search for type
517     std::pair<OTypeInfoMap::const_iterator, OTypeInfoMap::const_iterator> aPair = _rTypeInfo.equal_range(_nType);
518     OTypeInfoMap::const_iterator aIter = aPair.first;
519     if(aIter != _rTypeInfo.end()) // compare with end is correct here
520     {
521         for(;aIter != aPair.second;++aIter)
522         {
523             // search the best matching type
524             OExtendedTypeInfo* pInfo = aIter->second;
525             if  (   (   !_sTypeName.getLength()
526                     ||  (pInfo->aSimpleType.aTypeName.equalsIgnoreAsciiCase(_sTypeName))
527                     )
528                 &&  (pInfo->aSimpleType.nPrecision      >= _nPrecision)
529                 &&  (pInfo->aSimpleType.nMaximumScale   >= _nScale)
530 
531                 )
532                 break;
533         }
534 
535         if (aIter == aPair.second)
536         {
537             for(aIter = aPair.first; aIter != aPair.second; ++aIter)
538             {
539                 // search the best matching type (now comparing the local names)
540                 if  (   (aIter->second->aSimpleType.aLocalTypeName.equalsIgnoreAsciiCase(_sTypeName))
541                     &&  (aIter->second->aSimpleType.nPrecision      >= _nPrecision)
542                     &&  (aIter->second->aSimpleType.nMaximumScale   >= _nScale)
543                     )
544                 {
545 // we can not assert here because we could be in d&d
546 /*
547                     OSL_FAIL((  OString("getTypeInfoFromType: assuming column type ")
548                         +=  OString(aIter->second->aTypeName.getStr(), aIter->second->aTypeName.getLength(), osl_getThreadTextEncoding())
549                         +=  OString("\" (expected type name ")
550                         +=  OString(_sTypeName.getStr(), _sTypeName.getLength(), osl_getThreadTextEncoding())
551                         +=  OString(" matches the type's local name).")).getStr());
552 */
553                     break;
554                 }
555             }
556         }
557 
558         if (aIter == aPair.second)
559         {   // no match for the names, no match for the local names
560             // -> drop the precision and the scale restriction, accept any type with the property
561             // type id (nType)
562 
563             // we can not assert here because we could be in d&d
564             pTypeInfo = aPair.first->second;
565             _brForceToType = true;
566         }
567         else
568             pTypeInfo = aIter->second;
569     }
570     else if ( _sTypeName.getLength() )
571     {
572         ::comphelper::UStringMixEqual aCase(false);
573         // search for typeinfo where the typename is equal _sTypeName
574         OTypeInfoMap::const_iterator aFind = std::find_if(_rTypeInfo.begin(), _rTypeInfo.end(),
575             [&aCase, &_sTypeName] (const OTypeInfoMap::value_type& typeInfo) {
576                 return aCase(typeInfo.second->getDBName(), _sTypeName);
577             });
578 
579         if(aFind != _rTypeInfo.end())
580             pTypeInfo = aFind->second;
581     }
582 
583 // we can not assert here because we could be in d&d
584 //  OSL_ENSURE(pTypeInfo, "getTypeInfoFromType: no type info found for this type!");
585     return pTypeInfo;
586 }
587 
588 
589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
590