<commit_before>/* -*- 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): <NAME>
 *
 *  All parts contributed on or after August 2011:
 *
 *    Version: MPL 1.1 / GPLv3+ / LGPLv2.1+
 *
 *    The contents of this file are subject to the Mozilla Public License Version
 *    1.1 (the "License"); you may not use this file except in compliance with
 *    the License or as specified alternatively below. You may obtain a copy of
 *    the License at http://www.mozilla.org/MPL/
 *
 *    Software distributed under the License is distributed on an "AS IS" basis,
 *    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 *    for the specific language governing rights and limitations under the
 *    License.
 *
 *    Major Contributor(s):
 *    [ Copyright (C) 2011 <NAME> <<EMAIL>> ]
 *
 *    All Rights Reserved.
 *
 *    For minor contributions see the git repository.
 *
 *    Alternatively, the contents of this file may be used under the terms of
 *    either the GNU General Public License Version 3 or later (the "GPLv3+"), or
 *    the GNU Lesser General Public License Version 2.1 or later (the "LGPLv2.1+"),
 *    in which case the provisions of the GPLv3+ or the LGPLv2.1+ are applicable
 *    instead of those above.
 *
 ************************************************************************/

#include "pq_preparedstatement.hxx"
#include "pq_resultset.hxx"
#include "pq_tools.hxx"
#include "pq_statics.hxx"
#include "pq_statement.hxx"

#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>


#include <cppuhelper/typeprovider.hxx>
#include <cppuhelper/queryinterface.hxx>

#include <com/sun/star/beans/PropertyAttribute.hpp>

#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>

#include <string.h>

using osl::Mutex;
using osl::MutexGuard;

using rtl::OUString;
using rtl::OUStringToOString;
using rtl::OStringToOUString;
using rtl::OUStringBuffer;
using rtl::OStringBuffer;
using rtl::OString;

using com::sun::star::uno::Any;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Type;
using com::sun::star::uno::RuntimeException;
using com::sun::star::uno::Exception;
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::XWarningsSupplier;
using com::sun::star::sdbc::XCloseable;
using com::sun::star::sdbc::XPreparedStatement;
using com::sun::star::sdbc::XParameters;
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::XGeneratedResultSet;
using com::sun::star::sdbc::SQLException;

using com::sun::star::beans::Property;
using com::sun::star::beans::XPropertySetInfo;
using com::sun::star::beans::XPropertySet;
using com::sun::star::beans::XMultiPropertySet;
using com::sun::star::beans::XFastPropertySet;

#define ASCII_STR(x) OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
namespace pq_sdbc_driver
{
static ::cppu::IPropertyArrayHelper & getPreparedStatementPropertyArrayHelper()
{
    static ::cppu::IPropertyArrayHelper *pArrayHelper;
    if( ! pArrayHelper )
    {
        MutexGuard guard( Mutex::getGlobalMutex() );
        if( ! pArrayHelper )
        {
            static Property aTable[] =
                {
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("CursorName") ), 0,
                        ::getCppuType( (OUString *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("EscapeProcessing") ), 1,
                        ::getBooleanCppuType() , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("FetchDirection") ), 2,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("FetchSize") ), 3,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("MaxFieldSize") ), 4,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("MaxRows") ), 5,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("QueryTimeOut") ), 6,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetConcurrency") ), 7,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetType") ), 8,
                        ::getCppuType( (sal_Int32 *)0) , 0 )
                };
            OSL_ASSERT( sizeof(aTable)/ sizeof(Property)  == PREPARED_STATEMENT_SIZE );
            static ::cppu::OPropertyArrayHelper arrayHelper( aTable, PREPARED_STATEMENT_SIZE, sal_True );
            pArrayHelper = &arrayHelper;
        }
    }
    return *pArrayHelper;
}

static bool isOperator( char c )
{
    static const char * operators = "<>=()!/&%.,;";

    const char * w = operators;
    for( ; *w && *w != c ; w ++);
    return *w != 0;
}

static bool isNamedParameterStart( const rtl::OString & o , int index )
{
    return o[index] == ':' && (
        isWhitespace( o[index-1] ) || isOperator(o[index-1]) );
}

static bool isQuoted( const rtl::OString & str )
{
    return str[0] == '"' || str[0] == '\'';
}

PreparedStatement::PreparedStatement(
    const ::rtl::Reference< RefCountedMutex > & refMutex,
    const Reference< XConnection > & conn,
    struct ConnectionSettings *pSettings,
    const ::rtl::OString & stmt )
    : OComponentHelper( refMutex->mutex ),
      OPropertySetHelper( OComponentHelper::rBHelper ),
      m_connection( conn ),
      m_pSettings( pSettings ),
      m_stmt( stmt ),
      m_refMutex( refMutex ),
      m_lastOidInserted( InvalidOid )
{
    m_props[PREPARED_STATEMENT_QUERY_TIME_OUT] = makeAny( (sal_Int32)0 );
    m_props[PREPARED_STATEMENT_MAX_ROWS] = makeAny( (sal_Int32)0 );
    m_props[PREPARED_STATEMENT_RESULT_SET_CONCURRENCY] = makeAny(
        com::sun::star::sdbc::ResultSetConcurrency::READ_ONLY );
    m_props[PREPARED_STATEMENT_RESULT_SET_TYPE] = makeAny(
        com::sun::star::sdbc::ResultSetType::SCROLL_INSENSITIVE );

    splitSQL( m_stmt, m_splittedStatement );
    int elements = 0;
    for( int i = 0, max = m_splittedStatement.size(); i < max ; i ++ )
    {
        const OString &str = m_splittedStatement[i];
        // 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 = OStringVector ( elements );
}

PreparedStatement::~PreparedStatement()
{
    POSTGRE_TRACE( "dtor PreparedStatement" );
}

void PreparedStatement::checkColumnIndex( sal_Int32 parameterIndex )
{
    if( parameterIndex < 1 || parameterIndex > (sal_Int32) m_vars.size() )
    {
        OUStringBuffer buf( 128 );
        buf.appendAscii( "pq_preparedstatement: parameter index out of range (expected 1 to " );
        buf.append( (sal_Int32 ) m_vars.size() );
        buf.appendAscii( ", got " );
        buf.append( parameterIndex );
        buf.appendAscii( ", statement '" );
        buf.append( OStringToOUString( m_stmt, m_pSettings->encoding ) );
        buf.appendAscii( "')" );
        throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any () );
    }
}
void PreparedStatement::checkClosed() throw (SQLException, RuntimeException )
{
    if( ! m_pSettings || ! m_pSettings->pConnection )
        throw SQLException(
            ASCII_STR("pq_driver: PreparedStatement or connection has already been closed !" ),
            *this, OUString(),1,Any());
}

Any PreparedStatement::queryInterface( const Type & reqType ) throw (RuntimeException)
{
    Any ret;

    ret = OComponentHelper::queryInterface( reqType );
    if( ! ret.hasValue() )
        ret = ::cppu::queryInterface( reqType,
                                    static_cast< XWarningsSupplier * > ( this  ),
                                    static_cast< XPreparedStatement * > ( this ),
                                    static_cast< com::sun::star::sdbc::XResultSetMetaDataSupplier * > ( this ),
                                    static_cast< XParameters * > ( this ),
                                    static_cast< XCloseable * > ( this ),
                                    static_cast< XGeneratedResultSet * > ( this ),
                                    static_cast< XPropertySet * > ( this ),
                                    static_cast< XMultiPropertySet * > ( this ),
                                    static_cast< XFastPropertySet * > ( this ) );
    return ret;
}


Sequence< Type > PreparedStatement::getTypes() throw ( RuntimeException )
{
    static cppu::OTypeCollection *pCollection;
    if( ! pCollection )
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if( !pCollection )
        {
            static cppu::OTypeCollection collection(
                getCppuType( (Reference< XWarningsSupplier> *) 0 ),
                getCppuType( (Reference< XPreparedStatement> *) 0 ),
                getCppuType( (Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier> *) 0 ),
                getCppuType( (Reference< XParameters> *) 0 ),
                getCppuType( (Reference< XCloseable> *) 0 ),
                getCppuType( (Reference< XGeneratedResultSet> *) 0 ),
                getCppuType( (Reference< XPropertySet >*) 0 ),
                getCppuType( (Reference< XFastPropertySet > *) 0 ),
                getCppuType( (Reference< XMultiPropertySet > *) 0 ),
                OComponentHelper::getTypes());
            pCollection = &collection;
        }
    }
    return pCollection->getTypes();
}

Sequence< sal_Int8> PreparedStatement::getImplementationId() throw ( RuntimeException )
{
    static cppu::OImplementationId *pId;
    if( ! pId )
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if( ! pId )
        {
            static cppu::OImplementationId id(sal_False);
            pId = &id;
        }
    }
    return pId->getImplementationId();
}

void PreparedStatement::close(  ) throw (SQLException, RuntimeException)
{
    // let the connection die without acquired mutex !
    Reference< XConnection > r;
    Reference< XCloseable > resultSet;
    {
        MutexGuard guard( m_refMutex->mutex );
        m_pSettings = 0;
        r = m_connection;
        m_connection.clear();

        resultSet = m_lastResultset;
        m_lastResultset.clear();
    }
    if( resultSet.is() )
    {
        resultSet->close();
    }
}

void PreparedStatement::raiseSQLException(
    const char * errorMsg, const char *errorType )
    throw( SQLException )
{
    OUStringBuffer buf(128);
    buf.appendAscii( "pq_driver: ");
    if( errorType )
    {
        buf.appendAscii( "[" );
        buf.appendAscii( errorType );
        buf.appendAscii( "]" );
    }
    buf.append(
        rtl::OUString( errorMsg, strlen(errorMsg) , m_pSettings->encoding ) );
    buf.appendAscii( " (caused by statement '" );
    buf.appendAscii( m_executedStatement.getStr() );
    buf.appendAscii( "')" );
    OUString error = buf.makeStringAndClear();
    log( m_pSettings, LogLevel::ERROR, error );
    throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any() );
}

Reference< XResultSet > PreparedStatement::executeQuery( )
        throw (SQLException, RuntimeException)
{
    Reference< XCloseable > lastResultSet = m_lastResultset;
    if( lastResultSet.is() )
        lastResultSet->close();

    if( ! execute( ) )
    {
        raiseSQLException(  "not a query" );
    }
    return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
}

sal_Int32 PreparedStatement::executeUpdate( )
        throw (SQLException, RuntimeException)
{
    if( execute( ) )
    {
        raiseSQLException( "not a command" );
    }
    return m_multipleResultUpdateCount;
}

sal_Bool PreparedStatement::execute( )
        throw (SQLException, RuntimeException)
{
    osl::MutexGuard guard( m_refMutex->mutex );

    OStringBuffer buf( m_stmt.getLength() *2 );

    OStringVector::size_type vars = 0;
    for( OStringVector::size_type i = 0 ; i < m_splittedStatement.size() ; ++i )
    {
        // 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 "?"
        const OString &str = m_splittedStatement[i];
//         printf( "Splitted %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
                        for( ; 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();

    m_lastResultset.clear();
    m_lastTableInserted  = rtl::OUString();

    struct CommandData data;
    data.refMutex = m_refMutex;
    data.ppSettings = &m_pSettings;
    data.pLastOidInserted = &m_lastOidInserted;
    data.pLastQuery = &m_lastQuery;
    data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
    data.pMultipleResultAvailable = &m_multipleResultAvailable;
    data.pLastTableInserted = &m_lastTableInserted;<commit_msg>connectivity: fix some "empty body" warnings on Mac<commit_after>/* -*- 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): <NAME>
 *
 *  All parts contributed on or after August 2011:
 *
 *    Version: MPL 1.1 / GPLv3+ / LGPLv2.1+
 *
 *    The contents of this file are subject to the Mozilla Public License Version
 *    1.1 (the "License"); you may not use this file except in compliance with
 *    the License or as specified alternatively below. You may obtain a copy of
 *    the License at http://www.mozilla.org/MPL/
 *
 *    Software distributed under the License is distributed on an "AS IS" basis,
 *    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 *    for the specific language governing rights and limitations under the
 *    License.
 *
 *    Major Contributor(s):
 *    [ Copyright (C) 2011 <NAME> <<EMAIL>> ]
 *
 *    All Rights Reserved.
 *
 *    For minor contributions see the git repository.
 *
 *    Alternatively, the contents of this file may be used under the terms of
 *    either the GNU General Public License Version 3 or later (the "GPLv3+"), or
 *    the GNU Lesser General Public License Version 2.1 or later (the "LGPLv2.1+"),
 *    in which case the provisions of the GPLv3+ or the LGPLv2.1+ are applicable
 *    instead of those above.
 *
 ************************************************************************/

#include "pq_preparedstatement.hxx"
#include "pq_resultset.hxx"
#include "pq_tools.hxx"
#include "pq_statics.hxx"
#include "pq_statement.hxx"

#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>


#include <cppuhelper/typeprovider.hxx>
#include <cppuhelper/queryinterface.hxx>

#include <com/sun/star/beans/PropertyAttribute.hpp>

#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>

#include <string.h>

using osl::Mutex;
using osl::MutexGuard;

using rtl::OUString;
using rtl::OUStringToOString;
using rtl::OStringToOUString;
using rtl::OUStringBuffer;
using rtl::OStringBuffer;
using rtl::OString;

using com::sun::star::uno::Any;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Type;
using com::sun::star::uno::RuntimeException;
using com::sun::star::uno::Exception;
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::XWarningsSupplier;
using com::sun::star::sdbc::XCloseable;
using com::sun::star::sdbc::XPreparedStatement;
using com::sun::star::sdbc::XParameters;
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::XGeneratedResultSet;
using com::sun::star::sdbc::SQLException;

using com::sun::star::beans::Property;
using com::sun::star::beans::XPropertySetInfo;
using com::sun::star::beans::XPropertySet;
using com::sun::star::beans::XMultiPropertySet;
using com::sun::star::beans::XFastPropertySet;

#define ASCII_STR(x) OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
namespace pq_sdbc_driver
{
static ::cppu::IPropertyArrayHelper & getPreparedStatementPropertyArrayHelper()
{
    static ::cppu::IPropertyArrayHelper *pArrayHelper;
    if( ! pArrayHelper )
    {
        MutexGuard guard( Mutex::getGlobalMutex() );
        if( ! pArrayHelper )
        {
            static Property aTable[] =
                {
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("CursorName") ), 0,
                        ::getCppuType( (OUString *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("EscapeProcessing") ), 1,
                        ::getBooleanCppuType() , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("FetchDirection") ), 2,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("FetchSize") ), 3,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("MaxFieldSize") ), 4,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("MaxRows") ), 5,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("QueryTimeOut") ), 6,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetConcurrency") ), 7,
                        ::getCppuType( (sal_Int32 *)0) , 0 ),
                    Property(
                        OUString( RTL_CONSTASCII_USTRINGPARAM("ResultSetType") ), 8,
                        ::getCppuType( (sal_Int32 *)0) , 0 )
                };
            OSL_ASSERT( sizeof(aTable)/ sizeof(Property)  == PREPARED_STATEMENT_SIZE );
            static ::cppu::OPropertyArrayHelper arrayHelper( aTable, PREPARED_STATEMENT_SIZE, sal_True );
            pArrayHelper = &arrayHelper;
        }
    }
    return *pArrayHelper;
}

static bool isOperator( char c )
{
    static const char * operators = "<>=()!/&%.,;";

    const char * w = operators;
    while (*w && *w != c)
    {
        ++w;
    }
    return *w != 0;
}

static bool isNamedParameterStart( const rtl::OString & o , int index )
{
    return o[index] == ':' && (
        isWhitespace( o[index-1] ) || isOperator(o[index-1]) );
}

static bool isQuoted( const rtl::OString & str )
{
    return str[0] == '"' || str[0] == '\'';
}

PreparedStatement::PreparedStatement(
    const ::rtl::Reference< RefCountedMutex > & refMutex,
    const Reference< XConnection > & conn,
    struct ConnectionSettings *pSettings,
    const ::rtl::OString & stmt )
    : OComponentHelper( refMutex->mutex ),
      OPropertySetHelper( OComponentHelper::rBHelper ),
      m_connection( conn ),
      m_pSettings( pSettings ),
      m_stmt( stmt ),
      m_refMutex( refMutex ),
      m_lastOidInserted( InvalidOid )
{
    m_props[PREPARED_STATEMENT_QUERY_TIME_OUT] = makeAny( (sal_Int32)0 );
    m_props[PREPARED_STATEMENT_MAX_ROWS] = makeAny( (sal_Int32)0 );
    m_props[PREPARED_STATEMENT_RESULT_SET_CONCURRENCY] = makeAny(
        com::sun::star::sdbc::ResultSetConcurrency::READ_ONLY );
    m_props[PREPARED_STATEMENT_RESULT_SET_TYPE] = makeAny(
        com::sun::star::sdbc::ResultSetType::SCROLL_INSENSITIVE );

    splitSQL( m_stmt, m_splittedStatement );
    int elements = 0;
    for( int i = 0, max = m_splittedStatement.size(); i < max ; i ++ )
    {
        const OString &str = m_splittedStatement[i];
        // 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 = OStringVector ( elements );
}

PreparedStatement::~PreparedStatement()
{
    POSTGRE_TRACE( "dtor PreparedStatement" );
}

void PreparedStatement::checkColumnIndex( sal_Int32 parameterIndex )
{
    if( parameterIndex < 1 || parameterIndex > (sal_Int32) m_vars.size() )
    {
        OUStringBuffer buf( 128 );
        buf.appendAscii( "pq_preparedstatement: parameter index out of range (expected 1 to " );
        buf.append( (sal_Int32 ) m_vars.size() );
        buf.appendAscii( ", got " );
        buf.append( parameterIndex );
        buf.appendAscii( ", statement '" );
        buf.append( OStringToOUString( m_stmt, m_pSettings->encoding ) );
        buf.appendAscii( "')" );
        throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any () );
    }
}
void PreparedStatement::checkClosed() throw (SQLException, RuntimeException )
{
    if( ! m_pSettings || ! m_pSettings->pConnection )
        throw SQLException(
            ASCII_STR("pq_driver: PreparedStatement or connection has already been closed !" ),
            *this, OUString(),1,Any());
}

Any PreparedStatement::queryInterface( const Type & reqType ) throw (RuntimeException)
{
    Any ret;

    ret = OComponentHelper::queryInterface( reqType );
    if( ! ret.hasValue() )
        ret = ::cppu::queryInterface( reqType,
                                    static_cast< XWarningsSupplier * > ( this  ),
                                    static_cast< XPreparedStatement * > ( this ),
                                    static_cast< com::sun::star::sdbc::XResultSetMetaDataSupplier * > ( this ),
                                    static_cast< XParameters * > ( this ),
                                    static_cast< XCloseable * > ( this ),
                                    static_cast< XGeneratedResultSet * > ( this ),
                                    static_cast< XPropertySet * > ( this ),
                                    static_cast< XMultiPropertySet * > ( this ),
                                    static_cast< XFastPropertySet * > ( this ) );
    return ret;
}


