summaryrefslogtreecommitdiffstats
path: root/connectivity/source/drivers/postgresql/pq_statement.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'connectivity/source/drivers/postgresql/pq_statement.cxx')
-rw-r--r--connectivity/source/drivers/postgresql/pq_statement.cxx888
1 files changed, 888 insertions, 0 deletions
diff --git a/connectivity/source/drivers/postgresql/pq_statement.cxx b/connectivity/source/drivers/postgresql/pq_statement.cxx
new file mode 100644
index 000000000..7db4b2053
--- /dev/null
+++ b/connectivity/source/drivers/postgresql/pq_statement.cxx
@@ -0,0 +1,888 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Effective License of whole file:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
+ *
+ * The Contents of this file are made available subject to the terms of
+ * the GNU Lesser General Public License Version 2.1
+ *
+ * Copyright: 2000 by Sun Microsystems, Inc.
+ *
+ * Contributor(s): Joerg Budischewski
+ *
+ * All parts contributed on or after August 2011:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ ************************************************************************/
+
+#include <sal/log.hxx>
+#include "pq_statement.hxx"
+#include "pq_fakedupdateableresultset.hxx"
+#include "pq_updateableresultset.hxx"
+#include "pq_tools.hxx"
+#include "pq_statics.hxx"
+
+#include <osl/time.h>
+
+#include <rtl/ustrbuf.hxx>
+
+#include <comphelper/sequence.hxx>
+
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/KeyType.hpp>
+#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <string.h>
+#include <string_view>
+
+using osl::MutexGuard;
+
+
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Type;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::UNO_QUERY;
+
+using com::sun::star::lang::IllegalArgumentException;
+
+using com::sun::star::sdbc::XCloseable;
+using com::sun::star::sdbc::XStatement;
+using com::sun::star::sdbc::XPreparedStatement;
+using com::sun::star::sdbc::XParameters;
+using com::sun::star::sdbc::XRow;
+using com::sun::star::sdbc::XResultSet;
+using com::sun::star::sdbc::XConnection;
+using com::sun::star::sdbc::SQLException;
+
+using com::sun::star::sdbcx::XColumnsSupplier;
+using com::sun::star::sdbcx::XKeysSupplier;
+
+using com::sun::star::beans::Property;
+using com::sun::star::beans::XPropertySetInfo;
+using com::sun::star::beans::XPropertySet;
+
+using com::sun::star::container::XNameAccess;
+using com::sun::star::container::XEnumerationAccess;
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XIndexAccess;
+
+namespace pq_sdbc_driver
+{
+static ::cppu::IPropertyArrayHelper & getStatementPropertyArrayHelper()
+{
+ static ::cppu::OPropertyArrayHelper arrayHelper(
+ Sequence<Property>{
+ Property(
+ "CursorName", 0,
+ ::cppu::UnoType<OUString>::get() , 0 ),
+ Property(
+ "EscapeProcessing", 1,
+ cppu::UnoType<bool>::get() , 0 ),
+ Property(
+ "FetchDirection", 2,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "FetchSize", 3,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "MaxFieldSize", 4,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "MaxRows", 5,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "QueryTimeOut", 6,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "ResultSetConcurrency", 7,
+ ::cppu::UnoType<sal_Int32>::get() , 0 ),
+ Property(
+ "ResultSetType", 8,
+ ::cppu::UnoType<sal_Int32>::get() , 0 )},
+ true );
+
+ return arrayHelper;
+}
+
+Statement::Statement( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
+ const Reference< XConnection > & conn,
+ struct ConnectionSettings *pSettings )
+ : Statement_BASE( refMutex->GetMutex() )
+ , OPropertySetHelper( Statement_BASE::rBHelper )
+ , m_connection( conn )
+ , m_pSettings( pSettings )
+ , m_xMutex( refMutex )
+ , m_multipleResultAvailable(false)
+ , m_multipleResultUpdateCount(0)
+ , m_lastOidInserted(InvalidOid)
+{
+ m_props[STATEMENT_QUERY_TIME_OUT] <<= sal_Int32(0);
+ m_props[STATEMENT_MAX_ROWS] <<= sal_Int32(0);
+ m_props[STATEMENT_RESULT_SET_CONCURRENCY] <<=
+ css::sdbc::ResultSetConcurrency::READ_ONLY;
+ m_props[STATEMENT_RESULT_SET_TYPE] <<=
+ css::sdbc::ResultSetType::SCROLL_INSENSITIVE;
+}
+
+Statement::~Statement()
+{
+}
+
+void Statement::checkClosed()
+{
+ if( ! m_pSettings || ! m_pSettings->pConnection )
+ throw SQLException(
+ "pq_driver: Statement or connection has already been closed !",
+ *this, OUString(),1,Any());
+}
+
+Any Statement::queryInterface( const Type & rType )
+{
+ Any aRet = Statement_BASE::queryInterface(rType);
+ return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType);
+}
+
+
+Sequence< Type > Statement::getTypes()
+{
+ static Sequence< Type > collection(
+ ::comphelper::concatSequences(
+ OPropertySetHelper::getTypes(),
+ Statement_BASE::getTypes()));
+
+ return collection;
+}
+
+Sequence< sal_Int8> Statement::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void Statement::close( )
+{
+ // let the connection die without acquired mutex !
+ Reference< XConnection > r;
+ Reference< XCloseable > resultSet;
+ {
+ MutexGuard guard( m_xMutex->GetMutex() );
+ m_pSettings = nullptr;
+ r = m_connection;
+ m_connection.clear();
+
+ resultSet = m_lastResultset;
+ m_lastResultset.clear();
+ }
+ if( resultSet.is() )
+ {
+ resultSet->close();
+ }
+
+}
+
+void Statement::raiseSQLException(
+ std::u16string_view sql, const char * errorMsg )
+{
+ OUString error = "pq_driver: "
+ + OUString( errorMsg, strlen(errorMsg), ConnectionSettings::encoding )
+ + " (caused by statement '" + sql + "')";
+ SAL_WARN("connectivity.postgresql", error);
+ throw SQLException( error, *this, OUString(), 1, Any() );
+}
+
+Reference< XResultSet > Statement::executeQuery(const OUString& sql )
+{
+ if( ! execute( sql ) )
+ {
+ raiseSQLException( sql, "not a query" );
+ }
+ return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY );
+}
+
+sal_Int32 Statement::executeUpdate( const OUString& sql )
+{
+ if( execute( sql ) )
+ {
+ raiseSQLException( sql, "not a command" );
+ }
+ return m_multipleResultUpdateCount;
+}
+
+/// @throws SQLException
+static void raiseSQLException(
+ const Reference< XInterface> & owner,
+ std::string_view sql,
+ const char * errorMsg,
+ const char *errorType = nullptr )
+{
+ OUStringBuffer buf(128);
+ buf.append( "pq_driver: ");
+ if( errorType )
+ {
+ buf.append( "[" );
+ buf.appendAscii( errorType );
+ buf.append( "]" );
+ }
+ buf.append(
+ OUString( errorMsg, strlen(errorMsg) , ConnectionSettings::encoding ) );
+ buf.append( " (caused by statement '" );
+ buf.append( OStringToOUString( sql, ConnectionSettings::encoding ) );
+ buf.append( "')" );
+ OUString error = buf.makeStringAndClear();
+ SAL_WARN("connectivity.postgresql", error);
+ throw SQLException( error, owner, OUString(), 1, Any() );
+}
+
+
+// returns the elements of the primary key of the given table
+// static Sequence< Reference< css::beans::XPropertySet > > lookupKeys(
+static std::vector< OUString > lookupKeys(
+ const Reference< css::container::XNameAccess > &tables,
+ const OUString & table,
+ OUString *pSchema,
+ OUString *pTable)
+{
+ std::vector< OUString > ret;
+ Reference< XKeysSupplier > keySupplier;
+ Statics & st = getStatics();
+
+ if( tables->hasByName( table ) )
+ tables->getByName( table ) >>= keySupplier;
+ else if( -1 == table.indexOf( '.' ) )
+ {
+ // it wasn't a fully qualified name. Now need to skip through all tables.
+ Reference< XEnumerationAccess > enumerationAccess( tables, UNO_QUERY );
+
+ Reference< css::container::XEnumeration > enumeration =
+ enumerationAccess->createEnumeration();
+ while( enumeration->hasMoreElements() )
+ {
+ Reference< XPropertySet > set;
+ enumeration->nextElement() >>= set;
+ OUString name;
+// OUString schema;
+
+ if( set->getPropertyValue( st.NAME ) >>= name )
+ {
+// printf( "searching %s %s\n",
+// OUStringToOString( schema, RTL_TEXTENCODING_ASCII_US ).getStr(),
+// OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
+ if( name == table )
+ {
+
+ if( keySupplier.is() )
+ {
+ // is ambiguous, as I don't know postgresql searchpath,
+ // I can't continue here, as I may write to a different table
+ keySupplier.clear();
+ SAL_INFO("connectivity.postgresql", "Can't offer updateable result set because table " << name << " is duplicated, add schema to resolve ambiguity");
+ break;
+ }
+ keySupplier.set( set, UNO_QUERY );
+ }
+ }
+ }
+ }
+ else
+ {
+ SAL_INFO("connectivity.postgresql", "Can't offer updateable result set ( table " << table << " is unknown)");
+ }
+
+ if( keySupplier.is() )
+ {
+ Reference< XPropertySet > set( keySupplier, UNO_QUERY );
+ set->getPropertyValue( getStatics().NAME ) >>= *pTable;
+ set->getPropertyValue( getStatics().SCHEMA_NAME ) >>= *pSchema;
+ set.clear();
+
+ Reference< XEnumerationAccess > keys ( keySupplier->getKeys(), UNO_QUERY );
+ Reference< XEnumeration > enumeration = keys->createEnumeration();
+ while( enumeration->hasMoreElements() )
+ {
+ enumeration->nextElement() >>= set;
+ sal_Int32 keyType = 0;
+ if( (set->getPropertyValue( st.TYPE ) >>= keyType ) &&
+ keyType == css::sdbcx::KeyType::PRIMARY )
+ {
+ Reference< XColumnsSupplier > columns( set, UNO_QUERY );
+ Reference< XIndexAccess > indexAccess( columns->getColumns(), UNO_QUERY );
+
+ int length = indexAccess->getCount();
+ ret.resize( length );
+// printf( "primary key for Table %s is ",
+// OUStringToOString( table, RTL_TEXTENCODING_ASCII_US ).getStr() );
+ for( int i = 0 ; i < length ; i ++ )
+ {
+ indexAccess->getByIndex( i ) >>= set;
+ OUString name;
+ set->getPropertyValue( st.NAME ) >>= name;
+ ret[i] = name;
+// printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() );
+ }
+// printf( "\n" );
+ }
+ }
+ if( ret.empty() )
+ {
+ SAL_INFO("connectivity.postgresql", "Can't offer updateable result set ( table " << table << " does not have a primary key)");
+ }
+ }
+ return ret;
+}
+
+bool executePostgresCommand( const OString & cmd, struct CommandData *data )
+{
+ ConnectionSettings *pSettings = *(data->ppSettings);
+
+ sal_Int32 duration = osl_getGlobalTimer();
+ PGresult *result = PQexec( pSettings->pConnection, cmd.getStr() );
+ duration = osl_getGlobalTimer() - duration;
+ if( ! result )
+ raiseSQLException(
+ data->owner, cmd, PQerrorMessage( pSettings->pConnection ) );
+
+ ExecStatusType state = PQresultStatus( result );
+ *(data->pLastOidInserted) = 0;
+ data->pLastTableInserted->clear();
+ *(data->pLastQuery) = cmd;
+
+ bool ret = false;
+ switch( state )
+ {
+ case PGRES_COMMAND_OK:
+ {
+ *(data->pMultipleResultUpdateCount) = atoi( PQcmdTuples( result ) );
+ *(data->pMultipleResultAvailable) = false;
+
+ // in case an oid value is available, we retrieve it
+ *(data->pLastOidInserted) = PQoidValue( result );
+
+ // in case it was a single insert, extract the name of the table,
+ // otherwise the table name is empty
+ *(data->pLastTableInserted) =
+ extractTableFromInsert( OStringToOUString( cmd, ConnectionSettings::encoding ) );
+
+ OString strMain = "executed command '" + cmd + "' successfully ('" + OString::number(*( data->pMultipleResultUpdateCount ))
+ + "), duration=" + OString::number(duration) + "ms";
+
+ OString strOption;
+ if( *(data->pLastOidInserted) )
+ {
+ strOption += ", usedOid=" + OString::number( *(data->pLastOidInserted) ) + ", diagnosedTable="
+ + OUStringToOString(*data->pLastTableInserted, ConnectionSettings::encoding);
+ }
+ SAL_INFO("connectivity.postgresql", strMain + strOption);
+ PQclear( result );
+ break;
+ }
+ case PGRES_TUPLES_OK: // success
+ {
+ // In case it is a single table, it has a primary key and all columns
+ // belonging to the primary key are in the result set, allow updateable result sets
+ // otherwise, don't
+ OUString table, schema;
+ std::vector< OString > vec;
+ tokenizeSQL( cmd, vec );
+ OUString sourceTable =
+ OStringToOUString(
+ extractSingleTableFromSelect( vec ), ConnectionSettings::encoding );
+
+ if( data->concurrency ==
+ css::sdbc::ResultSetConcurrency::UPDATABLE )
+ {
+ OString aReason;
+ if( sourceTable.getLength() )
+ {
+ std::vector< OUString > sourceTableKeys = lookupKeys(
+ pSettings->tables.is() ?
+ pSettings->tables : data->tableSupplier->getTables() ,
+ sourceTable,
+ &schema,
+ &table);
+
+ // check, whether the columns are in the result set (required !)
+ int i;
+ for( i = 0 ; i < static_cast<int>(sourceTableKeys.size()) ; i ++ )
+ {
+ if( -1 == PQfnumber(
+ result,
+ OUStringToOString( sourceTableKeys[i] ,
+ ConnectionSettings::encoding ).getStr()) )
+ {
+ break;
+ }
+ }
+
+ if( !sourceTableKeys.empty() && i == static_cast<int>(sourceTableKeys.size()) )
+ {
+ *(data->pLastResultset) =
+ UpdateableResultSet::createFromPGResultSet(
+ data->refMutex, data->owner, data->ppSettings, result,
+ schema, table, std::move(sourceTableKeys) );
+ }
+ else if( ! table.getLength() )
+ {
+ aReason = "can't support updateable resultset, because a single table in the "
+ "WHERE part of the statement could not be identified (" + cmd + ".";
+ }
+ else if( !sourceTableKeys.empty() )
+ {
+ aReason = "can't support updateable resultset for table "
+ + OUStringToOString( schema, ConnectionSettings::encoding ) + "."
+ + OUStringToOString( table, ConnectionSettings::encoding )
+ + ", because resultset does not contain a part of the primary key ( column "
+ + OUStringToOString( sourceTableKeys[i], ConnectionSettings::encoding )
+ + " is missing )";
+ }
+ else
+ {
+
+ aReason = "can't support updateable resultset for table "
+ + OUStringToOString( schema, ConnectionSettings::encoding ) + "."
+ + OUStringToOString( table, ConnectionSettings::encoding )
+ + ", because resultset table does not have a primary key ";
+ }
+ }
+ else
+ {
+ SAL_WARN("connectivity.postgresql", "can't support updateable result for selects with multiple tables (" << cmd << ")");
+ }
+ if( ! (*(data->pLastResultset)).is() )
+ {
+ SAL_WARN("connectivity.postgresql", aReason);
+
+ // TODO: How to react here correctly ?
+ // remove this piece of code
+ *(data->pLastResultset) =
+ new FakedUpdateableResultSet(
+ data->refMutex, data->owner,
+ data->ppSettings,result, schema, table,
+ OStringToOUString( aReason, ConnectionSettings::encoding) );
+ }
+
+ }
+ else if( sourceTable.getLength() > 0)
+ {
+ splitConcatenatedIdentifier( sourceTable, &schema, &table );
+ }
+
+ sal_Int32 returnedRows = PQntuples( result );
+ if( ! data->pLastResultset->is() )
+ *(data->pLastResultset) =
+ Reference< XCloseable > (
+ new ResultSet(
+ data->refMutex, data->owner,
+ data->ppSettings,result, schema, table ) );
+ *(data->pMultipleResultAvailable) = true;
+ ret = true;
+ SAL_INFO("connectivity.postgresql", "executed query '" << cmd << "' successfully, duration=" << duration << "ms, returnedRows=" << returnedRows << ".");
+ break;
+ }
+ case PGRES_EMPTY_QUERY:
+ case PGRES_COPY_OUT:
+ case PGRES_COPY_IN:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ default:
+ raiseSQLException(
+ data->owner, cmd, PQresultErrorMessage( result ) , PQresStatus( state ) );
+ }
+ return ret;
+
+}
+
+static Sequence< OUString > getPrimaryKeyColumnNames(
+ const Reference< XConnection > & connection, const OUString &schemaName, const OUString &tableName )
+{
+ Sequence< OUString > ret;
+
+ Int2StringMap mapIndex2Name;
+ fillAttnum2attnameMap( mapIndex2Name, connection, schemaName, tableName );
+
+ // retrieve the primary key ...
+ Reference< XPreparedStatement > stmt = connection->prepareStatement(
+ "SELECT conkey " // 7
+ "FROM pg_constraint INNER JOIN pg_class ON conrelid = pg_class.oid "
+ "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
+ "LEFT JOIN pg_class AS class2 ON confrelid = class2.oid "
+ "LEFT JOIN pg_namespace AS nmsp2 ON class2.relnamespace=nmsp2.oid "
+ "WHERE pg_class.relname = ? AND pg_namespace.nspname = ? AND pg_constraint.contype='p'" );
+ DisposeGuard guard( stmt );
+ Reference< XParameters > paras( stmt, UNO_QUERY );
+ paras->setString( 1 , tableName );
+ paras->setString( 2 , schemaName );
+ Reference< XResultSet > rs = stmt->executeQuery();
+ Reference< XRow > xRow( rs , UNO_QUERY );
+
+ if( rs->next() )
+ {
+ ret = convertMappedIntArray2StringArray( mapIndex2Name, string2intarray(xRow->getString( 1 ) ) );
+ }
+ return ret;
+}
+
+static void getAutoValues(
+ String2StringMap & result,
+ const Reference< XConnection > & connection,
+ const OUString &schemaName,
+ const OUString & tableName,
+ const ConnectionSettings *pConnectionSettings )
+{
+ OUString strDefaultValue = getColExprForDefaultSettingVal(pConnectionSettings);
+ Reference< XPreparedStatement > stmt = connection->prepareStatement(
+ "SELECT pg_attribute.attname, " + strDefaultValue +
+ "FROM pg_class, pg_namespace, pg_attribute "
+ "LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND "
+ "pg_attribute.attnum = pg_attrdef.adnum "
+ "WHERE pg_attribute.attrelid = pg_class.oid AND "
+ "pg_class.relnamespace = pg_namespace.oid AND "
+ "pg_namespace.nspname = ? AND "
+ // LEM TODO: this is weird; why "LIKE" and not "="?
+ // Most probably gives problems if tableName contains '%'
+ "pg_class.relname LIKE ? AND "
+ + strDefaultValue + " != ''"
+ );
+ DisposeGuard guard( stmt );
+ Reference< XParameters > paras( stmt, UNO_QUERY );
+ paras->setString( 1 , schemaName );
+ paras->setString( 2 , tableName );
+ Reference< XResultSet > rs = stmt->executeQuery();
+ Reference< XRow > xRow( rs , UNO_QUERY );
+
+ while( rs->next() )
+ {
+ result[ OUStringToOString( xRow->getString( 1 ), RTL_TEXTENCODING_ASCII_US) ] =
+ OUStringToOString( xRow->getString(2), RTL_TEXTENCODING_ASCII_US );
+ }
+}
+
+Reference< XResultSet > getGeneratedValuesFromLastInsert(
+ ConnectionSettings *pConnectionSettings,
+ const Reference< XConnection > &connection,
+ sal_Int32 nLastOid,
+ const OUString & lastTableInserted,
+ const OString & lastQuery )
+{
+ Reference< XResultSet > ret;
+ OUString query;
+ OUString schemaName, tableName;
+ splitConcatenatedIdentifier(
+ lastTableInserted, &schemaName, &tableName );
+
+ if( nLastOid && lastTableInserted.getLength() )
+ {
+ OUStringBuffer buf( 128 );
+ buf.append( "SELECT * FROM " );
+ if( schemaName.getLength() )
+ bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
+ else
+ bufferQuoteIdentifier( buf, lastTableInserted, pConnectionSettings );
+ buf.append( " WHERE oid = " );
+ buf.append( nLastOid );
+ query = buf.makeStringAndClear();
+ }
+ else if ( lastTableInserted.getLength() && lastQuery.getLength() )
+ {
+ // extract nameValue Pairs
+ String2StringMap namedValues;
+ extractNameValuePairsFromInsert( namedValues, lastQuery );
+
+ // debug ...
+// OStringBuffer buf( 128);
+// buf.append( "extracting name/value from '" );
+// buf.append( lastQuery.getStr() );
+// buf.append( "' to [" );
+// for( String2StringMap::iterator ii = namedValues.begin() ; ii != namedValues.end() ; ++ii )
+// {
+// buf.append( ii->first.getStr() );
+// buf.append( "=" );
+// buf.append( ii->second.getStr() );
+// buf.append( "," );
+// }
+// buf.append( "]\n" );
+// printf( "%s", buf.makeStringAndClear() );
+
+ // TODO: make also unqualified tables names work here. Have a look at 2.8.3. The Schema Search Path
+ // in postgresql doc
+
+ const Sequence< OUString > keyColumnNames = getPrimaryKeyColumnNames( connection, schemaName, tableName );
+ if( keyColumnNames.hasElements() )
+ {
+ OUStringBuffer buf( 128 );
+ buf.append( "SELECT * FROM " );
+ bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings );
+ buf.append( " WHERE " );
+ bool bAdditionalCondition = false;
+ String2StringMap autoValues;
+ for( OUString const & columnNameUnicode : keyColumnNames )
+ {
+ OUString value;
+ OString columnName = OUStringToOString( columnNameUnicode, ConnectionSettings::encoding );
+ bool bColumnMatchNamedValue = false;
+ for (auto const& namedValue : namedValues)
+ {
+ if( columnName.equalsIgnoreAsciiCase( namedValue.first ) )
+ {
+ value = OStringToOUString( namedValue.second , ConnectionSettings::encoding );
+ bColumnMatchNamedValue = true;
+ break;
+ }
+ }
+
+ // check, if a column of the primary key was not inserted explicitly,
+ if( !bColumnMatchNamedValue )
+ {
+ if( autoValues.empty() )
+ {
+ getAutoValues( autoValues, connection, schemaName, tableName, pConnectionSettings );
+ }
+ // this could mean, that the column is a default or auto value, check this ...
+ bool bColumnMatchAutoValue = false;
+ for (auto const& autoValue : autoValues)
+ {
+ if( columnName.equalsIgnoreAsciiCase( autoValue.first ) )
+ {
+ // it is indeed an auto value.
+ value = OStringToOUString(autoValue.second, RTL_TEXTENCODING_ASCII_US );
+ // check, whether it is a sequence
+
+ if( autoValue.second.startsWith("nextval(") )
+ {
+ // retrieve current sequence value:
+ OUStringBuffer myBuf(128 );
+ myBuf.append( "SELECT currval(" );
+ myBuf.appendAscii( &(autoValue.second.getStr()[8]));
+ value = querySingleValue( connection, myBuf.makeStringAndClear() );
+ }
+ bColumnMatchAutoValue = true;
+ break;
+ }
+ }
+ if( !bColumnMatchAutoValue )
+ {
+ // it even was no autovalue, no sense to continue as we can't query the
+ // inserted row
+ buf.truncate();
+ break;
+ }
+ }
+
+ if( bAdditionalCondition )
+ buf.append( " AND " );
+ bufferQuoteIdentifier( buf, columnNameUnicode, pConnectionSettings );
+ buf.append( " = " );
+ buf.append( value );
+ bAdditionalCondition = true;
+ }
+ query = buf.makeStringAndClear();
+ }
+ }
+
+ if( query.getLength() )
+ {
+ Reference< css::sdbc::XStatement > stmt = connection->createStatement();
+ ret = stmt->executeQuery( query );
+ }
+
+ return ret;
+
+}
+
+sal_Bool Statement::execute( const OUString& sql )
+{
+ osl::MutexGuard guard( m_xMutex->GetMutex() );
+ checkClosed();
+ OString cmd = OUStringToOString( sql, m_pSettings );
+
+ Reference< XCloseable > lastResultSetHolder = m_lastResultset;
+ if( lastResultSetHolder.is() )
+ lastResultSetHolder->close();
+
+ m_lastResultset.clear();
+ m_lastTableInserted.clear();
+
+ struct CommandData data;
+ data.refMutex = m_xMutex;
+ data.ppSettings = &m_pSettings;
+ data.pLastOidInserted = &m_lastOidInserted;
+ data.pLastQuery = &m_lastQuery;
+ data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
+ data.pMultipleResultAvailable = &m_multipleResultAvailable;
+ data.pLastTableInserted = &m_lastTableInserted;
+ data.pLastResultset = &m_lastResultset;
+ data.owner = *this;
+ data.tableSupplier.set( m_connection, UNO_QUERY );
+ data.concurrency =
+ extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY );
+ return executePostgresCommand( cmd , &data );
+}
+
+Reference< XConnection > Statement::getConnection( )
+{
+ Reference< XConnection > ret;
+ {
+ MutexGuard guard( m_xMutex->GetMutex() );
+ checkClosed();
+ ret = m_connection;
+ }
+ return ret;
+}
+
+
+Any Statement::getWarnings( )
+{
+ return Any();
+}
+
+void Statement::clearWarnings( )
+{
+}
+
+Reference< css::sdbc::XResultSetMetaData > Statement::getMetaData()
+{
+ Reference< css::sdbc::XResultSetMetaData > ret;
+ Reference< css::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY );
+ if( supplier.is() )
+ ret = supplier->getMetaData();
+ return ret;
+}
+
+
+::cppu::IPropertyArrayHelper & Statement::getInfoHelper()
+{
+ return getStatementPropertyArrayHelper();
+}
+
+
+sal_Bool Statement::convertFastPropertyValue(
+ Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
+{
+ rOldValue = m_props[nHandle];
+ bool bRet;
+ switch( nHandle )
+ {
+ case STATEMENT_CURSOR_NAME:
+ {
+ OUString val;
+ bRet = ( rValue >>= val );
+ rConvertedValue <<= val;
+ break;
+ }
+ case STATEMENT_ESCAPE_PROCESSING:
+ {
+ bool val(false);
+ bRet = ( rValue >>= val );
+ rConvertedValue <<= val;
+ break;
+ }
+ case STATEMENT_FETCH_DIRECTION:
+ case STATEMENT_FETCH_SIZE:
+ case STATEMENT_MAX_FIELD_SIZE:
+ case STATEMENT_MAX_ROWS:
+ case STATEMENT_QUERY_TIME_OUT:
+ case STATEMENT_RESULT_SET_CONCURRENCY:
+ case STATEMENT_RESULT_SET_TYPE:
+ {
+ sal_Int32 val;
+ bRet = ( rValue >>= val );
+ rConvertedValue <<= val;
+ break;
+ }
+ default:
+ {
+ throw IllegalArgumentException(
+ "pq_statement: Invalid property handle ("
+ + OUString::number( nHandle ) + ")",
+ *this, 2 );
+ }
+ }
+ return bRet;
+}
+
+
+void Statement::setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle,const Any& rValue )
+{
+ m_props[nHandle] = rValue;
+}
+
+void Statement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ rValue = m_props[nHandle];
+}
+
+Reference < XPropertySetInfo > Statement::getPropertySetInfo()
+{
+ return OPropertySetHelper::createPropertySetInfo( getStatementPropertyArrayHelper() );
+}
+
+
+Reference< XResultSet > Statement::getResultSet( )
+{
+ return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY );
+}
+
+sal_Int32 Statement::getUpdateCount( )
+{
+ return m_multipleResultUpdateCount;
+}
+
+sal_Bool Statement::getMoreResults( )
+{
+ // The PostgreSQL C interface always returns a single result,
+ // so we will never have multiple ones.
+ // Implicitly close the open resultset (if any) as per spec,
+ // and setup to signal "no more result, neither as resultset,
+ // nor as update count".
+ Reference< XCloseable > lastResultSetHolder = m_lastResultset;
+ if( lastResultSetHolder.is() )
+ lastResultSetHolder->close();
+ m_multipleResultUpdateCount = -1;
+ return false;
+}
+
+
+void Statement::disposing()
+{
+ close();
+}
+
+Reference< XResultSet > Statement::getGeneratedValues( )
+{
+ osl::MutexGuard guard( m_xMutex->GetMutex() );
+ return getGeneratedValuesFromLastInsert(
+ m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */