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,
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 ************************************************************************/
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>
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
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
;
100 ClosableReference( ::rtl::ByteSequence id
, Connection
*that
)
101 : m_conn( that
), m_id(std::move( id
))
105 virtual void SAL_CALL
dispose() override
109 m_conn
->removeFromWeakMap(m_id
);
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
)) ,
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
) );
152 m_settings
.tables
.clear();
153 m_settings
.users
.clear();
155 for (auto const& statement
: m_myStatements
)
157 Reference
< XCloseable
> r
= statement
.second
;
159 vectorCloseable
.push_back( r
);
163 // close all created statements
164 for (auto const& elem
: vectorCloseable
)
167 // close all created statements
168 for (auto const& elem
: vectorDispose
)
176 void Connection::removeFromWeakMap( const ::rtl::ByteSequence
& id
)
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() );
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 ) );
198 Reference
< XPreparedStatement
> Connection::prepareStatement( const OUString
& sql
)
200 MutexGuard
guard( m_xMutex
->GetMutex() );
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));
214 Reference
< XPreparedStatement
> Connection::prepareCall( const OUString
& )
217 u
"pq_driver: Callable statements not supported"_ustr
,
218 Reference
< XInterface
> (), OUString() , 1, Any() );
222 OUString
Connection::nativeSQL( const OUString
& sql
)
227 void Connection::setAutoCommit( sal_Bool
)
232 sal_Bool
Connection::getAutoCommit()
238 void Connection::commit()
243 void Connection::rollback()
248 sal_Bool
Connection::isClosed()
250 return m_settings
.pConnection
== nullptr;
253 Reference
< XDatabaseMetaData
> Connection::getMetaData()
255 MutexGuard
guard( m_xMutex
->GetMutex() );
258 m_meta
= new DatabaseMetaData( m_xMutex
, this, &m_settings
);
262 void Connection::setReadOnly( sal_Bool
)
268 sal_Bool
Connection::isReadOnly()
274 void Connection::setCatalog( const OUString
& )
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
)
296 sal_Int32
Connection::getTransactionIsolation()
302 Reference
< XNameAccess
> Connection::getTypeMap()
304 Reference
< XNameAccess
> t
;
306 MutexGuard
guard( m_xMutex
->GetMutex() );
312 void Connection::setTypeMap( const Reference
< XNameAccess
>& typeMap
)
314 MutexGuard
guard( m_xMutex
->GetMutex() );
317 Any
Connection::getWarnings()
322 void Connection::clearWarnings()
330 std::vector
<char*> values
;
331 std::vector
<bool> acquired
;
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"
338 cstr_vector () { values
.reserve(8); acquired
.reserve(8); }
339 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 14
340 #pragma GCC diagnostic pop
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
)
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
)
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
,
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
[] = {
392 for( PropertyValue
const & prop
: args
)
395 for(const char* j
: keyword_list
)
397 if( prop
.Name
.equalsIgnoreAsciiCaseAscii( j
))
399 keywords
.push_back( j
, SAL_NO_ACQUIRE
);
408 tc
->convertTo( prop
.Value
, cppu::UnoType
<decltype(value
)>::get() ) >>= value
;
409 char *v
= strdup(OUStringToOString(value
, enc
).getStr());
410 values
.push_back ( v
);
415 SAL_WARN("connectivity.postgresql", "sdbc-postgresql: unknown argument '" << prop
.Name
<< "' having value: " << prop
.Value
);
420 void Connection::initialize( const Sequence
< Any
>& aArguments
)
423 Sequence
< PropertyValue
> args
;
425 Reference
< XTypeConverter
> tc( Converter::create(m_ctx
) );
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(),
446 tc
->convertTo( aArguments
[1], cppu::UnoType
<decltype(args
)>::get() ) >>= args
;
449 int nColon
= url
.indexOf( ':' );
452 nColon
= url
.indexOf( ':' , 1+ nColon
);
455 o
= rtl::OUStringToOString( url
.subView(nColon
+1), ConnectionSettings::encoding
);
459 cstr_vector keywords
;
462 if ( o
.getLength() > 0 )
465 const std::unique_ptr
<PQconninfoOption
, deleter_from_fn
<PQconninfoFree
>>
466 oOpts(PQconninfoParse(o
.getStr(), &err
));
467 if (oOpts
== nullptr)
469 OUString errorMessage
;
472 errorMessage
= OUString( err
, strlen(err
), ConnectionSettings::encoding
);
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".
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;
508 "Couldn't establish database connection to '" + url
+ "'\n"
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()
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
);
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
) );
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
;
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: */