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 "pq_statement.hxx"
59 : #include "pq_fakedupdateableresultset.hxx"
60 : #include "pq_updateableresultset.hxx"
61 : #include "pq_tools.hxx"
62 : #include "pq_statics.hxx"
63 :
64 : #include <osl/thread.h>
65 : #include <osl/time.h>
66 :
67 : #include <rtl/ustrbuf.hxx>
68 : #include <rtl/strbuf.hxx>
69 :
70 : #include <cppuhelper/typeprovider.hxx>
71 : #include <cppuhelper/queryinterface.hxx>
72 :
73 : #include <com/sun/star/beans/PropertyAttribute.hpp>
74 :
75 : #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
76 : #include <com/sun/star/sdbc/ResultSetType.hpp>
77 : #include <com/sun/star/sdbc/XParameters.hpp>
78 :
79 : #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
80 : #include <com/sun/star/sdbcx/KeyType.hpp>
81 : #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
82 :
83 : #include <com/sun/star/container/XIndexAccess.hpp>
84 : #include <com/sun/star/container/XEnumerationAccess.hpp>
85 :
86 : #include <string.h>
87 :
88 : using osl::Mutex;
89 : using osl::MutexGuard;
90 :
91 : using rtl::OUString;
92 : using rtl::OUStringBuffer;
93 : using rtl::OUStringToOString;
94 : using rtl::OStringToOUString;
95 : using rtl::OString;
96 :
97 : using com::sun::star::uno::Any;
98 : using com::sun::star::uno::makeAny;
99 : using com::sun::star::uno::Type;
100 : using com::sun::star::uno::RuntimeException;
101 : using com::sun::star::uno::Exception;
102 : using com::sun::star::uno::Sequence;
103 : using com::sun::star::uno::Reference;
104 : using com::sun::star::uno::XInterface;
105 : using com::sun::star::uno::UNO_QUERY;
106 :
107 : using com::sun::star::lang::IllegalArgumentException;
108 :
109 : using com::sun::star::sdbc::XWarningsSupplier;
110 : using com::sun::star::sdbc::XCloseable;
111 : using com::sun::star::sdbc::XStatement;
112 : using com::sun::star::sdbc::XPreparedStatement;
113 : using com::sun::star::sdbc::XParameters;
114 : using com::sun::star::sdbc::XRow;
115 : using com::sun::star::sdbc::XResultSet;
116 : using com::sun::star::sdbc::XGeneratedResultSet;
117 : using com::sun::star::sdbc::XConnection;
118 : using com::sun::star::sdbc::SQLException;
119 :
120 : using com::sun::star::sdbcx::XColumnsSupplier;
121 : using com::sun::star::sdbcx::XTablesSupplier;
122 : using com::sun::star::sdbcx::XKeysSupplier;
123 :
124 : using com::sun::star::beans::Property;
125 : using com::sun::star::beans::XPropertySetInfo;
126 : using com::sun::star::beans::XPropertySet;
127 : using com::sun::star::beans::XFastPropertySet;
128 : using com::sun::star::beans::XMultiPropertySet;
129 :
130 : using com::sun::star::container::XNameAccess;
131 : using com::sun::star::container::XEnumerationAccess;
132 : using com::sun::star::container::XEnumeration;
133 : using com::sun::star::container::XIndexAccess;
134 :
135 : #define ASCII_STR(x) OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
136 : namespace pq_sdbc_driver
137 : {
138 0 : static ::cppu::IPropertyArrayHelper & getStatementPropertyArrayHelper()
139 : {
140 : static ::cppu::IPropertyArrayHelper *pArrayHelper;
141 0 : if( ! pArrayHelper )
142 : {
143 0 : MutexGuard guard( Mutex::getGlobalMutex() );
144 0 : if( ! pArrayHelper )
145 : {
146 : static Property aTable[] =
147 : {
148 : Property(
149 : OUString( RTL_CONSTASCII_USTRINGPARAM("CursorName") ), 0,
150 0 : ::getCppuType( (OUString *)0) , 0 ),
151 : Property(
152 : OUString( RTL_CONSTASCII_USTRINGPARAM("EscapeProcessing") ), 1,
153 0 : ::getBooleanCppuType() , 0 ),
154 : Property(
155 : OUString( RTL_CONSTASCII_USTRINGPARAM("FetchDirection") ), 2,
156 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
157 : Property(
158 : OUString( RTL_CONSTASCII_USTRINGPARAM("FetchSize") ), 3,
159 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
160 : Property(
161 : OUString( RTL_CONSTASCII_USTRINGPARAM("MaxFieldSize") ), 4,
162 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
163 : Property(
164 : OUString( RTL_CONSTASCII_USTRINGPARAM("MaxRows") ), 5,
165 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
166 : Property(
167 : OUString( RTL_CONSTASCII_USTRINGPARAM("QueryTimeOut") ), 6,
168 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
169 : Property(
170 : OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetConcurrency") ), 7,
171 0 : ::getCppuType( (sal_Int32 *)0) , 0 ),
172 : Property(
173 : OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetType") ), 8,
174 0 : ::getCppuType( (sal_Int32 *)0) , 0 )
175 0 : };
176 : OSL_ASSERT( sizeof(aTable)/ sizeof(Property) == STATEMENT_SIZE );
177 0 : static ::cppu::OPropertyArrayHelper arrayHelper( aTable, STATEMENT_SIZE, sal_True );
178 0 : pArrayHelper = &arrayHelper;
179 0 : }
180 : }
181 0 : return *pArrayHelper;
182 : }
183 :
184 0 : Statement::Statement( const ::rtl::Reference< RefCountedMutex > & refMutex,
185 : const Reference< XConnection > & conn,
186 : struct ConnectionSettings *pSettings )
187 0 : : OComponentHelper( refMutex->mutex ),
188 : OPropertySetHelper( OComponentHelper::rBHelper ),
189 : m_connection( conn ),
190 : m_pSettings( pSettings ),
191 : m_refMutex( refMutex ),
192 0 : m_lastOidInserted( InvalidOid )
193 : {
194 0 : m_props[STATEMENT_QUERY_TIME_OUT] = makeAny( (sal_Int32)0 );
195 0 : m_props[STATEMENT_MAX_ROWS] = makeAny( (sal_Int32)0 );
196 : m_props[STATEMENT_RESULT_SET_CONCURRENCY] = makeAny(
197 0 : com::sun::star::sdbc::ResultSetConcurrency::READ_ONLY );
198 : m_props[STATEMENT_RESULT_SET_TYPE] = makeAny(
199 0 : com::sun::star::sdbc::ResultSetType::SCROLL_INSENSITIVE );
200 0 : }
201 :
202 0 : Statement::~Statement()
203 : {
204 : POSTGRE_TRACE( "dtor Statement" );
205 0 : }
206 :
207 0 : void Statement::checkClosed() throw (SQLException, RuntimeException )
208 : {
209 0 : if( ! m_pSettings || ! m_pSettings->pConnection )
210 : throw SQLException(
211 : ASCII_STR("pq_driver: Statement or connection has already been closed !" ),
212 0 : *this, OUString(),1,Any());
213 0 : }
214 :
215 0 : Any Statement::queryInterface( const Type & reqType ) throw (RuntimeException)
216 : {
217 0 : Any ret;
218 :
219 0 : ret = OComponentHelper::queryInterface( reqType );
220 0 : if( ! ret.hasValue() )
221 : ret = ::cppu::queryInterface( reqType,
222 : static_cast< XWarningsSupplier * > ( this ),
223 : static_cast< XStatement * > ( this ),
224 : static_cast< com::sun::star::sdbc::XResultSetMetaDataSupplier * > ( this ),
225 : static_cast< XCloseable * > ( this ),
226 : static_cast< XPropertySet * > ( this ),
227 : static_cast< XMultiPropertySet * > ( this ),
228 : static_cast< XGeneratedResultSet * > ( this ),
229 0 : static_cast< XFastPropertySet * > ( this ) );
230 0 : return ret;
231 : }
232 :
233 :
234 0 : Sequence< Type > Statement::getTypes() throw ( RuntimeException )
235 : {
236 : static cppu::OTypeCollection *pCollection;
237 0 : if( ! pCollection )
238 : {
239 0 : MutexGuard guard( osl::Mutex::getGlobalMutex() );
240 0 : if( !pCollection )
241 : {
242 : static cppu::OTypeCollection collection(
243 0 : getCppuType( (Reference< XWarningsSupplier> *) 0 ),
244 0 : getCppuType( (Reference< XStatement> *) 0 ),
245 0 : getCppuType( (Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier> *) 0 ),
246 0 : getCppuType( (Reference< XCloseable> *) 0 ),
247 0 : getCppuType( (Reference< XPropertySet >*) 0 ),
248 0 : getCppuType( (Reference< XFastPropertySet > *) 0 ),
249 0 : getCppuType( (Reference< XMultiPropertySet > *) 0 ),
250 0 : getCppuType( (Reference< XGeneratedResultSet > *) 0 ),
251 0 : OComponentHelper::getTypes());
252 0 : pCollection = &collection;
253 0 : }
254 : }
255 0 : return pCollection->getTypes();
256 : }
257 :
258 0 : Sequence< sal_Int8> Statement::getImplementationId() throw ( RuntimeException )
259 : {
260 : static cppu::OImplementationId *pId;
261 0 : if( ! pId )
262 : {
263 0 : MutexGuard guard( osl::Mutex::getGlobalMutex() );
264 0 : if( ! pId )
265 : {
266 0 : static cppu::OImplementationId id(sal_False);
267 0 : pId = &id;
268 0 : }
269 : }
270 0 : return pId->getImplementationId();
271 : }
272 :
273 0 : void Statement::close( ) throw (SQLException, RuntimeException)
274 : {
275 : // let the connection die without acquired mutex !
276 0 : Reference< XConnection > r;
277 0 : Reference< XCloseable > resultSet;
278 : {
279 0 : MutexGuard guard( m_refMutex->mutex );
280 0 : m_pSettings = 0;
281 0 : r = m_connection;
282 0 : m_connection.clear();
283 :
284 0 : resultSet = m_lastResultset;
285 0 : m_lastResultset.clear();
286 : }
287 0 : if( resultSet.is() )
288 : {
289 0 : resultSet->close();
290 : POSTGRE_TRACE( "statement closed" );
291 0 : }
292 :
293 0 : }
294 :
295 0 : void Statement::raiseSQLException(
296 : const OUString & sql, const char * errorMsg, const char *errorType )
297 : throw( SQLException )
298 : {
299 0 : OUStringBuffer buf(128);
300 0 : buf.appendAscii( "pq_driver: ");
301 0 : if( errorType )
302 : {
303 0 : buf.appendAscii( "[" );
304 0 : buf.appendAscii( errorType );
305 0 : buf.appendAscii( "]" );
306 : }
307 : buf.append(
308 0 : rtl::OUString( errorMsg, strlen(errorMsg) , m_pSettings->encoding ) );
309 0 : buf.appendAscii( " (caused by statement '" );
310 0 : buf.append( sql );
311 0 : buf.appendAscii( "')" );
312 0 : OUString error = buf.makeStringAndClear();
313 0 : log( m_pSettings, LogLevel::ERROR, error );
314 0 : throw SQLException( error, *this, OUString(), 1, Any() );
315 : }
316 :
317 0 : Reference< XResultSet > Statement::executeQuery(const OUString& sql )
318 : throw (SQLException, RuntimeException)
319 : {
320 0 : Reference< XCloseable > lastResultSetHolder = m_lastResultset;
321 0 : if( lastResultSetHolder.is() )
322 0 : lastResultSetHolder->close();
323 :
324 0 : if( ! execute( sql ) )
325 : {
326 0 : raiseSQLException( sql, "not a query" );
327 : }
328 0 : return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
329 : }
330 :
331 0 : sal_Int32 Statement::executeUpdate( const OUString& sql )
332 : throw (SQLException, RuntimeException)
333 : {
334 0 : if( execute( sql ) )
335 : {
336 0 : raiseSQLException( sql, "not a command" );
337 : }
338 0 : return m_multipleResultUpdateCount;
339 : }
340 :
341 :
342 0 : static void raiseSQLException(
343 : ConnectionSettings *pSettings,
344 : const Reference< XInterface> & owner,
345 : const OString & sql,
346 : const char * errorMsg,
347 : const char *errorType = 0 )
348 : throw( SQLException )
349 : {
350 0 : OUStringBuffer buf(128);
351 0 : buf.appendAscii( "pq_driver: ");
352 0 : if( errorType )
353 : {
354 0 : buf.appendAscii( "[" );
355 0 : buf.appendAscii( errorType );
356 0 : buf.appendAscii( "]" );
357 : }
358 : buf.append(
359 0 : rtl::OUString( errorMsg, strlen(errorMsg) , pSettings->encoding ) );
360 0 : buf.appendAscii( " (caused by statement '" );
361 0 : buf.append( rtl::OStringToOUString( sql, pSettings->encoding ) );
362 0 : buf.appendAscii( "')" );
363 0 : OUString error = buf.makeStringAndClear();
364 0 : log( pSettings, LogLevel::ERROR, error );
365 0 : throw SQLException( error, owner, OUString(), 1, Any() );
366 : }
367 :
368 :
369 : // returns the elements of the primary key of the given table
370 : // static Sequence< Reference< com::sun::star::beans::XPropertySet > > lookupKeys(
371 0 : static Sequence< ::rtl::OUString > lookupKeys(
372 : const Reference< com::sun::star::container::XNameAccess > &tables,
373 : const OUString & table,
374 : OUString *pSchema,
375 : OUString *pTable,
376 : ConnectionSettings *pSettings)
377 : {
378 0 : Sequence< ::rtl::OUString > ret;
379 0 : Reference< XKeysSupplier > keySupplier;
380 0 : Statics & st = getStatics();
381 :
382 0 : if( tables->hasByName( table ) )
383 0 : tables->getByName( table ) >>= keySupplier;
384 0 : else if( -1 == table.indexOf( '.' ) )
385 : {
386 : // it wasn't a fully qualified name. Now need to skip through all tables.
387 : Reference< XEnumerationAccess > enumerationAccess =
388 0 : Reference< XEnumerationAccess > ( tables, UNO_QUERY );
389 :
390 : Reference< com::sun::star::container::XEnumeration > enumeration =
391 0 : enumerationAccess->createEnumeration();
392 0 : while( enumeration->hasMoreElements() )
393 : {
394 0 : Reference< XPropertySet > set;
395 0 : enumeration->nextElement() >>= set;
396 0 : OUString name;
397 : // ::rtl::OUString schema;
398 :
399 0 : if( set->getPropertyValue( st.NAME ) >>= name )
400 : {
401 : // printf( "searching %s %s\n",
402 : // OUStringToOString( schema, RTL_TEXTENCODING_ASCII_US ).getStr(),
403 : // OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
404 0 : if( name == table )
405 : {
406 :
407 0 : if( keySupplier.is() )
408 : {
409 : // is ambigous, as I don't know postgresql searchpath,
410 : // I can't continue here, as I may write to a different table
411 0 : keySupplier.clear();
412 0 : if( isLog( pSettings, LogLevel::INFO ) )
413 : {
414 0 : rtl::OStringBuffer buf( 128 );
415 0 : buf.append( "Can't offer updateable result set because table " );
416 0 : buf.append( OUStringToOString(name, pSettings->encoding) );
417 0 : buf.append( " is duplicated, add schema to resolve ambiguity" );
418 0 : log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
419 : }
420 : break;
421 : }
422 0 : keySupplier = Reference< XKeysSupplier > ( set, UNO_QUERY );
423 : }
424 : }
425 0 : }
426 : }
427 : else
428 : {
429 0 : if( isLog( pSettings, LogLevel::INFO ) )
430 : {
431 0 : rtl::OStringBuffer buf( 128 );
432 0 : buf.append( "Can't offer updateable result set ( table " );
433 0 : buf.append( OUStringToOString(table, pSettings->encoding) );
434 0 : buf.append( " is unknown)" );
435 0 : log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
436 : }
437 : }
438 :
439 0 : if( keySupplier.is() )
440 : {
441 0 : Reference< XPropertySet > set( keySupplier, UNO_QUERY );
442 0 : set->getPropertyValue( getStatics().NAME ) >>= (*pTable);
443 0 : set->getPropertyValue( getStatics().SCHEMA_NAME ) >>= (*pSchema );
444 0 : set.clear();
445 :
446 0 : Reference< XEnumerationAccess > keys ( keySupplier->getKeys(), UNO_QUERY );
447 0 : Reference< XEnumeration > enumeration = keys->createEnumeration();
448 0 : while( enumeration->hasMoreElements() )
449 : {
450 0 : enumeration->nextElement() >>= set;
451 0 : sal_Int32 keyType = 0;
452 0 : if( (set->getPropertyValue( st.TYPE ) >>= keyType ) &&
453 : keyType == com::sun::star::sdbcx::KeyType::PRIMARY )
454 : {
455 0 : Reference< XColumnsSupplier > columns( set, UNO_QUERY );
456 : Reference< XIndexAccess > indexAccess =
457 0 : Reference< XIndexAccess > ( columns->getColumns(), UNO_QUERY );
458 :
459 0 : int length = indexAccess->getCount();
460 0 : ret.realloc( length );
461 : // printf( "primary key for Table %s is ",
462 : // OUStringToOString( table, RTL_TEXTENCODING_ASCII_US ).getStr() );
463 0 : for( int i = 0 ; i < length ; i ++ )
464 : {
465 0 : indexAccess->getByIndex( i ) >>= set;
466 0 : OUString name;
467 0 : set->getPropertyValue( st.NAME ) >>= name;
468 0 : ret[i] = name;
469 : // printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
470 0 : }
471 : // printf( "\n" );
472 : }
473 : }
474 0 : if( ! ret.getLength() )
475 : {
476 0 : if( isLog( pSettings, LogLevel::INFO ) )
477 : {
478 0 : rtl::OStringBuffer buf( 128 );
479 0 : buf.append( "Can't offer updateable result set ( table " );
480 0 : buf.append( OUStringToOString(table, pSettings->encoding) );
481 0 : buf.append( " does not have a primary key)" );
482 0 : log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
483 : }
484 0 : }
485 : }
486 0 : return ret;
487 : }
488 :
489 0 : bool executePostgresCommand( const rtl::OString & cmd, struct CommandData *data )
490 : {
491 0 : ConnectionSettings *pSettings = *(data->ppSettings);
492 :
493 0 : sal_Int32 duration = osl_getGlobalTimer();
494 0 : PGresult *result = PQexec( pSettings->pConnection, cmd.getStr() );
495 0 : duration = osl_getGlobalTimer() - duration;
496 0 : if( ! result )
497 : raiseSQLException(
498 0 : pSettings, data->owner, cmd, PQerrorMessage( pSettings->pConnection ) );
499 :
500 0 : ExecStatusType state = PQresultStatus( result );
501 0 : *(data->pLastOidInserted) = 0;
502 0 : *(data->pLastTableInserted) = rtl::OUString();
503 0 : *(data->pLastQuery) = cmd;
504 :
505 0 : sal_Bool ret = sal_False;
506 0 : switch( state )
507 : {
508 : case PGRES_COMMAND_OK:
509 : {
510 0 : *(data->pMultipleResultUpdateCount) = atoi( PQcmdTuples( result ) );
511 0 : *(data->pMultipleResultAvailable) = sal_False;
512 :
513 : // in case an oid value is available, we retrieve it
514 0 : *(data->pLastOidInserted) = PQoidValue( result );
515 :
516 : // in case it was a single insert, extract the name of the table,
517 : // otherwise the table name is empty
518 : *(data->pLastTableInserted) =
519 0 : extractTableFromInsert( OStringToOUString( cmd, pSettings->encoding ) );
520 0 : if( isLog( pSettings, LogLevel::SQL ) )
521 : {
522 0 : rtl::OStringBuffer buf( 128 );
523 0 : buf.append( "executed command '" );
524 0 : buf.append( cmd.getStr() );
525 0 : buf.append( "' sucessfully (" );
526 0 : buf.append( *( data->pMultipleResultUpdateCount ) );
527 0 : buf.append( ")" );
528 0 : buf.append( ", duration=" );
529 0 : buf.append( duration );
530 0 : buf.append( "ms" );
531 0 : if( *(data->pLastOidInserted) )
532 : {
533 0 : buf.append( ", usedOid=" );
534 0 : buf.append( *(data->pLastOidInserted) , 10 );
535 0 : buf.append( ", diagnosedTable=" );
536 : buf.append(
537 0 : OUStringToOString( *data->pLastTableInserted, pSettings->encoding ) );
538 : }
539 0 : log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
540 : }
541 0 : PQclear( result );
542 0 : break;
543 : }
544 : case PGRES_TUPLES_OK: // success
545 : {
546 : // In case it is a single table, it has a primary key and all columns
547 : // belonging to the primary key are in the result set, allow updateable result sets
548 : // otherwise, don't
549 0 : rtl::OUString table, schema;
550 0 : Sequence< OUString > sourceTableKeys;
551 0 : OStringVector vec;
552 0 : tokenizeSQL( cmd, vec );
553 : OUString sourceTable =
554 : OStringToOUString(
555 0 : extractSingleTableFromSelect( vec ), pSettings->encoding );
556 :
557 0 : if( data->concurrency ==
558 : com::sun::star::sdbc::ResultSetConcurrency::UPDATABLE )
559 : {
560 0 : OString aReason;
561 0 : if( sourceTable.getLength() )
562 : {
563 : sourceTableKeys = lookupKeys(
564 0 : pSettings->tables.is() ?
565 0 : pSettings->tables : data->tableSupplier->getTables() ,
566 : sourceTable,
567 : &schema,
568 : &table,
569 0 : pSettings);
570 :
571 : // check, whether the columns are in the result set (required !)
572 : int i;
573 0 : for( i = 0 ; i < sourceTableKeys.getLength() ; i ++ )
574 : {
575 0 : if( -1 == PQfnumber(
576 : result,
577 0 : OUStringToOString( sourceTableKeys[i] ,
578 0 : pSettings->encoding ).getStr()) )
579 : {
580 0 : break;
581 : }
582 : }
583 :
584 0 : if( sourceTableKeys.getLength() && i == sourceTableKeys.getLength() )
585 : {
586 : *(data->pLastResultset) =
587 : UpdateableResultSet::createFromPGResultSet(
588 : data->refMutex, data->owner, data->ppSettings, result,
589 0 : schema, table,sourceTableKeys );
590 : }
591 0 : else if( ! table.getLength() )
592 : {
593 0 : rtl::OStringBuffer buf( 128 );
594 : buf.append(
595 : RTL_CONSTASCII_STRINGPARAM(
596 : "can't support updateable resultset, because a single table in the "
597 0 : "WHERE part of the statement could not be identified (" ) );
598 0 : buf.append( cmd );
599 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( "." ) );
600 0 : aReason = buf.makeStringAndClear();
601 : }
602 0 : else if( sourceTableKeys.getLength() )
603 : {
604 0 : ::rtl::OStringBuffer buf( 128 );
605 : buf.append(
606 : RTL_CONSTASCII_STRINGPARAM(
607 0 : "can't support updateable resultset for table " ) );
608 0 : buf.append( rtl::OUStringToOString( schema, pSettings->encoding ) );
609 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( "." ) );
610 0 : buf.append( rtl::OUStringToOString( table, pSettings->encoding ) );
611 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( ", because resultset does not contain a part of the primary key ( column " ) );
612 0 : buf.append( rtl::OUStringToOString( sourceTableKeys[i], pSettings->encoding ) );
613 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( " is missing )") );
614 0 : aReason = buf.makeStringAndClear();
615 : }
616 : else
617 : {
618 :
619 0 : ::rtl::OStringBuffer buf( 128 );
620 : buf.append(
621 : RTL_CONSTASCII_STRINGPARAM(
622 0 : "can't support updateable resultset for table " ) );
623 0 : buf.append( rtl::OUStringToOString( schema, pSettings->encoding ) );
624 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( "." ) );
625 0 : buf.append( rtl::OUStringToOString( table, pSettings->encoding ) );
626 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( ", because resultset table does not have a primary key " ) );
627 0 : aReason = buf.makeStringAndClear();
628 : }
629 : }
630 : else
631 : {
632 0 : ::rtl::OStringBuffer buf( 128 );
633 : buf.append(
634 : RTL_CONSTASCII_STRINGPARAM(
635 0 : "can't support updateable result for selects with multiple tables (" ) );
636 0 : buf.append( cmd );
637 0 : buf.append( RTL_CONSTASCII_STRINGPARAM( ")" ) );
638 0 : log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
639 : }
640 0 : if( ! (*(data->pLastResultset)).is() )
641 : {
642 0 : if( isLog( pSettings, LogLevel::ERROR ) )
643 : {
644 0 : log( pSettings, LogLevel::ERROR, aReason.getStr());
645 : }
646 :
647 : // TODO: How to react here correctly ?
648 : // remove this piece of code
649 : *(data->pLastResultset) =
650 : new FakedUpdateableResultSet(
651 : data->refMutex, data->owner,
652 : data->ppSettings,result, schema, table,
653 0 : OStringToOUString( aReason, pSettings->encoding) );
654 0 : }
655 :
656 : }
657 0 : else if( sourceTable.getLength() && -1 != sourceTable.indexOf( '.' ) )
658 : {
659 0 : splitConcatenatedIdentifier( sourceTable, &schema, &table );
660 : }
661 :
662 0 : sal_Int32 returnedRows = PQntuples( result );
663 0 : if( ! data->pLastResultset->is() )
664 : *(data->pLastResultset) =
665 : Reference< XCloseable > (
666 : new ResultSet(
667 : data->refMutex, data->owner,
668 0 : data->ppSettings,result, schema, table ) );
669 0 : *(data->pMultipleResultAvailable) = sal_True;
670 0 : ret = sal_True;
671 0 : if( isLog( pSettings, LogLevel::SQL ) )
672 : {
673 0 : rtl::OStringBuffer buf( 128 );
674 0 : buf.append( RTL_CONSTASCII_STRINGPARAM("executed query '") );
675 0 : buf.append( cmd );
676 0 : buf.append( RTL_CONSTASCII_STRINGPARAM("' sucessfully") );
677 0 : buf.append( RTL_CONSTASCII_STRINGPARAM(", duration=") );
678 0 : buf.append( duration );
679 0 : buf.append( RTL_CONSTASCII_STRINGPARAM("ms, returnedRows=") );
680 0 : buf.append( returnedRows );
681 0 : buf.append( RTL_CONSTASCII_STRINGPARAM("." ) );
682 0 : log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
683 : }
684 0 : break;
685 : }
686 : case PGRES_EMPTY_QUERY:
687 : case PGRES_COPY_OUT:
688 : case PGRES_COPY_IN:
689 : case PGRES_BAD_RESPONSE:
690 : case PGRES_NONFATAL_ERROR:
691 : case PGRES_FATAL_ERROR:
692 : default:
693 : raiseSQLException(
694 0 : pSettings, data->owner, cmd, PQresultErrorMessage( result ) , PQresStatus( state ) );
695 : }
696 0 : return ret;
697 :
698 : }
699 :
700 0 : static Sequence< OUString > getPrimaryKeyColumnNames(
701 : const Reference< XConnection > & connection, const OUString &schemaName, const OUString &tableName )
702 : {
703 0 : Sequence< OUString > ret;
704 :
705 0 : Int2StringMap mapIndex2Name;
706 0 : fillAttnum2attnameMap( mapIndex2Name, connection, schemaName, tableName );
707 :
708 : // retrieve the primary key ...
709 0 : Reference< XPreparedStatement > stmt = connection->prepareStatement(
710 : ASCII_STR(
711 : "SELECT conkey " // 7
712 : "FROM pg_constraint INNER JOIN pg_class ON conrelid = pg_class.oid "
713 : "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
714 : "LEFT JOIN pg_class AS class2 ON confrelid = class2.oid "
715 : "LEFT JOIN pg_namespace AS nmsp2 ON class2.relnamespace=nmsp2.oid "
716 0 : "WHERE pg_class.relname = ? AND pg_namespace.nspname = ? AND pg_constraint.contype='p'" ) );
717 0 : DisposeGuard guard( stmt );
718 0 : Reference< XParameters > paras( stmt, UNO_QUERY );
719 0 : paras->setString( 1 , tableName );
720 0 : paras->setString( 2 , schemaName );
721 0 : Reference< XResultSet > rs = stmt->executeQuery();
722 0 : Reference< XRow > xRow( rs , UNO_QUERY );
723 :
724 0 : if( rs->next() )
725 : {
726 0 : ret = convertMappedIntArray2StringArray( mapIndex2Name, string2intarray(xRow->getString( 1 ) ) );
727 : }
728 0 : return ret;
729 : }
730 :
731 0 : static void getAutoValues(
732 : String2StringMap & result,
733 : const Reference< XConnection > & connection,
734 : const OUString &schemaName,
735 : const OUString & tableName )
736 : {
737 0 : Reference< XPreparedStatement > stmt = connection->prepareStatement(
738 : ASCII_STR("SELECT pg_attribute.attname, pg_attrdef.adsrc "
739 : "FROM pg_class, pg_namespace, pg_attribute "
740 : "LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND "
741 : "pg_attribute.attnum = pg_attrdef.adnum "
742 : "WHERE pg_attribute.attrelid = pg_class.oid AND "
743 : "pg_class.relnamespace = pg_namespace.oid AND "
744 : "pg_namespace.nspname = ? AND "
745 : // LEM TODO: this is weird; why "LIKE" and not "="?
746 : // Most probably gives problems if tableName contains '%'
747 : "pg_class.relname LIKE ? AND "
748 : "pg_attrdef.adsrc != ''"
749 0 : ) );
750 0 : DisposeGuard guard( stmt );
751 0 : Reference< XParameters > paras( stmt, UNO_QUERY );
752 0 : paras->setString( 1 , schemaName );
753 0 : paras->setString( 2 , tableName );
754 0 : Reference< XResultSet > rs = stmt->executeQuery();
755 0 : Reference< XRow > xRow( rs , UNO_QUERY );
756 :
757 0 : while( rs->next() )
758 : {
759 0 : result[ OUStringToOString( xRow->getString( 1 ), RTL_TEXTENCODING_ASCII_US) ] =
760 0 : OUStringToOString( xRow->getString(2), RTL_TEXTENCODING_ASCII_US );
761 0 : }
762 0 : }
763 :
764 0 : Reference< XResultSet > getGeneratedValuesFromLastInsert(
765 : ConnectionSettings *pConnectionSettings,
766 : const Reference< XConnection > &connection,
767 : sal_Int32 nLastOid,
768 : const rtl::OUString & lastTableInserted,
769 : const rtl::OString & lastQuery )
770 : {
771 0 : Reference< XResultSet > ret;
772 0 : OUString query;
773 0 : OUString schemaName, tableName;
774 : splitConcatenatedIdentifier(
775 0 : lastTableInserted, &schemaName, &tableName );
776 :
777 0 : if( nLastOid && lastTableInserted.getLength() )
778 : {
779 0 : OUStringBuffer buf( 128 );
780 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "SELECT * FROM " ) );
781 0 : if( schemaName.getLength() )
782 0 : bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
783 : else
784 0 : bufferQuoteIdentifier( buf, lastTableInserted, pConnectionSettings );
785 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( " WHERE oid = " ) );
786 0 : buf.append( nLastOid , 10 );
787 0 : query = buf.makeStringAndClear();
788 : }
789 0 : else if ( lastTableInserted.getLength() && lastQuery.getLength() )
790 : {
791 : // extract nameValue Pairs
792 0 : String2StringMap namedValues;
793 0 : extractNameValuePairsFromInsert( namedValues, lastQuery );
794 :
795 : // debug ...
796 : // rtl::OStringBuffer buf( 128);
797 : // buf.append( "extracting name/value from '" );
798 : // buf.append( lastQuery.getStr() );
799 : // buf.append( "' to [" );
800 : // for( String2StringMap::iterator ii = namedValues.begin() ; ii != namedValues.end() ; ++ii )
801 : // {
802 : // buf.append( ii->first.getStr() );
803 : // buf.append( "=" );
804 : // buf.append( ii->second.getStr() );
805 : // buf.append( "," );
806 : // }
807 : // buf.append( "]\n" );
808 : // printf( "%s", buf.makeStringAndClear() );
809 :
810 : // TODO: make also unqualified tables names work here. Have a look at 2.8.3. The Schema Search Path
811 : // in postgresql doc
812 :
813 0 : Sequence< OUString > keyColumnNames = getPrimaryKeyColumnNames( connection, schemaName, tableName );
814 0 : if( keyColumnNames.getLength() )
815 : {
816 0 : OUStringBuffer buf( 128 );
817 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "SELECT * FROM " ) );
818 0 : bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
819 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( " WHERE " ) );
820 0 : bool additionalCondition = false;
821 0 : String2StringMap autoValues;
822 0 : for( int i = 0 ; i < keyColumnNames.getLength() ; i ++ )
823 : {
824 0 : OUString value;
825 0 : OString columnName = OUStringToOString( keyColumnNames[i], pConnectionSettings->encoding );
826 0 : String2StringMap::iterator ii = namedValues.begin();
827 0 : for( ; ii != namedValues.end() ; ++ii )
828 : {
829 0 : if( columnName.equalsIgnoreAsciiCase( ii->first ) )
830 : {
831 0 : value = OStringToOUString( ii->second , pConnectionSettings->encoding );
832 0 : break;
833 : }
834 : }
835 :
836 : // check, if a column of the primary key was not inserted explicitly,
837 0 : if( ii == namedValues.end() )
838 : {
839 :
840 0 : if( autoValues.begin() == autoValues.end() )
841 : {
842 0 : getAutoValues( autoValues, connection, schemaName, tableName );
843 : }
844 : // this could mean, that the column is a default or auto value, check this ...
845 0 : String2StringMap::iterator j = autoValues.begin();
846 0 : for( ; j != autoValues.end() ; ++j )
847 : {
848 0 : if( columnName.equalsIgnoreAsciiCase( j->first ) )
849 : {
850 : // it is indeed an auto value.
851 0 : value = OStringToOUString(j->second, RTL_TEXTENCODING_ASCII_US );
852 : // check, whether it is a sequence
853 :
854 0 : if( rtl_str_shortenedCompare_WithLength(
855 0 : j->second.getStr(), j->second.getLength(),
856 0 : RTL_CONSTASCII_STRINGPARAM( "nextval(" ), 8 ) == 0 )
857 : {
858 : // retrieve current sequence value:
859 0 : OUStringBuffer myBuf(128 );
860 0 : myBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "SELECT currval(" ) );
861 0 : myBuf.appendAscii( &(j->second.getStr()[8]));
862 0 : value = querySingleValue( connection, myBuf.makeStringAndClear() );
863 : }
864 0 : break;
865 : }
866 : }
867 0 : if( j == autoValues.end() )
868 : {
869 : // it even was no autovalue, no sense to continue as we can't query the
870 : // inserted row
871 0 : buf = OUStringBuffer();
872 : break;
873 : }
874 : }
875 :
876 0 : if( additionalCondition )
877 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( " AND " ) );
878 0 : bufferQuoteIdentifier( buf, keyColumnNames[i], pConnectionSettings );
879 0 : buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( " = " ) );
880 0 : buf.append( value );
881 0 : additionalCondition = true;
882 0 : }
883 0 : query = buf.makeStringAndClear();
884 0 : }
885 : }
886 :
887 0 : if( query.getLength() )
888 : {
889 0 : Reference< com::sun::star::sdbc::XStatement > stmt = connection->createStatement();
890 0 : ret = stmt->executeQuery( query );
891 : }
892 :
893 0 : return ret;
894 :
895 : }
896 :
897 0 : sal_Bool Statement::execute( const OUString& sql )
898 : throw (SQLException, RuntimeException)
899 : {
900 0 : osl::MutexGuard guard( m_refMutex->mutex );
901 0 : checkClosed();
902 0 : OString cmd = OUStringToOString( sql, m_pSettings );
903 :
904 0 : m_lastResultset.clear();
905 0 : m_lastTableInserted = rtl::OUString();
906 :
907 0 : struct CommandData data;
908 0 : data.refMutex = m_refMutex;
909 0 : data.ppSettings = &m_pSettings;
910 0 : data.pLastOidInserted = &m_lastOidInserted;
911 0 : data.pLastQuery = &m_lastQuery;
912 0 : data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
913 0 : data.pMultipleResultAvailable = &m_multipleResultAvailable;
914 0 : data.pLastTableInserted = &m_lastTableInserted;
915 0 : data.pLastResultset = &m_lastResultset;
916 0 : data.owner = *this;
917 0 : data.tableSupplier = Reference< com::sun::star::sdbcx::XTablesSupplier >( m_connection, UNO_QUERY );
918 : data.concurrency =
919 0 : extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY );
920 0 : return executePostgresCommand( cmd , &data );
921 : }
922 :
923 0 : Reference< XConnection > Statement::getConnection( )
924 : throw (SQLException, RuntimeException)
925 : {
926 0 : Reference< XConnection > ret;
927 : {
928 0 : MutexGuard guard( m_refMutex->mutex );
929 0 : checkClosed();
930 0 : ret = m_connection;
931 : }
932 0 : return ret;
933 : }
934 :
935 :
936 0 : Any Statement::getWarnings( )
937 : throw (SQLException,RuntimeException)
938 : {
939 0 : return Any();
940 : }
941 :
942 0 : void Statement::clearWarnings( )
943 : throw (SQLException, RuntimeException)
944 : {
945 0 : }
946 :
947 0 : Reference< ::com::sun::star::sdbc::XResultSetMetaData > Statement::getMetaData()
948 : throw (SQLException,RuntimeException)
949 : {
950 0 : Reference< com::sun::star::sdbc::XResultSetMetaData > ret;
951 0 : Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY );
952 0 : if( supplier.is() )
953 0 : ret = supplier->getMetaData();
954 0 : return ret;
955 : }
956 :
957 :
958 0 : ::cppu::IPropertyArrayHelper & Statement::getInfoHelper()
959 : {
960 0 : return getStatementPropertyArrayHelper();
961 : }
962 :
963 :
964 0 : sal_Bool Statement::convertFastPropertyValue(
965 : Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
966 : throw (IllegalArgumentException)
967 : {
968 0 : rOldValue = m_props[nHandle];
969 : sal_Bool bRet;
970 0 : switch( nHandle )
971 : {
972 : case STATEMENT_CURSOR_NAME:
973 : {
974 0 : OUString val;
975 0 : bRet = ( rValue >>= val );
976 0 : rConvertedValue = makeAny( val );
977 0 : break;
978 : }
979 : case STATEMENT_ESCAPE_PROCESSING:
980 : {
981 : sal_Bool val;
982 0 : bRet = ( rValue >>= val );
983 0 : rConvertedValue = makeAny( val );
984 : break;
985 : }
986 : case STATEMENT_FETCH_DIRECTION:
987 : case STATEMENT_FETCH_SIZE:
988 : case STATEMENT_MAX_FIELD_SIZE:
989 : case STATEMENT_MAX_ROWS:
990 : case STATEMENT_QUERY_TIME_OUT:
991 : case STATEMENT_RESULT_SET_CONCURRENCY:
992 : case STATEMENT_RESULT_SET_TYPE:
993 : {
994 : sal_Int32 val;
995 0 : bRet = ( rValue >>= val );
996 0 : rConvertedValue = makeAny( val );
997 : break;
998 : }
999 : default:
1000 : {
1001 0 : OUStringBuffer buf(128);
1002 0 : buf.appendAscii( "pq_statement: Invalid property handle (" );
1003 0 : buf.append( nHandle );
1004 0 : buf.appendAscii( ")" );
1005 0 : throw IllegalArgumentException( buf.makeStringAndClear(), *this, 2 );
1006 : }
1007 : }
1008 0 : return bRet;
1009 : }
1010 :
1011 :
1012 0 : void Statement::setFastPropertyValue_NoBroadcast(
1013 : sal_Int32 nHandle,const Any& rValue ) throw (Exception)
1014 : {
1015 0 : m_props[nHandle] = rValue;
1016 0 : }
1017 :
1018 0 : void Statement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
1019 : {
1020 0 : rValue = m_props[nHandle];
1021 0 : }
1022 :
1023 0 : Reference < XPropertySetInfo > Statement::getPropertySetInfo()
1024 : throw(RuntimeException)
1025 : {
1026 0 : return OPropertySetHelper::createPropertySetInfo( getStatementPropertyArrayHelper() );
1027 : }
1028 :
1029 :
1030 0 : Reference< XResultSet > Statement::getResultSet( )
1031 : throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
1032 : {
1033 0 : return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
1034 : }
1035 :
1036 0 : sal_Int32 Statement::getUpdateCount( )
1037 : throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
1038 : {
1039 0 : return m_multipleResultUpdateCount;
1040 : }
1041 :
1042 0 : sal_Bool Statement::getMoreResults( )
1043 : throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
1044 : {
1045 0 : return sal_False;
1046 : }
1047 :
1048 :
1049 :
1050 0 : void Statement::disposing()
1051 : {
1052 0 : close();
1053 0 : }
1054 :
1055 0 : Reference< XResultSet > Statement::getGeneratedValues( )
1056 : throw (SQLException, RuntimeException)
1057 : {
1058 0 : osl::MutexGuard guard( m_refMutex->mutex );
1059 : return getGeneratedValuesFromLastInsert(
1060 0 : m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery );
1061 : }
1062 :
1063 : }
|