1 /*************************************************************************
3 * $RCSfile: pq_connection.cxx,v $
7 * last change: $Author: jbu $ $Date: 2007/01/07 13:50:37 $
9 * The Contents of this file are made available subject to the terms of
10 * either of the following licenses
12 * - GNU Lesser General Public License Version 2.1
13 * - Sun Industry Standards Source License Version 1.1
15 * Sun Microsystems Inc., October, 2000
17 * GNU Lesser General Public License Version 2.1
18 * =============================================
19 * Copyright 2000 by Sun Microsystems, Inc.
20 * 901 San Antonio Road, Palo Alto, CA 94303, USA
22 * This library is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU Lesser General Public
24 * License version 2.1, as published by the Free Software Foundation.
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * Lesser General Public License for more details.
31 * You should have received a copy of the GNU Lesser General Public
32 * License along with this library; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
37 * Sun Industry Standards Source License Version 1.1
38 * =================================================
39 * The contents of this file are subject to the Sun Industry Standards
40 * Source License Version 1.1 (the "License"); You may not use this file
41 * except in compliance with the License. You may obtain a copy of the
42 * License at http://www.openoffice.org/license.html.
44 * Software provided under this License is provided on an "AS IS" basis,
45 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
46 * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
47 * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
48 * See the License for the specific provisions governing your rights and
49 * obligations concerning the Software.
51 * The Initial Developer of the Original Code is: Joerg Budischewski
53 * Copyright: 2000 by Sun Microsystems, Inc.
55 * All Rights Reserved.
57 * Contributor(s): Joerg Budischewski
60 ************************************************************************/
67 #include "pq_connection.hxx"
68 #include "pq_statement.hxx"
69 #include "pq_preparedstatement.hxx"
70 #include "pq_databasemetadata.hxx"
71 #include "pq_xcontainer.hxx"
72 #include "pq_statics.hxx"
73 #include "pq_xtables.hxx"
74 #include "pq_xviews.hxx"
75 #include "pq_xusers.hxx"
77 #include <rtl/ustrbuf.hxx>
78 #include <rtl/strbuf.hxx>
80 #include <rtl/bootstrap.hxx>
81 #include <osl/module.h>
83 #include <cppuhelper/implementationentry.hxx>
84 #include <cppuhelper/implbase1.hxx>
86 #include <com/sun/star/beans/PropertyValue.hpp>
87 #include <com/sun/star/sdbc/XRow.hpp>
89 using rtl::OUStringBuffer
;
92 using rtl::OStringBuffer
;
93 using rtl::OUStringToOString
;
94 using osl::MutexGuard
;
96 using com::sun::star::container::XNameAccess
;
98 using com::sun::star::lang::XComponent
;
99 using com::sun::star::lang::XInitialization
;
100 using com::sun::star::lang::IllegalArgumentException
;
102 using com::sun::star::script::XTypeConverter
;
104 using com::sun::star::uno::RuntimeException
;
105 using com::sun::star::uno::Exception
;
106 using com::sun::star::uno::Sequence
;
107 using com::sun::star::uno::Reference
;
108 using com::sun::star::uno::XInterface
;
109 using com::sun::star::uno::UNO_QUERY
;
110 using com::sun::star::uno::XComponentContext
;
111 using com::sun::star::uno::Any
;
112 using com::sun::star::uno::makeAny
;
114 using com::sun::star::beans::PropertyValue
;
115 using com::sun::star::beans::XPropertySet
;
117 using com::sun::star::sdbc::XConnection
;
118 using com::sun::star::sdbc::XResultSet
;
119 using com::sun::star::sdbc::XRow
;
120 using com::sun::star::sdbc::XCloseable
;
121 using com::sun::star::sdbc::SQLException
;
122 using com::sun::star::sdbc::XWarningsSupplier
;
123 using com::sun::star::sdbc::XPreparedStatement
;
124 using com::sun::star::sdbc::XStatement
;
125 using com::sun::star::sdbc::XDatabaseMetaData
;
127 namespace pq_sdbc_driver
130 #define ASCII_STR(x) OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
133 // ______________________________________________________________________________
134 // Helper class for statement lifetime management
135 class ClosableReference
: public cppu::WeakImplHelper1
< com::sun::star::uno::XReference
>
138 ::rtl::ByteSequence m_id
;
140 ClosableReference( const ::rtl::ByteSequence
& id
, Connection
*that
)
141 : m_id( id
), m_conn( that
)
146 virtual ~ClosableReference()
152 virtual void SAL_CALL
dispose() throw ()
156 m_conn
->removeFromWeakMap(m_id
);
163 OUString
ConnectionGetImplementationName()
165 return OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.comp.connectivity.pq.Connection" ) );
167 com::sun::star::uno::Sequence
<rtl::OUString
> ConnectionGetSupportedServiceNames(void)
169 OUString
serv( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.sdbc.Connection" ) );
170 return Sequence
< OUString
> (&serv
,1);
173 static sal_Int32
readLogLevelFromConfiguration()
175 sal_Int32 loglevel
= LogLevel::NONE
;
177 osl_getModuleURLFromAddress(
178 (void*) readLogLevelFromConfiguration
, (rtl_uString
**) &fileName
);
179 fileName
= OUString( fileName
.getStr(), fileName
.lastIndexOf( '/' )+1 );
180 fileName
+= OUString::createFromAscii( SAL_CONFIGFILE("postgresql-sdbc.uno") );
181 rtl::Bootstrap
bootstrapHandle( fileName
);
184 if( bootstrapHandle
.getFrom( ASCII_STR( "PQ_LOGLEVEL" ), str
) )
186 if( str
.equalsAscii( "NONE" ) )
187 loglevel
= LogLevel::NONE
;
188 else if( str
.equalsAscii( "ERROR" ) )
189 loglevel
= LogLevel::ERROR
;
190 else if( str
.equalsAscii( "SQL" ) )
191 loglevel
= LogLevel::SQL
;
192 else if( str
.equalsAscii( "INFO" ) )
193 loglevel
= LogLevel::SQL
;
196 fprintf( stderr
, "unknown loglevel %s\n",
197 OUStringToOString( str
, RTL_TEXTENCODING_UTF8
).getStr() );
203 Connection::Connection(
204 const rtl::Reference
< RefCountedMutex
> &refMutex
,
205 const ::com::sun::star::uno::Reference
< ::com::sun::star::uno::XComponentContext
> & ctx
)
206 : ConnectionBase( refMutex
->mutex
),
208 m_refMutex( refMutex
)
210 m_settings
.loglevel
= readLogLevelFromConfiguration();
212 if( m_settings
.loglevel
> LogLevel::NONE
)
214 m_settings
.logFile
= fopen( "sdbc-pqsql.log", "a" );
215 if( m_settings
.logFile
)
217 setvbuf( m_settings
.logFile
, 0, _IONBF
, 0 );
218 log( &m_settings
, m_settings
.loglevel
, "set this loglevel" );
222 fprintf( stderr
, "Couldn't open sdbc-pqsql.log file\n" );
227 Connection::~Connection()
229 POSTGRE_TRACE( "dtor connection" );
230 if( m_settings
.pConnection
)
232 PQfinish( m_settings
.pConnection
);
233 m_settings
.pConnection
= 0;
235 if( m_settings
.logFile
)
237 fclose( m_settings
.logFile
);
238 m_settings
.logFile
= 0;
241 typedef ::std::list
< ::com::sun::star::uno::Reference
< ::com::sun::star::sdbc::XCloseable
> ,
242 ::pq_sdbc_driver::Allocator
< ::com::sun::star::uno::Reference
< ::com::sun::star::sdbc::XCloseable
> > > CloseableList
;
244 typedef ::std::list
< ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XComponent
> ,
245 ::pq_sdbc_driver::Allocator
< ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XComponent
> > > DisposeableList
;
247 void Connection::close() throw ( SQLException
, RuntimeException
)
250 DisposeableList lstDispose
;
252 MutexGuard
guard( m_refMutex
->mutex
);
253 // silently ignore, if the connection has been closed already
254 if( m_settings
.pConnection
)
256 log( &m_settings
, LogLevel::INFO
, "closing connection" );
257 PQfinish( m_settings
.pConnection
);
258 m_settings
.pConnection
= 0;
261 lstDispose
.push_back( Reference
< XComponent
> ( m_settings
.users
, UNO_QUERY
) );
262 lstDispose
.push_back( Reference
< XComponent
> ( m_settings
.tables
, UNO_QUERY
) );
263 lstDispose
.push_back( Reference
< XComponent
> ( m_meta
, UNO_QUERY
) );
265 m_settings
.tables
.clear();
266 m_settings
.users
.clear();
268 for( WeakHashMap::iterator ii
= m_myStatements
.begin() ;
269 ii
!= m_myStatements
.end() ;
272 Reference
< XCloseable
> r
= ii
->second
;
278 // close all created statements
279 for( CloseableList::iterator ii
= lst
.begin(); ii
!= lst
.end() ; ++ii
)
282 // close all created statements
283 for( DisposeableList::iterator iiDispose
= lstDispose
.begin();
284 iiDispose
!= lstDispose
.end() ; ++iiDispose
)
286 if( iiDispose
->is() )
287 iiDispose
->get()->dispose();
292 void Connection::removeFromWeakMap( const ::rtl::ByteSequence
& id
)
295 MutexGuard
guard( m_refMutex
->mutex
);
296 WeakHashMap::iterator ii
= m_myStatements
.find( id
);
297 if( ii
!= m_myStatements
.end() )
298 m_myStatements
.erase( ii
);
301 Reference
< XStatement
> Connection::createStatement( ) throw (SQLException
, RuntimeException
)
303 MutexGuard
guard( m_refMutex
->mutex
);
306 Statement
*stmt
= new Statement( m_refMutex
, this , &m_settings
);
307 Reference
< XStatement
> ret( stmt
);
308 ::rtl::ByteSequence
id( 16 );
309 rtl_createUuid( (sal_uInt8
*) id
.getConstArray(), 0 , sal_False
);
310 m_myStatements
[ id
] = Reference
< XCloseable
> ( stmt
);
311 stmt
->queryAdapter()->addReference( new ClosableReference( id
, this ) );
315 Reference
< XPreparedStatement
> Connection::prepareStatement( const ::rtl::OUString
& sql
)
316 throw (SQLException
, RuntimeException
)
318 MutexGuard
guard( m_refMutex
->mutex
);
321 rtl::OString byteSql
= OUStringToOString( sql
, m_settings
.encoding
);
322 PreparedStatement
*stmt
= new PreparedStatement( m_refMutex
, this, &m_settings
, byteSql
);
323 Reference
< XPreparedStatement
> ret
= stmt
;
325 ::rtl::ByteSequence
id( 16 );
326 rtl_createUuid( (sal_uInt8
*) id
.getConstArray(), 0 , sal_False
);
327 m_myStatements
[ id
] = Reference
< XCloseable
> ( stmt
);
328 stmt
->queryAdapter()->addReference( new ClosableReference( id
, this ) );
332 Reference
< XPreparedStatement
> Connection::prepareCall( const ::rtl::OUString
& sql
)
333 throw (SQLException
, RuntimeException
)
337 RTL_CONSTASCII_USTRINGPARAM( "pq_driver: Callable statements not supported" ) ),
338 Reference
< XInterface
> (), OUString() , 1, Any() );
342 ::rtl::OUString
Connection::nativeSQL( const ::rtl::OUString
& sql
)
343 throw (SQLException
, RuntimeException
)
348 void Connection::setAutoCommit( sal_Bool autoCommit
) throw (SQLException
, RuntimeException
)
353 sal_Bool
Connection::getAutoCommit( ) throw (SQLException
, RuntimeException
)
359 void Connection::commit( ) throw (SQLException
, RuntimeException
)
364 void Connection::rollback( ) throw (SQLException
, RuntimeException
)
369 sal_Bool
Connection::isClosed( ) throw (SQLException
, RuntimeException
)
371 return m_settings
.pConnection
== 0;
374 Reference
< XDatabaseMetaData
> Connection::getMetaData( )
375 throw (SQLException
, RuntimeException
)
377 MutexGuard
guard( m_refMutex
->mutex
);
380 m_meta
= new DatabaseMetaData( m_refMutex
, this, &m_settings
);
384 void Connection::setReadOnly( sal_Bool readOnly
) throw (SQLException
, RuntimeException
)
390 sal_Bool
Connection::isReadOnly( ) throw (SQLException
, RuntimeException
)
396 void Connection::setCatalog( const ::rtl::OUString
& catalog
)
397 throw (SQLException
, RuntimeException
)
402 ::rtl::OUString
Connection::getCatalog( ) throw (SQLException
, RuntimeException
)
405 MutexGuard ( m_refMutex
->mutex
);
406 if( m_settings
.pConnection
== 0 )
408 throw SQLException( ASCII_STR( "pq_connection: connection is closed" ), *this,
409 OUString(), 1, Any() );
411 char * p
= PQdb(m_settings
.pConnection
);
412 return OUString( p
, strlen(p
) , m_settings
.encoding
);
415 void Connection::setTransactionIsolation( sal_Int32 level
)
416 throw (SQLException
, RuntimeException
)
421 sal_Int32
Connection::getTransactionIsolation( ) throw (SQLException
, RuntimeException
)
427 Reference
< XNameAccess
> Connection::getTypeMap( ) throw (SQLException
, RuntimeException
)
429 Reference
< XNameAccess
> t
;
431 MutexGuard
guard( m_refMutex
->mutex
);
437 void Connection::setTypeMap( const Reference
< XNameAccess
>& typeMap
)
438 throw (SQLException
, RuntimeException
)
440 MutexGuard
guard( m_refMutex
->mutex
);
443 Any
Connection::getWarnings( ) throw (SQLException
, RuntimeException
)
448 void Connection::clearWarnings( ) throw (SQLException
, RuntimeException
)
453 static OString
properties2String( const OString initialString
,
454 const Sequence
< PropertyValue
> & args
,
455 const Reference
< XTypeConverter
> &tc
)
459 ret
.append( initialString
);
460 if( initialString
.getLength() )
463 bool initial
= false;
464 for( int i
= 0; i
< args
.getLength() ; i
++)
467 if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "password" ) ) )
469 ret
.append( "password=" );
471 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "user" ) ) )
473 ret
.append( "user=" );
475 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "port" ) ) )
477 ret
.append( "port=" );
479 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "dbname" ) ) )
481 ret
.append( "dbname=" );
483 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "connect_timeout" ) ) )
485 ret
.append( "connect_timeout=" );
487 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "options" ) ) )
489 ret
.append( "options=" );
491 else if( args
[i
].Name
.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "requiressl" ) ) )
493 ret
.append( "requiressl=" );
503 tc
->convertTo( args
[i
].Value
,getCppuType( &value
) ) >>= value
;
504 ret
.append( OUStringToOString( value
, RTL_TEXTENCODING_UTF8
) );
509 return ret
.makeStringAndClear();
512 void Connection::initialize( const Sequence
< Any
>& aArguments
)
513 throw (Exception
, RuntimeException
)
516 Sequence
< PropertyValue
> args
;
518 Reference
< XTypeConverter
> tc( m_ctx
->getServiceManager()->createInstanceWithContext(
519 OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.script.Converter" ) ), m_ctx
),
523 throw RuntimeException(
524 OUString( RTL_CONSTASCII_USTRINGPARAM("pq_driver: Couldn't instantiate converter service" )),
525 Reference
< XInterface
> () );
527 if( aArguments
.getLength() != 2 )
529 OUStringBuffer
buf(128);
530 buf
.appendAscii( "pq_driver: expected 2 arguments, got " );
531 buf
.append( aArguments
.getLength( ) );
532 throw IllegalArgumentException(buf
.makeStringAndClear(), Reference
< XInterface
> () , 0 );
535 if( ! (aArguments
[0] >>= url
) )
537 OUStringBuffer
buf(128);
538 buf
.appendAscii( "pq_driver: expected string as first argument, got " );
539 buf
.append( aArguments
[0].getValueType().getTypeName() );
540 throw IllegalArgumentException( buf
.makeStringAndClear() , *this, 0 );
543 tc
->convertTo( aArguments
[1], getCppuType( &args
) ) >>= args
;
546 int nColon
= url
.indexOf( ':' );
549 nColon
= url
.indexOf( ':' , 1+ nColon
);
552 o
= OUStringToOString( url
.getStr()+nColon
+1, m_settings
.encoding
);
555 o
= properties2String( o
, args
, tc
);
557 m_settings
.pConnection
= PQconnectdb( o
.getStr() );
558 if( ! m_settings
.pConnection
)
559 throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM( "pq_driver: out of memory" ) ),
560 Reference
< XInterface
> () );
561 if( PQstatus( m_settings
.pConnection
) == CONNECTION_BAD
)
563 OUStringBuffer
buf( 128 );
565 const char * error
= PQerrorMessage( m_settings
.pConnection
);
566 OUString
errorMessage( error
, strlen( error
) , RTL_TEXTENCODING_ASCII_US
);
567 buf
.appendAscii( "Couldn't establish database connection to '" );
569 buf
.appendAscii( "' (" );
570 buf
.append( errorMessage
);
571 buf
.appendAscii( ")" );
572 PQfinish( m_settings
.pConnection
);
573 m_settings
.pConnection
= 0;
574 throw SQLException( buf
.makeStringAndClear(), *this, errorMessage
, CONNECTION_BAD
, Any() );
576 PQsetClientEncoding( m_settings
.pConnection
, "UNICODE" );
577 char *p
= PQuser( m_settings
.pConnection
);
578 m_settings
.user
= OUString( p
, strlen(p
), RTL_TEXTENCODING_UTF8
);
579 p
= PQdb( m_settings
.pConnection
);
580 m_settings
.catalog
= OUString( p
, strlen(p
), RTL_TEXTENCODING_UTF8
);
583 if( isLog( &m_settings
, LogLevel::INFO
) )
585 OUStringBuffer
buf( 128 );
586 buf
.appendAscii( "connection to '" );
588 buf
.appendAscii( "' successfully opened" );
589 log( &m_settings
, LogLevel::INFO
, buf
.makeStringAndClear() );
593 void Connection::disposing()
598 void Connection::checkClosed() throw ( SQLException
, RuntimeException
)
600 if( !m_settings
.pConnection
)
601 throw SQLException( ASCII_STR( "pq_connection: Connection already closed" ),
602 *this, OUString(), 1, Any() );
605 Reference
< XNameAccess
> Connection::getTables( )
606 throw (::com::sun::star::uno::RuntimeException
)
608 if( isLog( &m_settings
, LogLevel::INFO
) )
610 log( &m_settings
, LogLevel::INFO
, "Connection::getTables() got called" );
612 MutexGuard
guard( m_refMutex
->mutex
);
613 if( !m_settings
.tables
.is() )
614 m_settings
.tables
= Tables::create( m_refMutex
, this, &m_settings
, &m_settings
.pTablesImpl
);
616 // TODO: how to overcome the performance problem ?
617 Reference
< com::sun::star::util::XRefreshable
> ( m_settings
.tables
, UNO_QUERY
)->refresh();
618 return m_settings
.tables
;
621 Reference
< XNameAccess
> Connection::getViews( )
622 throw (::com::sun::star::uno::RuntimeException
)
624 if( isLog( &m_settings
, LogLevel::INFO
) )
626 log( &m_settings
, LogLevel::INFO
, "Connection::getViews() got called" );
628 MutexGuard
guard( m_refMutex
->mutex
);
629 if( !m_settings
.views
.is() )
630 m_settings
.views
= Views::create( m_refMutex
, this, &m_settings
, &(m_settings
.pViewsImpl
) );
632 // TODO: how to overcome the performance problem ?
633 Reference
< com::sun::star::util::XRefreshable
> ( m_settings
.views
, UNO_QUERY
)->refresh();
634 return m_settings
.views
;
639 Reference
< XNameAccess
> Connection::getUsers( )
640 throw (::com::sun::star::uno::RuntimeException
)
642 if( isLog( &m_settings
, LogLevel::INFO
) )
644 log( &m_settings
, LogLevel::INFO
, "Connection::getUsers() got called" );
647 MutexGuard
guard( m_refMutex
->mutex
);
648 if( !m_settings
.users
.is() )
649 m_settings
.users
= Users::create( m_refMutex
, this, &m_settings
);
650 return m_settings
.users
;
654 Reference
< XInterface
> ConnectionCreateInstance(
655 const Reference
< XComponentContext
> & ctx
) throw (Exception
)
657 ::rtl::Reference
< RefCountedMutex
> ref
= new RefCountedMutex();
658 return * new Connection( ref
, ctx
);
663 bool isLog( ConnectionSettings
*settings
, int loglevel
)
665 return settings
->loglevel
>= loglevel
&& settings
->logFile
;
668 void log( ConnectionSettings
*settings
, sal_Int32 level
, const OUString
&logString
)
670 log( settings
, level
, OUStringToOString( logString
, settings
->encoding
).getStr() );
672 void log( ConnectionSettings
*settings
, sal_Int32 level
, const char *str
)
674 if( isLog( settings
, level
) )
676 static const char *strLevel
[] = { "NONE", "ERROR", "SQL", "INFO", "DATA" };
678 time_t t
= ::time( 0 );
681 pString
= asctime( localtime( &t
) );
685 memset( timestr
, 0 , 50);
687 ::localtime_r( &t
, ×truc
);
688 asctime_r( ×truc
, timestr
);
690 for( int i
= 0 ; pString
[i
] ; i
++ )
692 if( pString
[i
] <= 13 )
698 fprintf( settings
->logFile
, "%s [%s]: %s\n", pString
, strLevel
[level
], str
);
707 static struct cppu::ImplementationEntry g_entries
[] =
710 pq_sdbc_driver::ConnectionCreateInstance
, pq_sdbc_driver::ConnectionGetImplementationName
,
711 pq_sdbc_driver::ConnectionGetSupportedServiceNames
, cppu::createSingleComponentFactory
,
721 //==================================================================================================
722 void SAL_CALL
component_getImplementationEnvironment(
723 const sal_Char
** ppEnvTypeName
, uno_Environment
** ppEnv
)
725 *ppEnvTypeName
= CPPU_CURRENT_LANGUAGE_BINDING_NAME
;
727 //==================================================================================================
728 sal_Bool SAL_CALL
component_writeInfo(
729 void * pServiceManager
, void * pRegistryKey
)
731 return cppu::component_writeInfoHelper( pServiceManager
, pRegistryKey
, g_entries
);
733 //==================================================================================================
734 void * SAL_CALL
component_getFactory(
735 const sal_Char
* pImplName
, void * pServiceManager
, void * pRegistryKey
)
737 return cppu::component_getFactoryHelper( pImplName
, pServiceManager
, pRegistryKey
, g_entries
);