diff options
Diffstat (limited to 'connectivity/source/drivers/postgresql/pq_updateableresultset.cxx')
-rw-r--r-- | connectivity/source/drivers/postgresql/pq_updateableresultset.cxx | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx b/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx new file mode 100644 index 000000000..5c1b23e82 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx @@ -0,0 +1,550 @@ +/* -*- 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: 200? 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 <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> + +#include "pq_updateableresultset.hxx" +#include "pq_resultsetmetadata.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +#include <string.h> + +#include <connectivity/dbconversion.hxx> + +using osl::MutexGuard; + + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::sdbc::XGeneratedResultSet; +using com::sun::star::sdbc::XResultSetMetaDataSupplier; +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XCloseable; +using com::sun::star::sdbc::XColumnLocate; +using com::sun::star::sdbc::XResultSetUpdate; +using com::sun::star::sdbc::XRowUpdate; +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; + +using com::sun::star::beans::XFastPropertySet; +using com::sun::star::beans::XPropertySet; +using com::sun::star::beans::XMultiPropertySet; + +using namespace dbtools; + +namespace pq_sdbc_driver +{ + + +css::uno::Reference< css::sdbc::XCloseable > UpdateableResultSet::createFromPGResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **ppSettings, + PGresult *result, + const OUString &schema, + const OUString &table, + std::vector< OUString > && primaryKey ) +{ + sal_Int32 columnCount = PQnfields( result ); + sal_Int32 rowCount = PQntuples( result ); + std::vector< OUString > columnNames( columnCount ); + for( int i = 0 ; i < columnCount ; i ++ ) + { + char * name = PQfname( result, i ); + columnNames[i] = OUString( name, strlen(name), ConnectionSettings::encoding ); + } + std::vector< std::vector< Any > > data( rowCount ); + + // copy all the data into unicode strings (also binaries, as we yet + // don't know, what a binary is and what not!) + for( int row = 0 ; row < rowCount ; row ++ ) + { + std::vector< Any > aRow( columnCount ); + for( int col = 0 ; col < columnCount ; col ++ ) + { + if( ! PQgetisnull( result, row, col ) ) + { + char * val = PQgetvalue( result, row, col ); + + aRow[col] <<= + OUString( val, strlen( val ), ConnectionSettings::encoding ); + } + } + data[row] = aRow; + } + + rtl::Reference<UpdateableResultSet> pRS = new UpdateableResultSet( + mutex, owner, std::move(columnNames), std::move(data), ppSettings, schema, table, std::move(primaryKey) ); + + pRS->m_meta = new ResultSetMetaData( mutex, pRS,nullptr, ppSettings, result, schema, table ); + + PQclear( result ); // we don't need it anymore + + return pRS; +} + +css::uno::Any UpdateableResultSet::queryInterface( + const css::uno::Type & reqType ) +{ + Any ret = SequenceResultSet::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< XResultSetUpdate * > ( this ), + static_cast< XRowUpdate * > ( this ) ); + return ret; +} + + +css::uno::Sequence< css::uno::Type > UpdateableResultSet::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<XResultSetUpdate>::get(), + cppu::UnoType<XRowUpdate>::get(), + SequenceResultSet::getTypes()); + + return collection.getTypes(); + +} + +css::uno::Sequence< sal_Int8> UpdateableResultSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +OUString UpdateableResultSet::buildWhereClause() +{ + OUString ret; + if( !m_primaryKey.empty() ) + { + OUStringBuffer buf( 128 ); + buf.append( " WHERE " ); + for( size_t i = 0 ; i < m_primaryKey.size() ; i ++ ) + { + if( i > 0 ) + buf.append( " AND " ); + sal_Int32 index = findColumn( m_primaryKey[i] ); + bufferQuoteIdentifier( buf, m_primaryKey[i], *m_ppSettings ); + buf.append( " = " ); + bufferQuoteConstant( buf, getString( index ), *m_ppSettings ); + } + ret = buf.makeStringAndClear(); + } + return ret; +} + + +void UpdateableResultSet::insertRow( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::insertRow() got called"); + + if( ! m_insertRow ) + throw SQLException( + "pq_resultset.insertRow: moveToInsertRow has not been called !", + *this, OUString(), 1, Any() ); + + OUStringBuffer buf( 128 ); + buf.append( "INSERT INTO " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( " ( " ); + + int columns = 0; + for( UpdateableFieldVector::size_type i = 0 ; i < m_updateableField.size() ; i++ ) + { + if( m_updateableField[i].isTouched ) + { + if( columns > 0 ) + buf.append( ", " ); + columns ++; + bufferQuoteIdentifier( buf, m_columnNames[i], *m_ppSettings); + } + } + buf.append( " ) VALUES ( " ); + + columns = 0; + for(const UpdateableField & i : m_updateableField) + { + if( i.isTouched ) + { + if( columns > 0 ) + buf.append( " , " ); + columns ++; + bufferQuoteAnyConstant( buf, i.value, *m_ppSettings ); + +// OUString val; +// m_updateableField[i].value >>= val; +// buf.append( val ); +// OStringToOUString(val, (*m_ppSettings)->encoding ) ); + } + } + + buf.append( " )" ); + + Reference< XStatement > stmt = + extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + m_rowCount ++; + m_data.resize( m_rowCount ); + m_data[m_rowCount-1] = std::vector< Any > ( m_fieldCount ); + Reference< XGeneratedResultSet > result( stmt, UNO_QUERY ); + if( result.is() ) + { + Reference< XResultSet > rs = result->getGeneratedValues(); + if( rs.is() && rs->next() ) + { + Reference< XColumnLocate > columnLocate( rs, UNO_QUERY ); + Reference< XRow> xRow ( rs, UNO_QUERY ); + for( int i = 0 ; i < m_fieldCount ; i++ ) + { + int field = columnLocate->findColumn( m_columnNames[i] ); + if( field >= 1 ) + { + m_data[m_rowCount-1][i] <<= xRow->getString( field ); +// printf( "adding %s %s\n" , +// OUStringToOString( m_columnNames[i], RTL_TEXTENCODING_ASCII_US).getStr(), +// OUStringToOString( xRow->getString( field ), RTL_TEXTENCODING_ASCII_US).getStr() ); + + } + } + } + else + { + // do the best we can ( DEFAULT and AUTO increment values fail ! ) + for( int i = 0 ; i < m_fieldCount ; i ++ ) + { + if( m_updateableField[i].isTouched ) + m_data[m_rowCount-1][i] = m_updateableField[i].value; + } + } + } + + // cleanup + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::updateRow( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::updateRow() got called"); + + if( m_insertRow ) + throw SQLException( + "pq_resultset.updateRow: moveToCurrentRow has not been called !", + *this, OUString(), 1, Any() ); + + OUStringBuffer buf( 128 ); + buf.append( "UPDATE " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( "SET " ); + + int columns = 0; + for( UpdateableFieldVector::size_type i = 0; i < m_updateableField.size() ; i ++ ) + { + if( m_updateableField[i].isTouched ) + { + if( columns > 0 ) + buf.append( ", " ); + columns ++; + + buf.append( m_columnNames[i] ); + buf.append( " = " ); + bufferQuoteAnyConstant( buf, m_updateableField[i].value, *m_ppSettings ); +// OUString val; +// m_updateableField[i].value >>= val; +// bufferQuoteConstant( buf, val ): +// buf.append( val ); + } + } + buf.append( buildWhereClause() ); + + Reference< XStatement > stmt = extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + for( int i = 0 ; i < m_fieldCount ; i ++ ) + { + if( m_updateableField[i].isTouched ) + m_data[m_row][i] = m_updateableField[i].value; + } + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::deleteRow( ) +{ + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::deleteRow() got called"); + + if( m_insertRow ) + throw SQLException( + "pq_resultset.deleteRow: deleteRow cannot be called when on insert row !", + *this, OUString(), 1, Any() ); + + if( m_row < 0 || m_row >= m_rowCount ) + { + throw SQLException( + "deleteRow cannot be called on invalid row (" + + OUString::number(m_row) + ")", + *this, OUString(), 0, Any() ); + } + + Reference< XStatement > stmt = extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + OUStringBuffer buf( 128 ); + buf.append( "DELETE FROM " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( " " ); + buf.append( buildWhereClause() ); + + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + for( int i = m_row + 1; i < m_row ; i ++ ) + { + m_data[i-1] = m_data[i]; + } + m_rowCount --; + m_data.resize( m_rowCount ); + } + +void UpdateableResultSet::cancelRowUpdates( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::moveToInsertRow( ) +{ + m_insertRow = true; +} + +void UpdateableResultSet::moveToCurrentRow( ) +{ + m_insertRow = false; +} + +void UpdateableResultSet::checkUpdate( sal_Int32 columnIndex) +{ + checkColumnIndex( columnIndex ); + if( m_updateableField.empty() ) + m_updateableField = UpdateableFieldVector( m_fieldCount ); + m_updateableField[columnIndex-1].isTouched = true; +} + +void UpdateableResultSet::updateNull( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + m_updateableField[columnIndex-1].value = Any(); +} + +void UpdateableResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + Statics &st = getStatics(); + m_updateableField[columnIndex-1].value <<= ( x ? st.TRUE : st.FALSE ); + +} + +void UpdateableResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + updateInt(columnIndex,x); +} + +void UpdateableResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + updateInt( columnIndex, x ); +} + +void UpdateableResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + updateLong( columnIndex, x ); +// MutexGuard guard( m_xMutex->GetMutex() ); +// checkClosed(); +// checkUpdate( columnIndex ); + +// m_updateableField[columnIndex-1].value <<= OUString::valueOf( x ); + +} + +void UpdateableResultSet::updateLong( sal_Int32 columnIndex, sal_Int64 x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + +// OStringBuffer buf( 20 ); +// buf.append( "'" ); +// buf.append( (sal_Int64) x ); +// buf.append( "'" ); + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= x; +} + +void UpdateableResultSet::updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + size_t len; + unsigned char * escapedString = + PQescapeBytea( reinterpret_cast<unsigned char const *>(x.getConstArray()), x.getLength(), &len); + if( ! escapedString ) + { + throw SQLException( + "pq_preparedstatement.setBytes: Error during converting bytesequence to an SQL conform string", + *this, OUString(), 1, Any() ); + } +// buf.append( (const char *)escapedString, len -1 ); + + m_updateableField[columnIndex-1].value <<= + OUString( reinterpret_cast<char*>(escapedString), len, RTL_TEXTENCODING_ASCII_US ); + PQfreemem( escapedString ); +} + +void UpdateableResultSet::updateDate( sal_Int32 columnIndex, const css::util::Date& x ) +{ + updateString( columnIndex, DBTypeConversion::toDateString( x ) ); +} + +void UpdateableResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + updateString( columnIndex, DBTypeConversion::toTimeString( x ) ); +} + +void UpdateableResultSet::updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) +{ + updateString( columnIndex, DBTypeConversion::toDateTimeString( x ) ); +} + +void UpdateableResultSet::updateBinaryStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ +} + +void UpdateableResultSet::updateCharacterStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ +} + +void UpdateableResultSet::updateObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */ ) +{ +} + +void UpdateableResultSet::updateNumericObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */, sal_Int32 /* scale */ ) +{ +} + + +Sequence< Type > UpdateableResultSet::getStaticTypes( bool updateable ) +{ + if( updateable ) + { + cppu::OTypeCollection collection( + cppu::UnoType<XResultSetUpdate>::get(), + cppu::UnoType<XRowUpdate>::get(), +// cppu::UnoType<css::sdbcx::XRowLocate>::get(), + getStaticTypes( false /* updateable */ ) ); + return collection.getTypes(); + } + else + { + cppu::OTypeCollection collection( + cppu::UnoType<XResultSet>::get(), + cppu::UnoType<XResultSetMetaDataSupplier>::get(), + cppu::UnoType<XRow>::get(), + cppu::UnoType<XColumnLocate>::get(), + cppu::UnoType<XCloseable>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<css::lang::XComponent>::get(), // OComponentHelper + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<css::uno::XAggregation>::get(), + cppu::UnoType<css::uno::XWeak>::get()); + return collection.getTypes(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |