Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_statement.cxx
blob23727dbf126a45d34ae1d4ed520faa776cbd6fa6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * Effective License of whole file:
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 * MA 02111-1307 USA
20 * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
22 * The Contents of this file are made available subject to the terms of
23 * the GNU Lesser General Public License Version 2.1
25 * Copyright: 2000 by Sun Microsystems, Inc.
27 * Contributor(s): Joerg Budischewski
29 * All parts contributed on or after August 2011:
31 * This Source Code Form is subject to the terms of the Mozilla Public
32 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
35 ************************************************************************/
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"
43 #include <osl/thread.h>
44 #include <osl/time.h>
46 #include <rtl/ustrbuf.hxx>
47 #include <rtl/strbuf.hxx>
49 #include <cppuhelper/typeprovider.hxx>
50 #include <cppuhelper/queryinterface.hxx>
52 #include <com/sun/star/beans/PropertyAttribute.hpp>
54 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
55 #include <com/sun/star/sdbc/ResultSetType.hpp>
56 #include <com/sun/star/sdbc/XParameters.hpp>
58 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
59 #include <com/sun/star/sdbcx/KeyType.hpp>
60 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
62 #include <com/sun/star/container/XIndexAccess.hpp>
63 #include <com/sun/star/container/XEnumerationAccess.hpp>
65 #include <string.h>
67 using osl::Mutex;
68 using osl::MutexGuard;
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;
81 using com::sun::star::lang::IllegalArgumentException;
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;
94 using com::sun::star::sdbcx::XColumnsSupplier;
95 using com::sun::star::sdbcx::XTablesSupplier;
96 using com::sun::star::sdbcx::XKeysSupplier;
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;
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;
109 namespace pq_sdbc_driver
111 static ::cppu::IPropertyArrayHelper & getStatementPropertyArrayHelper()
113 static ::cppu::IPropertyArrayHelper *pArrayHelper;
114 if( ! pArrayHelper )
116 MutexGuard guard( Mutex::getGlobalMutex() );
117 if( ! pArrayHelper )
119 static Property aTable[] =
121 Property(
122 OUString("CursorName"), 0,
123 ::cppu::UnoType<OUString>::get() , 0 ),
124 Property(
125 OUString("EscapeProcessing"), 1,
126 ::getBooleanCppuType() , 0 ),
127 Property(
128 OUString("FetchDirection"), 2,
129 ::cppu::UnoType<sal_Int32>::get() , 0 ),
130 Property(
131 OUString("FetchSize"), 3,
132 ::cppu::UnoType<sal_Int32>::get() , 0 ),
133 Property(
134 OUString("MaxFieldSize"), 4,
135 ::cppu::UnoType<sal_Int32>::get() , 0 ),
136 Property(
137 OUString("MaxRows"), 5,
138 ::cppu::UnoType<sal_Int32>::get() , 0 ),
139 Property(
140 OUString("QueryTimeOut"), 6,
141 ::cppu::UnoType<sal_Int32>::get() , 0 ),
142 Property(
143 OUString("ResultSetConcurrency"), 7,
144 ::cppu::UnoType<sal_Int32>::get() , 0 ),
145 Property(
146 OUString("ResultSetType"), 8,
147 ::cppu::UnoType<sal_Int32>::get() , 0 )
149 OSL_ASSERT( sizeof(aTable)/ sizeof(Property) == STATEMENT_SIZE );
150 static ::cppu::OPropertyArrayHelper arrayHelper( aTable, STATEMENT_SIZE, sal_True );
151 pArrayHelper = &arrayHelper;
154 return *pArrayHelper;
157 Statement::Statement( const ::rtl::Reference< RefCountedMutex > & refMutex,
158 const Reference< XConnection > & conn,
159 struct ConnectionSettings *pSettings )
160 : 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 , m_lastOidInserted(InvalidOid)
169 m_props[STATEMENT_QUERY_TIME_OUT] = makeAny( (sal_Int32)0 );
170 m_props[STATEMENT_MAX_ROWS] = makeAny( (sal_Int32)0 );
171 m_props[STATEMENT_RESULT_SET_CONCURRENCY] = makeAny(
172 com::sun::star::sdbc::ResultSetConcurrency::READ_ONLY );
173 m_props[STATEMENT_RESULT_SET_TYPE] = makeAny(
174 com::sun::star::sdbc::ResultSetType::SCROLL_INSENSITIVE );
177 Statement::~Statement()
179 POSTGRE_TRACE( "dtor Statement" );
182 void Statement::checkClosed() throw (SQLException, RuntimeException )
184 if( ! m_pSettings || ! m_pSettings->pConnection )
185 throw SQLException(
186 "pq_driver: Statement or connection has already been closed !",
187 *this, OUString(),1,Any());
190 Any Statement::queryInterface( const Type & reqType ) throw (RuntimeException, std::exception)
192 Any ret;
194 ret = OComponentHelper::queryInterface( reqType );
195 if( ! ret.hasValue() )
196 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 static_cast< XFastPropertySet * > ( this ) );
205 return ret;
209 Sequence< Type > Statement::getTypes() throw ( RuntimeException, std::exception )
211 static cppu::OTypeCollection *pCollection;
212 if( ! pCollection )
214 MutexGuard guard( osl::Mutex::getGlobalMutex() );
215 if( !pCollection )
217 static cppu::OTypeCollection collection(
218 getCppuType( (Reference< XWarningsSupplier> *) 0 ),
219 getCppuType( (Reference< XStatement> *) 0 ),
220 getCppuType( (Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier> *) 0 ),
221 getCppuType( (Reference< XCloseable> *) 0 ),
222 getCppuType( (Reference< XPropertySet >*) 0 ),
223 getCppuType( (Reference< XFastPropertySet > *) 0 ),
224 getCppuType( (Reference< XMultiPropertySet > *) 0 ),
225 getCppuType( (Reference< XGeneratedResultSet > *) 0 ),
226 OComponentHelper::getTypes());
227 pCollection = &collection;
230 return pCollection->getTypes();
233 Sequence< sal_Int8> Statement::getImplementationId() throw ( RuntimeException, std::exception )
235 return css::uno::Sequence<sal_Int8>();
238 void Statement::close( ) throw (SQLException, RuntimeException, std::exception)
240 // let the connection die without acquired mutex !
241 Reference< XConnection > r;
242 Reference< XCloseable > resultSet;
244 MutexGuard guard( m_refMutex->mutex );
245 m_pSettings = 0;
246 r = m_connection;
247 m_connection.clear();
249 resultSet = m_lastResultset;
250 m_lastResultset.clear();
252 if( resultSet.is() )
254 resultSet->close();
255 POSTGRE_TRACE( "statement closed" );
260 void Statement::raiseSQLException(
261 const OUString & sql, const char * errorMsg, const char *errorType )
262 throw( SQLException )
264 OUStringBuffer buf(128);
265 buf.appendAscii( "pq_driver: ");
266 if( errorType )
268 buf.appendAscii( "[" );
269 buf.appendAscii( errorType );
270 buf.appendAscii( "]" );
272 buf.append(
273 OUString( errorMsg, strlen(errorMsg) , m_pSettings->encoding ) );
274 buf.appendAscii( " (caused by statement '" );
275 buf.append( sql );
276 buf.appendAscii( "')" );
277 OUString error = buf.makeStringAndClear();
278 log( m_pSettings, LogLevel::ERROR, error );
279 throw SQLException( error, *this, OUString(), 1, Any() );
282 Reference< XResultSet > Statement::executeQuery(const OUString& sql )
283 throw (SQLException, RuntimeException, std::exception)
285 Reference< XCloseable > lastResultSetHolder = m_lastResultset;
286 if( lastResultSetHolder.is() )
287 lastResultSetHolder->close();
289 if( ! execute( sql ) )
291 raiseSQLException( sql, "not a query" );
293 return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
296 sal_Int32 Statement::executeUpdate( const OUString& sql )
297 throw (SQLException, RuntimeException, std::exception)
299 if( execute( sql ) )
301 raiseSQLException( sql, "not a command" );
303 return m_multipleResultUpdateCount;
307 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 )
315 OUStringBuffer buf(128);
316 buf.appendAscii( "pq_driver: ");
317 if( errorType )
319 buf.appendAscii( "[" );
320 buf.appendAscii( errorType );
321 buf.appendAscii( "]" );
323 buf.append(
324 OUString( errorMsg, strlen(errorMsg) , pSettings->encoding ) );
325 buf.appendAscii( " (caused by statement '" );
326 buf.append( OStringToOUString( sql, pSettings->encoding ) );
327 buf.appendAscii( "')" );
328 OUString error = buf.makeStringAndClear();
329 log( pSettings, LogLevel::ERROR, error );
330 throw SQLException( error, owner, OUString(), 1, Any() );
334 // returns the elements of the primary key of the given table
335 // static Sequence< Reference< com::sun::star::beans::XPropertySet > > lookupKeys(
336 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)
343 Sequence< OUString > ret;
344 Reference< XKeysSupplier > keySupplier;
345 Statics & st = getStatics();
347 if( tables->hasByName( table ) )
348 tables->getByName( table ) >>= keySupplier;
349 else if( -1 == table.indexOf( '.' ) )
351 // it wasn't a fully qualified name. Now need to skip through all tables.
352 Reference< XEnumerationAccess > enumerationAccess =
353 Reference< XEnumerationAccess > ( tables, UNO_QUERY );
355 Reference< com::sun::star::container::XEnumeration > enumeration =
356 enumerationAccess->createEnumeration();
357 while( enumeration->hasMoreElements() )
359 Reference< XPropertySet > set;
360 enumeration->nextElement() >>= set;
361 OUString name;
362 // OUString schema;
364 if( set->getPropertyValue( st.NAME ) >>= name )
366 // printf( "searching %s %s\n",
367 // OUStringToOString( schema, RTL_TEXTENCODING_ASCII_US ).getStr(),
368 // OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
369 if( name == table )
372 if( keySupplier.is() )
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 keySupplier.clear();
377 if( isLog( pSettings, LogLevel::INFO ) )
379 OStringBuffer buf( 128 );
380 buf.append( "Can't offer updateable result set because table " );
381 buf.append( OUStringToOString(name, pSettings->encoding) );
382 buf.append( " is duplicated, add schema to resolve ambiguity" );
383 log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
385 break;
387 keySupplier = Reference< XKeysSupplier > ( set, UNO_QUERY );
392 else
394 if( isLog( pSettings, LogLevel::INFO ) )
396 OStringBuffer buf( 128 );
397 buf.append( "Can't offer updateable result set ( table " );
398 buf.append( OUStringToOString(table, pSettings->encoding) );
399 buf.append( " is unknown)" );
400 log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
404 if( keySupplier.is() )
406 Reference< XPropertySet > set( keySupplier, UNO_QUERY );
407 set->getPropertyValue( getStatics().NAME ) >>= (*pTable);
408 set->getPropertyValue( getStatics().SCHEMA_NAME ) >>= (*pSchema );
409 set.clear();
411 Reference< XEnumerationAccess > keys ( keySupplier->getKeys(), UNO_QUERY );
412 Reference< XEnumeration > enumeration = keys->createEnumeration();
413 while( enumeration->hasMoreElements() )
415 enumeration->nextElement() >>= set;
416 sal_Int32 keyType = 0;
417 if( (set->getPropertyValue( st.TYPE ) >>= keyType ) &&
418 keyType == com::sun::star::sdbcx::KeyType::PRIMARY )
420 Reference< XColumnsSupplier > columns( set, UNO_QUERY );
421 Reference< XIndexAccess > indexAccess =
422 Reference< XIndexAccess > ( columns->getColumns(), UNO_QUERY );
424 int length = indexAccess->getCount();
425 ret.realloc( length );
426 // printf( "primary key for Table %s is ",
427 // OUStringToOString( table, RTL_TEXTENCODING_ASCII_US ).getStr() );
428 for( int i = 0 ; i < length ; i ++ )
430 indexAccess->getByIndex( i ) >>= set;
431 OUString name;
432 set->getPropertyValue( st.NAME ) >>= name;
433 ret[i] = name;
434 // printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
436 // printf( "\n" );
439 if( ! ret.getLength() )
441 if( isLog( pSettings, LogLevel::INFO ) )
443 OStringBuffer buf( 128 );
444 buf.append( "Can't offer updateable result set ( table " );
445 buf.append( OUStringToOString(table, pSettings->encoding) );
446 buf.append( " does not have a primary key)" );
447 log( pSettings, LogLevel::INFO, buf.makeStringAndClear().getStr() );
451 return ret;
454 bool executePostgresCommand( const OString & cmd, struct CommandData *data )
456 ConnectionSettings *pSettings = *(data->ppSettings);
458 sal_Int32 duration = osl_getGlobalTimer();
459 PGresult *result = PQexec( pSettings->pConnection, cmd.getStr() );
460 duration = osl_getGlobalTimer() - duration;
461 if( ! result )
462 raiseSQLException(
463 pSettings, data->owner, cmd, PQerrorMessage( pSettings->pConnection ) );
465 ExecStatusType state = PQresultStatus( result );
466 *(data->pLastOidInserted) = 0;
467 *(data->pLastTableInserted) = OUString();
468 *(data->pLastQuery) = cmd;
470 bool ret = false;
471 switch( state )
473 case PGRES_COMMAND_OK:
475 *(data->pMultipleResultUpdateCount) = atoi( PQcmdTuples( result ) );
476 *(data->pMultipleResultAvailable) = false;
478 // in case an oid value is available, we retrieve it
479 *(data->pLastOidInserted) = PQoidValue( result );
481 // in case it was a single insert, extract the name of the table,
482 // otherwise the table name is empty
483 *(data->pLastTableInserted) =
484 extractTableFromInsert( OStringToOUString( cmd, pSettings->encoding ) );
485 if( isLog( pSettings, LogLevel::SQL ) )
487 OStringBuffer buf( 128 );
488 buf.append( "executed command '" );
489 buf.append( cmd.getStr() );
490 buf.append( "' successfully (" );
491 buf.append( *( data->pMultipleResultUpdateCount ) );
492 buf.append( ")" );
493 buf.append( ", duration=" );
494 buf.append( duration );
495 buf.append( "ms" );
496 if( *(data->pLastOidInserted) )
498 buf.append( ", usedOid=" );
499 buf.append( *(data->pLastOidInserted) , 10 );
500 buf.append( ", diagnosedTable=" );
501 buf.append(
502 OUStringToOString( *data->pLastTableInserted, pSettings->encoding ) );
504 log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
506 PQclear( result );
507 break;
509 case PGRES_TUPLES_OK: // success
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 OUString table, schema;
515 Sequence< OUString > sourceTableKeys;
516 OStringVector vec;
517 tokenizeSQL( cmd, vec );
518 OUString sourceTable =
519 OStringToOUString(
520 extractSingleTableFromSelect( vec ), pSettings->encoding );
522 if( data->concurrency ==
523 com::sun::star::sdbc::ResultSetConcurrency::UPDATABLE )
525 OString aReason;
526 if( sourceTable.getLength() )
528 sourceTableKeys = lookupKeys(
529 pSettings->tables.is() ?
530 pSettings->tables : data->tableSupplier->getTables() ,
531 sourceTable,
532 &schema,
533 &table,
534 pSettings);
536 // check, whether the columns are in the result set (required !)
537 int i;
538 for( i = 0 ; i < sourceTableKeys.getLength() ; i ++ )
540 if( -1 == PQfnumber(
541 result,
542 OUStringToOString( sourceTableKeys[i] ,
543 pSettings->encoding ).getStr()) )
545 break;
549 if( sourceTableKeys.getLength() && i == sourceTableKeys.getLength() )
551 *(data->pLastResultset) =
552 UpdateableResultSet::createFromPGResultSet(
553 data->refMutex, data->owner, data->ppSettings, result,
554 schema, table,sourceTableKeys );
556 else if( ! table.getLength() )
558 OStringBuffer buf( 128 );
559 buf.append( "can't support updateable resultset, because a single table in the "
560 "WHERE part of the statement could not be identified (" );
561 buf.append( cmd );
562 buf.append( "." );
563 aReason = buf.makeStringAndClear();
565 else if( sourceTableKeys.getLength() )
567 OStringBuffer buf( 128 );
568 buf.append( "can't support updateable resultset for table " );
569 buf.append( OUStringToOString( schema, pSettings->encoding ) );
570 buf.append( "." );
571 buf.append( OUStringToOString( table, pSettings->encoding ) );
572 buf.append( ", because resultset does not contain a part of the primary key ( column " );
573 buf.append( OUStringToOString( sourceTableKeys[i], pSettings->encoding ) );
574 buf.append( " is missing )" );
575 aReason = buf.makeStringAndClear();
577 else
580 OStringBuffer buf( 128 );
581 buf.append( "can't support updateable resultset for table " );
582 buf.append( OUStringToOString( schema, pSettings->encoding ) );
583 buf.append( "." );
584 buf.append( OUStringToOString( table, pSettings->encoding ) );
585 buf.append( ", because resultset table does not have a primary key " );
586 aReason = buf.makeStringAndClear();
589 else
591 OStringBuffer buf( 128 );
592 buf.append( "can't support updateable result for selects with multiple tables (" );
593 buf.append( cmd );
594 buf.append( ")" );
595 log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
597 if( ! (*(data->pLastResultset)).is() )
599 if( isLog( pSettings, LogLevel::ERROR ) )
601 log( pSettings, LogLevel::ERROR, aReason.getStr());
604 // TODO: How to react here correctly ?
605 // remove this piece of code
606 *(data->pLastResultset) =
607 new FakedUpdateableResultSet(
608 data->refMutex, data->owner,
609 data->ppSettings,result, schema, table,
610 OStringToOUString( aReason, pSettings->encoding) );
614 else if( sourceTable.getLength() > 0)
616 splitConcatenatedIdentifier( sourceTable, &schema, &table );
619 sal_Int32 returnedRows = PQntuples( result );
620 if( ! data->pLastResultset->is() )
621 *(data->pLastResultset) =
622 Reference< XCloseable > (
623 new ResultSet(
624 data->refMutex, data->owner,
625 data->ppSettings,result, schema, table ) );
626 *(data->pMultipleResultAvailable) = true;
627 ret = true;
628 if( isLog( pSettings, LogLevel::SQL ) )
630 OStringBuffer buf( 128 );
631 buf.append( "executed query '" );
632 buf.append( cmd );
633 buf.append( "' successfully" );
634 buf.append( ", duration=" );
635 buf.append( duration );
636 buf.append( "ms, returnedRows=" );
637 buf.append( returnedRows );
638 buf.append( "." );
639 log( pSettings, LogLevel::SQL, buf.makeStringAndClear().getStr() );
641 break;
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 pSettings, data->owner, cmd, PQresultErrorMessage( result ) , PQresStatus( state ) );
653 return ret;
657 static Sequence< OUString > getPrimaryKeyColumnNames(
658 const Reference< XConnection > & connection, const OUString &schemaName, const OUString &tableName )
660 Sequence< OUString > ret;
662 Int2StringMap mapIndex2Name;
663 fillAttnum2attnameMap( mapIndex2Name, connection, schemaName, tableName );
665 // retrieve the primary key ...
666 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 "WHERE pg_class.relname = ? AND pg_namespace.nspname = ? AND pg_constraint.contype='p'" );
673 DisposeGuard guard( stmt );
674 Reference< XParameters > paras( stmt, UNO_QUERY );
675 paras->setString( 1 , tableName );
676 paras->setString( 2 , schemaName );
677 Reference< XResultSet > rs = stmt->executeQuery();
678 Reference< XRow > xRow( rs , UNO_QUERY );
680 if( rs->next() )
682 ret = convertMappedIntArray2StringArray( mapIndex2Name, string2intarray(xRow->getString( 1 ) ) );
684 return ret;
687 static void getAutoValues(
688 String2StringMap & result,
689 const Reference< XConnection > & connection,
690 const OUString &schemaName,
691 const OUString & tableName )
693 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 != ''"
706 DisposeGuard guard( stmt );
707 Reference< XParameters > paras( stmt, UNO_QUERY );
708 paras->setString( 1 , schemaName );
709 paras->setString( 2 , tableName );
710 Reference< XResultSet > rs = stmt->executeQuery();
711 Reference< XRow > xRow( rs , UNO_QUERY );
713 while( rs->next() )
715 result[ OUStringToOString( xRow->getString( 1 ), RTL_TEXTENCODING_ASCII_US) ] =
716 OUStringToOString( xRow->getString(2), RTL_TEXTENCODING_ASCII_US );
720 Reference< XResultSet > getGeneratedValuesFromLastInsert(
721 ConnectionSettings *pConnectionSettings,
722 const Reference< XConnection > &connection,
723 sal_Int32 nLastOid,
724 const OUString & lastTableInserted,
725 const OString & lastQuery )
727 Reference< XResultSet > ret;
728 OUString query;
729 OUString schemaName, tableName;
730 splitConcatenatedIdentifier(
731 lastTableInserted, &schemaName, &tableName );
733 if( nLastOid && lastTableInserted.getLength() )
735 OUStringBuffer buf( 128 );
736 buf.append( "SELECT * FROM " );
737 if( schemaName.getLength() )
738 bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
739 else
740 bufferQuoteIdentifier( buf, lastTableInserted, pConnectionSettings );
741 buf.append( " WHERE oid = " );
742 buf.append( nLastOid , 10 );
743 query = buf.makeStringAndClear();
745 else if ( lastTableInserted.getLength() && lastQuery.getLength() )
747 // extract nameValue Pairs
748 String2StringMap namedValues;
749 extractNameValuePairsFromInsert( namedValues, lastQuery );
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() );
766 // TODO: make also unqualified tables names work here. Have a look at 2.8.3. The Schema Search Path
767 // in postgresql doc
769 Sequence< OUString > keyColumnNames = getPrimaryKeyColumnNames( connection, schemaName, tableName );
770 if( keyColumnNames.getLength() )
772 OUStringBuffer buf( 128 );
773 buf.append( "SELECT * FROM " );
774 bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
775 buf.append( " WHERE " );
776 bool additionalCondition = false;
777 String2StringMap autoValues;
778 for( int i = 0 ; i < keyColumnNames.getLength() ; i ++ )
780 OUString value;
781 OString columnName = OUStringToOString( keyColumnNames[i], pConnectionSettings->encoding );
782 String2StringMap::iterator ii = namedValues.begin();
783 for( ; ii != namedValues.end() ; ++ii )
785 if( columnName.equalsIgnoreAsciiCase( ii->first ) )
787 value = OStringToOUString( ii->second , pConnectionSettings->encoding );
788 break;
792 // check, if a column of the primary key was not inserted explicitly,
793 if( ii == namedValues.end() )
796 if( autoValues.begin() == autoValues.end() )
798 getAutoValues( autoValues, connection, schemaName, tableName );
800 // this could mean, that the column is a default or auto value, check this ...
801 String2StringMap::iterator j = autoValues.begin();
802 for( ; j != autoValues.end() ; ++j )
804 if( columnName.equalsIgnoreAsciiCase( j->first ) )
806 // it is indeed an auto value.
807 value = OStringToOUString(j->second, RTL_TEXTENCODING_ASCII_US );
808 // check, whether it is a sequence
810 if( rtl_str_shortenedCompare_WithLength(
811 j->second.getStr(), j->second.getLength(),
812 RTL_CONSTASCII_STRINGPARAM( "nextval(" ), 8 ) == 0 )
814 // retrieve current sequence value:
815 OUStringBuffer myBuf(128 );
816 myBuf.append( "SELECT currval(" );
817 myBuf.appendAscii( &(j->second.getStr()[8]));
818 value = querySingleValue( connection, myBuf.makeStringAndClear() );
820 break;
823 if( j == autoValues.end() )
825 // it even was no autovalue, no sense to continue as we can't query the
826 // inserted row
827 buf = OUStringBuffer();
828 break;
832 if( additionalCondition )
833 buf.append( " AND " );
834 bufferQuoteIdentifier( buf, keyColumnNames[i], pConnectionSettings );
835 buf.append( " = " );
836 buf.append( value );
837 additionalCondition = true;
839 query = buf.makeStringAndClear();
843 if( query.getLength() )
845 Reference< com::sun::star::sdbc::XStatement > stmt = connection->createStatement();
846 ret = stmt->executeQuery( query );
849 return ret;
853 sal_Bool Statement::execute( const OUString& sql )
854 throw (SQLException, RuntimeException, std::exception)
856 osl::MutexGuard guard( m_refMutex->mutex );
857 checkClosed();
858 OString cmd = OUStringToOString( sql, m_pSettings );
860 m_lastResultset.clear();
861 m_lastTableInserted = OUString();
863 struct CommandData data;
864 data.refMutex = m_refMutex;
865 data.ppSettings = &m_pSettings;
866 data.pLastOidInserted = &m_lastOidInserted;
867 data.pLastQuery = &m_lastQuery;
868 data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
869 data.pMultipleResultAvailable = &m_multipleResultAvailable;
870 data.pLastTableInserted = &m_lastTableInserted;
871 data.pLastResultset = &m_lastResultset;
872 data.owner = *this;
873 data.tableSupplier = Reference< com::sun::star::sdbcx::XTablesSupplier >( m_connection, UNO_QUERY );
874 data.concurrency =
875 extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY );
876 return executePostgresCommand( cmd , &data );
879 Reference< XConnection > Statement::getConnection( )
880 throw (SQLException, RuntimeException, std::exception)
882 Reference< XConnection > ret;
884 MutexGuard guard( m_refMutex->mutex );
885 checkClosed();
886 ret = m_connection;
888 return ret;
892 Any Statement::getWarnings( )
893 throw (SQLException,RuntimeException, std::exception)
895 return Any();
898 void Statement::clearWarnings( )
899 throw (SQLException, RuntimeException, std::exception)
903 Reference< ::com::sun::star::sdbc::XResultSetMetaData > Statement::getMetaData()
904 throw (SQLException,RuntimeException, std::exception)
906 Reference< com::sun::star::sdbc::XResultSetMetaData > ret;
907 Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY );
908 if( supplier.is() )
909 ret = supplier->getMetaData();
910 return ret;
914 ::cppu::IPropertyArrayHelper & Statement::getInfoHelper()
916 return getStatementPropertyArrayHelper();
920 sal_Bool Statement::convertFastPropertyValue(
921 Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
922 throw (IllegalArgumentException)
924 rOldValue = m_props[nHandle];
925 bool bRet;
926 switch( nHandle )
928 case STATEMENT_CURSOR_NAME:
930 OUString val;
931 bRet = ( rValue >>= val );
932 rConvertedValue = makeAny( val );
933 break;
935 case STATEMENT_ESCAPE_PROCESSING:
937 bool val(false);
938 bRet = ( rValue >>= val );
939 rConvertedValue = makeAny( val );
940 break;
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:
950 sal_Int32 val;
951 bRet = ( rValue >>= val );
952 rConvertedValue = makeAny( val );
953 break;
955 default:
957 OUStringBuffer buf(128);
958 buf.appendAscii( "pq_statement: Invalid property handle (" );
959 buf.append( nHandle );
960 buf.appendAscii( ")" );
961 throw IllegalArgumentException( buf.makeStringAndClear(), *this, 2 );
964 return bRet;
968 void Statement::setFastPropertyValue_NoBroadcast(
969 sal_Int32 nHandle,const Any& rValue ) throw (Exception, std::exception)
971 m_props[nHandle] = rValue;
974 void Statement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
976 rValue = m_props[nHandle];
979 Reference < XPropertySetInfo > Statement::getPropertySetInfo()
980 throw(RuntimeException, std::exception)
982 return OPropertySetHelper::createPropertySetInfo( getStatementPropertyArrayHelper() );
986 Reference< XResultSet > Statement::getResultSet( )
987 throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException, std::exception)
989 return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
992 sal_Int32 Statement::getUpdateCount( )
993 throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException, std::exception)
995 return m_multipleResultUpdateCount;
998 sal_Bool Statement::getMoreResults( )
999 throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException, std::exception)
1001 return sal_False;
1006 void Statement::disposing()
1008 close();
1011 Reference< XResultSet > Statement::getGeneratedValues( )
1012 throw (SQLException, RuntimeException, std::exception)
1014 osl::MutexGuard guard( m_refMutex->mutex );
1015 return getGeneratedValuesFromLastInsert(
1016 m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery );
1021 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */