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