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
