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 .
21 #include "mysqlc_connection.hxx"
22 #include "mysqlc_databasemetadata.hxx"
24 #include "mysqlc_driver.hxx"
25 #include "mysqlc_statement.hxx"
26 #include "mysqlc_preparedstatement.hxx"
27 #include "mysqlc_general.hxx"
29 #include <com/sun/star/sdbc/ColumnValue.hpp>
30 #include <com/sun/star/sdbc/XRow.hpp>
31 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
32 #include <com/sun/star/lang/DisposedException.hpp>
33 #include <com/sun/star/beans/NamedValue.hpp>
35 #include <osl/module.hxx>
36 #include <osl/thread.h>
38 #include <sal/log.hxx>
39 #include <osl/diagnose.h>
40 #include <rtl/uri.hxx>
41 #include <rtl/ustrbuf.hxx>
43 using namespace connectivity::mysqlc
;
47 using namespace com::sun::star::uno
;
48 using namespace com::sun::star::container
;
49 using namespace com::sun::star::lang
;
50 using namespace com::sun::star::beans
;
51 using namespace com::sun::star::sdbc
;
52 using ::osl::MutexGuard
;
54 #define MYSQLC_URI_PREFIX "sdbc:mysqlc:"
58 void lcl_executeUpdate(MYSQL
* pMySql
, const OString
& sql
)
60 mysql_real_query(pMySql
, sql
.getStr(), sql
.getLength());
65 OConnection::OConnection(MysqlCDriver
& _rDriver
)
66 : OMetaConnection_BASE(m_aMutex
)
68 , m_xMetaData(nullptr)
69 , m_xDriver(&_rDriver
)
73 OConnection::~OConnection()
81 void OConnection::construct(const OUString
& url
, const Sequence
<PropertyValue
>& info
)
83 MutexGuard
aGuard(m_aMutex
);
85 mysql_library_init(0, nullptr, nullptr);
88 // use TCP as connection
89 mysql_protocol_type protocol
= MYSQL_PROTOCOL_TCP
;
90 mysql_options(&m_mysql
, MYSQL_OPT_PROTOCOL
, &protocol
);
94 OUString
aHostName("localhost");
95 sal_Int32 nPort
= 3306;
98 m_settings
.encoding
= MysqlCDriver::getDefaultEncoding();
100 // parse url. Url has the following format:
101 // external server: sdbc:mysqlc:[hostname]:[port]/[dbname]
103 if (url
.startsWith(MYSQLC_URI_PREFIX
))
109 // sdbc:mysql:mysqlc:[hostname]:[port]/[dbname]
113 token
= url
.getToken(0, '/', nIndex
);
114 if (!token
.isEmpty())
116 sal_Int32 nIndex1
= 0;
117 OUString hostandport
= token
.getToken(0, ':', nIndex1
);
118 if (!hostandport
.isEmpty())
120 aHostName
= hostandport
;
121 hostandport
= token
.getToken(0, ':', nIndex1
);
122 if (!hostandport
.isEmpty() && nIndex1
)
124 nPort
= hostandport
.toInt32();
126 token
= url
.getToken(0, '/', nIndex
);
127 if (!token
.isEmpty() && nIndex
)
134 // get user and password for mysql connection
135 const PropertyValue
* pIter
= info
.getConstArray();
136 const PropertyValue
* pEnd
= pIter
+ info
.getLength();
137 OUString aUser
, aPass
, sUnixSocket
, sNamedPipe
;
138 bool unixSocketPassed
= false;
139 bool namedPipePassed
= false;
141 m_settings
.connectionURL
= url
;
142 for (; pIter
!= pEnd
; ++pIter
)
144 if (pIter
->Name
== "user")
146 OSL_VERIFY(pIter
->Value
>>= aUser
);
148 else if (pIter
->Name
== "password")
150 OSL_VERIFY(pIter
->Value
>>= aPass
);
152 else if (pIter
->Name
== "LocalSocket")
154 OSL_VERIFY(pIter
->Value
>>= sUnixSocket
);
155 unixSocketPassed
= !sUnixSocket
.isEmpty();
157 else if (pIter
->Name
== "NamedPipe")
159 OSL_VERIFY(pIter
->Value
>>= sNamedPipe
);
160 namedPipePassed
= !sNamedPipe
.isEmpty();
162 else if (pIter
->Name
== "PublicConnectionURL")
164 OSL_VERIFY(pIter
->Value
>>= m_settings
.connectionURL
);
166 else if (pIter
->Name
== "NewURL")
167 { // legacy name for "PublicConnectionURL"
168 OSL_VERIFY(pIter
->Value
>>= m_settings
.connectionURL
);
172 OString host_str
= OUStringToOString(aHostName
, m_settings
.encoding
);
173 OString user_str
= OUStringToOString(aUser
, m_settings
.encoding
);
174 OString pass_str
= OUStringToOString(aPass
, m_settings
.encoding
);
175 OString schema_str
= OUStringToOString(aDbName
, m_settings
.encoding
);
177 if (unixSocketPassed
)
179 socket_str
= OUStringToOString(sUnixSocket
, m_settings
.encoding
);
181 else if (namedPipePassed
)
183 socket_str
= OUStringToOString(sNamedPipe
, m_settings
.encoding
);
186 // flags can also be passed as last parameter
187 if (!mysql_real_connect(&m_mysql
, host_str
.getStr(), user_str
.getStr(), pass_str
.getStr(),
188 schema_str
.getStr(), nPort
, socket_str
.getStr(), 0))
189 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
), mysql_errno(&m_mysql
),
190 *this, getConnectionEncoding());
192 // Check if the server is 4.1 or above
193 if (getMysqlVersion() < 40100)
195 throw SQLException("MariaDB LibreOffice Connector requires MySQL Server 4.1 or above",
196 *this, OUString(), 0, Any());
199 lcl_executeUpdate(&m_mysql
, OString
{ "SET session sql_mode='ANSI_QUOTES'" });
200 lcl_executeUpdate(&m_mysql
, OString
{ "SET NAMES utf8" });
203 OUString
OConnection::getImplementationName()
205 return OUString("com.sun.star.sdbc.drivers.mysqlc.OConnection");
208 css::uno::Sequence
<OUString
> OConnection::getSupportedServiceNames()
210 css::uno::Sequence
<OUString
> s(1);
211 s
[0] = "com.sun.star.sdbc.Connection";
215 sal_Bool
OConnection::supportsService(OUString
const& ServiceName
)
217 return cppu::supportsService(this, ServiceName
);
220 Reference
<XStatement
> SAL_CALL
OConnection::createStatement()
222 MutexGuard
aGuard(m_aMutex
);
223 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
225 // create a statement
226 // the statement can only be executed once
227 Reference
<XStatement
> xReturn
= new OStatement(this);
228 m_aStatements
.push_back(WeakReferenceHelper(xReturn
));
233 Reference
<XPreparedStatement
> SAL_CALL
OConnection::prepareStatement(const OUString
& _sSql
)
235 MutexGuard
aGuard(m_aMutex
);
236 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
237 const OString sSqlStatement
238 = OUStringToOString(_sSql
, getConnectionEncoding()); // FIXME transform statement ?
240 MYSQL_STMT
* pStmt
= mysql_stmt_init(&m_mysql
);
241 mysql_stmt_prepare(pStmt
, sSqlStatement
.getStr(), sSqlStatement
.getLength());
243 unsigned int nErrorNum
= mysql_errno(&m_mysql
);
245 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
), nErrorNum
, *this,
246 getConnectionEncoding());
248 Reference
<XPreparedStatement
> xStatement
= new OPreparedStatement(this, pStmt
);
249 m_aStatements
.push_back(WeakReferenceHelper(xStatement
));
253 Reference
<XPreparedStatement
> SAL_CALL
OConnection::prepareCall(const OUString
& /*_sSql*/)
255 MutexGuard
aGuard(m_aMutex
);
256 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
258 mysqlc_sdbc_driver::throwFeatureNotImplementedException("OConnection::prepareCall", *this);
259 return Reference
<XPreparedStatement
>();
262 OUString SAL_CALL
OConnection::nativeSQL(const OUString
& /*_sSql*/)
264 MutexGuard
aGuard(m_aMutex
);
266 // const OUString sSqlStatement = transFormPreparedStatement( _sSql );
271 void SAL_CALL
OConnection::setAutoCommit(sal_Bool autoCommit
)
273 MutexGuard
aGuard(m_aMutex
);
274 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
275 if (!mysql_autocommit(&m_mysql
, autoCommit
))
276 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
), mysql_errno(&m_mysql
),
277 *this, getConnectionEncoding());
280 sal_Bool SAL_CALL
OConnection::getAutoCommit()
282 // you have to distinguish which if you are in autocommit mode or not
283 // at normal case true should be fine here
285 // TODO use SELECT @@autocommit query for that
286 MutexGuard
aGuard(m_aMutex
);
287 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
292 void SAL_CALL
OConnection::commit()
294 MutexGuard
aGuard(m_aMutex
);
295 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
297 if (!mysql_commit(&m_mysql
))
298 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
), mysql_errno(&m_mysql
),
299 *this, getConnectionEncoding());
302 void SAL_CALL
OConnection::rollback()
304 MutexGuard
aGuard(m_aMutex
);
305 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
307 if (!mysql_rollback(&m_mysql
))
308 mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql
), mysql_errno(&m_mysql
),
309 *this, getConnectionEncoding());
312 sal_Bool SAL_CALL
OConnection::isClosed()
314 MutexGuard
aGuard(m_aMutex
);
316 // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent)
317 return OConnection_BASE::rBHelper
.bDisposed
;
320 Reference
<XDatabaseMetaData
> SAL_CALL
OConnection::getMetaData()
322 MutexGuard
aGuard(m_aMutex
);
323 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
325 Reference
<XDatabaseMetaData
> xMetaData
= m_xMetaData
;
328 xMetaData
= new ODatabaseMetaData(*this, &m_mysql
);
329 m_xMetaData
= xMetaData
;
335 void SAL_CALL
OConnection::setReadOnly(sal_Bool readOnly
)
337 MutexGuard
aGuard(m_aMutex
);
338 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
340 m_settings
.readOnly
= readOnly
;
343 sal_Bool SAL_CALL
OConnection::isReadOnly()
345 MutexGuard
aGuard(m_aMutex
);
346 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
348 // return if your connection to readonly
349 return m_settings
.readOnly
;
352 void SAL_CALL
OConnection::setCatalog(const OUString
& /*catalog*/)
354 MutexGuard
aGuard(m_aMutex
);
355 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
360 OUString SAL_CALL
OConnection::getCatalog()
362 MutexGuard
aGuard(m_aMutex
);
363 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
369 void SAL_CALL
OConnection::setTransactionIsolation(sal_Int32
/*level*/)
371 MutexGuard
aGuard(m_aMutex
);
372 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
377 sal_Int32 SAL_CALL
OConnection::getTransactionIsolation()
379 MutexGuard
aGuard(m_aMutex
);
380 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
385 Reference
<XNameAccess
> SAL_CALL
OConnection::getTypeMap()
387 MutexGuard
aGuard(m_aMutex
);
388 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
390 Reference
<XNameAccess
> t
= m_typeMap
;
394 void SAL_CALL
OConnection::setTypeMap(const Reference
<XNameAccess
>& typeMap
)
396 MutexGuard
aGuard(m_aMutex
);
397 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
403 void SAL_CALL
OConnection::close()
406 we need block, because the mutex is a local variable,
407 which will guard the block
410 // we just dispose us
411 MutexGuard
aGuard(m_aMutex
);
412 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
414 mysql_close(&m_mysql
);
420 Any SAL_CALL
OConnection::getWarnings()
423 // when you collected some warnings -> return it
427 void SAL_CALL
OConnection::clearWarnings()
429 // you should clear your collected warnings here#
432 void OConnection::disposing()
434 // we noticed that we should be destroyed in near future so we have to dispose our statements
435 MutexGuard
aGuard(m_aMutex
);
437 for (auto const& statement
: m_aStatements
)
439 Reference
<XComponent
> xComp(statement
.get(), UNO_QUERY
);
445 m_aStatements
.clear();
447 m_xMetaData
= WeakReference
<XDatabaseMetaData
>();
449 OConnection_BASE::disposing();
452 sal_Int32
OConnection::getMysqlVersion()
454 MutexGuard
aGuard(m_aMutex
);
455 checkDisposed(OConnection_BASE::rBHelper
.bDisposed
);
457 unsigned long version
= mysql_get_server_version(&m_mysql
);
458 return static_cast<sal_Int32
>(version
);
461 OUString
OConnection::transFormPreparedStatement(const OUString
& _sSQL
)
463 OUString sSqlStatement
= _sSQL
;
464 if (!m_xParameterSubstitution
.is())
468 Sequence
<Any
> aArgs(1);
469 Reference
<XConnection
> xCon
= this;
470 aArgs
[0] <<= NamedValue("ActiveConnection", makeAny(xCon
));
472 m_xParameterSubstitution
.set(
473 m_xDriver
->getFactory()->createInstanceWithArguments(
474 "org.openoffice.comp.helper.ParameterSubstitution", aArgs
),
477 catch (const Exception
&)
481 if (m_xParameterSubstitution
.is())
485 sSqlStatement
= m_xParameterSubstitution
->substituteVariables(sSqlStatement
, true);
487 catch (const Exception
&)
491 return sSqlStatement
;
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */