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 ************************************************************************/
42 #include <boost/shared_ptr.hpp>
44 #include "pq_connection.hxx"
45 #include "pq_statement.hxx"
46 #include "pq_preparedstatement.hxx"
47 #include "pq_databasemetadata.hxx"
48 #include "pq_xcontainer.hxx"
49 #include "pq_statics.hxx"
50 #include "pq_xtables.hxx"
51 #include "pq_xviews.hxx"
52 #include "pq_xusers.hxx"
54 #include <rtl/ustrbuf.hxx>
55 #include <rtl/strbuf.hxx>
57 #include <rtl/bootstrap.hxx>
58 #include <osl/module.h>
60 #include <cppuhelper/implementationentry.hxx>
61 #include <cppuhelper/implbase1.hxx>
63 #include <com/sun/star/beans/PropertyValue.hpp>
64 #include <com/sun/star/script/Converter.hpp>
65 #include <com/sun/star/sdbc/XRow.hpp>
67 using osl::MutexGuard
;
69 using com::sun::star::container::XNameAccess
;
71 using com::sun::star::lang::XComponent
;
72 using com::sun::star::lang::XInitialization
;
73 using com::sun::star::lang::IllegalArgumentException
;
75 using com::sun::star::script::Converter
;
76 using com::sun::star::script::XTypeConverter
;
78 using com::sun::star::uno::RuntimeException
;
79 using com::sun::star::uno::Exception
;
80 using com::sun::star::uno::Sequence
;
81 using com::sun::star::uno::Reference
;
82 using com::sun::star::uno::XInterface
;
83 using com::sun::star::uno::UNO_QUERY
;
84 using com::sun::star::uno::XComponentContext
;
85 using com::sun::star::uno::Any
;
86 using com::sun::star::uno::makeAny
;
88 using com::sun::star::beans::PropertyValue
;
89 using com::sun::star::beans::XPropertySet
;
91 using com::sun::star::sdbc::XConnection
;
92 using com::sun::star::sdbc::XResultSet
;
93 using com::sun::star::sdbc::XRow
;
94 using com::sun::star::sdbc::XCloseable
;
95 using com::sun::star::sdbc::SQLException
;
96 using com::sun::star::sdbc::XWarningsSupplier
;
97 using com::sun::star::sdbc::XPreparedStatement
;
98 using com::sun::star::sdbc::XStatement
;
99 using com::sun::star::sdbc::XDatabaseMetaData
;
101 namespace pq_sdbc_driver
106 // Helper class for statement lifetime management
107 class ClosableReference
: public cppu::WeakImplHelper1
< com::sun::star::uno::XReference
>
110 ::rtl::ByteSequence m_id
;
112 ClosableReference( const ::rtl::ByteSequence
& id
, Connection
*that
)
113 : m_conn( that
), m_id( id
)
118 virtual ~ClosableReference()
124 virtual void SAL_CALL
dispose() throw (std::exception
) SAL_OVERRIDE
128 m_conn
->removeFromWeakMap(m_id
);
135 OUString
ConnectionGetImplementationName()
137 return OUString( "org.openoffice.comp.connectivity.pq.Connection.noext" );
139 com::sun::star::uno::Sequence
<OUString
> ConnectionGetSupportedServiceNames(void)
141 OUString
serv( "com.sun.star.sdbc.Connection" );
142 return Sequence
< OUString
> (&serv
,1);
145 static sal_Int32
readLogLevelFromConfiguration()
147 sal_Int32 loglevel
= LogLevel::NONE
;
149 osl_getModuleURLFromAddress(
150 (void*) readLogLevelFromConfiguration
, (rtl_uString
**) &fileName
);
151 fileName
= fileName
.copy( fileName
.lastIndexOf( '/' )+1 );
152 fileName
+= "postgresql-sdbc.ini";
153 rtl::Bootstrap
bootstrapHandle( fileName
);
156 if( bootstrapHandle
.getFrom( "PQ_LOGLEVEL", str
) )
159 loglevel
= LogLevel::NONE
;
160 else if ( str
== "ERROR" )
161 loglevel
= LogLevel::ERROR
;
162 else if ( str
== "SQL" )
163 loglevel
= LogLevel::SQL
;
164 else if ( str
== "INFO" )
165 loglevel
= LogLevel::INFO
;
168 fprintf( stderr
, "unknown loglevel %s\n",
169 OUStringToOString( str
, RTL_TEXTENCODING_UTF8
).getStr() );
175 Connection::Connection(
176 const rtl::Reference
< RefCountedMutex
> &refMutex
,
177 const ::com::sun::star::uno::Reference
< ::com::sun::star::uno::XComponentContext
> & ctx
)
178 : ConnectionBase( refMutex
->mutex
),
180 m_refMutex( refMutex
)
182 m_settings
.loglevel
= readLogLevelFromConfiguration();
184 if( m_settings
.loglevel
> LogLevel::NONE
)
186 m_settings
.logFile
= fopen( "sdbc-pqsql.log", "a" );
187 if( m_settings
.logFile
)
189 setvbuf( m_settings
.logFile
, 0, _IONBF
, 0 );
190 log( &m_settings
, m_settings
.loglevel
, "set this loglevel" );
194 fprintf( stderr
, "Couldn't open sdbc-pqsql.log file\n" );
199 Connection::~Connection()
201 POSTGRE_TRACE( "dtor connection" );
202 if( m_settings
.pConnection
)
204 PQfinish( m_settings
.pConnection
);
205 m_settings
.pConnection
= 0;
207 if( m_settings
.logFile
)
209 fclose( m_settings
.logFile
);
210 m_settings
.logFile
= 0;
213 typedef ::std::list
< ::com::sun::star::uno::Reference
< ::com::sun::star::sdbc::XCloseable
> > CloseableList
;
215 typedef ::std::list
< ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XComponent
> > DisposeableList
;
217 void Connection::close() throw ( SQLException
, RuntimeException
, std::exception
)
220 DisposeableList lstDispose
;
222 MutexGuard
guard( m_refMutex
->mutex
);
223 // silently ignore, if the connection has been closed already
224 if( m_settings
.pConnection
)
226 log( &m_settings
, LogLevel::INFO
, "closing connection" );
227 PQfinish( m_settings
.pConnection
);
228 m_settings
.pConnection
= 0;
231 lstDispose
.push_back( Reference
< XComponent
> ( m_settings
.users
, UNO_QUERY
) );
232 lstDispose
.push_back( Reference
< XComponent
> ( m_settings
.tables
, UNO_QUERY
) );
233 lstDispose
.push_back( Reference
< XComponent
> ( m_meta
, UNO_QUERY
) );
235 m_settings
.tables
.clear();
236 m_settings
.users
.clear();
238 for( WeakHashMap::iterator ii
= m_myStatements
.begin() ;
239 ii
!= m_myStatements
.end() ;
242 Reference
< XCloseable
> r
= ii
->second
;
248 // close all created statements
249 for( CloseableList::iterator ii
= lst
.begin(); ii
!= lst
.end() ; ++ii
)
252 // close all created statements
253 for( DisposeableList::iterator iiDispose
= lstDispose
.begin();
254 iiDispose
!= lstDispose
.end() ; ++iiDispose
)
256 if( iiDispose
->is() )
257 iiDispose
->get()->dispose();
262 void Connection::removeFromWeakMap( const ::rtl::ByteSequence
& id
)
265 MutexGuard
guard( m_refMutex
->mutex
);
266 WeakHashMap::iterator ii
= m_myStatements
.find( id
);
267 if( ii
!= m_myStatements
.end() )
268 m_myStatements
.erase( ii
);
271 Reference
< XStatement
> Connection::createStatement() throw (SQLException
, RuntimeException
, std::exception
)
273 MutexGuard
guard( m_refMutex
->mutex
);
276 Statement
*stmt
= new Statement( m_refMutex
, this , &m_settings
);
277 Reference
< XStatement
> ret( stmt
);
278 ::rtl::ByteSequence
id( 16 );
279 rtl_createUuid( (sal_uInt8
*) id
.getConstArray(), 0 , sal_False
);
280 m_myStatements
[ id
] = Reference
< XCloseable
> ( stmt
);
281 stmt
->queryAdapter()->addReference( new ClosableReference( id
, this ) );
285 Reference
< XPreparedStatement
> Connection::prepareStatement( const OUString
& sql
)
286 throw (SQLException
, RuntimeException
, std::exception
)
288 MutexGuard
guard( m_refMutex
->mutex
);
291 OString byteSql
= OUStringToOString( sql
, m_settings
.encoding
);
292 PreparedStatement
*stmt
= new PreparedStatement( m_refMutex
, this, &m_settings
, byteSql
);
293 Reference
< XPreparedStatement
> ret
= stmt
;
295 ::rtl::ByteSequence
id( 16 );
296 rtl_createUuid( (sal_uInt8
*) id
.getConstArray(), 0 , sal_False
);
297 m_myStatements
[ id
] = Reference
< XCloseable
> ( stmt
);
298 stmt
->queryAdapter()->addReference( new ClosableReference( id
, this ) );
302 Reference
< XPreparedStatement
> Connection::prepareCall( const OUString
& )
303 throw (SQLException
, RuntimeException
, std::exception
)
306 OUString( "pq_driver: Callable statements not supported" ),
307 Reference
< XInterface
> (), OUString() , 1, Any() );
311 OUString
Connection::nativeSQL( const OUString
& sql
)
312 throw (SQLException
, RuntimeException
, std::exception
)
317 void Connection::setAutoCommit( sal_Bool
) throw (SQLException
, RuntimeException
, std::exception
)
322 sal_Bool
Connection::getAutoCommit() throw (SQLException
, RuntimeException
, std::exception
)
328 void Connection::commit() throw (SQLException
, RuntimeException
, std::exception
)
333 void Connection::rollback() throw (SQLException
, RuntimeException
, std::exception
)
338 sal_Bool
Connection::isClosed() throw (SQLException
, RuntimeException
, std::exception
)
340 return m_settings
.pConnection
== 0;
343 Reference
< XDatabaseMetaData
> Connection::getMetaData()
344 throw (SQLException
, RuntimeException
, std::exception
)
346 MutexGuard
guard( m_refMutex
->mutex
);
349 m_meta
= new DatabaseMetaData( m_refMutex
, this, &m_settings
);
353 void Connection::setReadOnly( sal_Bool
) throw (SQLException
, RuntimeException
, std::exception
)
359 sal_Bool
Connection::isReadOnly() throw (SQLException
, RuntimeException
, std::exception
)
365 void Connection::setCatalog( const OUString
& )
366 throw (SQLException
, RuntimeException
, std::exception
)
371 OUString
Connection::getCatalog() throw (SQLException
, RuntimeException
, std::exception
)
373 MutexGuard
guard( m_refMutex
->mutex
);
374 if( m_settings
.pConnection
== 0 )
376 throw SQLException( "pq_connection: connection is closed", *this,
377 OUString(), 1, Any() );
379 char * p
= PQdb(m_settings
.pConnection
);
380 return OUString( p
, strlen(p
) , m_settings
.encoding
);
383 void Connection::setTransactionIsolation( sal_Int32
)
384 throw (SQLException
, RuntimeException
, std::exception
)
389 sal_Int32
Connection::getTransactionIsolation() throw (SQLException
, RuntimeException
, std::exception
)
395 Reference
< XNameAccess
> Connection::getTypeMap() throw (SQLException
, RuntimeException
, std::exception
)
397 Reference
< XNameAccess
> t
;
399 MutexGuard
guard( m_refMutex
->mutex
);
405 void Connection::setTypeMap( const Reference
< XNameAccess
>& typeMap
)
406 throw (SQLException
, RuntimeException
, std::exception
)
408 MutexGuard
guard( m_refMutex
->mutex
);
411 Any
Connection::getWarnings() throw (SQLException
, RuntimeException
, std::exception
)
416 void Connection::clearWarnings() throw (SQLException
, RuntimeException
, std::exception
)
422 std::vector
<char*> values
;
423 std::vector
<bool> acquired
;
425 cstr_vector () : values(), acquired() { values
.reserve(8); acquired
.reserve(8); }
428 OSL_ENSURE(values
.size() == acquired
.size(), "pq_connection: cstr_vector values and acquired size mismatch");
429 std::vector
<char*>::iterator pv
= values
.begin();
430 std::vector
<bool>::iterator pa
= acquired
.begin();
431 const std::vector
<char*>::iterator pve
= values
.end();
432 for( ; pv
< pve
; ++pv
, ++pa
)
436 void push_back(const char* s
, __sal_NoAcquire
)
438 values
.push_back(const_cast<char*>(s
));
439 acquired
.push_back(false);
441 void push_back(char* s
)
444 acquired
.push_back(true);
446 // This const_cast is there for compatibility with PostgreSQL <= 9.1;
447 // PostgreSQL >= 9.2 has the right const qualifiers in the headers
448 // for a return type of "char const*const*".
449 char const** c_array() const { return const_cast <const char**>(&values
[0]); }
452 static void properties2arrays( const Sequence
< PropertyValue
> & args
,
453 const Reference
< XTypeConverter
> &tc
,
454 rtl_TextEncoding enc
,
455 cstr_vector
&keywords
,
458 // LEM TODO: can we just blindly take all properties?
459 // I.e. they are prefiltered to have only relevant ones?
460 // Else, at least support all keywords from
461 // http://www.postgresql.org/docs/9.0/interactive/libpq-connect.html
463 static const char* keyword_list
[] = {
473 for( int i
= 0; i
< args
.getLength() ; ++i
)
476 for( size_t j
= 0; j
< SAL_N_ELEMENTS( keyword_list
); j
++)
478 if( args
[i
].Name
.equalsIgnoreAsciiCaseAscii( keyword_list
[j
] ))
480 keywords
.push_back( keyword_list
[j
], SAL_NO_ACQUIRE
);
489 tc
->convertTo( args
[i
].Value
, getCppuType( &value
) ) >>= value
;
490 char *v
= strdup(OUStringToOString(value
, enc
).getStr());
491 values
.push_back ( v
);
496 OSL_TRACE("sdbc-postgresql: unknown argument '%s'", OUStringToOString( args
[i
].Name
, RTL_TEXTENCODING_UTF8
).getStr() );
501 void Connection::initialize( const Sequence
< Any
>& aArguments
)
502 throw (Exception
, RuntimeException
, std::exception
)
505 Sequence
< PropertyValue
> args
;
507 Reference
< XTypeConverter
> tc( Converter::create(m_ctx
) );
510 throw RuntimeException(
511 OUString("pq_driver: Couldn't instantiate converter service" ),
512 Reference
< XInterface
> () );
514 if( aArguments
.getLength() != 2 )
516 OUStringBuffer
buf(128);
517 buf
.appendAscii( "pq_driver: expected 2 arguments, got " );
518 buf
.append( aArguments
.getLength( ) );
519 throw IllegalArgumentException(buf
.makeStringAndClear(), Reference
< XInterface
> () , 0 );
522 if( ! (aArguments
[0] >>= url
) )
524 OUStringBuffer
buf(128);
525 buf
.appendAscii( "pq_driver: expected string as first argument, got " );
526 buf
.append( aArguments
[0].getValueType().getTypeName() );
527 throw IllegalArgumentException( buf
.makeStringAndClear() , *this, 0 );
530 tc
->convertTo( aArguments
[1], getCppuType( &args
) ) >>= args
;
533 int nColon
= url
.indexOf( ':' );
536 nColon
= url
.indexOf( ':' , 1+ nColon
);
539 o
= OUStringToOString( url
.getStr()+nColon
+1, m_settings
.encoding
);
543 cstr_vector keywords
;
546 if ( o
.getLength() > 0 )
549 boost::shared_ptr
<PQconninfoOption
> oOpts(PQconninfoParse(o
.getStr(), &err
), PQconninfoFree
);
550 if ( oOpts
.get() == NULL
)
552 OUString errorMessage
;
555 errorMessage
= OUString( err
, strlen(err
), m_settings
.encoding
);
559 errorMessage
= "#no error message#";
560 OUStringBuffer
buf( 128 );
561 buf
.appendAscii( "Error in database URL '" );
563 buf
.appendAscii( "':\n" );
564 buf
.append( errorMessage
);
565 // HY092 is "Invalid attribute/option identifier."
566 // Just the most likely error; the error might be HY024 "Invalid attribute value".
567 throw SQLException( buf
.makeStringAndClear(), *this, OUString("HY092"), 5, Any() );
570 for ( PQconninfoOption
* opt
= oOpts
.get(); opt
->keyword
!= NULL
; ++opt
)
572 if ( opt
->val
!= NULL
)
574 keywords
.push_back(strdup(opt
->keyword
));
575 values
.push_back(strdup(opt
->val
));
579 properties2arrays( args
, tc
, m_settings
.encoding
, keywords
, values
);
580 keywords
.push_back(NULL
, SAL_NO_ACQUIRE
);
581 values
.push_back(NULL
, SAL_NO_ACQUIRE
);
583 m_settings
.pConnection
= PQconnectdbParams( keywords
.c_array(), values
.c_array(), 0 );
585 if( ! m_settings
.pConnection
)
586 throw RuntimeException("pq_driver: out of memory",
587 Reference
< XInterface
> () );
588 if( PQstatus( m_settings
.pConnection
) == CONNECTION_BAD
)
590 OUStringBuffer
buf( 128 );
592 const char * error
= PQerrorMessage( m_settings
.pConnection
);
593 OUString
errorMessage( error
, strlen( error
) , RTL_TEXTENCODING_ASCII_US
);
594 buf
.appendAscii( "Couldn't establish database connection to '" );
596 buf
.appendAscii( "'\n" );
597 buf
.append( errorMessage
);
598 PQfinish( m_settings
.pConnection
);
599 m_settings
.pConnection
= 0;
600 throw SQLException( buf
.makeStringAndClear(), *this, errorMessage
, CONNECTION_BAD
, Any() );
602 PQsetClientEncoding( m_settings
.pConnection
, "UNICODE" );
603 char *p
= PQuser( m_settings
.pConnection
);
604 m_settings
.user
= OUString( p
, strlen(p
), RTL_TEXTENCODING_UTF8
);
605 p
= PQdb( m_settings
.pConnection
);
606 m_settings
.catalog
= OUString( p
, strlen(p
), RTL_TEXTENCODING_UTF8
);
609 if( isLog( &m_settings
, LogLevel::INFO
) )
611 OUStringBuffer
buf( 128 );
612 buf
.appendAscii( "connection to '" );
614 buf
.appendAscii( "' successfully opened" );
615 log( &m_settings
, LogLevel::INFO
, buf
.makeStringAndClear() );
619 void Connection::disposing()
624 void Connection::checkClosed() throw ( SQLException
, RuntimeException
)
626 if( !m_settings
.pConnection
)
627 throw SQLException( "pq_connection: Connection already closed",
628 *this, OUString(), 1, Any() );
631 Reference
< XNameAccess
> Connection::getTables()
632 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
634 if( isLog( &m_settings
, LogLevel::INFO
) )
636 log( &m_settings
, LogLevel::INFO
, "Connection::getTables() got called" );
638 MutexGuard
guard( m_refMutex
->mutex
);
639 if( !m_settings
.tables
.is() )
640 m_settings
.tables
= Tables::create( m_refMutex
, this, &m_settings
, &m_settings
.pTablesImpl
);
642 // TODO: how to overcome the performance problem ?
643 Reference
< com::sun::star::util::XRefreshable
> ( m_settings
.tables
, UNO_QUERY
)->refresh();
644 return m_settings
.tables
;
647 Reference
< XNameAccess
> Connection::getViews()
648 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
650 if( isLog( &m_settings
, LogLevel::INFO
) )
652 log( &m_settings
, LogLevel::INFO
, "Connection::getViews() got called" );
654 MutexGuard
guard( m_refMutex
->mutex
);
655 if( !m_settings
.views
.is() )
656 m_settings
.views
= Views::create( m_refMutex
, this, &m_settings
, &(m_settings
.pViewsImpl
) );
658 // TODO: how to overcome the performance problem ?
659 Reference
< com::sun::star::util::XRefreshable
> ( m_settings
.views
, UNO_QUERY
)->refresh();
660 return m_settings
.views
;
665 Reference
< XNameAccess
> Connection::getUsers()
666 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
668 if( isLog( &m_settings
, LogLevel::INFO
) )
670 log( &m_settings
, LogLevel::INFO
, "Connection::getUsers() got called" );
673 MutexGuard
guard( m_refMutex
->mutex
);
674 if( !m_settings
.users
.is() )
675 m_settings
.users
= Users::create( m_refMutex
, this, &m_settings
);
676 return m_settings
.users
;
680 Reference
< XInterface
> ConnectionCreateInstance(
681 const Reference
< XComponentContext
> & ctx
) throw (Exception
)
683 ::rtl::Reference
< RefCountedMutex
> ref
= new RefCountedMutex();
684 return * new Connection( ref
, ctx
);
689 bool isLog( ConnectionSettings
*settings
, int loglevel
)
691 return settings
->loglevel
>= loglevel
&& settings
->logFile
;
694 void log( ConnectionSettings
*settings
, sal_Int32 level
, const OUString
&logString
)
696 log( settings
, level
, OUStringToOString( logString
, settings
->encoding
).getStr() );
698 void log( ConnectionSettings
*settings
, sal_Int32 level
, const char *str
)
700 if( isLog( settings
, level
) )
702 static const char *strLevel
[] = { "NONE", "ERROR", "SQL", "INFO", "DATA" };
704 time_t t
= ::time( 0 );
707 pString
= asctime( localtime( &t
) );
711 memset( timestr
, 0 , 50);
713 ::localtime_r( &t
, ×truc
);
714 asctime_r( ×truc
, timestr
);
716 for( int i
= 0 ; pString
[i
] ; i
++ )
718 if( pString
[i
] <= 13 )
724 fprintf( settings
->logFile
, "%s [%s]: %s\n", pString
, strLevel
[level
], str
);
733 static const struct cppu::ImplementationEntry g_entries
[] =
736 pq_sdbc_driver::ConnectionCreateInstance
, pq_sdbc_driver::ConnectionGetImplementationName
,
737 pq_sdbc_driver::ConnectionGetSupportedServiceNames
, cppu::createSingleComponentFactory
,
747 SAL_DLLPUBLIC_EXPORT
void * SAL_CALL
postgresql_sdbc_impl_component_getFactory(
748 const sal_Char
* pImplName
, void * pServiceManager
, void * pRegistryKey
)
750 return cppu::component_getFactoryHelper( pImplName
, pServiceManager
, pRegistryKey
, g_entries
);
755 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */