Use correct object
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_connection.cxx
blob74402197a0a567fa7b99ab1b39087aada80c391a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * Effective License of whole file:
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.
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.
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
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
25 * Copyright: 2000 by Sun Microsystems, Inc.
27 * Contributor(s): Joerg Budischewski
29 * All parts contributed on or after August 2011:
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/.
35 ************************************************************************/
37 #include <utility>
38 #include <vector>
39 #include <string.h>
41 #include <memory>
43 #include "pq_connection.hxx"
44 #include "pq_statement.hxx"
45 #include "pq_tools.hxx"
46 #include "pq_preparedstatement.hxx"
47 #include "pq_databasemetadata.hxx"
48 #include "pq_xtables.hxx"
49 #include "pq_xviews.hxx"
50 #include "pq_xusers.hxx"
52 #include <rtl/ref.hxx>
53 #include <rtl/uuid.h>
54 #include <sal/log.hxx>
56 #include <cppuhelper/implbase.hxx>
58 #include <com/sun/star/beans/PropertyValue.hpp>
59 #include <com/sun/star/script/Converter.hpp>
60 #include <com/sun/star/sdbc/SQLException.hpp>
62 using osl::MutexGuard;
64 using com::sun::star::container::XNameAccess;
66 using com::sun::star::lang::XComponent;
67 using com::sun::star::lang::IllegalArgumentException;
69 using com::sun::star::script::Converter;
70 using com::sun::star::script::XTypeConverter;
72 using com::sun::star::uno::RuntimeException;
73 using com::sun::star::uno::Sequence;
74 using com::sun::star::uno::Reference;
75 using com::sun::star::uno::XInterface;
76 using com::sun::star::uno::UNO_QUERY;
77 using com::sun::star::uno::UNO_QUERY_THROW;
78 using com::sun::star::uno::XComponentContext;
79 using com::sun::star::uno::Any;
81 using com::sun::star::beans::PropertyValue;
83 using com::sun::star::sdbc::XCloseable;
84 using com::sun::star::sdbc::SQLException;
85 using com::sun::star::sdbc::XPreparedStatement;
86 using com::sun::star::sdbc::XStatement;
87 using com::sun::star::sdbc::XDatabaseMetaData;
89 namespace pq_sdbc_driver
92 namespace {
94 // Helper class for statement lifetime management
95 class ClosableReference : public cppu::WeakImplHelper< css::uno::XReference >
97 rtl::Reference<Connection> m_conn;
98 ::rtl::ByteSequence m_id;
99 public:
100 ClosableReference( ::rtl::ByteSequence id , Connection *that )
101 : m_conn( that ), m_id(std::move( id ))
105 virtual void SAL_CALL dispose() override
107 if( m_conn.is() )
109 m_conn->removeFromWeakMap(m_id);
110 m_conn.clear();
117 Connection::Connection(
118 const rtl::Reference< comphelper::RefCountedMutex > &refMutex,
119 css::uno::Reference< css::uno::XComponentContext > ctx )
120 : ConnectionBase( refMutex->GetMutex() ),
121 m_ctx(std::move( ctx )) ,
122 m_xMutex( refMutex )
126 Connection::~Connection()
128 if( m_settings.pConnection )
130 PQfinish( m_settings.pConnection );
131 m_settings.pConnection = nullptr;
135 void Connection::close()
137 std::vector< css::uno::Reference< css::sdbc::XCloseable > > vectorCloseable;
138 std::vector< css::uno::Reference< css::lang::XComponent > > vectorDispose;
140 MutexGuard guard( m_xMutex->GetMutex() );
141 // silently ignore, if the connection has been closed already
142 if( m_settings.pConnection )
144 SAL_INFO("connectivity.postgresql", "closing connection");
145 PQfinish( m_settings.pConnection );
146 m_settings.pConnection = nullptr;
149 vectorDispose.push_back( Reference< XComponent > ( m_settings.users, UNO_QUERY ) );
150 vectorDispose.push_back( Reference< XComponent > ( m_settings.tables , UNO_QUERY ) );
151 m_meta.clear();
152 m_settings.tables.clear();
153 m_settings.users.clear();
155 for (auto const& statement : m_myStatements)
157 Reference< XCloseable > r = statement.second;
158 if( r.is() )
159 vectorCloseable.push_back( r );
163 // close all created statements
164 for (auto const& elem : vectorCloseable)
165 elem->close();
167 // close all created statements
168 for (auto const& elem : vectorDispose)
170 if( elem.is() )
171 elem->dispose();
176 void Connection::removeFromWeakMap( const ::rtl::ByteSequence & id )
178 // shrink the list !
179 MutexGuard guard( m_xMutex->GetMutex() );
180 WeakHashMap::iterator ii = m_myStatements.find( id );
181 if( ii != m_myStatements.end() )
182 m_myStatements.erase( ii );
185 Reference< XStatement > Connection::createStatement()
187 MutexGuard guard( m_xMutex->GetMutex() );
188 checkClosed();
190 rtl::Reference<Statement> stmt = new Statement( m_xMutex, this , &m_settings );
191 ::rtl::ByteSequence id( 16 );
192 rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false );
193 m_myStatements[ id ] = Reference< XCloseable > ( stmt );
194 stmt->queryAdapter()->addReference( new ClosableReference( std::move(id), this ) );
195 return stmt;
198 Reference< XPreparedStatement > Connection::prepareStatement( const OUString& sql )
200 MutexGuard guard( m_xMutex->GetMutex() );
201 checkClosed();
203 OString byteSql = OUStringToOString( sql, ConnectionSettings::encoding );
204 rtl::Reference<PreparedStatement> stmt
205 = new PreparedStatement( m_xMutex, this, &m_settings, byteSql );
207 ::rtl::ByteSequence id( 16 );
208 rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false );
209 m_myStatements[ id ] = Reference< XCloseable > ( stmt );
210 stmt->queryAdapter()->addReference(new ClosableReference(std::move(id), this));
211 return stmt;
214 Reference< XPreparedStatement > Connection::prepareCall( const OUString& )
216 throw SQLException(
217 u"pq_driver: Callable statements not supported"_ustr,
218 Reference< XInterface > (), OUString() , 1, Any() );
222 OUString Connection::nativeSQL( const OUString& sql )
224 return sql;
227 void Connection::setAutoCommit( sal_Bool )
229 // UNSUPPORTED
232 sal_Bool Connection::getAutoCommit()
234 // UNSUPPORTED
235 return true;
238 void Connection::commit()
240 // UNSUPPORTED
243 void Connection::rollback()
245 // UNSUPPORTED
248 sal_Bool Connection::isClosed()
250 return m_settings.pConnection == nullptr;
253 Reference< XDatabaseMetaData > Connection::getMetaData()
255 MutexGuard guard( m_xMutex->GetMutex() );
256 checkClosed();
257 if( ! m_meta.is() )
258 m_meta = new DatabaseMetaData( m_xMutex, this, &m_settings );
259 return m_meta;
262 void Connection::setReadOnly( sal_Bool )
264 // UNSUPPORTED
268 sal_Bool Connection::isReadOnly()
270 // UNSUPPORTED
271 return false;
274 void Connection::setCatalog( const OUString& )
276 // UNSUPPORTED
279 OUString Connection::getCatalog()
281 MutexGuard guard( m_xMutex->GetMutex() );
282 if( m_settings.pConnection == nullptr )
284 throw SQLException( u"pq_connection: connection is closed"_ustr, *this,
285 OUString(), 1, Any() );
287 char * p = PQdb(m_settings.pConnection );
288 return OUString( p, strlen(p) , ConnectionSettings::encoding );
291 void Connection::setTransactionIsolation( sal_Int32 )
293 // UNSUPPORTED
296 sal_Int32 Connection::getTransactionIsolation()
298 // UNSUPPORTED
299 return 0;
302 Reference< XNameAccess > Connection::getTypeMap()
304 Reference< XNameAccess > t;
306 MutexGuard guard( m_xMutex->GetMutex() );
307 t = m_typeMap;
309 return t;
312 void Connection::setTypeMap( const Reference< XNameAccess >& typeMap )
314 MutexGuard guard( m_xMutex->GetMutex() );
315 m_typeMap = typeMap;
317 Any Connection::getWarnings()
319 return Any();
322 void Connection::clearWarnings()
326 namespace {
328 class cstr_vector
330 std::vector<char*> values;
331 std::vector<bool> acquired;
332 public:
333 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 14
334 #pragma GCC diagnostic push
335 #pragma GCC diagnostic ignored "-Warray-bounds"
336 #pragma GCC diagnostic ignored "-Wstringop-overflow"
337 #endif
338 cstr_vector () { values.reserve(8); acquired.reserve(8); }
339 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 14
340 #pragma GCC diagnostic pop
341 #endif
342 ~cstr_vector ()
344 OSL_ENSURE(values.size() == acquired.size(), "pq_connection: cstr_vector values and acquired size mismatch");
345 std::vector<bool>::const_iterator pa = acquired.begin();
346 for( const auto& v : values )
348 if (*pa)
349 free(v);
350 ++pa;
353 void push_back(const char* s, __sal_NoAcquire)
355 values.push_back(const_cast<char*>(s));
356 acquired.push_back(false);
358 void push_back(char* s)
360 values.push_back(s);
361 acquired.push_back(true);
363 // This const_cast is there for compatibility with PostgreSQL <= 9.1;
364 // PostgreSQL >= 9.2 has the right const qualifiers in the headers
365 // for a return type of "char const*const*".
366 char const** c_array() const { return const_cast <const char**>(values.data()); }
371 static void properties2arrays( const Sequence< PropertyValue > & args,
372 const Reference< XTypeConverter> &tc,
373 rtl_TextEncoding enc,
374 cstr_vector &keywords,
375 cstr_vector &values)
377 // LEM TODO: can we just blindly take all properties?
378 // I.e. they are prefiltered to have only relevant ones?
379 // Else, at least support all keywords from
380 // http://www.postgresql.org/docs/9.0/interactive/libpq-connect.html
382 static const char* keyword_list[] = {
383 "password",
384 "user",
385 "port",
386 "dbname",
387 "connect_timeout",
388 "options",
389 "requiressl"
392 for( PropertyValue const & prop : args )
394 bool append = false;
395 for(const char* j : keyword_list)
397 if( prop.Name.equalsIgnoreAsciiCaseAscii( j ))
399 keywords.push_back( j, SAL_NO_ACQUIRE );
400 append = true;
401 break;
405 if( append )
407 OUString value;
408 tc->convertTo( prop.Value, cppu::UnoType<decltype(value)>::get() ) >>= value;
409 char *v = strdup(OUStringToOString(value, enc).getStr());
410 values.push_back ( v );
412 else
414 // ignore for now
415 SAL_WARN("connectivity.postgresql", "sdbc-postgresql: unknown argument '" << prop.Name << "' having value: " << prop.Value );
420 void Connection::initialize( const Sequence< Any >& aArguments )
422 OUString url;
423 Sequence< PropertyValue > args;
425 Reference< XTypeConverter > tc( Converter::create(m_ctx) );
426 if( ! tc.is() )
428 throw RuntimeException(
429 u"pq_driver: Couldn't instantiate converter service"_ustr );
431 if( aArguments.getLength() != 2 )
433 throw IllegalArgumentException(
434 "pq_driver: expected 2 arguments, got " + OUString::number( aArguments.getLength( ) ),
435 Reference< XInterface > () , 0 );
438 if( ! (aArguments[0] >>= url) )
440 throw IllegalArgumentException(
441 "pq_driver: expected string as first argument, got "
442 + aArguments[0].getValueTypeName(),
443 *this, 0 );
446 tc->convertTo( aArguments[1], cppu::UnoType<decltype(args)>::get() ) >>= args;
448 OString o;
449 int nColon = url.indexOf( ':' );
450 if( nColon != -1 )
452 nColon = url.indexOf( ':' , 1+ nColon );
453 if( nColon != -1 )
455 o = rtl::OUStringToOString( url.subView(nColon+1), ConnectionSettings::encoding );
459 cstr_vector keywords;
460 cstr_vector values;
462 if ( o.getLength() > 0 )
464 char *err;
465 const std::unique_ptr<PQconninfoOption, deleter_from_fn<PQconninfoFree>>
466 oOpts(PQconninfoParse(o.getStr(), &err));
467 if (oOpts == nullptr)
469 OUString errorMessage;
470 if ( err != nullptr)
472 errorMessage = OUString( err, strlen(err), ConnectionSettings::encoding );
473 PQfreemem(err);
475 else
476 errorMessage = "#no error message#";
477 // HY092 is "Invalid attribute/option identifier."
478 // Just the most likely error; the error might be HY024 "Invalid attribute value".
479 throw SQLException(
480 "Error in database URL '" + url + "':\n" + errorMessage,
481 *this, u"HY092"_ustr, 5, Any() );
484 for ( PQconninfoOption * opt = oOpts.get(); opt->keyword != nullptr; ++opt)
486 if ( opt->val != nullptr )
488 keywords.push_back(strdup(opt->keyword));
489 values.push_back(strdup(opt->val));
493 properties2arrays( args , tc, ConnectionSettings::encoding, keywords, values );
494 keywords.push_back(nullptr, SAL_NO_ACQUIRE);
495 values.push_back(nullptr, SAL_NO_ACQUIRE);
497 m_settings.pConnection = PQconnectdbParams( keywords.c_array(), values.c_array(), 0 );
499 if( ! m_settings.pConnection )
500 throw RuntimeException(u"pq_driver: out of memory"_ustr );
501 if( PQstatus( m_settings.pConnection ) == CONNECTION_BAD )
503 const char * error = PQerrorMessage( m_settings.pConnection );
504 OUString errorMessage( error, strlen( error) , RTL_TEXTENCODING_ASCII_US );
505 PQfinish( m_settings.pConnection );
506 m_settings.pConnection = nullptr;
507 throw SQLException(
508 "Couldn't establish database connection to '" + url + "'\n"
509 + errorMessage,
510 *this, errorMessage, CONNECTION_BAD, Any() );
512 PQsetClientEncoding( m_settings.pConnection, "UNICODE" );
513 char *p = PQuser( m_settings.pConnection );
514 m_settings.user = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
515 p = PQdb( m_settings.pConnection );
516 m_settings.catalog = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
517 m_settings.tc = std::move(tc);
519 SAL_INFO("connectivity.postgresql", "connection to '" << url << "' successfully opened");
522 void Connection::disposing()
524 close();
527 void Connection::checkClosed()
529 if( !m_settings.pConnection )
530 throw SQLException( u"pq_connection: Connection already closed"_ustr,
531 *this, OUString(), 1, Any() );
534 Reference< XNameAccess > Connection::getTables()
536 SAL_INFO("connectivity.postgresql", "Connection::getTables() got called");
537 MutexGuard guard( m_xMutex->GetMutex() );
538 if( !m_settings.tables.is() )
539 m_settings.tables = Tables::create( m_xMutex, this, &m_settings , &m_settings.pTablesImpl);
540 else
541 // TODO: how to overcome the performance problem ?
542 Reference< css::util::XRefreshable > ( m_settings.tables, UNO_QUERY_THROW )->refresh();
543 return m_settings.tables;
546 Reference< XNameAccess > Connection::getViews()
548 SAL_INFO("connectivity.postgresql", "Connection::getViews() got called");
549 MutexGuard guard( m_xMutex->GetMutex() );
550 if( !m_settings.views.is() )
551 m_settings.views = Views::create( m_xMutex, this, &m_settings, &(m_settings.pViewsImpl) );
552 else
553 // TODO: how to overcome the performance problem ?
554 Reference< css::util::XRefreshable > ( m_settings.views, UNO_QUERY_THROW )->refresh();
555 return m_settings.views;
559 Reference< XNameAccess > Connection::getUsers()
561 SAL_INFO("connectivity.postgresql", "Connection::getUsers() got called");
563 MutexGuard guard( m_xMutex->GetMutex() );
564 if( !m_settings.users.is() )
565 m_settings.users = Users::create( m_xMutex, this, &m_settings );
566 return m_settings.users;
569 } // end namespace
571 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
572 connectivity_postgresql_Connection_get_implementation(
573 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
575 ::rtl::Reference< comphelper::RefCountedMutex > ref = new comphelper::RefCountedMutex;
576 return cppu::acquire(new pq_sdbc_driver::Connection( ref, context ));
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */