1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "mysqlc_connection.hxx"
21 #include "mysqlc_databasemetadata.hxx"
23 #include "mysqlc_driver.hxx"
24 #include "mysqlc_statement.hxx"
25 #include "mysqlc_preparedstatement.hxx"
26 #include "mysqlc_general.hxx"
28 #include <com/sun/star/beans/NamedValue.hpp>
30 #include <osl/diagnose.h>
31 #include <cppuhelper/supportsservice.hxx>
33 using namespace connectivity::mysqlc
;
35 using namespace com::sun::star::uno
;
36 using namespace com::sun::star::container
;
37 using namespace com::sun::star::lang
;
38 using namespace com::sun::star::beans
;
39 using namespace com::sun::star::sdbc
;
40 using ::osl::MutexGuard
;
42 #define MYSQLC_URI_PREFIX "sdbc:mysqlc:"
46 void lcl_executeUpdate(MYSQL
* pMySql
, const OString
& sql
)
48 mysql_real_query(pMySql
, sql
.getStr(), sql
.getLength());
53 OConnection::OConnection(MysqlCDriver
& _rDriver
)
54 : OMetaConnection_BASE(m_aMutex
)
56 , m_xMetaData(nullptr)
57 , m_xDriver(&_rDriver
)
61 OConnection::~OConnection()
69 void OConnection::construct(const OUString
& url
, const Sequence
<PropertyValue
>& info
)
71 MutexGuard
aGuard(m_aMutex
);
73 mysql_library_init(0, nullptr, nullptr);
76 // use TCP as connection
77 mysql_protocol_type protocol
= MYSQL_PROTOCOL_TCP
;
78 mysql_options(&m_mysql
, MYSQL_OPT_PROTOCOL
, &protocol
);
79 OString charset_name
{ "utf8mb4" };
80 mysql_options(&m_mysql
, MYSQL_SET_CHARSET_NAME
, charset_name
.getStr());
84 OUString
aHostName("localhost");
85 sal_Int32 nPort
= 3306;
88 m_settings
.encoding
= MysqlCDriver::getDefaultEncoding();
90 // parse url. Url has the following format:
91 // external server: sdbc:mysqlc:[hostname]:[port]/[dbname]
93 if (url
.startsWith(MYSQLC_URI_PREFIX
))
99 // sdbc:mysql:mysqlc:[hostname]:[port]/[dbname]
103 token
= url
.getToken(0, '/', nIndex
);
104 if (!token
.isEmpty())
106 sal_Int32 nIndex1
= 0;
107 OUString hostandport
= token
.getToken(0, ':', nIndex1
);
108 if (!hostandport
.isEmpty())
110 aHostName
= hostandport
;
111 hostandport
= token
.getToken(0, ':', nIndex1
);
112 if (!hostandport
.isEmpty() && nIndex1
)
114 nPort
= hostandport
.toInt32();
116 token
= url
.getToken(0, '/', nIndex
);
117 if (!token
.isEmpty() && nIndex
)
124 // get user and password for mysql connection
125 const PropertyValue
* pIter
= info
.getConstArray();
126 const PropertyValue
* pEnd
= pIter
+ info
.getLength();
127 OUString aUser
, aPass
, sUnixSocket
, sNamedPipe
;
128 bool unixSocketPassed
= false;
129 bool namedPipePassed
= false;
131 m_settings
.connectionURL
= url
;
132 for (; pIter
!= pEnd
; ++pIter
)
134 if (pIter
->Name
== "user")
136 OSL_VERIFY(pIter
->Value
>>= aUser
);
138 else if (pIter
->Name
== "password")
140 OSL_VERIFY(pIter
->Value
>>= aPass
);
142 else if (pIter
->Name
== "LocalSocket")
144 OSL_VERIFY(pIter
->Value
>>= sUnixSocket
);
145 unixSocketPassed
= !sUnixSocket
.isEmpty();
147 else if (pIter
->Name
== "NamedPipe")
149 OSL_VERIFY(pIter
->Value
>>= sNamedPipe
);
150 namedPipePassed
= !sNamedPipe
.isEmpty();
152 else if (pIter
->Name
== "PublicConnectionURL")
154 OSL_VERIFY(pIter
->Value
>>= m_settings
.connectionURL
);
156 else if (pIter
->Name
== "NewURL")
157 { // legacy name for "PublicConnectionURL"
158 OSL_VERIFY(pIter
->Value
>>= m_settings
.connectionURL
);
162 OString host_str
= OUStringToOString(aHostName
, m_settings
.encoding
);
163 OString user_str
= OUStringToOString(aUser
, m_settings
.encoding
);
164 OString pass_str
= OUStringToOString(aPass
, m_settings
.encoding
);
165 OString schema_str
= OUStringToOString(aDbName
, m_settings
.encoding
);
167 if (unixSocketPassed
)
169 socket_str
= OUStringToOString(sUnixSocket
, m_settings
.encoding
);
171 else if (namedPipePassed
)
173 socket_str
= OUStringToOString(sNamedPipe
, m_settings
.encoding
);
176 // flags can also be passed as last parameter
177 if (!mysql_real_connect(&m_mysql
, host_str
.getStr(), user_str
.getStr(), pass_str
.getStr(),
178 schema_str
.getStr(), nPort
, socket_str
.getStr(),
179 CLIENT_MULTI_STATEMENTS
))
180 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(
181 mysql_error(&m_mysql
), mysql_sqlstate(&m_mysql
), mysql_errno(&m_mysql
), *this,
182 getConnectionEncoding());
184 // Check if the server is 4.1 or above
185 if (getMysqlVersion() < 40100)
187 throw SQLException("MariaDB LibreOffice Connector requires MySQL Server 4.1 or above",
188 *this, OUString(), 0, Any());
191 lcl_executeUpdate(&m_mysql
,
192 OString
{ "SET session sql_mode='ANSI_QUOTES,NO_AUTO_VALUE_ON_ZERO'" });
193 lcl_executeUpdate(&m_mysql
, OString
{ "SET NAMES utf8mb4" });
196 OUString
OConnection::getImplementationName()
198 return "com.sun.star.sdbc.drivers.mysqlc.OConnection";
201 css::uno::Sequence
<OUString
> OConnection::getSupportedServiceNames()
203 return { "com.sun.star.sdbc.Connection" };
206 sal_Bool
OConnection::supportsService(OUString
const& ServiceName
)
208 return cppu::supportsService(this, ServiceName
);
211 Reference
<XStatement
> SAL_CALL
OConnection::createStatement()
213 MutexGuard
aGuard(m_aMutex
);
214 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
216 // create a statement
217 // the statement can only be executed once
218 Reference
<XStatement
> xReturn
= new OStatement(this);
219 m_aStatements
.push_back(WeakReferenceHelper(xReturn
));
224 Reference
<XPreparedStatement
> SAL_CALL
OConnection::prepareStatement(const OUString
& _sSql
)
226 MutexGuard
aGuard(m_aMutex
);
227 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
228 const OString sSqlStatement
229 = OUStringToOString(_sSql
, getConnectionEncoding()); // FIXME transform statement ?
231 MYSQL_STMT
* pStmt
= mysql_stmt_init(&m_mysql
);
232 mysql_stmt_prepare(pStmt
, sSqlStatement
.getStr(), sSqlStatement
.getLength());
234 unsigned int nErrorNum
= mysql_errno(&m_mysql
);
236 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
),
237 mysql_sqlstate(&m_mysql
), nErrorNum
, *this,
238 getConnectionEncoding());
240 Reference
<XPreparedStatement
> xStatement
= new OPreparedStatement(this, pStmt
);
241 m_aStatements
.push_back(WeakReferenceHelper(xStatement
));
245 Reference
<XPreparedStatement
> SAL_CALL
OConnection::prepareCall(const OUString
& /*_sSql*/)
247 MutexGuard
aGuard(m_aMutex
);
248 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
250 mysqlc_sdbc_driver::throwFeatureNotImplementedException("OConnection::prepareCall", *this);
251 return Reference
<XPreparedStatement
>();
254 OUString SAL_CALL
OConnection::nativeSQL(const OUString
& /*_sSql*/)
256 MutexGuard
aGuard(m_aMutex
);
258 // const OUString sSqlStatement = transFormPreparedStatement( _sSql );
263 void SAL_CALL
OConnection::setAutoCommit(sal_Bool autoCommit
)
265 MutexGuard
aGuard(m_aMutex
);
266 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
267 if (!mysql_autocommit(&m_mysql
, autoCommit
))
268 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(
269 mysql_error(&m_mysql
), mysql_sqlstate(&m_mysql
), mysql_errno(&m_mysql
), *this,
270 getConnectionEncoding());
273 sal_Bool SAL_CALL
OConnection::getAutoCommit()
275 // you have to distinguish which if you are in autocommit mode or not
276 // at normal case true should be fine here
278 // TODO use SELECT @@autocommit query for that
279 MutexGuard
aGuard(m_aMutex
);
280 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
285 void SAL_CALL
OConnection::commit()
287 MutexGuard
aGuard(m_aMutex
);
288 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
290 if (!mysql_commit(&m_mysql
))
291 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(
292 mysql_error(&m_mysql
), mysql_sqlstate(&m_mysql
), mysql_errno(&m_mysql
), *this,
293 getConnectionEncoding());
296 void SAL_CALL
OConnection::rollback()
298 MutexGuard
aGuard(m_aMutex
);
299 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
301 if (!mysql_rollback(&m_mysql
))
302 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(
303 mysql_error(&m_mysql
), mysql_sqlstate(&m_mysql
), mysql_errno(&m_mysql
), *this,
304 getConnectionEncoding());
307 sal_Bool SAL_CALL
OConnection::isClosed()
309 MutexGuard
aGuard(m_aMutex
);
311 // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent)
312 return OConnection_BASE::rBHelper
.bDisposed
;
315 Reference
<XDatabaseMetaData
> SAL_CALL
OConnection::getMetaData()
317 MutexGuard
aGuard(m_aMutex
);
318 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
320 Reference
<XDatabaseMetaData
> xMetaData
= m_xMetaData
;
323 xMetaData
= new ODatabaseMetaData(*this, &m_mysql
);
324 m_xMetaData
= xMetaData
;
330 void SAL_CALL
OConnection::setReadOnly(sal_Bool readOnly
)
332 MutexGuard
aGuard(m_aMutex
);
333 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
335 m_settings
.readOnly
= readOnly
;
338 sal_Bool SAL_CALL
OConnection::isReadOnly()
340 MutexGuard
aGuard(m_aMutex
);
341 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
343 // return if your connection to readonly
344 return m_settings
.readOnly
;
347 void SAL_CALL
OConnection::setCatalog(const OUString
& /*catalog*/)
349 MutexGuard
aGuard(m_aMutex
);
350 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
355 OUString SAL_CALL
OConnection::getCatalog()
357 MutexGuard
aGuard(m_aMutex
);
358 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
364 void SAL_CALL
OConnection::setTransactionIsolation(sal_Int32
/*level*/)
366 MutexGuard
aGuard(m_aMutex
);
367 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
372 sal_Int32 SAL_CALL
OConnection::getTransactionIsolation()
374 MutexGuard
aGuard(m_aMutex
);
375 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
380 Reference
<XNameAccess
> SAL_CALL
OConnection::getTypeMap()
382 MutexGuard
aGuard(m_aMutex
);
383 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
385 Reference
<XNameAccess
> t
= m_typeMap
;
389 void SAL_CALL
OConnection::setTypeMap(const Reference
<XNameAccess
>& typeMap
)
391 MutexGuard
aGuard(m_aMutex
);
392 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
398 void SAL_CALL
OConnection::close()
401 we need block, because the mutex is a local variable,
402 which will guard the block
405 // we just dispose us
406 MutexGuard
aGuard(m_aMutex
);
407 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
409 mysql_close(&m_mysql
);
415 Any SAL_CALL
OConnection::getWarnings()
418 // when you collected some warnings -> return it
422 void SAL_CALL
OConnection::clearWarnings()
424 // you should clear your collected warnings here#
427 void OConnection::disposing()
429 // we noticed that we should be destroyed in near future so we have to dispose our statements
430 MutexGuard
aGuard(m_aMutex
);
432 for (auto const& statement
: m_aStatements
)
434 Reference
<XComponent
> xComp(statement
.get(), UNO_QUERY
);
440 m_aStatements
.clear();
442 m_xMetaData
= WeakReference
<XDatabaseMetaData
>();
444 OConnection_BASE::disposing();
447 sal_Int32
OConnection::getMysqlVersion()
449 MutexGuard
aGuard(m_aMutex
);
450 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
452 unsigned long version
= mysql_get_server_version(&m_mysql
);
453 return static_cast<sal_Int32
>(version
);
456 OUString
OConnection::transFormPreparedStatement(const OUString
& _sSQL
)
458 OUString sSqlStatement
= _sSQL
;
459 if (!m_xParameterSubstitution
.is())
463 Sequence
<Any
> aArgs(1);
464 Reference
<XConnection
> xCon
= this;
465 aArgs
[0] <<= NamedValue("ActiveConnection", makeAny(xCon
));
467 m_xParameterSubstitution
.set(
468 m_xDriver
->getFactory()->createInstanceWithArguments(
469 "org.openoffice.comp.helper.ParameterSubstitution", aArgs
),
472 catch (const Exception
&)
476 if (m_xParameterSubstitution
.is())
480 sSqlStatement
= m_xParameterSubstitution
->substituteVariables(sSqlStatement
, true);
482 catch (const Exception
&)
486 return sSqlStatement
;
489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */