Line data Source code
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 : * Version: MPL 1.1 / GPLv3+ / LGPLv2.1+
32 : *
33 : * The contents of this file are subject to the Mozilla Public License Version
34 : * 1.1 (the "License"); you may not use this file except in compliance with
35 : * the License or as specified alternatively below. You may obtain a copy of
36 : * the License at http://www.mozilla.org/MPL/
37 : *
38 : * Software distributed under the License is distributed on an "AS IS" basis,
39 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
40 : * for the specific language governing rights and limitations under the
41 : * License.
42 : *
43 : * Major Contributor(s):
44 : * [ Copyright (C) 2011 Lionel Elie Mamane <lionel@mamane.lu> ]
45 : *
46 : * All Rights Reserved.
47 : *
48 : * For minor contributions see the git repository.
49 : *
50 : * Alternatively, the contents of this file may be used under the terms of
51 : * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
52 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPLv2.1+"),
53 : * in which case the provisions of the GPLv3+ or the LGPLv2.1+ are applicable
54 : * instead of those above.
55 : *
56 : ************************************************************************/
57 :
58 : #include <list>
59 : #include <stdio.h>
60 : #include <time.h>
61 : #include <string.h>
62 :
63 : #include <boost/shared_ptr.hpp>
64 :
65 : #include "pq_connection.hxx"
66 : #include "pq_statement.hxx"
67 : #include "pq_preparedstatement.hxx"
68 : #include "pq_databasemetadata.hxx"
69 : #include "pq_xcontainer.hxx"
70 : #include "pq_statics.hxx"
71 : #include "pq_xtables.hxx"
72 : #include "pq_xviews.hxx"
73 : #include "pq_xusers.hxx"
74 :
75 : #include <rtl/ustrbuf.hxx>
76 : #include <rtl/strbuf.hxx>
77 : #include <rtl/uuid.h>
78 : #include <rtl/bootstrap.hxx>
79 : #include <osl/module.h>
80 :
81 : #include <cppuhelper/implementationentry.hxx>
82 : #include <cppuhelper/implbase1.hxx>
83 :
84 : #include <com/sun/star/beans/PropertyValue.hpp>
85 : #include <com/sun/star/script/Converter.hpp>
86 : #include <com/sun/star/sdbc/XRow.hpp>
87 :
88 : using rtl::OUStringBuffer;
89 : using rtl::OUString;
90 : using rtl::OString;
91 : using rtl::OStringBuffer;
92 : using rtl::OUStringToOString;
93 : using osl::MutexGuard;
94 :
95 : using com::sun::star::container::XNameAccess;
96 :
97 : using com::sun::star::lang::XComponent;
98 : using com::sun::star::lang::XInitialization;
99 : using com::sun::star::lang::IllegalArgumentException;
100 :
101 : using com::sun::star::script::Converter;
102 : using com::sun::star::script::XTypeConverter;
103 :
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;
113 :
114 : using com::sun::star::beans::PropertyValue;
115 : using com::sun::star::beans::XPropertySet;
116 :
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;
126 :
127 : namespace pq_sdbc_driver
128 : {
129 :
130 : #define ASCII_STR(x) OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
131 :
132 :
133 : // ______________________________________________________________________________
134 : // Helper class for statement lifetime management
135 : class ClosableReference : public cppu::WeakImplHelper1< com::sun::star::uno::XReference >
136 : {
137 : Connection *m_conn;
138 : ::rtl::ByteSequence m_id;
139 : public:
140 0 : ClosableReference( const ::rtl::ByteSequence & id , Connection *that )
141 0 : : m_conn( that ), m_id( id )
142 : {
143 0 : that->acquire();
144 0 : }
145 :
146 0 : virtual ~ClosableReference()
147 0 : {
148 0 : if( m_conn )
149 0 : m_conn->release();
150 0 : }
151 :
152 0 : virtual void SAL_CALL dispose() throw ()
153 : {
154 0 : if( m_conn )
155 : {
156 0 : m_conn->removeFromWeakMap(m_id);
157 0 : m_conn->release();
158 0 : m_conn = 0;
159 : }
160 0 : }
161 : };
162 :
163 0 : OUString ConnectionGetImplementationName()
164 : {
165 0 : return OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.comp.connectivity.pq.Connection.noext" ) );
166 : }
167 0 : com::sun::star::uno::Sequence<rtl::OUString> ConnectionGetSupportedServiceNames(void)
168 : {
169 0 : OUString serv( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.sdbc.Connection" ) );
170 0 : return Sequence< OUString> (&serv,1);
171 : }
172 :
173 0 : static sal_Int32 readLogLevelFromConfiguration()
174 : {
175 0 : sal_Int32 loglevel = LogLevel::NONE;
176 0 : OUString fileName;
177 : osl_getModuleURLFromAddress(
178 0 : (void*) readLogLevelFromConfiguration, (rtl_uString **) &fileName );
179 0 : fileName = OUString( fileName.getStr(), fileName.lastIndexOf( '/' )+1 );
180 0 : fileName += OUString::createFromAscii( "postgresql-sdbc.ini" );
181 0 : rtl::Bootstrap bootstrapHandle( fileName );
182 :
183 0 : OUString str;
184 0 : if( bootstrapHandle.getFrom( ASCII_STR( "PQ_LOGLEVEL" ), str ) )
185 : {
186 0 : if ( str == "NONE" )
187 0 : loglevel = LogLevel::NONE;
188 0 : else if ( str == "ERROR" )
189 0 : loglevel = LogLevel::ERROR;
190 0 : else if ( str == "SQL" )
191 0 : loglevel = LogLevel::SQL;
192 0 : else if ( str == "INFO" )
193 0 : loglevel = LogLevel::INFO;
194 : else
195 : {
196 : fprintf( stderr, "unknown loglevel %s\n",
197 0 : OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
198 : }
199 : }
200 0 : return loglevel;
201 : }
202 :
203 0 : Connection::Connection(
204 : const rtl::Reference< RefCountedMutex > &refMutex,
205 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > & ctx )
206 0 : : ConnectionBase( refMutex->mutex ),
207 : m_ctx( ctx ) ,
208 0 : m_refMutex( refMutex )
209 : {
210 0 : m_settings.loglevel = readLogLevelFromConfiguration();
211 :
212 0 : if( m_settings.loglevel > LogLevel::NONE )
213 : {
214 0 : m_settings.logFile = fopen( "sdbc-pqsql.log", "a" );
215 0 : if( m_settings.logFile )
216 : {
217 0 : setvbuf( m_settings.logFile, 0, _IONBF, 0 );
218 0 : log( &m_settings, m_settings.loglevel , "set this loglevel" );
219 : }
220 : else
221 : {
222 0 : fprintf( stderr, "Couldn't open sdbc-pqsql.log file\n" );
223 : }
224 : }
225 0 : }
226 :
227 0 : Connection::~Connection()
228 : {
229 : POSTGRE_TRACE( "dtor connection" );
230 0 : if( m_settings.pConnection )
231 : {
232 0 : PQfinish( m_settings.pConnection );
233 0 : m_settings.pConnection = 0;
234 : }
235 0 : if( m_settings.logFile )
236 : {
237 0 : fclose( m_settings.logFile );
238 0 : m_settings.logFile = 0;
239 : }
240 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;
243 :
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;
246 :
247 0 : void Connection::close() throw ( SQLException, RuntimeException )
248 : {
249 0 : CloseableList lst;
250 0 : DisposeableList lstDispose;
251 : {
252 0 : MutexGuard guard( m_refMutex->mutex );
253 : // silently ignore, if the connection has been closed already
254 0 : if( m_settings.pConnection )
255 : {
256 0 : log( &m_settings, LogLevel::INFO, "closing connection" );
257 0 : PQfinish( m_settings.pConnection );
258 0 : m_settings.pConnection = 0;
259 : }
260 :
261 0 : lstDispose.push_back( Reference< XComponent > ( m_settings.users, UNO_QUERY ) );
262 0 : lstDispose.push_back( Reference< XComponent > ( m_settings.tables , UNO_QUERY ) );
263 0 : lstDispose.push_back( Reference< XComponent > ( m_meta, UNO_QUERY ) );
264 0 : m_meta.clear();
265 0 : m_settings.tables.clear();
266 0 : m_settings.users.clear();
267 :
268 0 : for( WeakHashMap::iterator ii = m_myStatements.begin() ;
269 0 : ii != m_myStatements.end() ;
270 : ++ii )
271 : {
272 0 : Reference< XCloseable > r = ii->second;
273 0 : if( r.is() )
274 0 : lst.push_back( r );
275 0 : }
276 : }
277 :
278 : // close all created statements
279 0 : for( CloseableList::iterator ii = lst.begin(); ii != lst.end() ; ++ii )
280 0 : ii->get()->close();
281 :
282 : // close all created statements
283 0 : for( DisposeableList::iterator iiDispose = lstDispose.begin();
284 0 : iiDispose != lstDispose.end() ; ++iiDispose )
285 : {
286 0 : if( iiDispose->is() )
287 0 : iiDispose->get()->dispose();
288 0 : }
289 0 : }
290 :
291 :
292 0 : void Connection::removeFromWeakMap( const ::rtl::ByteSequence & id )
293 : {
294 : // shrink the list !
295 0 : MutexGuard guard( m_refMutex->mutex );
296 0 : WeakHashMap::iterator ii = m_myStatements.find( id );
297 0 : if( ii != m_myStatements.end() )
298 0 : m_myStatements.erase( ii );
299 0 : }
300 :
301 0 : Reference< XStatement > Connection::createStatement() throw (SQLException, RuntimeException)
302 : {
303 0 : MutexGuard guard( m_refMutex->mutex );
304 0 : checkClosed();
305 :
306 0 : Statement *stmt = new Statement( m_refMutex, this , &m_settings );
307 0 : Reference< XStatement > ret( stmt );
308 0 : ::rtl::ByteSequence id( 16 );
309 0 : rtl_createUuid( (sal_uInt8*) id.getConstArray(), 0 , sal_False );
310 0 : m_myStatements[ id ] = Reference< XCloseable > ( stmt );
311 0 : stmt->queryAdapter()->addReference( new ClosableReference( id, this ) );
312 0 : return ret;
313 : }
314 :
315 0 : Reference< XPreparedStatement > Connection::prepareStatement( const ::rtl::OUString& sql )
316 : throw (SQLException, RuntimeException)
317 : {
318 0 : MutexGuard guard( m_refMutex->mutex );
319 0 : checkClosed();
320 :
321 0 : rtl::OString byteSql = OUStringToOString( sql, m_settings.encoding );
322 0 : PreparedStatement *stmt = new PreparedStatement( m_refMutex, this, &m_settings, byteSql );
323 0 : Reference< XPreparedStatement > ret = stmt;
324 :
325 0 : ::rtl::ByteSequence id( 16 );
326 0 : rtl_createUuid( (sal_uInt8*) id.getConstArray(), 0 , sal_False );
327 0 : m_myStatements[ id ] = Reference< XCloseable > ( stmt );
328 0 : stmt->queryAdapter()->addReference( new ClosableReference( id, this ) );
329 0 : return ret;
330 : }
331 :
332 0 : Reference< XPreparedStatement > Connection::prepareCall( const ::rtl::OUString& )
333 : throw (SQLException, RuntimeException)
334 : {
335 : throw SQLException(
336 : OUString(
337 : RTL_CONSTASCII_USTRINGPARAM( "pq_driver: Callable statements not supported" ) ),
338 0 : Reference< XInterface > (), OUString() , 1, Any() );
339 : }
340 :
341 :
342 0 : ::rtl::OUString Connection::nativeSQL( const ::rtl::OUString& sql )
343 : throw (SQLException, RuntimeException)
344 : {
345 0 : return sql;
346 : }
347 :
348 0 : void Connection::setAutoCommit( sal_Bool ) throw (SQLException, RuntimeException)
349 : {
350 : // UNSUPPORTED
351 0 : }
352 :
353 0 : sal_Bool Connection::getAutoCommit() throw (SQLException, RuntimeException)
354 : {
355 : // UNSUPPORTED
356 0 : return sal_True;
357 : }
358 :
359 0 : void Connection::commit() throw (SQLException, RuntimeException)
360 : {
361 : // UNSUPPORTED
362 0 : }
363 :
364 0 : void Connection::rollback() throw (SQLException, RuntimeException)
365 : {
366 : // UNSUPPORTED
367 0 : }
368 :
369 0 : sal_Bool Connection::isClosed() throw (SQLException, RuntimeException)
370 : {
371 0 : return m_settings.pConnection == 0;
372 : }
373 :
374 0 : Reference< XDatabaseMetaData > Connection::getMetaData()
375 : throw (SQLException, RuntimeException)
376 : {
377 0 : MutexGuard guard( m_refMutex->mutex );
378 0 : checkClosed();
379 0 : if( ! m_meta.is() )
380 0 : m_meta = new DatabaseMetaData( m_refMutex, this, &m_settings );
381 0 : return m_meta;
382 : }
383 :
384 0 : void Connection::setReadOnly( sal_Bool ) throw (SQLException, RuntimeException)
385 : {
386 : // UNSUPPORTED
387 :
388 0 : }
389 :
390 0 : sal_Bool Connection::isReadOnly() throw (SQLException, RuntimeException)
391 : {
392 : // UNSUPPORTED
393 0 : return sal_False;
394 : }
395 :
396 0 : void Connection::setCatalog( const ::rtl::OUString& )
397 : throw (SQLException, RuntimeException)
398 : {
399 : // UNSUPPORTED
400 0 : }
401 :
402 0 : ::rtl::OUString Connection::getCatalog() throw (SQLException, RuntimeException)
403 : {
404 0 : OUString ret;
405 0 : MutexGuard guard( m_refMutex->mutex );
406 0 : if( m_settings.pConnection == 0 )
407 : {
408 : throw SQLException( ASCII_STR( "pq_connection: connection is closed" ), *this,
409 0 : OUString(), 1, Any() );
410 : }
411 0 : char * p = PQdb(m_settings.pConnection );
412 0 : return OUString( p, strlen(p) , m_settings.encoding );
413 : }
414 :
415 0 : void Connection::setTransactionIsolation( sal_Int32 )
416 : throw (SQLException, RuntimeException)
417 : {
418 : // UNSUPPORTED
419 0 : }
420 :
421 0 : sal_Int32 Connection::getTransactionIsolation() throw (SQLException, RuntimeException)
422 : {
423 : // UNSUPPORTED
424 0 : return 0;
425 : }
426 :
427 0 : Reference< XNameAccess > Connection::getTypeMap() throw (SQLException, RuntimeException)
428 : {
429 0 : Reference< XNameAccess > t;
430 : {
431 0 : MutexGuard guard( m_refMutex->mutex );
432 0 : t = m_typeMap;
433 : }
434 0 : return t;
435 : }
436 :
437 0 : void Connection::setTypeMap( const Reference< XNameAccess >& typeMap )
438 : throw (SQLException, RuntimeException)
439 : {
440 0 : MutexGuard guard( m_refMutex->mutex );
441 0 : m_typeMap = typeMap;
442 0 : }
443 0 : Any Connection::getWarnings() throw (SQLException, RuntimeException)
444 : {
445 0 : return Any();
446 : }
447 :
448 0 : void Connection::clearWarnings() throw (SQLException, RuntimeException)
449 : {
450 0 : }
451 :
452 : class cstr_vector
453 : {
454 : std::vector<char*> values;
455 : std::vector<bool> acquired;
456 : public:
457 0 : cstr_vector () : values(), acquired() { values.reserve(8); acquired.reserve(8); }
458 0 : ~cstr_vector ()
459 0 : {
460 : OSL_ENSURE(values.size() == acquired.size(), "pq_connection: cstr_vector values and acquired size mismatch");
461 0 : std::vector<char*>::iterator pv = values.begin();
462 0 : std::vector<bool>::iterator pa = acquired.begin();
463 0 : const std::vector<char*>::iterator pve = values.end();
464 0 : for( ; pv < pve ; ++pv, ++pa )
465 0 : if (*pa)
466 0 : free(*pv);
467 0 : }
468 0 : void push_back(const char* s, __sal_NoAcquire)
469 : {
470 0 : values.push_back(const_cast<char*>(s));
471 0 : acquired.push_back(false);
472 0 : }
473 0 : void push_back(char* s)
474 : {
475 0 : values.push_back(s);
476 0 : acquired.push_back(true);
477 0 : }
478 : // This const_cast is there for compatibility with PostgreSQL <= 9.1;
479 : // PostgreSQL >= 9.2 has the right const qualifiers in the headers
480 : // for a return type of "char const*const*".
481 0 : char const** c_array() const { return const_cast <const char**>(&values[0]); }
482 : };
483 :
484 0 : static void properties2arrays( const Sequence< PropertyValue > & args,
485 : const Reference< XTypeConverter> &tc,
486 : rtl_TextEncoding enc,
487 : cstr_vector &keywords,
488 : cstr_vector &values)
489 : {
490 : // LEM TODO: can we just blindly take all properties?
491 : // I.e. they are prefiltered to have only relevant ones?
492 : // Else, at least support all keywords from
493 : // http://www.postgresql.org/docs/9.0/interactive/libpq-connect.html
494 0 : for( int i = 0; i < args.getLength() ; ++i )
495 : {
496 0 : bool append = true;
497 : // TODO: rewrite this as a static table of keywords, and a loop over these keywords.
498 0 : if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "password" ) ) )
499 : {
500 0 : keywords.push_back( "password", SAL_NO_ACQUIRE );
501 : }
502 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "user" ) ) )
503 : {
504 0 : keywords.push_back( "user", SAL_NO_ACQUIRE );
505 : }
506 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "port" ) ) )
507 : {
508 0 : keywords.push_back( "port", SAL_NO_ACQUIRE );
509 : }
510 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "dbname" ) ) )
511 : {
512 0 : keywords.push_back( "dbname", SAL_NO_ACQUIRE );
513 : }
514 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "connect_timeout" ) ) )
515 : {
516 0 : keywords.push_back( "connect_timeout", SAL_NO_ACQUIRE );
517 : }
518 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "options" ) ) )
519 : {
520 0 : keywords.push_back( "options", SAL_NO_ACQUIRE );
521 : }
522 0 : else if( args[i].Name.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "requiressl" ) ) )
523 : {
524 0 : keywords.push_back( "requiressl", SAL_NO_ACQUIRE );
525 : }
526 : else
527 : {
528 0 : append = false;
529 : // ignore for now
530 : OSL_TRACE("sdbc-postgresql: unknown argument '%s'", ::rtl::OUStringToOString( args[i].Name, RTL_TEXTENCODING_UTF8 ).getStr() );
531 : }
532 0 : if( append )
533 : {
534 0 : OUString value;
535 0 : tc->convertTo( args[i].Value, getCppuType( &value) ) >>= value;
536 0 : char *v = strdup(rtl::OUStringToOString(value, enc).getStr());
537 0 : values.push_back ( v );
538 : }
539 : }
540 0 : }
541 :
542 0 : void Connection::initialize( const Sequence< Any >& aArguments )
543 : throw (Exception, RuntimeException)
544 : {
545 0 : OUString url;
546 0 : Sequence< PropertyValue > args;
547 :
548 0 : Reference< XTypeConverter > tc( Converter::create(m_ctx) );
549 0 : if( ! tc.is() )
550 : {
551 : throw RuntimeException(
552 : OUString( RTL_CONSTASCII_USTRINGPARAM("pq_driver: Couldn't instantiate converter service" )),
553 0 : Reference< XInterface > () );
554 : }
555 0 : if( aArguments.getLength() != 2 )
556 : {
557 0 : OUStringBuffer buf(128);
558 0 : buf.appendAscii( "pq_driver: expected 2 arguments, got " );
559 0 : buf.append( aArguments.getLength( ) );
560 0 : throw IllegalArgumentException(buf.makeStringAndClear(), Reference< XInterface > () , 0 );
561 : }
562 :
563 0 : if( ! (aArguments[0] >>= url) )
564 : {
565 0 : OUStringBuffer buf(128);
566 0 : buf.appendAscii( "pq_driver: expected string as first argument, got " );
567 0 : buf.append( aArguments[0].getValueType().getTypeName() );
568 0 : throw IllegalArgumentException( buf.makeStringAndClear() , *this, 0 );
569 : }
570 :
571 0 : tc->convertTo( aArguments[1], getCppuType( &args ) ) >>= args;
572 :
573 0 : OString o;
574 0 : int nColon = url.indexOf( ':' );
575 0 : if( nColon != -1 )
576 : {
577 0 : nColon = url.indexOf( ':' , 1+ nColon );
578 0 : if( nColon != -1 )
579 : {
580 0 : o = OUStringToOString( url.getStr()+nColon+1, m_settings.encoding );
581 : }
582 : }
583 : {
584 0 : cstr_vector keywords;
585 0 : cstr_vector values;
586 :
587 0 : if ( o.getLength() > 0 )
588 : {
589 : char *err;
590 0 : boost::shared_ptr<PQconninfoOption> oOpts(PQconninfoParse(o.getStr(), &err), PQconninfoFree);
591 0 : if ( oOpts.get() == NULL )
592 : {
593 0 : OUString errorMessage;
594 0 : if ( err != NULL)
595 : {
596 0 : errorMessage = OUString( err, strlen(err), m_settings.encoding );
597 0 : free(err);
598 : }
599 : else
600 0 : errorMessage = OUString(RTL_CONSTASCII_USTRINGPARAM("#no error message#"));
601 0 : OUStringBuffer buf( 128 );
602 0 : buf.appendAscii( "Error in database URL '" );
603 0 : buf.append( url );
604 0 : buf.appendAscii( "':\n" );
605 0 : buf.append( errorMessage );
606 : // HY092 is "Invalid attribute/option identifier."
607 : // Just the most likely error; the error might be HY024 "Invalid attribute value".
608 0 : throw SQLException( buf.makeStringAndClear(), *this, OUString(RTL_CONSTASCII_USTRINGPARAM("HY092")), 5, Any() );
609 : }
610 :
611 0 : for ( PQconninfoOption * opt = oOpts.get(); opt->keyword != NULL; ++opt)
612 : {
613 0 : if ( opt->val != NULL )
614 : {
615 0 : keywords.push_back(strdup(opt->keyword));
616 0 : values.push_back(strdup(opt->val));
617 : }
618 0 : }
619 : }
620 0 : properties2arrays( args , tc, m_settings.encoding, keywords, values );
621 0 : keywords.push_back(NULL, SAL_NO_ACQUIRE);
622 0 : values.push_back(NULL, SAL_NO_ACQUIRE);
623 :
624 0 : m_settings.pConnection = PQconnectdbParams( keywords.c_array(), values.c_array(), 0 );
625 : }
626 0 : if( ! m_settings.pConnection )
627 : throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM( "pq_driver: out of memory" ) ),
628 0 : Reference< XInterface > () );
629 0 : if( PQstatus( m_settings.pConnection ) == CONNECTION_BAD )
630 : {
631 0 : OUStringBuffer buf( 128 );
632 :
633 0 : const char * error = PQerrorMessage( m_settings.pConnection );
634 0 : OUString errorMessage( error, strlen( error) , RTL_TEXTENCODING_ASCII_US );
635 0 : buf.appendAscii( "Couldn't establish database connection to '" );
636 0 : buf.append( url );
637 0 : buf.appendAscii( "'\n" );
638 0 : buf.append( errorMessage );
639 0 : PQfinish( m_settings.pConnection );
640 0 : m_settings.pConnection = 0;
641 0 : throw SQLException( buf.makeStringAndClear(), *this, errorMessage, CONNECTION_BAD, Any() );
642 : }
643 0 : PQsetClientEncoding( m_settings.pConnection, "UNICODE" );
644 0 : char *p = PQuser( m_settings.pConnection );
645 0 : m_settings.user = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
646 0 : p = PQdb( m_settings.pConnection );
647 0 : m_settings.catalog = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8);
648 0 : m_settings.tc = tc;
649 :
650 0 : if( isLog( &m_settings, LogLevel::INFO ) )
651 : {
652 0 : OUStringBuffer buf( 128 );
653 0 : buf.appendAscii( "connection to '" );
654 0 : buf.append( url );
655 0 : buf.appendAscii( "' successfully opened" );
656 0 : log( &m_settings, LogLevel::INFO, buf.makeStringAndClear() );
657 0 : }
658 0 : }
659 :
660 0 : void Connection::disposing()
661 : {
662 0 : close();
663 0 : }
664 :
665 0 : void Connection::checkClosed() throw ( SQLException, RuntimeException )
666 : {
667 0 : if( !m_settings.pConnection )
668 : throw SQLException( ASCII_STR( "pq_connection: Connection already closed" ),
669 0 : *this, OUString(), 1, Any() );
670 0 : }
671 :
672 0 : Reference< XNameAccess > Connection::getTables()
673 : throw (::com::sun::star::uno::RuntimeException)
674 : {
675 0 : if( isLog( &m_settings, LogLevel::INFO ) )
676 : {
677 0 : log( &m_settings, LogLevel::INFO, "Connection::getTables() got called" );
678 : }
679 0 : MutexGuard guard( m_refMutex->mutex );
680 0 : if( !m_settings.tables.is() )
681 0 : m_settings.tables = Tables::create( m_refMutex, this, &m_settings , &m_settings.pTablesImpl);
682 : else
683 : // TODO: how to overcome the performance problem ?
684 0 : Reference< com::sun::star::util::XRefreshable > ( m_settings.tables, UNO_QUERY )->refresh();
685 0 : return m_settings.tables;
686 : }
687 :
688 0 : Reference< XNameAccess > Connection::getViews()
689 : throw (::com::sun::star::uno::RuntimeException)
690 : {
691 0 : if( isLog( &m_settings, LogLevel::INFO ) )
692 : {
693 0 : log( &m_settings, LogLevel::INFO, "Connection::getViews() got called" );
694 : }
695 0 : MutexGuard guard( m_refMutex->mutex );
696 0 : if( !m_settings.views.is() )
697 0 : m_settings.views = Views::create( m_refMutex, this, &m_settings, &(m_settings.pViewsImpl) );
698 : else
699 : // TODO: how to overcome the performance problem ?
700 0 : Reference< com::sun::star::util::XRefreshable > ( m_settings.views, UNO_QUERY )->refresh();
701 0 : return m_settings.views;
702 : }
703 :
704 :
705 :
706 0 : Reference< XNameAccess > Connection::getUsers()
707 : throw (::com::sun::star::uno::RuntimeException)
708 : {
709 0 : if( isLog( &m_settings, LogLevel::INFO ) )
710 : {
711 0 : log( &m_settings, LogLevel::INFO, "Connection::getUsers() got called" );
712 : }
713 :
714 0 : MutexGuard guard( m_refMutex->mutex );
715 0 : if( !m_settings.users.is() )
716 0 : m_settings.users = Users::create( m_refMutex, this, &m_settings );
717 0 : return m_settings.users;
718 : }
719 :
720 :
721 0 : Reference< XInterface > ConnectionCreateInstance(
722 : const Reference< XComponentContext > & ctx ) throw (Exception)
723 : {
724 0 : ::rtl::Reference< RefCountedMutex > ref = new RefCountedMutex();
725 0 : return * new Connection( ref, ctx );
726 : }
727 :
728 :
729 :
730 0 : bool isLog( ConnectionSettings *settings, int loglevel )
731 : {
732 0 : return settings->loglevel >= loglevel && settings->logFile;
733 : }
734 :
735 0 : void log( ConnectionSettings *settings, sal_Int32 level, const OUString &logString )
736 : {
737 0 : log( settings, level, OUStringToOString( logString, settings->encoding ).getStr() );
738 0 : }
739 0 : void log( ConnectionSettings *settings, sal_Int32 level, const char *str )
740 : {
741 0 : if( isLog( settings, level ) )
742 : {
743 : static const char *strLevel[] = { "NONE", "ERROR", "SQL", "INFO", "DATA" };
744 :
745 0 : time_t t = ::time( 0 );
746 : char *pString;
747 : #ifdef SAL_W32
748 : pString = asctime( localtime( &t ) );
749 : #else
750 : struct tm timestruc;
751 : char timestr[50];
752 0 : memset( timestr, 0 , 50);
753 0 : pString = timestr;
754 0 : ::localtime_r( &t , ×truc );
755 0 : asctime_r( ×truc, timestr );
756 : #endif
757 0 : for( int i = 0 ; pString[i] ; i ++ )
758 : {
759 0 : if( pString[i] <= 13 )
760 : {
761 0 : pString[i] = 0;
762 0 : break;
763 : }
764 : }
765 0 : fprintf( settings->logFile, "%s [%s]: %s\n", pString, strLevel[level], str );
766 : }
767 0 : }
768 :
769 :
770 : }
771 :
772 :
773 :
774 : static struct cppu::ImplementationEntry g_entries[] =
775 : {
776 : {
777 : pq_sdbc_driver::ConnectionCreateInstance, pq_sdbc_driver::ConnectionGetImplementationName,
778 : pq_sdbc_driver::ConnectionGetSupportedServiceNames, cppu::createSingleComponentFactory,
779 : 0 , 0
780 : },
781 : { 0, 0, 0, 0, 0, 0 }
782 : };
783 :
784 :
785 : extern "C"
786 : {
787 :
788 0 : SAL_DLLPUBLIC_EXPORT void * SAL_CALL postgresql_sdbc_impl_component_getFactory(
789 : const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
790 : {
791 0 : return cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
792 : }
793 :
794 : }
|