Sequence< Type > PreparedStatement::getTypes() throw ( RuntimeException )
{
    static cppu::OTypeCollection *pCollection;
    if( ! pCollection )
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if( !pCollection )
        {
            static cppu::OTypeCollection collection(
                getCppuType( (Reference< XWarningsSupplier> *) 0 ),
                getCppuType( (Reference< XPreparedStatement> *) 0 ),
                getCppuType( (Reference< com::sun::star::sdbc::XResultSetMetaDataSupplier> *) 0 ),
                getCppuType( (Reference< XParameters> *) 0 ),
                getCppuType( (Reference< XCloseable> *) 0 ),
                getCppuType( (Reference< XGeneratedResultSet> *) 0 ),
                getCppuType( (Reference< XPropertySet >*) 0 ),
                getCppuType( (Reference< XFastPropertySet > *) 0 ),
                getCppuType( (Reference< XMultiPropertySet > *) 0 ),
                OComponentHelper::getTypes());
            pCollection = &collection;
        }
    }
    return pCollection->getTypes();
}

Sequence< sal_Int8> PreparedStatement::getImplementationId() throw ( RuntimeException )
{
    static cppu::OImplementationId *pId;
    if( ! pId )
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if( ! pId )
        {
            static cppu::OImplementationId id(sal_False);
            pId = &id;
        }
    }
    return pId->getImplementationId();
}

void PreparedStatement::close(  ) throw (SQLException, RuntimeException)
{
    // let the connection die without acquired mutex !
    Reference< XConnection > r;
    Reference< XCloseable > resultSet;
    {
        MutexGuard guard( m_refMutex->mutex );
        m_pSettings = 0;
        r = m_connection;
        m_connection.clear();

        resultSet = m_lastResultset;
        m_lastResultset.clear();
    }
    if( resultSet.is() )
    {
        resultSet->close();
    }
}

void PreparedStatement::raiseSQLException(
    const char * errorMsg, const char *errorType )
    throw( SQLException )
{
    OUStringBuffer buf(128);
    buf.appendAscii( "pq_driver: ");
    if( errorType )
    {
        buf.appendAscii( "[" );
        buf.appendAscii( errorType );
        buf.appendAscii( "]" );
    }
    buf.append(
        rtl::OUString( errorMsg, strlen(errorMsg) , m_pSettings->encoding ) );
    buf.appendAscii( " (caused by statement '" );
    buf.appendAscii( m_executedStatement.getStr() );
    buf.appendAscii( "')" );
    OUString error = buf.makeStringAndClear();
    log( m_pSettings, LogLevel::ERROR, error );
    throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any() );
}

Reference< XResultSet > PreparedStatement::executeQuery( )
        throw (SQLException, RuntimeException)
{
    Reference< XCloseable > lastResultSet = m_lastResultset;
    if( lastResultSet.is() )
        lastResultSet->close();

    if( ! execute( ) )
    {
        raiseSQLException(  "not a query" );
    }
    return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
}

sal_Int32 PreparedStatement::executeUpdate( )
        throw (SQLException, RuntimeException)
{
    if( execute( ) )
    {
        raiseSQLException( "not a command" );
    }
    return m_multipleResultUpdateCount;
}

sal_Bool PreparedStatement::execute( )
        throw (SQLException, RuntimeException)
{
    osl::MutexGuard guard( m_refMutex->mutex );

    OStringBuffer buf( m_stmt.getLength() *2 );

    OStringVector::size_type vars = 0;
    for( OStringVector::size_type i = 0 ; i < m_splittedStatement.size() ; ++i )
    {
        // 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 "?"
        const OString &str = m_splittedStatement[i];
//         printf( "Splitted %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();

    m_lastResultset.clear();
    m_lastTableInserted  = rtl::OUString();

    struct CommandData data;
    data.refMutex = m_refMutex;
    data.ppSettings = &m_pSettings;
    data.pLastOidInserted = &m_lastOidInserted;
    data.pLastQuery = &m_lastQuery;
    data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount;
    data.pMultipleResultAvailable = &m_multipleResultAvailable;
    data.pLastTableInserted = &m_lastTableInserted;
{'fraction_non_alphanumeric': 0.12689290275497173, 'fraction_numerical': 0.008818342151675485, 'mean_word_length': 2.3246057420137487, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 21, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3018994', 'n_tokens_mistral': 10019, 'n_tokens_neox': 9573, 'n_words': 2281}