1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_connectivity.hxx"
30 
31 #include "connectivity/dbmetadata.hxx"
32 #include "connectivity/dbexception.hxx"
33 #include "connectivity/DriversConfig.hxx"
34 #include "resource/common_res.hrc"
35 #include "resource/sharedresources.hxx"
36 
37 /** === begin UNO includes === **/
38 #include <com/sun/star/lang/IllegalArgumentException.hpp>
39 #include <com/sun/star/container/XChild.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/beans/PropertyValue.hpp>
42 #include <com/sun/star/beans/XPropertySetInfo.hpp>
43 #include <com/sun/star/sdb/BooleanComparisonMode.hpp>
44 #include <com/sun/star/sdbc/XDatabaseMetaData2.hpp>
45 #include <com/sun/star/sdbcx/XUsersSupplier.hpp>
46 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
47 #include <com/sun/star/sdbc/XDriverAccess.hpp>
48 /** === end UNO includes === **/
49 
50 #include <tools/diagnose_ex.h>
51 #include <comphelper/namedvaluecollection.hxx>
52 #include <comphelper/componentcontext.hxx>
53 #include <comphelper/processfactory.hxx>
54 
55 #include <boost/optional.hpp>
56 
57 //........................................................................
58 namespace dbtools
59 {
60 //........................................................................
61 
62     /** === begin UNO using === **/
63     using ::com::sun::star::uno::Reference;
64     using ::com::sun::star::sdbc::XConnection;
65     using ::com::sun::star::sdbc::XConnection;
66     using ::com::sun::star::sdbc::XDatabaseMetaData;
67     using ::com::sun::star::sdbc::XDatabaseMetaData2;
68     using ::com::sun::star::lang::IllegalArgumentException;
69     using ::com::sun::star::uno::Exception;
70     using ::com::sun::star::uno::Any;
71     using ::com::sun::star::container::XChild;
72     using ::com::sun::star::uno::UNO_QUERY_THROW;
73     using ::com::sun::star::beans::XPropertySet;
74     using ::com::sun::star::uno::Sequence;
75     using ::com::sun::star::beans::PropertyValue;
76     using ::com::sun::star::beans::XPropertySetInfo;
77     using ::com::sun::star::uno::UNO_QUERY;
78     using ::com::sun::star::sdbcx::XUsersSupplier;
79     using ::com::sun::star::sdbcx::XDataDefinitionSupplier;
80     using ::com::sun::star::sdbc::XDriverAccess;
81     using ::com::sun::star::uno::UNO_SET_THROW;
82     /** === end UNO using === **/
83     namespace BooleanComparisonMode = ::com::sun::star::sdb::BooleanComparisonMode;
84 
85     //====================================================================
86 	//= DatabaseMetaData_Impl
87 	//====================================================================
88     struct DatabaseMetaData_Impl
89     {
90         Reference< XConnection >        xConnection;
91         Reference< XDatabaseMetaData >  xConnectionMetaData;
92         ::connectivity::DriversConfig   aDriverConfig;
93 
94         ::boost::optional< ::rtl::OUString >    sCachedIdentifierQuoteString;
95         ::boost::optional< ::rtl::OUString >    sCachedCatalogSeparator;
96 
97         DatabaseMetaData_Impl()
98             :xConnection()
99             ,xConnectionMetaData()
100             ,aDriverConfig( ::comphelper::getProcessServiceFactory() )
101             ,sCachedIdentifierQuoteString()
102             ,sCachedCatalogSeparator()
103         {
104         }
105     };
106 
107 	//--------------------------------------------------------------------
108     namespace
109     {
110 	    //................................................................
111         static void lcl_construct( DatabaseMetaData_Impl& _metaDataImpl, const Reference< XConnection >& _connection )
112         {
113             _metaDataImpl.xConnection = _connection;
114             if ( !_metaDataImpl.xConnection.is() )
115                 return;
116 
117             _metaDataImpl.xConnectionMetaData = _connection->getMetaData();
118             if ( !_metaDataImpl.xConnectionMetaData.is() )
119                 throw IllegalArgumentException();
120         }
121 
122 	    //................................................................
123         static void lcl_checkConnected( const DatabaseMetaData_Impl& _metaDataImpl )
124         {
125             if ( !_metaDataImpl.xConnection.is() || !_metaDataImpl.xConnectionMetaData.is() )
126             {
127                 ::connectivity::SharedResources aResources;
128                 const ::rtl::OUString sError( aResources.getResourceString(STR_NO_CONNECTION_GIVEN));
129                 throwSQLException( sError, SQL_CONNECTION_DOES_NOT_EXIST, NULL );
130             }
131         }
132 
133 	    //................................................................
134         static bool lcl_getDriverSetting( const sal_Char* _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting )
135         {
136             lcl_checkConnected( _metaData );
137             const ::comphelper::NamedValueCollection& rDriverMetaData = _metaData.aDriverConfig.getMetaData( _metaData.xConnectionMetaData->getURL() );
138             if ( !rDriverMetaData.has( _asciiName ) )
139                 return false;
140             _out_setting = rDriverMetaData.get( _asciiName );
141             return true;
142         }
143 
144 	    //................................................................
145         static bool lcl_getConnectionSetting( const sal_Char* _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting )
146         {
147             try
148             {
149                 Reference< XChild > xConnectionAsChild( _metaData.xConnection, UNO_QUERY );
150                 if ( xConnectionAsChild.is() )
151                 {
152                     Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY_THROW );
153                     Reference< XPropertySet > xDataSourceSettings(
154                         xDataSource->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Settings" ) ) ),
155                         UNO_QUERY_THROW );
156 
157                     _out_setting = xDataSourceSettings->getPropertyValue( ::rtl::OUString::createFromAscii( _asciiName ) );
158                 }
159                 else
160                 {
161                     Reference< XDatabaseMetaData2 > xExtendedMetaData( _metaData.xConnectionMetaData, UNO_QUERY_THROW );
162                     ::comphelper::NamedValueCollection aSettings( xExtendedMetaData->getConnectionInfo() );
163                     _out_setting = aSettings.get( _asciiName );
164                     return _out_setting.hasValue();
165                 }
166                 return true;
167             }
168             catch( const Exception& )
169             {
170             	DBG_UNHANDLED_EXCEPTION();
171             }
172             return false;
173         }
174 
175         //................................................................
176         static const ::rtl::OUString& lcl_getConnectionStringSetting(
177             const DatabaseMetaData_Impl& _metaData, ::boost::optional< ::rtl::OUString >& _cachedSetting,
178             ::rtl::OUString (SAL_CALL XDatabaseMetaData::*_getter)() )
179         {
180             if ( !_cachedSetting )
181             {
182                 lcl_checkConnected( _metaData );
183                 try
184                 {
185                     _cachedSetting.reset( (_metaData.xConnectionMetaData.get()->*_getter)() );
186                 }
187                 catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
188             }
189             return *_cachedSetting;
190         }
191     }
192 
193 	//====================================================================
194 	//= DatabaseMetaData
195 	//====================================================================
196 	//--------------------------------------------------------------------
197     DatabaseMetaData::DatabaseMetaData()
198         :m_pImpl( new DatabaseMetaData_Impl )
199     {
200     }
201 
202 	//--------------------------------------------------------------------
203     DatabaseMetaData::DatabaseMetaData( const Reference< XConnection >& _connection )
204         :m_pImpl( new DatabaseMetaData_Impl )
205     {
206         lcl_construct( *m_pImpl, _connection );
207     }
208 
209 	//--------------------------------------------------------------------
210     DatabaseMetaData::DatabaseMetaData( const DatabaseMetaData& _copyFrom )
211         :m_pImpl( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) )
212     {
213     }
214 
215 	//--------------------------------------------------------------------
216     DatabaseMetaData& DatabaseMetaData::operator=( const DatabaseMetaData& _copyFrom )
217     {
218         if ( this == &_copyFrom )
219             return *this;
220 
221         m_pImpl.reset( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) );
222         return *this;
223     }
224 
225 	//--------------------------------------------------------------------
226     DatabaseMetaData::~DatabaseMetaData()
227     {
228     }
229 
230 	//--------------------------------------------------------------------
231     bool DatabaseMetaData::isConnected() const
232     {
233         return m_pImpl->xConnection.is();
234     }
235 
236     //--------------------------------------------------------------------
237     bool DatabaseMetaData::supportsSubqueriesInFrom() const
238     {
239         lcl_checkConnected( *m_pImpl );
240 
241         bool supportsSubQueries = false;
242         try
243         {
244             sal_Int32 maxTablesInselect = m_pImpl->xConnectionMetaData->getMaxTablesInSelect();
245             supportsSubQueries = ( maxTablesInselect > 1 ) || ( maxTablesInselect == 0 );
246             // TODO: is there a better way to determine this? The above is not really true. More precise,
247             // it's a *very* generous heuristics ...
248         }
249         catch( const Exception& )
250         {
251         	DBG_UNHANDLED_EXCEPTION();
252         }
253         return supportsSubQueries;
254     }
255 
256     //--------------------------------------------------------------------
257     bool DatabaseMetaData::supportsPrimaryKeys() const
258     {
259         lcl_checkConnected( *m_pImpl );
260 
261         bool doesSupportPrimaryKeys = false;
262         try
263         {
264             Any setting;
265             if  (   !( lcl_getConnectionSetting( "PrimaryKeySupport", *m_pImpl, setting ) )
266                 ||  !( setting >>= doesSupportPrimaryKeys )
267                 )
268                 doesSupportPrimaryKeys = m_pImpl->xConnectionMetaData->supportsCoreSQLGrammar();
269         }
270         catch( const Exception& )
271         {
272         	DBG_UNHANDLED_EXCEPTION();
273         }
274         return doesSupportPrimaryKeys;
275     }
276 
277     //--------------------------------------------------------------------
278     const ::rtl::OUString&  DatabaseMetaData::getIdentifierQuoteString() const
279     {
280         return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedIdentifierQuoteString, &XDatabaseMetaData::getIdentifierQuoteString );
281     }
282 
283     //--------------------------------------------------------------------
284     const ::rtl::OUString&  DatabaseMetaData::getCatalogSeparator() const
285     {
286         return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedCatalogSeparator, &XDatabaseMetaData::getCatalogSeparator );
287     }
288 
289     //--------------------------------------------------------------------
290     bool DatabaseMetaData::restrictIdentifiersToSQL92() const
291     {
292         lcl_checkConnected( *m_pImpl );
293 
294         bool restrict( false );
295         Any setting;
296         if ( lcl_getConnectionSetting( "EnableSQL92Check", *m_pImpl, setting ) )
297             OSL_VERIFY( setting >>= restrict );
298         return restrict;
299     }
300 
301     //--------------------------------------------------------------------
302     bool DatabaseMetaData::generateASBeforeCorrelationName() const
303     {
304         bool doGenerate( true );
305         Any setting;
306         if ( lcl_getConnectionSetting( "GenerateASBeforeCorrelationName", *m_pImpl, setting ) )
307             OSL_VERIFY( setting >>= doGenerate );
308         return doGenerate;
309     }
310     //--------------------------------------------------------------------
311     bool DatabaseMetaData::shouldEscapeDateTime() const
312     {
313         bool doGenerate( true );
314         Any setting;
315         if ( lcl_getConnectionSetting( "EscapeDateTime", *m_pImpl, setting ) )
316             OSL_VERIFY( setting >>= doGenerate );
317         return doGenerate;
318     }
319     //--------------------------------------------------------------------
320     bool DatabaseMetaData::isAutoIncrementPrimaryKey() const
321     {
322         bool is( true );
323         Any setting;
324         if ( lcl_getDriverSetting( "AutoIncrementIsPrimaryKey", *m_pImpl, setting ) )
325             OSL_VERIFY( setting >>= is );
326         return is;
327     }
328     //--------------------------------------------------------------------
329     sal_Int32 DatabaseMetaData::getBooleanComparisonMode() const
330     {
331         sal_Int32 mode( BooleanComparisonMode::EQUAL_INTEGER );
332         Any setting;
333         if ( lcl_getConnectionSetting( "BooleanComparisonMode", *m_pImpl, setting ) )
334             OSL_VERIFY( setting >>= mode );
335         return mode;
336     }
337     //--------------------------------------------------------------------
338     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();
349         }
350         try
351         {
352             if ( !bSupport )
353             {
354                 const ::rtl::OUString url = m_pImpl->xConnectionMetaData->getURL();
355                 char pMySQL[] = "sdbc:mysql";
356 			    bSupport = url.matchAsciiL(pMySQL,(sizeof(pMySQL)/sizeof(pMySQL[0]))-1);
357             }
358         }
359         catch( const Exception& )
360         {
361         	DBG_UNHANDLED_EXCEPTION();
362         }
363         return bSupport;
364     }
365 
366     //--------------------------------------------------------------------
367     bool DatabaseMetaData::supportsColumnAliasInOrderBy() const
368     {
369         bool doGenerate( true );
370         Any setting;
371         if ( lcl_getConnectionSetting( "ColumnAliasInOrderBy", *m_pImpl, setting ) )
372             OSL_VERIFY( setting >>= doGenerate );
373         return doGenerate;
374     }
375 
376     //--------------------------------------------------------------------
377     bool DatabaseMetaData::supportsUserAdministration( const ::comphelper::ComponentContext& _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< XDriverAccess > xDriverManager(
391                     _rContext.createComponent( "com.sun.star.sdbc.DriverManager" ), UNO_QUERY_THROW );
392                 Reference< XDataDefinitionSupplier > xDriver( xDriverManager->getDriverByURL( m_pImpl->xConnectionMetaData->getURL() ), UNO_QUERY );
393                 if ( xDriver.is() )
394                     xUsersSupp.set( xDriver->getDataDefinitionByConnection( m_pImpl->xConnection ), UNO_QUERY );
395             }
396 
397             isSupported = ( xUsersSupp.is() && xUsersSupp->getUsers().is() );
398         }
399         catch( const Exception& )
400         {
401         	DBG_UNHANDLED_EXCEPTION();
402         }
403         return isSupported;
404     }
405 
406     //--------------------------------------------------------------------
407     bool DatabaseMetaData::displayEmptyTableFolders() const
408     {
409         bool doDisplay( true );
410 #ifdef IMPLEMENTED_LATER
411         Any setting;
412         if ( lcl_getConnectionSetting( "DisplayEmptyTableFolders", *m_pImpl, setting ) )
413             OSL_VERIFY( setting >>= doDisplay );
414 #else
415         try
416         {
417             Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW );
418             ::rtl::OUString sConnectionURL( xMeta->getURL() );
419             doDisplay = sConnectionURL.compareToAscii( RTL_CONSTASCII_STRINGPARAM( "sdbc:mysql:mysqlc" ) ) == 0;
420         }
421         catch( const Exception& )
422         {
423         	DBG_UNHANDLED_EXCEPTION();
424         }
425 #endif
426         return doDisplay;
427     }
428     //--------------------------------------------------------------------
429     bool DatabaseMetaData::supportsThreads() const
430     {
431         bool bSupported( true );
432         try
433         {
434             Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW );
435             ::rtl::OUString sConnectionURL( xMeta->getURL() );
436             bSupported = sConnectionURL.compareToAscii( RTL_CONSTASCII_STRINGPARAM( "sdbc:mysql:mysqlc" ) ) != 0;
437         }
438         catch( const Exception& )
439         {
440         	DBG_UNHANDLED_EXCEPTION();
441         }
442         return bSupported;
443     }
444 
445 //........................................................................
446 } // namespace dbtools
447 //........................................................................
448 
449