1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_connectivity.hxx"
26 #include "mysql/YDriver.hxx"
27 #include "mysql/YCatalog.hxx"
28 #include <osl/diagnose.h>
29 #include <comphelper/namedvaluecollection.hxx>
30 #include "connectivity/dbexception.hxx"
31 #include <connectivity/dbcharset.hxx>
32 #include <com/sun/star/sdbc/XDriverAccess.hpp>
33 #include "TConnection.hxx"
34 #include "resource/common_res.hrc"
35 #include "resource/sharedresources.hxx"
36 
37 //........................................................................
38 namespace connectivity
39 {
40 //........................................................................
41 	using namespace mysql;
42 	using namespace ::com::sun::star::uno;
43 	using namespace ::com::sun::star::sdbc;
44 	using namespace ::com::sun::star::sdbcx;
45 	using namespace ::com::sun::star::beans;
46 	using namespace ::com::sun::star::lang;
47 
48 	namespace mysql
49 	{
50 		Reference< XInterface >  SAL_CALL ODriverDelegator_CreateInstance(const Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxFac) throw( Exception )
51 		{
52 			return *(new ODriverDelegator(_rxFac));
53 		}
54 	}
55 
56 
57 	//====================================================================
58 	//= ODriverDelegator
59 	//====================================================================
60 	//--------------------------------------------------------------------
61 	ODriverDelegator::ODriverDelegator(const Reference< XMultiServiceFactory >& _rxFactory)
62 		: ODriverDelegator_BASE(m_aMutex)
63 		,m_xFactory(_rxFactory)
64 		,m_eDriverType(D_ODBC)
65 	{
66 	}
67 
68 	//--------------------------------------------------------------------
69 	ODriverDelegator::~ODriverDelegator()
70 	{
71 		try
72 		{
73 			::comphelper::disposeComponent(m_xODBCDriver);
74             ::comphelper::disposeComponent(m_xNativeDriver);
75 			TJDBCDrivers::iterator aIter = m_aJdbcDrivers.begin();
76 			TJDBCDrivers::iterator aEnd = m_aJdbcDrivers.end();
77 			for ( ;aIter != aEnd;++aIter )
78 				::comphelper::disposeComponent(aIter->second);
79 		}
80 		catch(const Exception&)
81 		{
82 		}
83 	}
84 
85 	// --------------------------------------------------------------------------------
86 	void ODriverDelegator::disposing()
87 	{
88 		::osl::MutexGuard aGuard(m_aMutex);
89 
90 
91 		for (TWeakPairVector::iterator i = m_aConnections.begin(); m_aConnections.end() != i; ++i)
92 		{
93 			Reference<XInterface > xTemp = i->first.get();
94 			::comphelper::disposeComponent(xTemp);
95 		}
96 		m_aConnections.clear();
97 		TWeakPairVector().swap(m_aConnections);
98 
99 		ODriverDelegator_BASE::disposing();
100 	}
101 
102 	namespace
103 	{
104 		sal_Bool isOdbcUrl(const ::rtl::OUString& _sUrl)
105 		{
106 			return _sUrl.copy(0,16).equalsAscii("sdbc:mysql:odbc:");
107 		}
108         //--------------------------------------------------------------------
109         sal_Bool isNativeUrl(const ::rtl::OUString& _sUrl)
110 		{
111             return (!_sUrl.compareTo(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("sdbc:mysql:mysqlc:")), sizeof("sdbc:mysql:mysqlc:")-1));
112 		}
113         //--------------------------------------------------------------------
114         T_DRIVERTYPE lcl_getDriverType(const ::rtl::OUString& _sUrl)
115         {
116             T_DRIVERTYPE eRet = D_JDBC;
117             if ( isOdbcUrl(_sUrl ) )
118                 eRet = D_ODBC;
119             else if ( isNativeUrl(_sUrl ) )
120                 eRet = D_NATIVE;
121             return eRet;
122         }
123 		//--------------------------------------------------------------------
124 		::rtl::OUString transformUrl(const ::rtl::OUString& _sUrl)
125 		{
126 			::rtl::OUString sNewUrl = _sUrl.copy(11);
127 			if ( isOdbcUrl( _sUrl ) )
128 				sNewUrl = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("sdbc:")) + sNewUrl;
129             else if ( isNativeUrl( _sUrl ) )
130                 sNewUrl = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("sdbc:")) + sNewUrl;
131 			else
132 			{
133 				sNewUrl = sNewUrl.copy(5);
134 
135 				::rtl::OUString sTempUrl = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jdbc:mysql://"));
136 
137 				sTempUrl += sNewUrl;
138 				sNewUrl = sTempUrl;
139 			}
140 			return sNewUrl;
141 		}
142 		//--------------------------------------------------------------------
143 		Reference< XDriver > lcl_loadDriver(const Reference< XMultiServiceFactory >& _rxFactory,const ::rtl::OUString& _sUrl)
144 		{
145 			Reference<XDriverAccess> xDriverAccess(_rxFactory->createInstance(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.sdbc.DriverManager")) ),UNO_QUERY);
146 			OSL_ENSURE(xDriverAccess.is(),"Could not load driver manager!");
147 			Reference< XDriver > xDriver;
148 			if ( xDriverAccess.is() )
149 				xDriver = xDriverAccess->getDriverByURL(_sUrl);
150 			return xDriver;
151 		}
152 		//--------------------------------------------------------------------
153 		Sequence< PropertyValue > lcl_convertProperties(T_DRIVERTYPE _eType,const Sequence< PropertyValue >& info,const ::rtl::OUString& _sUrl)
154 		{
155 			::std::vector<PropertyValue> aProps;
156 			const PropertyValue* pSupported = info.getConstArray();
157 			const PropertyValue* pEnd = pSupported + info.getLength();
158 
159 			aProps.reserve(info.getLength() + 5);
160 			for (;pSupported != pEnd; ++pSupported)
161 			{
162 				aProps.push_back( *pSupported );
163 			}
164 
165 			if ( _eType == D_ODBC )
166 			{
167 				aProps.push_back( PropertyValue(
168 									::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Silent"))
169 									,0
170 									,makeAny(sal_True)
171 									,PropertyState_DIRECT_VALUE) );
172 				aProps.push_back( PropertyValue(
173 									::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("PreventGetVersionColumns"))
174 									,0
175 									,makeAny(sal_True)
176 									,PropertyState_DIRECT_VALUE) );
177 			}
178 			else if ( _eType == D_JDBC )
179 			{
180 				aProps.push_back( PropertyValue(
181 									::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("JavaDriverClass"))
182 									,0
183 									,makeAny(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.mysql.jdbc.Driver")))
184 									,PropertyState_DIRECT_VALUE) );
185 			}
186             else
187             {
188                 aProps.push_back( PropertyValue(
189 									::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("PublicConnectionURL"))
190 									,0
191 									,makeAny(_sUrl)
192 									,PropertyState_DIRECT_VALUE) );
193             }
194 			aProps.push_back( PropertyValue(
195 								::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("IsAutoRetrievingEnabled"))
196 								,0
197 								,makeAny(sal_True)
198 								,PropertyState_DIRECT_VALUE) );
199 			aProps.push_back( PropertyValue(
200 								::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AutoRetrievingStatement"))
201 								,0
202 								,makeAny(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SELECT LAST_INSERT_ID()")))
203 								,PropertyState_DIRECT_VALUE) );
204 			aProps.push_back( PropertyValue(
205 								::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParameterNameSubstitution"))
206 								,0
207 								,makeAny(sal_True)
208 								,PropertyState_DIRECT_VALUE) );
209 			PropertyValue* pProps = aProps.empty() ? 0 : &aProps[0];
210 			return Sequence< PropertyValue >(pProps, aProps.size());
211 		}
212 	}
213 	//--------------------------------------------------------------------
214 	Reference< XDriver > ODriverDelegator::loadDriver( const ::rtl::OUString& url, const Sequence< PropertyValue >& info )
215 	{
216 		Reference< XDriver > xDriver;
217 		const ::rtl::OUString sCuttedUrl = transformUrl(url);
218         const T_DRIVERTYPE eType = lcl_getDriverType( url );
219 		if ( eType == D_ODBC )
220 		{
221 			if ( !m_xODBCDriver.is() )
222 				m_xODBCDriver = lcl_loadDriver(m_xFactory,sCuttedUrl);
223 			xDriver = m_xODBCDriver;
224 		} // if ( bIsODBC )
225         else if ( eType == D_NATIVE )
226 		{
227 			if ( !m_xNativeDriver.is() )
228 				m_xNativeDriver = lcl_loadDriver(m_xFactory,sCuttedUrl);
229 			xDriver = m_xNativeDriver;
230 		}
231 		else
232 		{
233             ::comphelper::NamedValueCollection aSettings( info );
234             ::rtl::OUString sDriverClass(RTL_CONSTASCII_USTRINGPARAM("com.mysql.jdbc.Driver"));
235             sDriverClass = aSettings.getOrDefault( "JavaDriverClass", sDriverClass );
236 
237 			TJDBCDrivers::iterator aFind = m_aJdbcDrivers.find(sDriverClass);
238 			if ( aFind == m_aJdbcDrivers.end() )
239 				aFind = m_aJdbcDrivers.insert(TJDBCDrivers::value_type(sDriverClass,lcl_loadDriver(m_xFactory,sCuttedUrl))).first;
240 			xDriver = aFind->second;
241 		}
242 
243 		return xDriver;
244 	}
245 
246 	//--------------------------------------------------------------------
247 	Reference< XConnection > SAL_CALL ODriverDelegator::connect( const ::rtl::OUString& url, const Sequence< PropertyValue >& info ) throw (SQLException, RuntimeException)
248 	{
249 		Reference< XConnection > xConnection;
250 		if ( acceptsURL(url) )
251 		{
252 			Reference< XDriver > xDriver;
253 			xDriver = loadDriver(url,info);
254 			if ( xDriver.is() )
255 			{
256 				::rtl::OUString sCuttedUrl = transformUrl(url);
257                 const T_DRIVERTYPE eType = lcl_getDriverType( url );
258 				Sequence< PropertyValue > aConvertedProperties = lcl_convertProperties(eType,info,url);
259                 if ( eType == D_JDBC )
260                 {
261                     ::comphelper::NamedValueCollection aSettings( info );
262                     ::rtl::OUString sIanaName = aSettings.getOrDefault( "CharSet", ::rtl::OUString() );
263                     if ( sIanaName.getLength() )
264                     {
265                         ::dbtools::OCharsetMap aLookupIanaName;
266 			            ::dbtools::OCharsetMap::const_iterator aLookup = aLookupIanaName.find(sIanaName, ::dbtools::OCharsetMap::IANA());
267 			            if (aLookup != aLookupIanaName.end() )
268                         {
269                             ::rtl::OUString sAdd;
270                             if ( RTL_TEXTENCODING_UTF8 == (*aLookup).getEncoding() )
271                             {
272                                 static const ::rtl::OUString s_sCharSetOp(RTL_CONSTASCII_USTRINGPARAM("useUnicode=true&"));
273                                 if ( !sCuttedUrl.matchIgnoreAsciiCase(s_sCharSetOp) )
274                                 {
275                                     sAdd = s_sCharSetOp;
276                                 } // if ( !sCuttedUrl.matchIgnoreAsciiCase(s_sCharSetOp) )
277                             } // if ( RTL_TEXTENCODING_UTF8 == (*aLookup).getEncoding() )
278                             if ( sCuttedUrl.indexOf('?') == -1 )
279                                 sCuttedUrl += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("?"));
280                             else
281                                 sCuttedUrl += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("&"));
282                             sCuttedUrl += sAdd;
283                             sCuttedUrl += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("characterEncoding="));
284                             sCuttedUrl += sIanaName;
285                         }
286                     }
287                 } // if ( !bIsODBC )
288 
289 				xConnection = xDriver->connect( sCuttedUrl, aConvertedProperties );
290 				if ( xConnection.is() )
291 				{
292 					OMetaConnection* pMetaConnection = NULL;
293 					// now we have to set the URL to get the correct answer for metadata()->getURL()
294 					Reference< XUnoTunnel> xTunnel(xConnection,UNO_QUERY);
295 					if ( xTunnel.is() )
296 					{
297 						pMetaConnection = reinterpret_cast<OMetaConnection*>(xTunnel->getSomething( OMetaConnection::getUnoTunnelImplementationId() ));
298 						if ( pMetaConnection )
299 							pMetaConnection->setURL(url);
300 					}
301 					m_aConnections.push_back(TWeakPair(WeakReferenceHelper(xConnection),TWeakConnectionPair(WeakReferenceHelper(),pMetaConnection)));
302 				}
303 			}
304 		}
305 		return xConnection;
306 	}
307 
308 	//--------------------------------------------------------------------
309 	sal_Bool SAL_CALL ODriverDelegator::acceptsURL( const ::rtl::OUString& url ) throw (SQLException, RuntimeException)
310 	{
311 		Sequence< PropertyValue > info;
312 
313         sal_Bool bOK =  url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "sdbc:mysql:odbc:" ) )
314                     ||  url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "sdbc:mysql:jdbc:" ) )
315                     ||  (   url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "sdbc:mysql:mysqlc:" ) )
316                         &&  loadDriver( url, info ).is()
317                         );
318 		return bOK;
319 	}
320 
321 	//--------------------------------------------------------------------
322 	Sequence< DriverPropertyInfo > SAL_CALL ODriverDelegator::getPropertyInfo( const ::rtl::OUString& url, const Sequence< PropertyValue >& /*info*/ ) throw (SQLException, RuntimeException)
323 	{
324 		::std::vector< DriverPropertyInfo > aDriverInfo;
325 		if ( !acceptsURL(url) )
326             return Sequence< DriverPropertyInfo >();
327 
328 		Sequence< ::rtl::OUString > aBoolean(2);
329 		aBoolean[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("0"));
330 		aBoolean[1] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("1"));
331 
332 
333 		aDriverInfo.push_back(DriverPropertyInfo(
334 				::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharSet"))
335 				,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharSet of the database."))
336 				,sal_False
337 				,::rtl::OUString()
338 				,Sequence< ::rtl::OUString >())
339 				);
340 		aDriverInfo.push_back(DriverPropertyInfo(
341 				::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SuppressVersionColumns"))
342 				,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Display version columns (when available)."))
343 				,sal_False
344 				,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("0"))
345 				,aBoolean)
346 				);
347         const T_DRIVERTYPE eType = lcl_getDriverType( url );
348 		if ( eType == D_JDBC )
349 		{
350 			aDriverInfo.push_back(DriverPropertyInfo(
351 					::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("JavaDriverClass"))
352 					,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("The JDBC driver class name."))
353 					,sal_True
354 					,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.mysql.jdbc.Driver"))
355 					,Sequence< ::rtl::OUString >())
356 					);
357 		}
358 
359         return Sequence< DriverPropertyInfo >(&aDriverInfo[0],aDriverInfo.size());
360 	}
361 
362 	//--------------------------------------------------------------------
363 	sal_Int32 SAL_CALL ODriverDelegator::getMajorVersion(  ) throw (RuntimeException)
364 	{
365 		return 1;
366 	}
367 
368 	//--------------------------------------------------------------------
369 	sal_Int32 SAL_CALL ODriverDelegator::getMinorVersion(  ) throw (RuntimeException)
370 	{
371 		return 0;
372 	}
373 
374 	//--------------------------------------------------------------------
375 	Reference< XTablesSupplier > SAL_CALL ODriverDelegator::getDataDefinitionByConnection( const Reference< XConnection >& connection ) throw (SQLException, RuntimeException)
376 	{
377 		::osl::MutexGuard aGuard( m_aMutex );
378 		checkDisposed(ODriverDelegator_BASE::rBHelper.bDisposed);
379 
380 		Reference< XTablesSupplier > xTab;
381 		Reference< XUnoTunnel> xTunnel(connection,UNO_QUERY);
382 		if ( xTunnel.is() )
383 		{
384 			OMetaConnection* pConnection = reinterpret_cast<OMetaConnection*>(xTunnel->getSomething( OMetaConnection::getUnoTunnelImplementationId() ));
385 			if ( pConnection )
386 			{
387 				TWeakPairVector::iterator aEnd = m_aConnections.end();
388 				for (TWeakPairVector::iterator i = m_aConnections.begin(); aEnd != i; ++i)
389 				{
390 					if ( i->second.second == pConnection )
391 					{
392 						xTab = Reference< XTablesSupplier >(i->second.first.get().get(),UNO_QUERY);
393 						if ( !xTab.is() )
394 						{
395 							xTab = new OMySQLCatalog(connection);
396 							i->second.first = WeakReferenceHelper(xTab);
397 						}
398 						break;
399 					}
400 				}
401 			}
402         } // if ( xTunnel.is() )
403         if ( !xTab.is() )
404         {
405             TWeakPairVector::iterator aEnd = m_aConnections.end();
406             for (TWeakPairVector::iterator i = m_aConnections.begin(); aEnd != i; ++i)
407             {
408                 Reference< XConnection > xTemp(i->first.get(),UNO_QUERY);
409                 if ( xTemp == connection )
410                 {
411                     xTab = Reference< XTablesSupplier >(i->second.first.get().get(),UNO_QUERY);
412                     if ( !xTab.is() )
413                     {
414                         xTab = new OMySQLCatalog(connection);
415                         i->second.first = WeakReferenceHelper(xTab);
416                     }
417                     break;
418                 }
419             }
420         }
421 		return xTab;
422 	}
423 
424 	//--------------------------------------------------------------------
425 	Reference< XTablesSupplier > SAL_CALL ODriverDelegator::getDataDefinitionByURL( const ::rtl::OUString& url, const Sequence< PropertyValue >& info ) throw (SQLException, RuntimeException)
426 	{
427 		if ( ! acceptsURL(url) )
428         {
429             ::connectivity::SharedResources aResources;
430             const ::rtl::OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR);
431             ::dbtools::throwGenericSQLException(sMessage ,*this);
432         } // if ( ! acceptsURL(url) )
433 
434 		return getDataDefinitionByConnection(connect(url,info));
435 	}
436 
437 	// XServiceInfo
438 	// --------------------------------------------------------------------------------
439 	//------------------------------------------------------------------------------
440 	rtl::OUString ODriverDelegator::getImplementationName_Static(  ) throw(RuntimeException)
441 	{
442 		return rtl::OUString::createFromAscii("org.openoffice.comp.drivers.MySQL.Driver");
443 	}
444 	//------------------------------------------------------------------------------
445 	Sequence< ::rtl::OUString > ODriverDelegator::getSupportedServiceNames_Static(  ) throw (RuntimeException)
446 	{
447 		Sequence< ::rtl::OUString > aSNS( 2 );
448 		aSNS[0] = ::rtl::OUString::createFromAscii("com.sun.star.sdbc.Driver");
449 		aSNS[1] = ::rtl::OUString::createFromAscii("com.sun.star.sdbcx.Driver");
450 		return aSNS;
451 	}
452 	//------------------------------------------------------------------
453 	::rtl::OUString SAL_CALL ODriverDelegator::getImplementationName(  ) throw(RuntimeException)
454 	{
455 		return getImplementationName_Static();
456 	}
457 
458 	//------------------------------------------------------------------
459 	sal_Bool SAL_CALL ODriverDelegator::supportsService( const ::rtl::OUString& _rServiceName ) throw(RuntimeException)
460 	{
461 		Sequence< ::rtl::OUString > aSupported(getSupportedServiceNames());
462 		const ::rtl::OUString* pSupported = aSupported.getConstArray();
463 		const ::rtl::OUString* pEnd = pSupported + aSupported.getLength();
464 		for (;pSupported != pEnd && !pSupported->equals(_rServiceName); ++pSupported)
465 			;
466 
467 		return pSupported != pEnd;
468 	}
469 	//------------------------------------------------------------------
470 	Sequence< ::rtl::OUString > SAL_CALL ODriverDelegator::getSupportedServiceNames(  ) throw(RuntimeException)
471 	{
472 		return getSupportedServiceNames_Static();
473 	}
474 	//------------------------------------------------------------------
475 //........................................................................
476 }	// namespace connectivity
477 //........................................................................
478