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,
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>
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>
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(
111 ::cppu::UnoType
<OUString
>::get() , 0 ),
113 "EscapeProcessing", 1,
114 cppu::UnoType
<bool>::get() , 0 ),
117 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
120 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
123 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
126 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
129 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
131 "ResultSetConcurrency", 7,
132 ::cppu::UnoType
<sal_Int32
>::get() , 0 ),
135 ::cppu::UnoType
<sal_Int32
>::get() , 0 )},
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
)
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()));
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;
205 m_connection
.clear();
207 resultSet
= m_lastResultset
;
208 m_lastResultset
.clear();
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: ");
224 OUString( errorMsg
, strlen(errorMsg
), ConnectionSettings::encoding
) );
225 buf
.append( " (caused by statement '" );
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
)
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
,
256 const char * errorMsg
,
257 const char *errorType
= nullptr )
259 OUStringBuffer
buf(128);
260 buf
.append( "pq_driver: ");
264 buf
.appendAscii( errorType
);
268 OUString( errorMsg
, strlen(errorMsg
) , ConnectionSettings::encoding
) );
269 buf
.append( " (caused by statement '" );
270 buf
.append( OStringToOUString( sql
, ConnectionSettings::encoding
) );
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
,
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
;
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() );
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
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());
331 keySupplier
.set( set
, UNO_QUERY
);
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
;
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
;
376 set
->getPropertyValue( st
.NAME
) >>= name
;
378 // printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
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());
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
;
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
;
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
) );
437 buf
.append( ", duration=" );
438 buf
.append( duration
);
440 if( *(data
->pLastOidInserted
) )
442 buf
.append( ", usedOid=" );
443 buf
.append( *(data
->pLastOidInserted
) );
444 buf
.append( ", diagnosedTable=" );
446 OUStringToOString( *data
->pLastTableInserted
, ConnectionSettings::encoding
) );
448 log(pSettings
, LogLevel::Sql
, buf
.makeStringAndClear().getStr());
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
458 OUString table
, schema
;
459 std::vector
< OString
> vec
;
460 tokenizeSQL( cmd
, vec
);
461 OUString sourceTable
=
463 extractSingleTableFromSelect( vec
), ConnectionSettings::encoding
);
465 if( data
->concurrency
==
466 css::sdbc::ResultSetConcurrency::UPDATABLE
)
469 if( sourceTable
.getLength() )
471 std::vector
< OUString
> sourceTableKeys
= lookupKeys(
472 pSettings
->tables
.is() ?
473 pSettings
->tables
: data
->tableSupplier
->getTables() ,
479 // check, whether the columns are in the result set (required !)
481 for( i
= 0 ; i
< static_cast<int>(sourceTableKeys
.size()) ; i
++ )
485 OUStringToOString( sourceTableKeys
[i
] ,
486 ConnectionSettings::encoding
).getStr()) )
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 (" );
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
) );
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();
523 OStringBuffer
buf( 128 );
524 buf
.append( "can't support updateable resultset for table " );
525 buf
.append( OUStringToOString( schema
, ConnectionSettings::encoding
) );
527 buf
.append( OUStringToOString( table
, ConnectionSettings::encoding
) );
528 buf
.append( ", because resultset table does not have a primary key " );
529 aReason
= buf
.makeStringAndClear();
534 OStringBuffer
buf( 128 );
535 buf
.append( "can't support updateable result for selects with multiple tables (" );
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
> (
567 data
->refMutex
, data
->owner
,
568 data
->ppSettings
,result
, schema
, table
) );
569 *(data
->pMultipleResultAvailable
) = true;
571 if (isLog(pSettings
, LogLevel::Sql
))
573 OStringBuffer
buf( 128 );
574 buf
.append( "executed query '" );
576 buf
.append( "' successfully" );
577 buf
.append( ", duration=" );
578 buf
.append( duration
);
579 buf
.append( "ms, returnedRows=" );
580 buf
.append( returnedRows
);
582 log(pSettings
, LogLevel::Sql
, buf
.makeStringAndClear().getStr());
586 case PGRES_EMPTY_QUERY
:
589 case PGRES_BAD_RESPONSE
:
590 case PGRES_NONFATAL_ERROR
:
591 case PGRES_FATAL_ERROR
:
594 pSettings
, data
->owner
, cmd
, PQresultErrorMessage( result
) , PQresStatus( state
) );
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
);
625 ret
= convertMappedIntArray2StringArray( mapIndex2Name
, string2intarray(xRow
->getString( 1 ) ) );
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
);
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
,
667 const OUString
& lastTableInserted
,
668 const OString
& lastQuery
)
670 Reference
< XResultSet
> ret
;
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
);
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
);
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 )
701 // buf.append( ii->first.getStr() );
702 // buf.append( "=" );
703 // buf.append( ii->second.getStr() );
704 // buf.append( "," );
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
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
++ )
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;
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;
765 if( !bColumnMatchAutoValue
)
767 // it even was no autovalue, no sense to continue as we can't query the
774 if( bAdditionalCondition
)
775 buf
.append( " AND " );
776 bufferQuoteIdentifier( buf
, keyColumnNames
[i
], pConnectionSettings
);
779 bAdditionalCondition
= true;
781 query
= buf
.makeStringAndClear();
785 if( query
.getLength() )
787 Reference
< css::sdbc::XStatement
> stmt
= connection
->createStatement();
788 ret
= stmt
->executeQuery( query
);
795 sal_Bool
Statement::execute( const OUString
& sql
)
797 osl::MutexGuard
guard( m_xMutex
->GetMutex() );
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
;
818 data
.tableSupplier
.set( m_connection
, UNO_QUERY
);
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() );
836 Any
Statement::getWarnings( )
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
);
850 ret
= supplier
->getMetaData();
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
];
868 case STATEMENT_CURSOR_NAME
:
871 bRet
= ( rValue
>>= val
);
872 rConvertedValue
<<= val
;
875 case STATEMENT_ESCAPE_PROCESSING
:
878 bRet
= ( rValue
>>= val
);
879 rConvertedValue
<<= val
;
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
:
891 bRet
= ( rValue
>>= val
);
892 rConvertedValue
<<= val
;
897 throw IllegalArgumentException(
898 "pq_statement: Invalid property handle ("
899 + OUString::number( nHandle
) + ")",
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;
949 void Statement::disposing()
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: */