/* -*- 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 #include "pq_preparedstatement.hxx" #include "pq_tools.hxx" #include "pq_statics.hxx" #include "pq_statement.hxx" #include #include #include #include #include #include #include #include #include #include #include #include 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::UNO_QUERY; using com::sun::star::lang::IllegalArgumentException; using com::sun::star::sdbc::XCloseable; using com::sun::star::sdbc::XResultSet; using com::sun::star::sdbc::XRef; using com::sun::star::sdbc::XBlob; using com::sun::star::sdbc::XClob; using com::sun::star::sdbc::XArray; using com::sun::star::sdbc::XConnection; using com::sun::star::sdbc::SQLException; using com::sun::star::beans::Property; using com::sun::star::beans::XPropertySetInfo; using namespace dbtools; namespace pq_sdbc_driver { static ::cppu::IPropertyArrayHelper & getPreparedStatementPropertyArrayHelper() { static ::cppu::OPropertyArrayHelper arrayHelper( Sequence{ Property( "CursorName", 0, ::cppu::UnoType::get() , 0 ), Property( "EscapeProcessing", 1, cppu::UnoType::get() , 0 ), Property( "FetchDirection", 2, ::cppu::UnoType::get() , 0 ), Property( "FetchSize", 3, ::cppu::UnoType::get() , 0 ), Property( "MaxFieldSize", 4, ::cppu::UnoType::get() , 0 ), Property( "MaxRows", 5, ::cppu::UnoType::get() , 0 ), Property( "QueryTimeOut", 6, ::cppu::UnoType::get() , 0 ), Property( "ResultSetConcurrency", 7, ::cppu::UnoType::get() , 0 ), Property( "ResultSetType", 8, ::cppu::UnoType::get() , 0 )}, true ); static ::cppu::IPropertyArrayHelper *pArrayHelper = &arrayHelper; return *pArrayHelper; } static bool isOperator( char c ) { static const char * const operators = "<>=()!/&%.,;"; const char * w = operators; while (*w && *w != c) { ++w; } return *w != 0; } static bool isNamedParameterStart( std::string_view o , int index ) { return o[index] == ':' && ( isWhitespace( o[index-1] ) || isOperator(o[index-1]) ); } static bool isQuoted( std::string_view str ) { return str[0] == '"' || str[0] == '\''; } PreparedStatement::PreparedStatement( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, const Reference< XConnection > & conn, struct ConnectionSettings *pSettings, const OString & stmt ) : PreparedStatement_BASE(refMutex->GetMutex()) , OPropertySetHelper(PreparedStatement_BASE::rBHelper) , m_connection(conn) , m_pSettings(pSettings) , m_stmt(stmt) , m_xMutex(refMutex) , m_multipleResultAvailable(false) , m_multipleResultUpdateCount(0) , m_lastOidInserted( InvalidOid ) { m_props[PREPARED_STATEMENT_QUERY_TIME_OUT] <<= sal_Int32(0); m_props[PREPARED_STATEMENT_MAX_ROWS] <<= sal_Int32(0); m_props[PREPARED_STATEMENT_RESULT_SET_CONCURRENCY] <<= css::sdbc::ResultSetConcurrency::READ_ONLY; m_props[PREPARED_STATEMENT_RESULT_SET_TYPE] <<= css::sdbc::ResultSetType::SCROLL_INSENSITIVE; splitSQL( m_stmt, m_splittedStatement ); int elements = 0; for(const OString & str : m_splittedStatement) { // ignore quoted strings... if( ! isQuoted( str ) ) { // the ':' cannot be the first or the last part of the // token, // the ? cannot be the first part of the token , so we start // at one for( int index = 1 ; index < str.getLength() ; index ++ ) { if( str[index] == '?' || isNamedParameterStart( str , index ) ) { elements ++; } } } } m_vars = std::vector< OString >( elements ); } PreparedStatement::~PreparedStatement() { } void PreparedStatement::checkColumnIndex( sal_Int32 parameterIndex ) { if( parameterIndex < 1 || o3tl::make_unsigned(parameterIndex) > m_vars.size() ) { throw SQLException( "pq_preparedstatement: parameter index out of range (expected 1 to " + OUString::number( m_vars.size() ) + ", got " + OUString::number( parameterIndex ) + ", statement '" + OStringToOUString( m_stmt, ConnectionSettings::encoding ) + "')", *this, OUString(), 1, Any () ); } } void PreparedStatement::checkClosed() { if( ! m_pSettings || ! m_pSettings->pConnection ) throw SQLException( "pq_driver: PreparedStatement or connection has already been closed !", *this, OUString(),1,Any()); } Any PreparedStatement::queryInterface( const Type & rType ) { Any aRet = PreparedStatement_BASE::queryInterface(rType); return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); } Sequence< Type > PreparedStatement::getTypes() { static Sequence< Type > collection( ::comphelper::concatSequences( OPropertySetHelper::getTypes(), PreparedStatement_BASE::getTypes())); return collection; } Sequence< sal_Int8> PreparedStatement::getImplementationId() { return css::uno::Sequence(); } void PreparedStatement::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 PreparedStatement::raiseSQLException( const char * errorMsg ) { OUStringBuffer buf(128); buf.append( "pq_driver: "); buf.append( OUString( errorMsg, strlen(errorMsg) , ConnectionSettings::encoding ) ); buf.append( " (caused by statement '" ); buf.appendAscii( m_executedStatement.getStr() ); buf.append( "')" ); OUString error = buf.makeStringAndClear(); SAL_WARN("connectivity.postgresql", error); throw SQLException( error, *this, OUString(), 1, Any() ); } Reference< XResultSet > PreparedStatement::executeQuery( ) { if( ! execute( ) ) { raiseSQLException( "not a query" ); } return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); } sal_Int32 PreparedStatement::executeUpdate( ) { if( execute( ) ) { raiseSQLException( "not a command" ); } return m_multipleResultUpdateCount; } sal_Bool PreparedStatement::execute( ) { osl::MutexGuard guard( m_xMutex->GetMutex() ); OStringBuffer buf( m_stmt.getLength() *2 ); std::vector< OString >::size_type vars = 0; for(const OString & str : m_splittedStatement) { // LEM TODO: instead of this manual mucking with SQL // could we use PQexecParams / PQExecPrepared / ...? // Only snafu is giving the types of the parameters and // that it needs $1, $2, etc instead of "?" // printf( "Split %d %s\n" , i , str.getStr() ); if( isQuoted( str ) ) { buf.append( str ); } else { int start = 0,index; for( index = 1 ; index < str.getLength() ; index ++ ) { if( str[index] == '?' ) { buf.append( str.getStr()+start, index - start ); buf.append( m_vars[vars] ); vars ++; start =index+1; } else { if ( isNamedParameterStart( str, index ) ) { buf.append( str.getStr()+start, index -start ); buf.append( m_vars[vars] ); // skip to the end of the named parameter while ( index < str.getLength() && !( isWhitespace(str[index]) || isOperator (str[index]))) { ++index; } start = index; vars ++; } } } // if( index +1 >= str.getLength() ) // { buf.append( str.getStr() + start, index -start ); // } } } m_executedStatement = buf.makeStringAndClear(); Reference< XCloseable > lastResultSet = m_lastResultset; if( lastResultSet.is() ) lastResultSet->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( m_executedStatement , &data ); // see pq_statement.cxx } Reference< XConnection > PreparedStatement::getConnection( ) { Reference< XConnection > ret; { MutexGuard guard( m_xMutex->GetMutex() ); checkClosed(); ret = m_connection; } return ret; } void PreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 ) { MutexGuard guard( m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = OString( "NULL" ); } void PreparedStatement::setObjectNull( sal_Int32 parameterIndex, sal_Int32, const OUString& ) { MutexGuard guard( m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = OString( "NULL" ); } void PreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) { MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); if( x ) m_vars[parameterIndex-1] = OString( "'t'" ); else m_vars[parameterIndex-1] = OString( "'f'" ); } void PreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) { setInt(parameterIndex,x); } void PreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) { setInt(parameterIndex, x ); } void PreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) { // printf( "setString %d %d\n ", parameterIndex, x); MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; } void PreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) { MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; } void PreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) { MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; } void PreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) { MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; } void PreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) { // printf( "setString %d %s\n ", parameterIndex, // OUStringToOString( x , RTL_TEXTENCODING_ASCII_US ).getStr()); MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); OStringBuffer buf( 20 ); buf.append( "'" ); OString y = OUStringToOString( x, ConnectionSettings::encoding ); buf.ensureCapacity( y.getLength() * 2 + 2 ); int len = PQescapeString( const_cast(buf.getStr())+1, y.getStr() , y.getLength() ); buf.setLength( 1 + len ); buf.append( "'" ); m_vars[parameterIndex-1] = buf.makeStringAndClear(); } void PreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) { MutexGuard guard(m_xMutex->GetMutex() ); checkClosed(); checkColumnIndex( parameterIndex ); size_t len; const std::unique_ptr> escapedString( PQescapeBytea( reinterpret_cast(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() ); } m_vars[parameterIndex-1] = OString::Concat("'") + std::string_view(reinterpret_cast(escapedString.get()), len -1) + "'"; } void PreparedStatement::setDate( sal_Int32 parameterIndex, const css::util::Date& x ) { setString( parameterIndex, DBTypeConversion::toDateString( x ) ); } void PreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) { setString( parameterIndex, DBTypeConversion::toTimeString( x ) ); } void PreparedStatement::setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) { setString( parameterIndex, DBTypeConversion::toDateTimeString( x ) ); } void PreparedStatement::setBinaryStream( sal_Int32, const Reference< css::io::XInputStream >&, sal_Int32 ) { throw SQLException( "pq_preparedstatement: setBinaryStream not implemented", *this, OUString(), 1, Any () ); } void PreparedStatement::setCharacterStream( sal_Int32, const Reference< css::io::XInputStream >&, sal_Int32 ) { throw SQLException( "pq_preparedstatement: setCharacterStream not implemented", *this, OUString(), 1, Any () ); } void PreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x ) { if( ! implSetObject( this, parameterIndex, x )) { throw SQLException( "pq_preparedstatement::setObject: can't convert value of type " + x.getValueTypeName(), *this, OUString(), 1, Any () ); } } void PreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 ) { if( css::sdbc::DataType::DECIMAL == targetSqlType || css::sdbc::DataType::NUMERIC == targetSqlType ) { double myDouble = 0.0; OUString myString; if( x >>= myDouble ) { myString = OUString::number( myDouble ); } else { x >>= myString; } if( myString.isEmpty() ) { throw SQLException( "pq_preparedstatement::setObjectWithInfo: can't convert value of type " + x.getValueTypeName() + " to type DECIMAL or NUMERIC", *this, OUString(), 1, Any () ); } setString( parameterIndex, myString ); } else { setObject( parameterIndex, x ); } } void PreparedStatement::setRef( sal_Int32, const Reference< XRef >& ) { throw SQLException( "pq_preparedstatement: setRef not implemented", *this, OUString(), 1, Any () ); } void PreparedStatement::setBlob( sal_Int32, const Reference< XBlob >& ) { throw SQLException( "pq_preparedstatement: setBlob not implemented", *this, OUString(), 1, Any () ); } void PreparedStatement::setClob( sal_Int32, const Reference< XClob >& ) { throw SQLException( "pq_preparedstatement: setClob not implemented", *this, OUString(), 1, Any () ); } void PreparedStatement::setArray( sal_Int32 parameterIndex, const Reference< XArray >& x ) { setString( parameterIndex, array2String( x->getArray( nullptr ) ) ); } void PreparedStatement::clearParameters( ) { MutexGuard guard(m_xMutex->GetMutex() ); m_vars = std::vector< OString >( m_vars.size() ); } Any PreparedStatement::getWarnings( ) { return Any(); } void PreparedStatement::clearWarnings( ) { } Reference< css::sdbc::XResultSetMetaData > PreparedStatement::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 & PreparedStatement::getInfoHelper() { return getPreparedStatementPropertyArrayHelper(); } sal_Bool PreparedStatement::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) { bool bRet; rOldValue = m_props[nHandle]; switch( nHandle ) { case PREPARED_STATEMENT_CURSOR_NAME: { OUString val; bRet = ( rValue >>= val ); rConvertedValue <<= val; break; } case PREPARED_STATEMENT_ESCAPE_PROCESSING: { bool val(false); bRet = ( rValue >>= val ); rConvertedValue <<= val; break; } case PREPARED_STATEMENT_FETCH_DIRECTION: case PREPARED_STATEMENT_FETCH_SIZE: case PREPARED_STATEMENT_MAX_FIELD_SIZE: case PREPARED_STATEMENT_MAX_ROWS: case PREPARED_STATEMENT_QUERY_TIME_OUT: case PREPARED_STATEMENT_RESULT_SET_CONCURRENCY: case PREPARED_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 PreparedStatement::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,const Any& rValue ) { m_props[nHandle] = rValue; } void PreparedStatement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const { rValue = m_props[nHandle]; } Reference < XPropertySetInfo > PreparedStatement::getPropertySetInfo() { return OPropertySetHelper::createPropertySetInfo( getPreparedStatementPropertyArrayHelper() ); } void PreparedStatement::disposing() { close(); } Reference< XResultSet > PreparedStatement::getResultSet( ) { return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); } sal_Int32 PreparedStatement::getUpdateCount( ) { return m_multipleResultUpdateCount; } sal_Bool PreparedStatement::getMoreResults( ) { Reference< XCloseable > lastResultSet = m_lastResultset; if( lastResultSet.is() ) lastResultSet->close(); m_multipleResultUpdateCount = -1; return false; } Reference< XResultSet > PreparedStatement::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: */