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 21 #include <connectivity/dbmetadata.hxx> 22 #include <connectivity/dbexception.hxx> 23 #include <connectivity/DriversConfig.hxx> 24 #include <strings.hrc> 25 #include <resource/sharedresources.hxx> 26 27 #include <com/sun/star/lang/IllegalArgumentException.hpp> 28 #include <com/sun/star/container/XChild.hpp> 29 #include <com/sun/star/beans/XPropertySet.hpp> 30 #include <com/sun/star/sdb/BooleanComparisonMode.hpp> 31 #include <com/sun/star/sdbc/XDatabaseMetaData2.hpp> 32 #include <com/sun/star/sdbcx/XUsersSupplier.hpp> 33 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> 34 #include <com/sun/star/sdbc/DriverManager.hpp> 35 36 #include <tools/diagnose_ex.h> 37 #include <comphelper/namedvaluecollection.hxx> 38 #include <comphelper/processfactory.hxx> 39 #include <sal/macros.h> 40 #include <sal/log.hxx> 41 42 #include <boost/optional.hpp> 43 44 45 namespace dbtools 46 { 47 48 49 using ::com::sun::star::uno::Reference; 50 using ::com::sun::star::sdbc::XConnection; 51 using ::com::sun::star::sdbc::XDatabaseMetaData; 52 using ::com::sun::star::sdbc::XDatabaseMetaData2; 53 using ::com::sun::star::lang::IllegalArgumentException; 54 using ::com::sun::star::uno::Exception; 55 using ::com::sun::star::uno::Any; 56 using ::com::sun::star::uno::XComponentContext; 57 using ::com::sun::star::container::XChild; 58 using ::com::sun::star::uno::UNO_QUERY_THROW; 59 using ::com::sun::star::beans::XPropertySet; 60 using ::com::sun::star::uno::UNO_QUERY; 61 using ::com::sun::star::sdbcx::XUsersSupplier; 62 using ::com::sun::star::sdbcx::XDataDefinitionSupplier; 63 using ::com::sun::star::sdbc::DriverManager; 64 using ::com::sun::star::sdbc::XDriverManager2; 65 using ::com::sun::star::uno::UNO_SET_THROW; 66 67 namespace BooleanComparisonMode = ::com::sun::star::sdb::BooleanComparisonMode; 68 69 struct DatabaseMetaData_Impl 70 { 71 Reference< XConnection > xConnection; 72 Reference< XDatabaseMetaData > xConnectionMetaData; 73 ::connectivity::DriversConfig aDriverConfig; 74 75 ::boost::optional< OUString > sCachedIdentifierQuoteString; 76 ::boost::optional< OUString > sCachedCatalogSeparator; 77 DatabaseMetaData_Impldbtools::DatabaseMetaData_Impl78 DatabaseMetaData_Impl() 79 :xConnection() 80 ,xConnectionMetaData() 81 ,aDriverConfig( ::comphelper::getProcessComponentContext() ) 82 ,sCachedIdentifierQuoteString() 83 ,sCachedCatalogSeparator() 84 { 85 } 86 }; 87 88 89 namespace 90 { 91 lcl_construct(DatabaseMetaData_Impl & _metaDataImpl,const Reference<XConnection> & _connection)92 void lcl_construct( DatabaseMetaData_Impl& _metaDataImpl, const Reference< XConnection >& _connection ) 93 { 94 _metaDataImpl.xConnection = _connection; 95 if ( !_metaDataImpl.xConnection.is() ) 96 return; 97 98 _metaDataImpl.xConnectionMetaData = _connection->getMetaData(); 99 if ( !_metaDataImpl.xConnectionMetaData.is() ) 100 throw IllegalArgumentException(); 101 } 102 103 lcl_checkConnected(const DatabaseMetaData_Impl & _metaDataImpl)104 void lcl_checkConnected( const DatabaseMetaData_Impl& _metaDataImpl ) 105 { 106 if ( !_metaDataImpl.xConnection.is() || !_metaDataImpl.xConnectionMetaData.is() ) 107 { 108 ::connectivity::SharedResources aResources; 109 const OUString sError( aResources.getResourceString(STR_NO_CONNECTION_GIVEN)); 110 throwSQLException( sError, StandardSQLState::CONNECTION_DOES_NOT_EXIST, nullptr ); 111 } 112 } 113 114 lcl_getDriverSetting(const sal_Char * _asciiName,const DatabaseMetaData_Impl & _metaData,Any & _out_setting)115 bool lcl_getDriverSetting( const sal_Char* _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting ) 116 { 117 lcl_checkConnected( _metaData ); 118 const ::comphelper::NamedValueCollection& rDriverMetaData = _metaData.aDriverConfig.getMetaData( _metaData.xConnectionMetaData->getURL() ); 119 if ( !rDriverMetaData.has( _asciiName ) ) 120 return false; 121 _out_setting = rDriverMetaData.get( _asciiName ); 122 return true; 123 } 124 125 lcl_getConnectionSetting(const sal_Char * _asciiName,const DatabaseMetaData_Impl & _metaData,Any & _out_setting)126 bool lcl_getConnectionSetting( const sal_Char* _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting ) 127 { 128 try 129 { 130 Reference< XChild > xConnectionAsChild( _metaData.xConnection, UNO_QUERY ); 131 if ( xConnectionAsChild.is() ) 132 { 133 Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY_THROW ); 134 Reference< XPropertySet > xDataSourceSettings( 135 xDataSource->getPropertyValue("Settings"), 136 UNO_QUERY_THROW ); 137 138 _out_setting = xDataSourceSettings->getPropertyValue( OUString::createFromAscii( _asciiName ) ); 139 } 140 else 141 { 142 Reference< XDatabaseMetaData2 > xExtendedMetaData( _metaData.xConnectionMetaData, UNO_QUERY_THROW ); 143 ::comphelper::NamedValueCollection aSettings( xExtendedMetaData->getConnectionInfo() ); 144 _out_setting = aSettings.get( _asciiName ); 145 return _out_setting.hasValue(); 146 } 147 return true; 148 } 149 catch( const Exception& ) 150 { 151 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 152 } 153 return false; 154 } 155 156 lcl_getConnectionStringSetting(const DatabaseMetaData_Impl & _metaData,::boost::optional<OUString> & _cachedSetting,OUString (SAL_CALL XDatabaseMetaData::* _getter)())157 const OUString& lcl_getConnectionStringSetting( 158 const DatabaseMetaData_Impl& _metaData, ::boost::optional< OUString >& _cachedSetting, 159 OUString (SAL_CALL XDatabaseMetaData::*_getter)() ) 160 { 161 if ( !_cachedSetting ) 162 { 163 lcl_checkConnected( _metaData ); 164 try 165 { 166 _cachedSetting = (_metaData.xConnectionMetaData.get()->*_getter)(); 167 } 168 catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); } 169 } 170 return *_cachedSetting; 171 } 172 } 173 DatabaseMetaData()174 DatabaseMetaData::DatabaseMetaData() 175 :m_pImpl( new DatabaseMetaData_Impl ) 176 { 177 } 178 DatabaseMetaData(const Reference<XConnection> & _connection)179 DatabaseMetaData::DatabaseMetaData( const Reference< XConnection >& _connection ) 180 :m_pImpl( new DatabaseMetaData_Impl ) 181 { 182 lcl_construct( *m_pImpl, _connection ); 183 } 184 185 DatabaseMetaData(const DatabaseMetaData & _copyFrom)186 DatabaseMetaData::DatabaseMetaData( const DatabaseMetaData& _copyFrom ) 187 :m_pImpl( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) ) 188 { 189 } 190 DatabaseMetaData(DatabaseMetaData && _copyFrom)191 DatabaseMetaData::DatabaseMetaData(DatabaseMetaData&& _copyFrom) noexcept 192 :m_pImpl(std::move(_copyFrom.m_pImpl)) 193 { 194 } 195 operator =(const DatabaseMetaData & _copyFrom)196 DatabaseMetaData& DatabaseMetaData::operator=( const DatabaseMetaData& _copyFrom ) 197 { 198 if ( this == &_copyFrom ) 199 return *this; 200 201 m_pImpl.reset( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) ); 202 return *this; 203 } 204 operator =(DatabaseMetaData && _copyFrom)205 DatabaseMetaData& DatabaseMetaData::operator=(DatabaseMetaData&& _copyFrom) noexcept 206 { 207 m_pImpl = std::move(_copyFrom.m_pImpl); 208 return *this; 209 } 210 ~DatabaseMetaData()211 DatabaseMetaData::~DatabaseMetaData() 212 { 213 } 214 isConnected() const215 bool DatabaseMetaData::isConnected() const 216 { 217 return m_pImpl->xConnection.is(); 218 } 219 220 supportsSubqueriesInFrom() const221 bool DatabaseMetaData::supportsSubqueriesInFrom() const 222 { 223 lcl_checkConnected( *m_pImpl ); 224 225 bool bSupportsSubQueries = false; 226 try 227 { 228 sal_Int32 maxTablesInselect = m_pImpl->xConnectionMetaData->getMaxTablesInSelect(); 229 bSupportsSubQueries = ( maxTablesInselect > 1 ) || ( maxTablesInselect == 0 ); 230 // TODO: is there a better way to determine this? The above is not really true. More precise, 231 // it's a *very* generous heuristics ... 232 } 233 catch( const Exception& ) 234 { 235 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 236 } 237 return bSupportsSubQueries; 238 } 239 240 supportsPrimaryKeys() const241 bool DatabaseMetaData::supportsPrimaryKeys() const 242 { 243 lcl_checkConnected( *m_pImpl ); 244 245 bool bDoesSupportPrimaryKeys = false; 246 try 247 { 248 Any setting; 249 if ( !( lcl_getConnectionSetting( "PrimaryKeySupport", *m_pImpl, setting ) ) 250 || !( setting >>= bDoesSupportPrimaryKeys ) 251 ) 252 bDoesSupportPrimaryKeys = m_pImpl->xConnectionMetaData->supportsCoreSQLGrammar() 253 || m_pImpl->xConnectionMetaData->supportsANSI92EntryLevelSQL(); 254 } 255 catch( const Exception& ) 256 { 257 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 258 } 259 return bDoesSupportPrimaryKeys; 260 } 261 262 getIdentifierQuoteString() const263 const OUString& DatabaseMetaData::getIdentifierQuoteString() const 264 { 265 return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedIdentifierQuoteString, &XDatabaseMetaData::getIdentifierQuoteString ); 266 } 267 268 getCatalogSeparator() const269 const OUString& DatabaseMetaData::getCatalogSeparator() const 270 { 271 return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedCatalogSeparator, &XDatabaseMetaData::getCatalogSeparator ); 272 } 273 274 restrictIdentifiersToSQL92() const275 bool DatabaseMetaData::restrictIdentifiersToSQL92() const 276 { 277 lcl_checkConnected( *m_pImpl ); 278 279 bool restrict( false ); 280 Any setting; 281 if ( lcl_getConnectionSetting( "EnableSQL92Check", *m_pImpl, setting ) ) 282 if( ! (setting >>= restrict) ) 283 SAL_WARN("connectivity.commontools", "restrictIdentifiersToSQL92: unable to assign EnableSQL92Check"); 284 return restrict; 285 } 286 287 generateASBeforeCorrelationName() const288 bool DatabaseMetaData::generateASBeforeCorrelationName() const 289 { 290 bool doGenerate( false ); 291 Any setting; 292 if ( lcl_getConnectionSetting( "GenerateASBeforeCorrelationName", *m_pImpl, setting ) ) 293 if( ! (setting >>= doGenerate) ) 294 SAL_WARN("connectivity.commontools", "generateASBeforeCorrelationName: unable to assign GenerateASBeforeCorrelationName"); 295 return doGenerate; 296 } 297 shouldEscapeDateTime() const298 bool DatabaseMetaData::shouldEscapeDateTime() const 299 { 300 bool doGenerate( true ); 301 Any setting; 302 if ( lcl_getConnectionSetting( "EscapeDateTime", *m_pImpl, setting ) ) 303 if( ! (setting >>= doGenerate) ) 304 SAL_WARN("connectivity.commontools", "shouldEscapeDateTime: unable to assign EscapeDateTime"); 305 return doGenerate; 306 } 307 shouldSubstituteParameterNames() const308 bool DatabaseMetaData::shouldSubstituteParameterNames() const 309 { 310 bool doSubstitute( true ); 311 Any setting; 312 if ( lcl_getConnectionSetting( "ParameterNameSubstitution", *m_pImpl, setting ) ) 313 if( ! (setting >>= doSubstitute) ) 314 SAL_WARN("connectivity.commontools", "shouldSubstituteParameterNames: unable to assign ParameterNameSubstitution"); 315 return doSubstitute; 316 } 317 isAutoIncrementPrimaryKey() const318 bool DatabaseMetaData::isAutoIncrementPrimaryKey() const 319 { 320 bool is( true ); 321 Any setting; 322 if ( lcl_getDriverSetting( "AutoIncrementIsPrimaryKey", *m_pImpl, setting ) ) 323 if( ! (setting >>= is) ) 324 SAL_WARN("connectivity.commontools", "isAutoIncrementPrimaryKey: unable to assign AutoIncrementIsPrimaryKey"); 325 return is; 326 } 327 getBooleanComparisonMode() const328 sal_Int32 DatabaseMetaData::getBooleanComparisonMode() const 329 { 330 sal_Int32 mode( BooleanComparisonMode::EQUAL_INTEGER ); 331 Any setting; 332 if ( lcl_getConnectionSetting( "BooleanComparisonMode", *m_pImpl, setting ) ) 333 if( ! (setting >>= mode) ) 334 SAL_WARN("connectivity.commontools", "getBooleanComparisonMode: unable to assign BooleanComparisonMode"); 335 return mode; 336 } 337 supportsRelations() const338 bool DatabaseMetaData::supportsRelations() const 339 { 340 lcl_checkConnected( *m_pImpl ); 341 bool bSupport = false; 342 try 343 { 344 bSupport = m_pImpl->xConnectionMetaData->supportsIntegrityEnhancementFacility(); 345 } 346 catch( const Exception& ) 347 { 348 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 349 } 350 try 351 { 352 if ( !bSupport ) 353 { 354 const OUString url = m_pImpl->xConnectionMetaData->getURL(); 355 bSupport = url.startsWith("sdbc:mysql"); 356 } 357 } 358 catch( const Exception& ) 359 { 360 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 361 } 362 return bSupport; 363 } 364 365 supportsColumnAliasInOrderBy() const366 bool DatabaseMetaData::supportsColumnAliasInOrderBy() const 367 { 368 bool doGenerate( true ); 369 Any setting; 370 if ( lcl_getConnectionSetting( "ColumnAliasInOrderBy", *m_pImpl, setting ) ) 371 if( ! (setting >>= doGenerate) ) 372 SAL_WARN("connectivity.commontools", "supportsColumnAliasInOrderBy: unable to assign ColumnAliasInOrderBy"); 373 return doGenerate; 374 } 375 376 supportsUserAdministration(const Reference<XComponentContext> & _rContext) const377 bool DatabaseMetaData::supportsUserAdministration( const Reference<XComponentContext>& _rContext ) const 378 { 379 lcl_checkConnected( *m_pImpl ); 380 381 bool isSupported( false ); 382 try 383 { 384 // find the XUsersSupplier interface 385 // - either directly at the connection 386 Reference< XUsersSupplier > xUsersSupp( m_pImpl->xConnection, UNO_QUERY ); 387 if ( !xUsersSupp.is() ) 388 { 389 // - or at the driver manager 390 Reference< XDriverManager2 > xDriverManager = DriverManager::create( _rContext ); 391 Reference< XDataDefinitionSupplier > xDriver( xDriverManager->getDriverByURL( m_pImpl->xConnectionMetaData->getURL() ), UNO_QUERY ); 392 if ( xDriver.is() ) 393 xUsersSupp.set( xDriver->getDataDefinitionByConnection( m_pImpl->xConnection ), UNO_QUERY ); 394 } 395 396 isSupported = ( xUsersSupp.is() && xUsersSupp->getUsers().is() ); 397 } 398 catch( const Exception& ) 399 { 400 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 401 } 402 return isSupported; 403 } 404 405 displayEmptyTableFolders() const406 bool DatabaseMetaData::displayEmptyTableFolders() const 407 { 408 bool doDisplay( true ); 409 #ifdef IMPLEMENTED_LATER 410 Any setting; 411 if ( lcl_getConnectionSetting( "DisplayEmptyTableFolders", *m_pImpl, setting ) ) 412 if( ! (setting >>= doDisplay) ) 413 SAL_WARN("connectivity.commontools", "displayEmptyTableFolders: unable to assign DisplayEmptyTableFolders"); 414 #else 415 try 416 { 417 Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW ); 418 OUString sConnectionURL( xMeta->getURL() ); 419 doDisplay = sConnectionURL.startsWith( "sdbc:mysql:mysqlc" ); 420 } 421 catch( const Exception& ) 422 { 423 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 424 } 425 #endif 426 return doDisplay; 427 } 428 supportsThreads() const429 bool DatabaseMetaData::supportsThreads() const 430 { 431 bool bSupported( true ); 432 try 433 { 434 Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW ); 435 OUString sConnectionURL( xMeta->getURL() ); 436 bSupported = !sConnectionURL.startsWith( "sdbc:mysql:mysqlc" ); 437 } 438 catch( const Exception& ) 439 { 440 DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); 441 } 442 return bSupported; 443 } 444 445 446 } // namespace dbtools 447 448 449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 450