LibreOffice Module connectivity (master) 1
pq_connection.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*************************************************************************
3 *
4 * Effective License of whole file:
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 * MA 02111-1307 USA
19 *
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
21 *
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
24 *
25 * Copyright: 2000 by Sun Microsystems, Inc.
26 *
27 * Contributor(s): Joerg Budischewski
28 *
29 * All parts contributed on or after August 2011:
30 *
31 * This Source Code Form is subject to the terms of the Mozilla Public
32 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
34 *
35 ************************************************************************/
36
37#include <utility>
38#include <vector>
39#include <string.h>
40
41#include <memory>
42
43#include "pq_connection.hxx"
44#include "pq_statement.hxx"
45#include "pq_tools.hxx"
48#include "pq_xtables.hxx"
49#include "pq_xviews.hxx"
50#include "pq_xusers.hxx"
51
52#include <rtl/ref.hxx>
53#include <rtl/uuid.h>
54#include <sal/log.hxx>
55
57
58#include <com/sun/star/beans/PropertyValue.hpp>
59#include <com/sun/star/script/Converter.hpp>
60#include <com/sun/star/sdbc/SQLException.hpp>
61
62using osl::MutexGuard;
63
64using com::sun::star::container::XNameAccess;
65
66using com::sun::star::lang::XComponent;
67using com::sun::star::lang::IllegalArgumentException;
68
69using com::sun::star::script::Converter;
70using com::sun::star::script::XTypeConverter;
71
72using com::sun::star::uno::RuntimeException;
75using com::sun::star::uno::XInterface;
76using com::sun::star::uno::UNO_QUERY;
77using com::sun::star::uno::UNO_QUERY_THROW;
78using com::sun::star::uno::XComponentContext;
79using com::sun::star::uno::Any;
80
81using com::sun::star::beans::PropertyValue;
82
83using com::sun::star::sdbc::XCloseable;
84using com::sun::star::sdbc::SQLException;
85using com::sun::star::sdbc::XPreparedStatement;
86using com::sun::star::sdbc::XStatement;
87using com::sun::star::sdbc::XDatabaseMetaData;
88
89namespace pq_sdbc_driver
90{
91
92namespace {
93
94// Helper class for statement lifetime management
95class ClosableReference : public cppu::WeakImplHelper< css::uno::XReference >
96{
98 ::rtl::ByteSequence m_id;
99public:
100 ClosableReference( ::rtl::ByteSequence id , Connection *that )
101 : m_conn( that ), m_id(std::move( id ))
102 {
103 }
104
105 virtual void SAL_CALL dispose() override
106 {
107 if( m_conn.is() )
108 {
109 m_conn->removeFromWeakMap(m_id);
110 m_conn.clear();
111 }
112 }
113};
114
115}
116
119 css::uno::Reference< css::uno::XComponentContext > ctx )
120 : ConnectionBase( refMutex->GetMutex() ),
121 m_ctx(std::move( ctx )) ,
122 m_xMutex( refMutex )
123{
124}
125
127{
129 {
130 PQfinish( m_settings.pConnection );
131 m_settings.pConnection = nullptr;
132 }
133}
134
136{
137 std::vector< css::uno::Reference< css::sdbc::XCloseable > > vectorCloseable;
138 std::vector< css::uno::Reference< css::lang::XComponent > > vectorDispose;
139 {
140 MutexGuard guard( m_xMutex->GetMutex() );
141 // silently ignore, if the connection has been closed already
143 {
144 SAL_INFO("connectivity.postgresql", "closing connection");
145 PQfinish( m_settings.pConnection );
146 m_settings.pConnection = nullptr;
147 }
148
149 vectorDispose.push_back( Reference< XComponent > ( m_settings.users, UNO_QUERY ) );
150 vectorDispose.push_back( Reference< XComponent > ( m_settings.tables , UNO_QUERY ) );
151 vectorDispose.push_back( Reference< XComponent > ( m_meta, UNO_QUERY ) );
152 m_meta.clear();
153 m_settings.tables.clear();
154 m_settings.users.clear();
155
156 for (auto const& statement : m_myStatements)
157 {
158 Reference< XCloseable > r = statement.second;
159 if( r.is() )
160 vectorCloseable.push_back( r );
161 }
162 }
163
164 // close all created statements
165 for (auto const& elem : vectorCloseable)
166 elem->close();
167
168 // close all created statements
169 for (auto const& elem : vectorDispose)
170 {
171 if( elem.is() )
172 elem->dispose();
173 }
174}
175
176
177void Connection::removeFromWeakMap( const ::rtl::ByteSequence & id )
178{
179 // shrink the list !
180 MutexGuard guard( m_xMutex->GetMutex() );
181 WeakHashMap::iterator ii = m_myStatements.find( id );
182 if( ii != m_myStatements.end() )
183 m_myStatements.erase( ii );
184}
185
187{
188 MutexGuard guard( m_xMutex->GetMutex() );
189 checkClosed();
190
192 ::rtl::ByteSequence id( 16 );
193 rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false );
195 stmt->queryAdapter()->addReference( new ClosableReference( id, this ) );
196 return stmt;
197}
198
200{
201 MutexGuard guard( m_xMutex->GetMutex() );
202 checkClosed();
203
206 = new PreparedStatement( m_xMutex, this, &m_settings, byteSql );
207
208 ::rtl::ByteSequence id( 16 );
209 rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false );
211 stmt->queryAdapter()->addReference( new ClosableReference( id, this ) );
212 return stmt;
213}
214
216{
217 throw SQLException(
218 "pq_driver: Callable statements not supported",
219 Reference< XInterface > (), OUString() , 1, Any() );
220}
221
222
223OUString Connection::nativeSQL( const OUString& sql )
224{
225 return sql;
226}
227
229{
230 // UNSUPPORTED
231}
232
234{
235 // UNSUPPORTED
236 return true;
237}
238
240{
241 // UNSUPPORTED
242}
243
245{
246 // UNSUPPORTED
247}
248
250{
251 return m_settings.pConnection == nullptr;
252}
253
255{
256 MutexGuard guard( m_xMutex->GetMutex() );
257 checkClosed();
258 if( ! m_meta.is() )
260 return m_meta;
261}
262
264{
265 // UNSUPPORTED
266
267}
268
270{
271 // UNSUPPORTED
272 return false;
273}
274
275void Connection::setCatalog( const OUString& )
276{
277 // UNSUPPORTED
278}
279
281{
282 MutexGuard guard( m_xMutex->GetMutex() );
283 if( m_settings.pConnection == nullptr )
284 {
285 throw SQLException( "pq_connection: connection is closed", *this,
286 OUString(), 1, Any() );
287 }
288 char * p = PQdb(m_settings.pConnection );
289 return OUString( p, strlen(p) , ConnectionSettings::encoding );
290}
291
293{
294 // UNSUPPORTED
295}
296
298{
299 // UNSUPPORTED
300 return 0;
301}
302
304{
306 {
307 MutexGuard guard( m_xMutex->GetMutex() );
308 t = m_typeMap;
309 }
310 return t;
311}
312
314{
315 MutexGuard guard( m_xMutex->GetMutex() );
316 m_typeMap = typeMap;
317}
319{
320 return Any();
321}
322
324{
325}
326
327namespace {
328
329class cstr_vector
330{
331 std::vector<char*> values;
332 std::vector<bool> acquired;
333public:
334 cstr_vector () { values.reserve(8); acquired.reserve(8); }
335 ~cstr_vector ()
336 {
337 OSL_ENSURE(values.size() == acquired.size(), "pq_connection: cstr_vector values and acquired size mismatch");
338 std::vector<bool>::const_iterator pa = acquired.begin();
339 for( const auto& v : values )
340 {
341 if (*pa)
342 free(v);
343 ++pa;
344 }
345 }
346 void push_back(const char* s, __sal_NoAcquire)
347 {
348 values.push_back(const_cast<char*>(s));
349 acquired.push_back(false);
350 }
351 void push_back(char* s)
352 {
353 values.push_back(s);
354 acquired.push_back(true);
355 }
356 // This const_cast is there for compatibility with PostgreSQL <= 9.1;
357 // PostgreSQL >= 9.2 has the right const qualifiers in the headers
358 // for a return type of "char const*const*".
359 char const** c_array() const { return const_cast <const char**>(values.data()); }
360};
361
362}
363
366 rtl_TextEncoding enc,
367 cstr_vector &keywords,
368 cstr_vector &values)
369{
370 // LEM TODO: can we just blindly take all properties?
371 // I.e. they are prefiltered to have only relevant ones?
372 // Else, at least support all keywords from
373 // http://www.postgresql.org/docs/9.0/interactive/libpq-connect.html
374
375 static const char* keyword_list[] = {
376 "password",
377 "user",
378 "port",
379 "dbname",
380 "connect_timeout",
381 "options",
382 "requiressl"
383 };
384
385 for( PropertyValue const & prop : args )
386 {
387 bool append = false;
388 for(const char* j : keyword_list)
389 {
390 if( prop.Name.equalsIgnoreAsciiCaseAscii( j ))
391 {
392 keywords.push_back( j, SAL_NO_ACQUIRE );
393 append = true;
394 break;
395 }
396 }
397
398 if( append )
399 {
400 OUString value;
401 tc->convertTo( prop.Value, cppu::UnoType<decltype(value)>::get() ) >>= value;
402 char *v = strdup(OUStringToOString(value, enc).getStr());
403 values.push_back ( v );
404 }
405 else
406 {
407 // ignore for now
408 SAL_WARN("connectivity.postgresql", "sdbc-postgresql: unknown argument '" << prop.Name << "' having value: " << prop.Value );
409 }
410 }
411}
412
414{
415 OUString url;
417
418 Reference< XTypeConverter > tc( Converter::create(m_ctx) );
419 if( ! tc.is() )
420 {
421 throw RuntimeException(
422 "pq_driver: Couldn't instantiate converter service" );
423 }
424 if( aArguments.getLength() != 2 )
425 {
426 throw IllegalArgumentException(
427 "pq_driver: expected 2 arguments, got " + OUString::number( aArguments.getLength( ) ),
429 }
430
431 if( ! (aArguments[0] >>= url) )
432 {
433 throw IllegalArgumentException(
434 "pq_driver: expected string as first argument, got "
435 + aArguments[0].getValueType().getTypeName(),
436 *this, 0 );
437 }
438
439 tc->convertTo( aArguments[1], cppu::UnoType<decltype(args)>::get() ) >>= args;
440
441 OString o;
442 int nColon = url.indexOf( ':' );
443 if( nColon != -1 )
444 {
445 nColon = url.indexOf( ':' , 1+ nColon );
446 if( nColon != -1 )
447 {
448 o = rtl::OUStringToOString( url.subView(nColon+1), ConnectionSettings::encoding );
449 }
450 }
451 {
452 cstr_vector keywords;
453 cstr_vector values;
454
455 if ( o.getLength() > 0 )
456 {
457 char *err;
458 const std::unique_ptr<PQconninfoOption, deleter_from_fn<PQconninfoFree>>
459 oOpts(PQconninfoParse(o.getStr(), &err));
460 if (oOpts == nullptr)
461 {
462 OUString errorMessage;
463 if ( err != nullptr)
464 {
465 errorMessage = OUString( err, strlen(err), ConnectionSettings::encoding );
466 PQfreemem(err);
467 }
468 else
469 errorMessage = "#no error message#";
470 // HY092 is "Invalid attribute/option identifier."
471 // Just the most likely error; the error might be HY024 "Invalid attribute value".
472 throw SQLException(
473 "Error in database URL '" + url + "':\n" + errorMessage,
474 *this, "HY092", 5, Any() );
475 }
476
477 for ( PQconninfoOption * opt = oOpts.get(); opt->keyword != nullptr; ++opt)
478 {
479 if ( opt->val != nullptr )
480 {
481 keywords.push_back(strdup(opt->keyword));
482 values.push_back(strdup(opt->val));
483 }
484 }
485 }
487 keywords.push_back(nullptr, SAL_NO_ACQUIRE);
488 values.push_back(nullptr, SAL_NO_ACQUIRE);
489
490 m_settings.pConnection = PQconnectdbParams( keywords.c_array(), values.c_array(), 0 );
491 }
492 if( ! m_settings.pConnection )
493 throw RuntimeException("pq_driver: out of memory" );
494 if( PQstatus( m_settings.pConnection ) == CONNECTION_BAD )
495 {
496 const char * error = PQerrorMessage( m_settings.pConnection );
497 OUString errorMessage( error, strlen( error) , RTL_TEXTENCODING_ASCII_US );
498 PQfinish( m_settings.pConnection );
499 m_settings.pConnection = nullptr;
500 throw SQLException(
501 "Couldn't establish database connection to '" + url + "'\n"
502 + errorMessage,
503 *this, errorMessage, CONNECTION_BAD, Any() );
504 }
505 PQsetClientEncoding( m_settings.pConnection, "UNICODE" );
506 char *p = PQuser( m_settings.pConnection );
507 m_settings.user = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
508 p = PQdb( m_settings.pConnection );
509 m_settings.catalog = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
510 m_settings.tc = tc;
511
512 SAL_INFO("connectivity.postgresql", "connection to '" << url << "' successfully opened");
513}
514
516{
517 close();
518}
519
521{
523 throw SQLException( "pq_connection: Connection already closed",
524 *this, OUString(), 1, Any() );
525}
526
528{
529 SAL_INFO("connectivity.postgresql", "Connection::getTables() got called");
530 MutexGuard guard( m_xMutex->GetMutex() );
531 if( !m_settings.tables.is() )
533 else
534 // TODO: how to overcome the performance problem ?
535 Reference< css::util::XRefreshable > ( m_settings.tables, UNO_QUERY_THROW )->refresh();
536 return m_settings.tables;
537}
538
540{
541 SAL_INFO("connectivity.postgresql", "Connection::getViews() got called");
542 MutexGuard guard( m_xMutex->GetMutex() );
543 if( !m_settings.views.is() )
545 else
546 // TODO: how to overcome the performance problem ?
547 Reference< css::util::XRefreshable > ( m_settings.views, UNO_QUERY_THROW )->refresh();
548 return m_settings.views;
549}
550
551
553{
554 SAL_INFO("connectivity.postgresql", "Connection::getUsers() got called");
555
556 MutexGuard guard( m_xMutex->GetMutex() );
557 if( !m_settings.users.is() )
559 return m_settings.users;
560}
561
562} // end namespace
563
564extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
566 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
567{
569 return cppu::acquire(new pq_sdbc_driver::Connection( ref, context ));
570}
571
572/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
XPropertyListType t
virtual ~Connection() override
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &aArguments) override
css::uno::Reference< css::sdbc::XDatabaseMetaData > m_meta
virtual sal_Int32 SAL_CALL getTransactionIsolation() override
virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement(const OUString &sql) override
virtual void SAL_CALL setReadOnly(sal_Bool readOnly) override
virtual sal_Bool SAL_CALL isClosed() override
virtual void SAL_CALL setTransactionIsolation(sal_Int32 level) override
virtual void SAL_CALL rollback() override
virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall(const OUString &sql) override
virtual void SAL_CALL commit() override
virtual void SAL_CALL close() override
css::uno::Reference< css::uno::XComponentContext > m_ctx
virtual sal_Bool SAL_CALL getAutoCommit() override
virtual css::uno::Any SAL_CALL getWarnings() override
virtual void SAL_CALL setAutoCommit(sal_Bool autoCommit) override
ConnectionSettings m_settings
virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getViews() override
::rtl::Reference< comphelper::RefCountedMutex > m_xMutex
virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables() override
virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData() override
virtual void SAL_CALL setCatalog(const OUString &catalog) override
virtual OUString SAL_CALL getCatalog() override
virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap() override
virtual void SAL_CALL setTypeMap(const css::uno::Reference< css::container::XNameAccess > &typeMap) override
virtual sal_Bool SAL_CALL isReadOnly() override
virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement() override
virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getUsers() override
Connection(const rtl::Reference< comphelper::RefCountedMutex > &refMutex, css::uno::Reference< css::uno::XComponentContext > ctx)
virtual void SAL_CALL disposing() override
virtual void SAL_CALL clearWarnings() override
css::uno::Reference< css::container::XNameAccess > m_typeMap
void removeFromWeakMap(const ::rtl::ByteSequence &seq)
virtual OUString SAL_CALL nativeSQL(const OUString &sql) override
static css::uno::Reference< css::container::XNameAccess > create(const ::rtl::Reference< comphelper::RefCountedMutex > &refMutex, const css::uno::Reference< css::sdbc::XConnection > &origin, ConnectionSettings *pSettings, rtl::Reference< Tables > *ppTables)
Definition: pq_xtables.cxx:355
static css::uno::Reference< css::container::XNameAccess > create(const ::rtl::Reference< comphelper::RefCountedMutex > &refMutex, const css::uno::Reference< css::sdbc::XConnection > &origin, ConnectionSettings *pSettings)
Definition: pq_xusers.cxx:185
static css::uno::Reference< css::container::XNameAccess > create(const ::rtl::Reference< comphelper::RefCountedMutex > &refMutex, const css::uno::Reference< css::sdbc::XConnection > &origin, ConnectionSettings *pSettings, rtl::Reference< Views > *ppViews)
Definition: pq_xviews.cxx:205
Any value
float v
Sequence< PropertyValue > aArguments
void * p
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
err
rtl::OUString getTypeName(rtl::OUString const &rEnvDcp)
ctx
args
static void properties2arrays(const Sequence< PropertyValue > &args, const Reference< XTypeConverter > &tc, rtl_TextEncoding enc, cstr_vector &keywords, cstr_vector &values)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
Definition: pq_tools.cxx:100
cppu::WeakComponentImplHelper< css::sdbc::XConnection, css::sdbc::XWarningsSupplier, css::lang::XInitialization, css::sdbcx::XTablesSupplier, css::sdbcx::XViewsSupplier, css::sdbcx::XUsersSupplier > ConnectionBase
void dispose()
std::vector< bool > acquired
::rtl::ByteSequence m_id
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * connectivity_postgresql_Connection_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
rtl::Reference< Connection > m_conn
std::vector< char * > values
css::uno::Reference< css::script::XTypeConverter > tc
rtl::Reference< Views > pViewsImpl
css::uno::Reference< css::container::XNameAccess > views
css::uno::Reference< css::container::XNameAccess > users
css::uno::Reference< css::container::XNameAccess > tables
static const rtl_TextEncoding encoding
rtl::Reference< Tables > pTablesImpl
unsigned char sal_uInt8
unsigned char sal_Bool