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 <odbc/OTools.hxx>
21 #include <odbc/OConnection.hxx>
22 #include <odbc/ODatabaseMetaData.hxx>
23 #include <odbc/OFunctions.hxx>
24 #include <odbc/ODriver.hxx>
25 #include <odbc/OStatement.hxx>
26 #include <odbc/OPreparedStatement.hxx>
27 #include <connectivity/dbcharset.hxx>
28 #include <connectivity/dbexception.hxx>
29 
30 #include <sal/log.hxx>
31 
32 #include <string.h>
33 
34 using namespace connectivity::odbc;
35 using namespace connectivity;
36 using namespace dbtools;
37 
38 
39 using namespace com::sun::star::uno;
40 using namespace com::sun::star::lang;
41 using namespace com::sun::star::beans;
42 using namespace com::sun::star::sdbc;
43 
44 OConnection::OConnection(const SQLHANDLE _pDriverHandle,ODBCDriver* _pDriver)
45                          :m_xDriver(_pDriver)
46                          ,m_aConnectionHandle(nullptr)
47                          ,m_pDriverHandleCopy(_pDriverHandle)
48                          ,m_nStatementCount(0)
49                          ,m_bClosed(false)
50                          ,m_bUseCatalog(false)
51                          ,m_bUseOldDateFormat(false)
52                          ,m_bIgnoreDriverPrivileges(false)
53                          ,m_bPreventGetVersionColumns(false)
54                          ,m_bReadOnly(true)
55 {
56 }
57 
58 OConnection::~OConnection()
59 {
60     if(!isClosed(  ))
61         close();
62 
63     if ( SQL_NULL_HANDLE != m_aConnectionHandle )
64     {
65         SQLRETURN rc;
66 
67         rc = N3SQLDisconnect( m_aConnectionHandle );
68         OSL_ENSURE( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO, "Failure from SQLDisconnect" );
69 
70         rc = N3SQLFreeHandle( SQL_HANDLE_DBC, m_aConnectionHandle );
71         OSL_ENSURE( rc == SQL_SUCCESS , "Failure from SQLFreeHandle for connection");
72 
73         m_aConnectionHandle = SQL_NULL_HANDLE;
74     }
75 }
76 
77 oslGenericFunction OConnection::getOdbcFunction(ODBC3SQLFunctionId _nIndex)  const
78 {
79     OSL_ENSURE(m_xDriver, "OConnection::getOdbcFunction: m_xDriver is null!");
80     return m_xDriver->getOdbcFunction(_nIndex);
81 }
82 
83 SQLRETURN OConnection::OpenConnection(const OUString& aConnectStr, sal_Int32 nTimeOut, bool bSilent)
84 {
85     ::osl::MutexGuard aGuard( m_aMutex );
86 
87     if (m_aConnectionHandle == SQL_NULL_HANDLE)
88         return -1;
89 
90     SQLRETURN nSQLRETURN = 0;
91     SDB_ODBC_CHAR szConnStrOut[4096] = {};
92     SDB_ODBC_CHAR szConnStrIn[2048] = {};
93     SQLSMALLINT cbConnStrOut;
94     OString aConStr(OUStringToOString(aConnectStr,getTextEncoding()));
95     memcpy(szConnStrIn, aConStr.getStr(), std::min<sal_Int32>(sal_Int32(2048),aConStr.getLength()));
96 
97 #ifndef MACOSX
98     N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_LOGIN_TIMEOUT,reinterpret_cast<SQLPOINTER>(nTimeOut),SQL_IS_UINTEGER);
99 #else
100     (void)nTimeOut; /* WaE */
101 #endif
102 
103 #ifdef LINUX
104     (void) bSilent;
105     nSQLRETURN = N3SQLDriverConnect(m_aConnectionHandle,
106                       nullptr,
107                       szConnStrIn,
108                       static_cast<SQLSMALLINT>(std::min(sal_Int32(2048),aConStr.getLength())),
109                       szConnStrOut,
110                       SQLSMALLINT(sizeof(szConnStrOut)/sizeof(SDB_ODBC_CHAR)) -1,
111                       &cbConnStrOut,
112                       SQL_DRIVER_NOPROMPT);
113     if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA || SQL_SUCCESS_WITH_INFO == nSQLRETURN)
114         return nSQLRETURN;
115 #else
116 
117     SQLUSMALLINT nSilent =  bSilent ? SQL_DRIVER_NOPROMPT : SQL_DRIVER_COMPLETE;
118     nSQLRETURN = N3SQLDriverConnect(m_aConnectionHandle,
119                       nullptr,
120                       szConnStrIn,
121                       static_cast<SQLSMALLINT>(std::min<sal_Int32>(sal_Int32(2048),aConStr.getLength())),
122                       szConnStrOut,
123                       SQLSMALLINT(sizeof szConnStrOut),
124                       &cbConnStrOut,
125                       nSilent);
126     if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA)
127         return nSQLRETURN;
128 
129     m_bClosed = false;
130 
131 #endif //LINUX
132 
133     try
134     {
135         OUString aVal;
136         OTools::GetInfo(this,m_aConnectionHandle,SQL_DATA_SOURCE_READ_ONLY,aVal,*this,getTextEncoding());
137         m_bReadOnly = aVal == "Y";
138     }
139     catch(Exception&)
140     {
141         m_bReadOnly = true;
142     }
143     try
144     {
145         OUString sVersion;
146         OTools::GetInfo(this,m_aConnectionHandle,SQL_DRIVER_ODBC_VER,sVersion,*this,getTextEncoding());
147         m_bUseOldDateFormat =  sVersion == "02.50" || sVersion == "02.00";
148     }
149     catch(Exception&)
150     {
151     }
152 
153 
154     // autocommit is always default
155 
156     if (!m_bReadOnly)
157         N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_ON),SQL_IS_INTEGER);
158 
159     return nSQLRETURN;
160 }
161 
162 SQLRETURN OConnection::Construct(const OUString& url,const Sequence< PropertyValue >& info)
163 {
164     m_aConnectionHandle  = SQL_NULL_HANDLE;
165     m_sURL  = url;
166     setConnectionInfo(info);
167 
168     N3SQLAllocHandle(SQL_HANDLE_DBC,m_pDriverHandleCopy,&m_aConnectionHandle);
169     if(m_aConnectionHandle == SQL_NULL_HANDLE)
170         throw SQLException();
171 
172     sal_Int32 nLen = url.indexOf(':');
173     nLen = url.indexOf(':',nLen+2);
174     OUString aDSN("DSN="), aUID, aPWD, aSysDrvSettings;
175     aDSN += url.copy(nLen+1);
176 
177     sal_Int32 nTimeout = 20;
178     bool bSilent = true;
179     const PropertyValue *pBegin = info.getConstArray();
180     const PropertyValue *pEnd   = pBegin + info.getLength();
181     for(;pBegin != pEnd;++pBegin)
182     {
183         if( pBegin->Name == "Timeout")
184         {
185             if( ! (pBegin->Value >>= nTimeout) )
186                 SAL_WARN("connectivity.odbc", "Construct: unable to get property Timeout");
187         }
188         else if( pBegin->Name == "Silent")
189         {
190             if( ! (pBegin->Value >>= bSilent) )
191                 SAL_WARN("connectivity.odbc", "Construct: unable to get property Silent");
192         }
193         else if( pBegin->Name == "IgnoreDriverPrivileges")
194         {
195             if( ! (pBegin->Value >>= m_bIgnoreDriverPrivileges) )
196                 SAL_WARN("connectivity.odbc", "Construct: unable to get property IgnoreDriverPrivileges");
197         }
198         else if( pBegin->Name == "PreventGetVersionColumns")
199         {
200             if( ! (pBegin->Value >>= m_bPreventGetVersionColumns) )
201                 SAL_WARN("connectivity.odbc", "Construct: unable to get property PreventGetVersionColumns");
202         }
203         else if( pBegin->Name == "IsAutoRetrievingEnabled")
204         {
205             bool bAutoRetrievingEnabled = false;
206             if( ! (pBegin->Value >>= bAutoRetrievingEnabled) )
207                 SAL_WARN("connectivity.odbc", "Construct: unable to get property IsAutoRetrievingEnabled");
208             enableAutoRetrievingEnabled(bAutoRetrievingEnabled);
209         }
210         else if( pBegin->Name == "AutoRetrievingStatement")
211         {
212             OUString sGeneratedValueStatement;
213             if( ! (pBegin->Value >>= sGeneratedValueStatement) )
214                 SAL_WARN("connectivity.odbc", "Construct: unable to get property AutoRetrievingStatement");
215             setAutoRetrievingStatement(sGeneratedValueStatement);
216         }
217         else if( pBegin->Name == "user")
218         {
219             if( ! (pBegin->Value >>= aUID) )
220                 SAL_WARN("connectivity.odbc", "Construct: unable to get property user");
221             aDSN += ";UID=" + aUID;
222         }
223         else if( pBegin->Name == "password")
224         {
225             if( ! (pBegin->Value >>= aPWD) )
226                 SAL_WARN("connectivity.odbc", "Construct: unable to get property password");
227             aDSN += ";PWD=" + aPWD;
228         }
229         else if( pBegin->Name == "UseCatalog")
230         {
231              if( !( pBegin->Value >>= m_bUseCatalog) )
232                 SAL_WARN("connectivity.odbc", "Construct: unable to get property UseCatalog");
233         }
234         else if( pBegin->Name == "SystemDriverSettings")
235         {
236             if( ! (pBegin->Value >>= aSysDrvSettings) )
237                 SAL_WARN("connectivity.odbc", "Construct: unable to get property SystemDriverSettings");
238             aDSN += ";" + aSysDrvSettings;
239         }
240         else if( pBegin->Name == "CharSet")
241         {
242             OUString sIanaName;
243             if( ! (pBegin->Value >>= sIanaName) )
244                 SAL_WARN("connectivity.odbc", "Construct: unable to get property CharSet");
245 
246             ::dbtools::OCharsetMap aLookupIanaName;
247             ::dbtools::OCharsetMap::const_iterator aLookup = aLookupIanaName.findIanaName(sIanaName);
248             if (aLookup != aLookupIanaName.end())
249                 m_nTextEncoding = (*aLookup).getEncoding();
250             else
251                 m_nTextEncoding = RTL_TEXTENCODING_DONTKNOW;
252             if(m_nTextEncoding == RTL_TEXTENCODING_DONTKNOW)
253                 m_nTextEncoding = osl_getThreadTextEncoding();
254         }
255     }
256     m_sUser = aUID;
257 
258     SQLRETURN nSQLRETURN = OpenConnection(aDSN,nTimeout, bSilent);
259     if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA)
260     {
261         OTools::ThrowException(this,nSQLRETURN,m_aConnectionHandle,SQL_HANDLE_DBC,*this,false);
262     }
263     return nSQLRETURN;
264 }
265 // XServiceInfo
266 
267 IMPLEMENT_SERVICE_INFO(OConnection, "com.sun.star.sdbc.drivers.odbc.OConnection", "com.sun.star.sdbc.Connection")
268 
269 
270 Reference< XStatement > SAL_CALL OConnection::createStatement(  )
271 {
272     ::osl::MutexGuard aGuard( m_aMutex );
273     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
274 
275     Reference< XStatement > xReturn = new OStatement(this);
276     m_aStatements.push_back(WeakReferenceHelper(xReturn));
277     return xReturn;
278 }
279 
280 Reference< XPreparedStatement > SAL_CALL OConnection::prepareStatement( const OUString& sql )
281 {
282     ::osl::MutexGuard aGuard( m_aMutex );
283     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
284 
285     Reference< XPreparedStatement > xReturn = new OPreparedStatement(this,sql);
286     m_aStatements.push_back(WeakReferenceHelper(xReturn));
287     return xReturn;
288 }
289 
290 Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& /*sql*/ )
291 {
292     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this );
293     return nullptr;
294 }
295 
296 OUString SAL_CALL OConnection::nativeSQL( const OUString& sql )
297 {
298     ::osl::MutexGuard aGuard( m_aMutex );
299 
300     OString aSql(OUStringToOString(sql,getTextEncoding()));
301     char pOut[2048];
302     SQLINTEGER nOutLen;
303     OTools::ThrowException(this,N3SQLNativeSql(m_aConnectionHandle,reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(aSql.getStr())),aSql.getLength(),reinterpret_cast<SDB_ODBC_CHAR*>(pOut),sizeof pOut - 1,&nOutLen),m_aConnectionHandle,SQL_HANDLE_DBC,*this);
304     return OUString(pOut,nOutLen,getTextEncoding());
305 }
306 
307 void SAL_CALL OConnection::setAutoCommit( sal_Bool autoCommit )
308 {
309     ::osl::MutexGuard aGuard( m_aMutex );
310     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
311 
312 
313     OTools::ThrowException(this,N3SQLSetConnectAttr(m_aConnectionHandle,
314                                    SQL_ATTR_AUTOCOMMIT,
315                                    reinterpret_cast<SQLPOINTER>((autoCommit) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF) ,SQL_IS_INTEGER),
316                                    m_aConnectionHandle,SQL_HANDLE_DBC,*this);
317 }
318 
319 sal_Bool SAL_CALL OConnection::getAutoCommit(  )
320 {
321     ::osl::MutexGuard aGuard( m_aMutex );
322     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
323 
324 
325     sal_uInt32 nOption = 0;
326     OTools::ThrowException(this,N3SQLGetConnectAttr(m_aConnectionHandle,
327                                    SQL_ATTR_AUTOCOMMIT, &nOption,0,nullptr),m_aConnectionHandle,SQL_HANDLE_DBC,*this);
328     return nOption == SQL_AUTOCOMMIT_ON ;
329 }
330 
331 void SAL_CALL OConnection::commit(  )
332 {
333     ::osl::MutexGuard aGuard( m_aMutex );
334     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
335 
336 
337     OTools::ThrowException(this,N3SQLEndTran(SQL_HANDLE_DBC,m_aConnectionHandle,SQL_COMMIT),m_aConnectionHandle,SQL_HANDLE_DBC,*this);
338 }
339 
340 void SAL_CALL OConnection::rollback(  )
341 {
342     ::osl::MutexGuard aGuard( m_aMutex );
343     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
344 
345 
346     OTools::ThrowException(this,N3SQLEndTran(SQL_HANDLE_DBC,m_aConnectionHandle,SQL_ROLLBACK),m_aConnectionHandle,SQL_HANDLE_DBC,*this);
347 }
348 
349 sal_Bool SAL_CALL OConnection::isClosed(  )
350 {
351     ::osl::MutexGuard aGuard( m_aMutex );
352 
353     return OConnection_BASE::rBHelper.bDisposed;
354 }
355 
356 Reference< XDatabaseMetaData > SAL_CALL OConnection::getMetaData(  )
357 {
358     ::osl::MutexGuard aGuard( m_aMutex );
359     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
360 
361     Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
362     if(!xMetaData.is())
363     {
364         xMetaData = new ODatabaseMetaData(m_aConnectionHandle,this);
365         m_xMetaData = xMetaData;
366     }
367 
368     return xMetaData;
369 }
370 
371 void SAL_CALL OConnection::setReadOnly( sal_Bool readOnly )
372 {
373     ::osl::MutexGuard aGuard( m_aMutex );
374     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
375 
376 
377     OTools::ThrowException(this,
378         N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_ACCESS_MODE,reinterpret_cast< SQLPOINTER >( readOnly ),SQL_IS_INTEGER),
379         m_aConnectionHandle,SQL_HANDLE_DBC,*this);
380 }
381 
382 sal_Bool SAL_CALL OConnection::isReadOnly()
383 {
384     // const member which will initialized only once
385     return m_bReadOnly;
386 }
387 
388 void SAL_CALL OConnection::setCatalog( const OUString& catalog )
389 {
390     ::osl::MutexGuard aGuard( m_aMutex );
391     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
392 
393 
394     OString aCat(OUStringToOString(catalog,getTextEncoding()));
395     OTools::ThrowException(this,
396         N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_CURRENT_CATALOG,const_cast<char *>(aCat.getStr()),SQL_NTS),
397         m_aConnectionHandle,SQL_HANDLE_DBC,*this);
398 }
399 
400 OUString SAL_CALL OConnection::getCatalog(  )
401 {
402     ::osl::MutexGuard aGuard( m_aMutex );
403     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
404 
405 
406     SQLINTEGER nValueLen;
407     char pCat[1024];
408     OTools::ThrowException(this,
409         N3SQLGetConnectAttr(m_aConnectionHandle,SQL_ATTR_CURRENT_CATALOG,pCat,(sizeof pCat)-1,&nValueLen),
410         m_aConnectionHandle,SQL_HANDLE_DBC,*this);
411 
412     return OUString(pCat,nValueLen,getTextEncoding());
413 }
414 
415 void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 level )
416 {
417     ::osl::MutexGuard aGuard( m_aMutex );
418     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
419 
420 
421     OTools::ThrowException(this,N3SQLSetConnectAttr(m_aConnectionHandle,
422                                    SQL_ATTR_TXN_ISOLATION,
423                                    reinterpret_cast<SQLPOINTER>(level),SQL_IS_INTEGER),
424                                    m_aConnectionHandle,SQL_HANDLE_DBC,*this);
425 }
426 
427 sal_Int32 SAL_CALL OConnection::getTransactionIsolation(  )
428 {
429     ::osl::MutexGuard aGuard( m_aMutex );
430     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
431 
432 
433     sal_Int32 nTxn = 0;
434     SQLINTEGER nValueLen;
435     OTools::ThrowException(this,
436         N3SQLGetConnectAttr(m_aConnectionHandle,SQL_ATTR_TXN_ISOLATION,&nTxn,sizeof nTxn,&nValueLen),
437         m_aConnectionHandle,SQL_HANDLE_DBC,*this);
438     return nTxn;
439 }
440 
441 Reference< css::container::XNameAccess > SAL_CALL OConnection::getTypeMap(  )
442 {
443     ::osl::MutexGuard aGuard( m_aMutex );
444     checkDisposed(OConnection_BASE::rBHelper.bDisposed);
445 
446 
447     return nullptr;
448 }
449 
450 void SAL_CALL OConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ )
451 {
452     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
453 }
454 
455 // XCloseable
456 void SAL_CALL OConnection::close(  )
457 {
458     {
459         ::osl::MutexGuard aGuard( m_aMutex );
460         checkDisposed(OConnection_BASE::rBHelper.bDisposed);
461 
462     }
463     dispose();
464 }
465 
466 // XWarningsSupplier
467 Any SAL_CALL OConnection::getWarnings(  )
468 {
469     return Any();
470 }
471 
472 void SAL_CALL OConnection::clearWarnings(  )
473 {
474 }
475 
476 void OConnection::disposing()
477 {
478     ::osl::MutexGuard aGuard(m_aMutex);
479 
480     OConnection_BASE::disposing();
481 
482     for (auto const& connection : m_aConnections)
483         connection.second->dispose();
484 
485     m_aConnections.clear();
486 
487     if(!m_bClosed)
488         N3SQLDisconnect(m_aConnectionHandle);
489     m_bClosed   = true;
490 }
491 
492 SQLHANDLE OConnection::createStatementHandle()
493 {
494     rtl::Reference<OConnection> xConnectionTemp = this;
495     bool bNew = false;
496     try
497     {
498         sal_Int32 nMaxStatements = getMetaData()->getMaxStatements();
499         if(nMaxStatements && nMaxStatements <= m_nStatementCount)
500         {
501             rtl::Reference xConnection(new OConnection(m_pDriverHandleCopy,m_xDriver.get()));
502             xConnection->Construct(m_sURL,getConnectionInfo());
503             xConnectionTemp = xConnection;
504             bNew = true;
505         }
506     }
507     catch(SQLException&)
508     {
509     }
510 
511     SQLHANDLE aStatementHandle = SQL_NULL_HANDLE;
512     N3SQLAllocHandle(SQL_HANDLE_STMT,xConnectionTemp->getConnection(),&aStatementHandle);
513     ++m_nStatementCount;
514     if(bNew)
515         m_aConnections.emplace(aStatementHandle,xConnectionTemp);
516 
517     return aStatementHandle;
518 
519 }
520 
521 void OConnection::freeStatementHandle(SQLHANDLE& _pHandle)
522 {
523     if( SQL_NULL_HANDLE == _pHandle )
524         return;
525 
526     auto aFind = m_aConnections.find(_pHandle);
527 
528     N3SQLFreeStmt(_pHandle,SQL_RESET_PARAMS);
529     N3SQLFreeStmt(_pHandle,SQL_UNBIND);
530     N3SQLFreeStmt(_pHandle,SQL_CLOSE);
531     N3SQLFreeHandle(SQL_HANDLE_STMT,_pHandle);
532 
533     _pHandle = SQL_NULL_HANDLE;
534 
535     if(aFind != m_aConnections.end())
536     {
537         aFind->second->dispose();
538         m_aConnections.erase(aFind);
539     }
540     --m_nStatementCount;
541 }
542 
543 
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
545