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