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