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