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