bump product version to 6.3.0.0.beta1
[LibreOffice.git] / connectivity / source / drivers / postgresql / pq_statement.cxx
blob7796cac8cc107798dd325b7536d0d12c1ada2729
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 <comphelper/sequence.hxx>
54 #include <com/sun/star/beans/PropertyAttribute.hpp>
56 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
57 #include <com/sun/star/sdbc/ResultSetType.hpp>
58 #include <com/sun/star/sdbc/SQLException.hpp>
59 #include <com/sun/star/sdbc/XParameters.hpp>
61 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
62 #include <com/sun/star/sdbcx/KeyType.hpp>
63 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
65 #include <com/sun/star/container/XIndexAccess.hpp>
66 #include <com/sun/star/container/XEnumerationAccess.hpp>
68 #include <string.h>
70 using osl::MutexGuard;
73 using com::sun::star::uno::Any;
74 using com::sun::star::uno::Type;
75 using com::sun::star::uno::Sequence;
76 using com::sun::star::uno::Reference;
77 using com::sun::star::uno::XInterface;
78 using com::sun::star::uno::UNO_QUERY;
80 using com::sun::star::lang::IllegalArgumentException;
82 using com::sun::star::sdbc::XCloseable;
83 using com::sun::star::sdbc::XStatement;
84 using com::sun::star::sdbc::XPreparedStatement;
85 using com::sun::star::sdbc::XParameters;
86 using com::sun::star::sdbc::XRow;
87 using com::sun::star::sdbc::XResultSet;
88 using com::sun::star::sdbc::XConnection;
89 using com::sun::star::sdbc::SQLException;
91 using com::sun::star::sdbcx::XColumnsSupplier;
92 using com::sun::star::sdbcx::XKeysSupplier;
94 using com::sun::star::beans::Property;
95 using com::sun::star::beans::XPropertySetInfo;
96 using com::sun::star::beans::XPropertySet;
98 using com::sun::star::container::XNameAccess;
99 using com::sun::star::container::XEnumerationAccess;
100 using com::sun::star::container::XEnumeration;
101 using com::sun::star::container::XIndexAccess;
103 namespace pq_sdbc_driver
105 static ::cppu::IPropertyArrayHelper & getStatementPropertyArrayHelper()
107 static ::cppu::OPropertyArrayHelper arrayHelper(
108 Sequence<Property>{
109 Property(
110 "CursorName", 0,
111 ::cppu::UnoType<OUString>::get() , 0 ),
112 Property(
113 "EscapeProcessing", 1,
114 cppu::UnoType<bool>::get() , 0 ),
115 Property(
116 "FetchDirection", 2,
117 ::cppu::UnoType<sal_Int32>::get() , 0 ),
118 Property(
119 "FetchSize", 3,
120 ::cppu::UnoType<sal_Int32>::get() , 0 ),
121 Property(
122 "MaxFieldSize", 4,
123 ::cppu::UnoType<sal_Int32>::get() , 0 ),
124 Property(
125 "MaxRows", 5,
126 ::cppu::UnoType<sal_Int32>::get() , 0 ),
127 Property(
128 "QueryTimeOut", 6,
129 ::cppu::UnoType<sal_Int32>::get() , 0 ),
130 Property(
131 "ResultSetConcurrency", 7,
132 ::cppu::UnoType<sal_Int32>::get() , 0 ),
133 Property(
134 "ResultSetType", 8,
135 ::cppu::UnoType<sal_Int32>::get() , 0 )},
136 true );
138 return arrayHelper;
141 Statement::Statement( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
142 const Reference< XConnection > & conn,
143 struct ConnectionSettings *pSettings )
144 : Statement_BASE( refMutex->GetMutex() )
145 , OPropertySetHelper( Statement_BASE::rBHelper )
146 , m_connection( conn )
147 , m_pSettings( pSettings )
148 , m_xMutex( refMutex )
149 , m_multipleResultAvailable(false)
150 , m_multipleResultUpdateCount(0)
151 , m_lastOidInserted(InvalidOid)
153 m_props[STATEMENT_QUERY_TIME_OUT] <<= sal_Int32(0);
154 m_props[STATEMENT_MAX_ROWS] <<= sal_Int32(0);
155 m_props[STATEMENT_RESULT_SET_CONCURRENCY] <<=
156 css::sdbc::ResultSetConcurrency::READ_ONLY;
157 m_props[STATEMENT_RESULT_SET_TYPE] <<=
158 css::sdbc::ResultSetType::SCROLL_INSENSITIVE;
161 Statement::~Statement()
163 POSTGRE_TRACE( "dtor Statement" );
166 void Statement::checkClosed()
168 if( ! m_pSettings || ! m_pSettings->pConnection )
169 throw SQLException(
170 "pq_driver: Statement or connection has already been closed !",
171 *this, OUString(),1,Any());
174 Any Statement::queryInterface( const Type & rType )
176 Any aRet = Statement_BASE::queryInterface(rType);
177 return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType);
181 Sequence< Type > Statement::getTypes()
183 static Sequence< Type > collection(
184 ::comphelper::concatSequences(
185 OPropertySetHelper::getTypes(),
186 Statement_BASE::getTypes()));
188 return collection;
191 Sequence< sal_Int8> Statement::getImplementationId()
193 return css::uno::Sequence<sal_Int8>();
196 void Statement::close( )
198 // let the connection die without acquired mutex !
199 Reference< XConnection > r;
200 Reference< XCloseable > resultSet;
202 MutexGuard guard( m_xMutex->GetMutex() );
203 m_pSettings = nullptr;
204 r = m_connection;
205 m_connection.clear();
207 resultSet = m_lastResultset;
208 m_lastResultset.clear();
210 if( resultSet.is() )
212 resultSet->close();
213 POSTGRE_TRACE( "statement closed" );
218 void Statement::raiseSQLException(
219 const OUString & sql, const char * errorMsg )
221 OUStringBuffer buf(128);
222 buf.append( "pq_driver: ");
223 buf.append(
224 OUString( errorMsg, strlen(errorMsg), ConnectionSettings::encoding ) );
225 buf.append( " (caused by statement '" );
226 buf.append( sql );
227 buf.append( "')" );
228 OUString error = buf.makeStringAndClear();
229 log(m_pSettings, LogLevel::Error, error);
230 throw SQLException( error, *this, OUString(), 1, Any() );
233 Reference< XResultSet > Statement::executeQuery(const OUString& sql )
235 if( ! execute( sql ) )
237 raiseSQLException( sql, "not a query" );
239 return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY );
242 sal_Int32 Statement::executeUpdate( const OUString& sql )
244 if( execute( sql ) )
246 raiseSQLException( sql, "not a command" );
248 return m_multipleResultUpdateCount;
251 /// @throws SQLException
252 static void raiseSQLException(
253 ConnectionSettings *pSettings,
254 const Reference< XInterface> & owner,
255 const OString & sql,
256 const char * errorMsg,
257 const char *errorType = nullptr )
259 OUStringBuffer buf(128);
260 buf.append( "pq_driver: ");
261 if( errorType )
263 buf.append( "[" );
264 buf.appendAscii( errorType );
265 buf.append( "]" );
267 buf.append(
268 OUString( errorMsg, strlen(errorMsg) , ConnectionSettings::encoding ) );
269 buf.append( " (caused by statement '" );
270 buf.append( OStringToOUString( sql, ConnectionSettings::encoding ) );
271 buf.append( "')" );
272 OUString error = buf.makeStringAndClear();
273 log(pSettings, LogLevel::Error, error);
274 throw SQLException( error, owner, OUString(), 1, Any() );
278 // returns the elements of the primary key of the given table
279 // static Sequence< Reference< css::beans::XPropertySet > > lookupKeys(
280 static std::vector< OUString > lookupKeys(
281 const Reference< css::container::XNameAccess > &tables,
282 const OUString & table,
283 OUString *pSchema,
284 OUString *pTable,
285 ConnectionSettings *pSettings)
287 std::vector< OUString > ret;
288 Reference< XKeysSupplier > keySupplier;
289 Statics & st = getStatics();
291 if( tables->hasByName( table ) )
292 tables->getByName( table ) >>= keySupplier;
293 else if( -1 == table.indexOf( '.' ) )
295 // it wasn't a fully qualified name. Now need to skip through all tables.
296 Reference< XEnumerationAccess > enumerationAccess =
297 Reference< XEnumerationAccess > ( tables, UNO_QUERY );
299 Reference< css::container::XEnumeration > enumeration =
300 enumerationAccess->createEnumeration();
301 while( enumeration->hasMoreElements() )
303 Reference< XPropertySet > set;
304 enumeration->nextElement() >>= set;
305 OUString name;
306 // OUString schema;
308 if( set->getPropertyValue( st.NAME ) >>= name )
310 // printf( "searching %s %s\n",
311 // OUStringToOString( schema, RTL_TEXTENCODING_ASCII_US ).getStr(),
312 // OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
313 if( name == table )
316 if( keySupplier.is() )
318 // is ambiguous, as I don't know postgresql searchpath,
319 // I can't continue here, as I may write to a different table
320 keySupplier.clear();
321 if (isLog(pSettings, LogLevel::Info))
323 OStringBuffer buf( 128 );
324 buf.append( "Can't offer updateable result set because table " );
325 buf.append( OUStringToOString(name, ConnectionSettings::encoding) );
326 buf.append( " is duplicated, add schema to resolve ambiguity" );
327 log(pSettings, LogLevel::Info, buf.makeStringAndClear().getStr());
329 break;
331 keySupplier.set( set, UNO_QUERY );
336 else
338 if (isLog(pSettings, LogLevel::Info))
340 OStringBuffer buf( 128 );
341 buf.append( "Can't offer updateable result set ( table " );
342 buf.append( OUStringToOString(table, ConnectionSettings::encoding) );
343 buf.append( " is unknown)" );
344 log(pSettings, LogLevel::Info, buf.makeStringAndClear().getStr());
348 if( keySupplier.is() )
350 Reference< XPropertySet > set( keySupplier, UNO_QUERY );
351 set->getPropertyValue( getStatics().NAME ) >>= *pTable;
352 set->getPropertyValue( getStatics().SCHEMA_NAME ) >>= *pSchema;
353 set.clear();
355 Reference< XEnumerationAccess > keys ( keySupplier->getKeys(), UNO_QUERY );
356 Reference< XEnumeration > enumeration = keys->createEnumeration();
357 while( enumeration->hasMoreElements() )
359 enumeration->nextElement() >>= set;
360 sal_Int32 keyType = 0;
361 if( (set->getPropertyValue( st.TYPE ) >>= keyType ) &&
362 keyType == css::sdbcx::KeyType::PRIMARY )
364 Reference< XColumnsSupplier > columns( set, UNO_QUERY );
365 Reference< XIndexAccess > indexAccess =
366 Reference< XIndexAccess > ( columns->getColumns(), UNO_QUERY );
368 int length = indexAccess->getCount();
369 ret.resize( length );
370 // printf( "primary key for Table %s is ",
371 // OUStringToOString( table, RTL_TEXTENCODING_ASCII_US ).getStr() );
372 for( int i = 0 ; i < length ; i ++ )
374 indexAccess->getByIndex( i ) >>= set;
375 OUString name;
376 set->getPropertyValue( st.NAME ) >>= name;
377 ret[i] = name;
378 // printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
380 // printf( "\n" );
383 if( ret.empty() )
385 if (isLog(pSettings, LogLevel::Info))
387 OStringBuffer buf( 128 );
388 buf.append( "Can't offer updateable result set ( table " );
389 buf.append( OUStringToOString(table, ConnectionSettings::encoding) );
390 buf.append( " does not have a primary key)" );
391 log(pSettings, LogLevel::Info, buf.makeStringAndClear().getStr());
395 return ret;
398 bool executePostgresCommand( const OString & cmd, struct CommandData *data )
400 ConnectionSettings *pSettings = *(data->ppSettings);
402 sal_Int32 duration = osl_getGlobalTimer();
403 PGresult *result = PQexec( pSettings->pConnection, cmd.getStr() );
404 duration = osl_getGlobalTimer() - duration;
405 if( ! result )
406 raiseSQLException(
407 pSettings, data->owner, cmd, PQerrorMessage( pSettings->pConnection ) );
409 ExecStatusType state = PQresultStatus( result );
410 *(data->pLastOidInserted) = 0;
411 data->pLastTableInserted->clear();
412 *(data->pLastQuery) = cmd;
414 bool ret = false;
415 switch( state )
417 case PGRES_COMMAND_OK:
419 *(data->pMultipleResultUpdateCount) = atoi( PQcmdTuples( result ) );
420 *(data->pMultipleResultAvailable) = false;
422 // in case an oid value is available, we retrieve it
423 *(data->pLastOidInserted) = PQoidValue( result );
425 // in case it was a single insert, extract the name of the table,
426 // otherwise the table name is empty
427 *(data->pLastTableInserted) =
428 extractTableFromInsert( OStringToOUString( cmd, ConnectionSettings::encoding ) );
429 if( isLog( pSettings, LogLevel::Sql ) )
431 OStringBuffer buf( 128 );
432 buf.append( "executed command '" );
433 buf.append( cmd.getStr() );
434 buf.append( "' successfully (" );
435 buf.append( *( data->pMultipleResultUpdateCount ) );
436 buf.append( ")" );
437 buf.append( ", duration=" );
438 buf.append( duration );
439 buf.append( "ms" );
440 if( *(data->pLastOidInserted) )
442 buf.append( ", usedOid=" );
443 buf.append( *(data->pLastOidInserted) );
444 buf.append( ", diagnosedTable=" );
445 buf.append(
446 OUStringToOString( *data->pLastTableInserted, ConnectionSettings::encoding ) );
448 log(pSettings, LogLevel::Sql, buf.makeStringAndClear().getStr());
450 PQclear( result );
451 break;
453 case PGRES_TUPLES_OK: // success
455 // In case it is a single table, it has a primary key and all columns
456 // belonging to the primary key are in the result set, allow updateable result sets
457 // otherwise, don't
458 OUString table, schema;
459 std::vector< OString > vec;
460 tokenizeSQL( cmd, vec );
461 OUString sourceTable =
462 OStringToOUString(
463 extractSingleTableFromSelect( vec ), ConnectionSettings::encoding );
465 if( data->concurrency ==
466 css::sdbc::ResultSetConcurrency::UPDATABLE )
468 OString aReason;
469 if( sourceTable.getLength() )
471 std::vector< OUString > sourceTableKeys = lookupKeys(
472 pSettings->tables.is() ?
473 pSettings->tables : data->tableSupplier->getTables() ,
474 sourceTable,
475 &schema,
476 &table,
477 pSettings);
479 // check, whether the columns are in the result set (required !)
480 int i;
481 for( i = 0 ; i < static_cast<int>(sourceTableKeys.size()) ; i ++ )
483 if( -1 == PQfnumber(
484 result,
485 OUStringToOString( sourceTableKeys[i] ,
486 ConnectionSettings::encoding ).getStr()) )
488 break;
492 if( !sourceTableKeys.empty() && i == static_cast<int>(sourceTableKeys.size()) )
494 *(data->pLastResultset) =
495 UpdateableResultSet::createFromPGResultSet(
496 data->refMutex, data->owner, data->ppSettings, result,
497 schema, table,sourceTableKeys );
499 else if( ! table.getLength() )
501 OStringBuffer buf( 128 );
502 buf.append( "can't support updateable resultset, because a single table in the "
503 "WHERE part of the statement could not be identified (" );
504 buf.append( cmd );
505 buf.append( "." );
506 aReason = buf.makeStringAndClear();
508 else if( !sourceTableKeys.empty() )
510 OStringBuffer buf( 128 );
511 buf.append( "can't support updateable resultset for table " );
512 buf.append( OUStringToOString( schema, ConnectionSettings::encoding ) );
513 buf.append( "." );
514 buf.append( OUStringToOString( table, ConnectionSettings::encoding ) );
515 buf.append( ", because resultset does not contain a part of the primary key ( column " );
516 buf.append( OUStringToOString( sourceTableKeys[i], ConnectionSettings::encoding ) );
517 buf.append( " is missing )" );
518 aReason = buf.makeStringAndClear();
520 else
523 OStringBuffer buf( 128 );
524 buf.append( "can't support updateable resultset for table " );
525 buf.append( OUStringToOString( schema, ConnectionSettings::encoding ) );
526 buf.append( "." );
527 buf.append( OUStringToOString( table, ConnectionSettings::encoding ) );
528 buf.append( ", because resultset table does not have a primary key " );
529 aReason = buf.makeStringAndClear();
532 else
534 OStringBuffer buf( 128 );
535 buf.append( "can't support updateable result for selects with multiple tables (" );
536 buf.append( cmd );
537 buf.append( ")" );
538 log(pSettings, LogLevel::Sql, buf.makeStringAndClear().getStr() );
540 if( ! (*(data->pLastResultset)).is() )
542 if (isLog( pSettings, LogLevel::Error))
544 log(pSettings, LogLevel::Error, aReason.getStr());
547 // TODO: How to react here correctly ?
548 // remove this piece of code
549 *(data->pLastResultset) =
550 new FakedUpdateableResultSet(
551 data->refMutex, data->owner,
552 data->ppSettings,result, schema, table,
553 OStringToOUString( aReason, ConnectionSettings::encoding) );
557 else if( sourceTable.getLength() > 0)
559 splitConcatenatedIdentifier( sourceTable, &schema, &table );
562 sal_Int32 returnedRows = PQntuples( result );
563 if( ! data->pLastResultset->is() )
564 *(data->pLastResultset) =
565 Reference< XCloseable > (
566 new ResultSet(
567 data->refMutex, data->owner,
568 data->ppSettings,result, schema, table ) );
569 *(data->pMultipleResultAvailable) = true;
570 ret = true;
571 if (isLog(pSettings, LogLevel::Sql))
573 OStringBuffer buf( 128 );
574 buf.append( "executed query '" );
575 buf.append( cmd );
576 buf.append( "' successfully" );
577 buf.append( ", duration=" );
578 buf.append( duration );
579 buf.append( "ms, returnedRows=" );
580 buf.append( returnedRows );
581 buf.append( "." );
582 log(pSettings, LogLevel::Sql, buf.makeStringAndClear().getStr());
584 break;
586 case PGRES_EMPTY_QUERY:
587 case PGRES_COPY_OUT:
588 case PGRES_COPY_IN:
589 case PGRES_BAD_RESPONSE:
590 case PGRES_NONFATAL_ERROR:
591 case PGRES_FATAL_ERROR:
592 default:
593 raiseSQLException(
594 pSettings, data->owner, cmd, PQresultErrorMessage( result ) , PQresStatus( state ) );
596 return ret;
600 static Sequence< OUString > getPrimaryKeyColumnNames(
601 const Reference< XConnection > & connection, const OUString &schemaName, const OUString &tableName )
603 Sequence< OUString > ret;
605 Int2StringMap mapIndex2Name;
606 fillAttnum2attnameMap( mapIndex2Name, connection, schemaName, tableName );
608 // retrieve the primary key ...
609 Reference< XPreparedStatement > stmt = connection->prepareStatement(
610 "SELECT conkey " // 7
611 "FROM pg_constraint INNER JOIN pg_class ON conrelid = pg_class.oid "
612 "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
613 "LEFT JOIN pg_class AS class2 ON confrelid = class2.oid "
614 "LEFT JOIN pg_namespace AS nmsp2 ON class2.relnamespace=nmsp2.oid "
615 "WHERE pg_class.relname = ? AND pg_namespace.nspname = ? AND pg_constraint.contype='p'" );
616 DisposeGuard guard( stmt );
617 Reference< XParameters > paras( stmt, UNO_QUERY );
618 paras->setString( 1 , tableName );
619 paras->setString( 2 , schemaName );
620 Reference< XResultSet > rs = stmt->executeQuery();
621 Reference< XRow > xRow( rs , UNO_QUERY );
623 if( rs->next() )
625 ret = convertMappedIntArray2StringArray( mapIndex2Name, string2intarray(xRow->getString( 1 ) ) );
627 return ret;
630 static void getAutoValues(
631 String2StringMap & result,
632 const Reference< XConnection > & connection,
633 const OUString &schemaName,
634 const OUString & tableName )
636 Reference< XPreparedStatement > stmt = connection->prepareStatement(
637 "SELECT pg_attribute.attname, pg_attrdef.adsrc "
638 "FROM pg_class, pg_namespace, pg_attribute "
639 "LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND "
640 "pg_attribute.attnum = pg_attrdef.adnum "
641 "WHERE pg_attribute.attrelid = pg_class.oid AND "
642 "pg_class.relnamespace = pg_namespace.oid AND "
643 "pg_namespace.nspname = ? AND "
644 // LEM TODO: this is weird; why "LIKE" and not "="?
645 // Most probably gives problems if tableName contains '%'
646 "pg_class.relname LIKE ? AND "
647 "pg_attrdef.adsrc != ''"
649 DisposeGuard guard( stmt );
650 Reference< XParameters > paras( stmt, UNO_QUERY );
651 paras->setString( 1 , schemaName );
652 paras->setString( 2 , tableName );
653 Reference< XResultSet > rs = stmt->executeQuery();
654 Reference< XRow > xRow( rs , UNO_QUERY );
656 while( rs->next() )
658 result[ OUStringToOString( xRow->getString( 1 ), RTL_TEXTENCODING_ASCII_US) ] =
659 OUStringToOString( xRow->getString(2), RTL_TEXTENCODING_ASCII_US );
663 Reference< XResultSet > getGeneratedValuesFromLastInsert(
664 ConnectionSettings *pConnectionSettings,
665 const Reference< XConnection > &connection,
666 sal_Int32 nLastOid,
667 const OUString & lastTableInserted,
668 const OString & lastQuery )
670 Reference< XResultSet > ret;
671 OUString query;
672 OUString schemaName, tableName;
673 splitConcatenatedIdentifier(
674 lastTableInserted, &schemaName, &tableName );
676 if( nLastOid && lastTableInserted.getLength() )
678 OUStringBuffer buf( 128 );
679 buf.append( "SELECT * FROM " );
680 if( schemaName.getLength() )
681 bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
682 else
683 bufferQuoteIdentifier( buf, lastTableInserted, pConnectionSettings );
684 buf.append( " WHERE oid = " );
685 buf.append( nLastOid );
686 query = buf.makeStringAndClear();
688 else if ( lastTableInserted.getLength() && lastQuery.getLength() )
690 // extract nameValue Pairs
691 String2StringMap namedValues;
692 extractNameValuePairsFromInsert( namedValues, lastQuery );
694 // debug ...
695 // OStringBuffer buf( 128);
696 // buf.append( "extracting name/value from '" );
697 // buf.append( lastQuery.getStr() );
698 // buf.append( "' to [" );
699 // for( String2StringMap::iterator ii = namedValues.begin() ; ii != namedValues.end() ; ++ii )
700 // {
701 // buf.append( ii->first.getStr() );
702 // buf.append( "=" );
703 // buf.append( ii->second.getStr() );
704 // buf.append( "," );
705 // }
706 // buf.append( "]\n" );
707 // printf( "%s", buf.makeStringAndClear() );
709 // TODO: make also unqualified tables names work here. Have a look at 2.8.3. The Schema Search Path
710 // in postgresql doc
712 Sequence< OUString > keyColumnNames = getPrimaryKeyColumnNames( connection, schemaName, tableName );
713 if( keyColumnNames.getLength() )
715 OUStringBuffer buf( 128 );
716 buf.append( "SELECT * FROM " );
717 bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
718 buf.append( " WHERE " );
719 bool bAdditionalCondition = false;
720 String2StringMap autoValues;
721 for( int i = 0 ; i < keyColumnNames.getLength() ; i ++ )
723 OUString value;
724 OString columnName = OUStringToOString( keyColumnNames[i], ConnectionSettings::encoding );
725 bool bColumnMatchNamedValue = false;
726 for (auto const& namedValue : namedValues)
728 if( columnName.equalsIgnoreAsciiCase( namedValue.first ) )
730 value = OStringToOUString( namedValue.second , ConnectionSettings::encoding );
731 bColumnMatchNamedValue = true;
732 break;
736 // check, if a column of the primary key was not inserted explicitly,
737 if( !bColumnMatchNamedValue )
739 if( autoValues.empty() )
741 getAutoValues( autoValues, connection, schemaName, tableName );
743 // this could mean, that the column is a default or auto value, check this ...
744 bool bColumnMatchAutoValue = false;
745 for (auto const& autoValue : autoValues)
747 if( columnName.equalsIgnoreAsciiCase( autoValue.first ) )
749 // it is indeed an auto value.
750 value = OStringToOUString(autoValue.second, RTL_TEXTENCODING_ASCII_US );
751 // check, whether it is a sequence
753 if( autoValue.second.startsWith("nextval(") )
755 // retrieve current sequence value:
756 OUStringBuffer myBuf(128 );
757 myBuf.append( "SELECT currval(" );
758 myBuf.appendAscii( &(autoValue.second.getStr()[8]));
759 value = querySingleValue( connection, myBuf.makeStringAndClear() );
761 bColumnMatchAutoValue = true;
762 break;
765 if( !bColumnMatchAutoValue )
767 // it even was no autovalue, no sense to continue as we can't query the
768 // inserted row
769 buf.truncate();
770 break;
774 if( bAdditionalCondition )
775 buf.append( " AND " );
776 bufferQuoteIdentifier( buf, keyColumnNames[i], pConnectionSettings );
777 buf.append( " = " );
778 buf.append( value );
779 bAdditionalCondition = true;
781 query = buf.makeStringAndClear();
785 if( query.getLength() )
787 Reference< css::sdbc::XStatement > stmt = connection->createStatement();
788 ret = stmt->executeQuery( query );
791 return ret;
795 sal_Bool Statement::execute( const OUString& sql )
797 osl::MutexGuard guard( m_xMutex->GetMutex() );
798 checkClosed();
799 OString cmd = OUStringToOString( sql, m_pSettings );
801 Reference< XCloseable > lastResultSetHolder = m_lastResultset;
802 if( lastResultSetHolder.is() )
803 lastResultSetHolder->close();
805 m_lastResultset.clear();
806 m_lastTableInserted.clear();
808 struct CommandData data;
809 data.refMutex = m_xMutex;
810 data.ppSettings = &m_pSettings;
811 data.pLastOidInserted = &m_lastOidInserted;
812 data.pLastQuery = &m_lastQuery;
813 data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
814 data.pMultipleResultAvailable = &m_multipleResultAvailable;
815 data.pLastTableInserted = &m_lastTableInserted;
816 data.pLastResultset = &m_lastResultset;
817 data.owner = *this;
818 data.tableSupplier.set( m_connection, UNO_QUERY );
819 data.concurrency =
820 extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY );
821 return executePostgresCommand( cmd , &data );
824 Reference< XConnection > Statement::getConnection( )
826 Reference< XConnection > ret;
828 MutexGuard guard( m_xMutex->GetMutex() );
829 checkClosed();
830 ret = m_connection;
832 return ret;
836 Any Statement::getWarnings( )
838 return Any();
841 void Statement::clearWarnings( )
845 Reference< css::sdbc::XResultSetMetaData > Statement::getMetaData()
847 Reference< css::sdbc::XResultSetMetaData > ret;
848 Reference< css::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY );
849 if( supplier.is() )
850 ret = supplier->getMetaData();
851 return ret;
855 ::cppu::IPropertyArrayHelper & Statement::getInfoHelper()
857 return getStatementPropertyArrayHelper();
861 sal_Bool Statement::convertFastPropertyValue(
862 Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
864 rOldValue = m_props[nHandle];
865 bool bRet;
866 switch( nHandle )
868 case STATEMENT_CURSOR_NAME:
870 OUString val;
871 bRet = ( rValue >>= val );
872 rConvertedValue <<= val;
873 break;
875 case STATEMENT_ESCAPE_PROCESSING:
877 bool val(false);
878 bRet = ( rValue >>= val );
879 rConvertedValue <<= val;
880 break;
882 case STATEMENT_FETCH_DIRECTION:
883 case STATEMENT_FETCH_SIZE:
884 case STATEMENT_MAX_FIELD_SIZE:
885 case STATEMENT_MAX_ROWS:
886 case STATEMENT_QUERY_TIME_OUT:
887 case STATEMENT_RESULT_SET_CONCURRENCY:
888 case STATEMENT_RESULT_SET_TYPE:
890 sal_Int32 val;
891 bRet = ( rValue >>= val );
892 rConvertedValue <<= val;
893 break;
895 default:
897 throw IllegalArgumentException(
898 "pq_statement: Invalid property handle ("
899 + OUString::number( nHandle ) + ")",
900 *this, 2 );
903 return bRet;
907 void Statement::setFastPropertyValue_NoBroadcast(
908 sal_Int32 nHandle,const Any& rValue )
910 m_props[nHandle] = rValue;
913 void Statement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
915 rValue = m_props[nHandle];
918 Reference < XPropertySetInfo > Statement::getPropertySetInfo()
920 return OPropertySetHelper::createPropertySetInfo( getStatementPropertyArrayHelper() );
924 Reference< XResultSet > Statement::getResultSet( )
926 return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY );
929 sal_Int32 Statement::getUpdateCount( )
931 return m_multipleResultUpdateCount;
934 sal_Bool Statement::getMoreResults( )
936 // The PostgreSQL C interface always returns a single result,
937 // so we will never have multiple ones.
938 // Implicitly close the open resultset (if any) as per spec,
939 // and setup to signal "no more result, neither as resultset,
940 // nor as update count".
941 Reference< XCloseable > lastResultSetHolder = m_lastResultset;
942 if( lastResultSetHolder.is() )
943 lastResultSetHolder->close();
944 m_multipleResultUpdateCount = -1;
945 return false;
949 void Statement::disposing()
951 close();
954 Reference< XResultSet > Statement::getGeneratedValues( )
956 osl::MutexGuard guard( m_xMutex->GetMutex() );
957 return getGeneratedValuesFromLastInsert(
958 m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery );
963 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */