/* -*- 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:
 *
 *    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 Lionel Elie Mamane <lionel@mamane.lu> ]
 *
 *    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;
    data.pLastResultset = &m_lastResultset;
    data.owner = *this;
    data.tableSupplier = Reference< com::sun::star::sdbcx::XTablesSupplier >( 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(  )
        throw (SQLException, RuntimeException)
{
    Reference< XConnection > ret;
    {
        MutexGuard guard( m_refMutex->mutex );
        checkClosed();
        ret = m_connection;
    }
    return ret;
}


void PreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 sqlType )
    throw (SQLException, RuntimeException)
{
    (void)sqlType;
    MutexGuard guard( m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    m_vars[parameterIndex-1] = OString( "NULL" );
}

void PreparedStatement::setObjectNull(
    sal_Int32 parameterIndex, sal_Int32 sqlType, const ::rtl::OUString& typeName )
    throw (SQLException, RuntimeException)
{
    (void) sqlType; (void) typeName;
    MutexGuard guard( m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    m_vars[parameterIndex-1] = OString( "NULL" );
}


void PreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    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 )
    throw (SQLException, RuntimeException)
{
    setInt(parameterIndex,x);
}

void PreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x )
    throw (SQLException, RuntimeException)
{
    setInt(parameterIndex, x );
}

void PreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x )
    throw (SQLException, RuntimeException)
{
//     printf( "setString %d %d\n ",  parameterIndex, x);
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    buf.append( (sal_Int32) x );
    buf.append( "'" );
    m_vars[parameterIndex-1] = buf.makeStringAndClear();
}

void PreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    buf.append( (sal_Int64) x );
    buf.append( "'" );
    m_vars[parameterIndex-1] = buf.makeStringAndClear();
}

void PreparedStatement::setFloat( sal_Int32 parameterIndex, float x )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    buf.append( x );
    buf.append( "'" );
    m_vars[parameterIndex-1] = buf.makeStringAndClear();
}

void PreparedStatement::setDouble( sal_Int32 parameterIndex, double x )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    buf.append( x );
    buf.append( "'" );
    m_vars[parameterIndex-1] = buf.makeStringAndClear();
}

void PreparedStatement::setString( sal_Int32 parameterIndex, const ::rtl::OUString& x )
    throw (SQLException, RuntimeException)
{
//     printf( "setString %d %s\n ", parameterIndex,
//             OUStringToOString( x , RTL_TEXTENCODING_ASCII_US ).getStr());
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    OString y = OUStringToOString( x, m_pSettings->encoding );
    buf.ensureCapacity( y.getLength() * 2 + 2 );
    int len = PQescapeString( ((char*)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 )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    checkClosed();
    checkColumnIndex( parameterIndex );
    OStringBuffer buf( 20 );
    buf.append( "'" );
    size_t len;
    unsigned char * escapedString =
        PQescapeBytea( (unsigned char *)x.getConstArray(), x.getLength(), &len);
    if( ! escapedString )
    {
        throw SQLException(
            ASCII_STR("pq_preparedstatement.setBytes: Error during converting bytesequence to an SQL conform string" ),
            *this, OUString(), 1, Any() );
    }
    buf.append( (const sal_Char *)escapedString, len -1 );
    free( escapedString );
    buf.append( "'" );
    m_vars[parameterIndex-1] = buf.makeStringAndClear();
}


void PreparedStatement::setDate( sal_Int32 parameterIndex, const ::com::sun::star::util::Date& x )
    throw (SQLException, RuntimeException)
{
    setString( parameterIndex, date2String( x ) );
}

void PreparedStatement::setTime( sal_Int32 parameterIndex, const ::com::sun::star::util::Time& x )
    throw (SQLException, RuntimeException)
{
    setString( parameterIndex, time2String( x ) );
}

void PreparedStatement::setTimestamp(
    sal_Int32 parameterIndex, const ::com::sun::star::util::DateTime& x )
    throw (SQLException, RuntimeException)
{
    setString( parameterIndex, dateTime2String( x ) );
}

void PreparedStatement::setBinaryStream(
    sal_Int32 parameterIndex,
    const Reference< ::com::sun::star::io::XInputStream >& x,
    sal_Int32 length )
    throw (SQLException, RuntimeException)
{
    (void) parameterIndex; (void)x; (void) length;
    throw SQLException(
        ASCII_STR( "pq_preparedstatement: setBinaryStream not implemented" ),
        *this, OUString(), 1, Any () );
}

void PreparedStatement::setCharacterStream(
    sal_Int32 parameterIndex,
    const Reference< ::com::sun::star::io::XInputStream >& x,
    sal_Int32 length )
    throw (SQLException, RuntimeException)
{
    (void) parameterIndex; (void)x; (void) length;
    throw SQLException(
        ASCII_STR( "pq_preparedstatement: setCharacterStream not implemented" ),
        *this, OUString(), 1, Any () );
}

void PreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x )
    throw (SQLException, RuntimeException)
{
    if( ! implSetObject( this, parameterIndex, x ))
    {
        OUStringBuffer buf;
        buf.append( ASCII_STR("pq_preparedstatement::setObject: can't convert value of type " ) );
        buf.append( x.getValueTypeName() );
        throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any () );
    }
}

void PreparedStatement::setObjectWithInfo(
    sal_Int32 parameterIndex,
    const Any& x,
    sal_Int32 targetSqlType,
    sal_Int32 scale )
    throw (SQLException, RuntimeException)
{
    (void) scale;
    if( com::sun::star::sdbc::DataType::DECIMAL == targetSqlType ||
        com::sun::star::sdbc::DataType::NUMERIC == targetSqlType )
    {
        double myDouble = 0.0;
        OUString myString;
        if( x >>= myDouble )
        {
            myString = OUString::valueOf( myDouble );
        }
        else
        {
            x >>= myString;
        }
        if( myString.getLength() )
        {
//              printf( "setObjectWithInfo %s\n", OUStringToOString(myString,RTL_TEXTENCODING_ASCII_US).getStr());
            setString( parameterIndex, myString );
        }
        else
        {
            OUStringBuffer buf;
            buf.append( ASCII_STR("pq_preparedstatement::setObjectWithInfo: can't convert value of type " ) );
            buf.append( x.getValueTypeName() );
            buf.append( ASCII_STR(" to type DECIMAL or NUMERIC" ) );
            throw SQLException( buf.makeStringAndClear(), *this, OUString(), 1, Any () );
        }
    }
    else
    {
        setObject( parameterIndex, x );
    }

}

void PreparedStatement::setRef(
    sal_Int32 parameterIndex,
    const Reference< XRef >& x )
    throw (SQLException, RuntimeException)
{
    (void) parameterIndex; (void)x;
    throw SQLException(
        ASCII_STR( "pq_preparedstatement: setRef not implemented" ),
        *this, OUString(), 1, Any () );
}

void PreparedStatement::setBlob(
    sal_Int32 parameterIndex,
    const Reference< XBlob >& x )
    throw (SQLException, RuntimeException)
{
    (void) parameterIndex; (void)x;
    throw SQLException(
        ASCII_STR( "pq_preparedstatement: setBlob not implemented" ),
        *this, OUString(), 1, Any () );
}

void PreparedStatement::setClob(
    sal_Int32 parameterIndex,
    const Reference< XClob >& x )
    throw (SQLException, RuntimeException)
{
    (void) parameterIndex; (void)x;
    throw SQLException(
        ASCII_STR( "pq_preparedstatement: setClob not implemented" ),
        *this, OUString(), 1, Any () );
}

void PreparedStatement::setArray(
    sal_Int32 parameterIndex,
    const Reference< XArray >& x )
    throw (SQLException, RuntimeException)
{
    setString( parameterIndex, array2String( x->getArray( 0 ) ) );
}

void PreparedStatement::clearParameters(  )
    throw (SQLException, RuntimeException)
{
    MutexGuard guard(m_refMutex->mutex );
    m_vars = OStringVector ( m_vars.size() );
}

Any PreparedStatement::getWarnings(  )
        throw (SQLException,RuntimeException)
{
    return Any();
}

void PreparedStatement::clearWarnings(  )
        throw (SQLException, RuntimeException)
{
}

Reference< ::com::sun::star::sdbc::XResultSetMetaData > PreparedStatement::getMetaData()
            throw (SQLException,RuntimeException)
{
    Reference< com::sun::star::sdbc::XResultSetMetaData > ret;
    Reference< com::sun::star::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 )
        throw (IllegalArgumentException)
{
    sal_Bool bRet;
    rOldValue = m_props[nHandle];
    switch( nHandle )
    {
    case PREPARED_STATEMENT_CURSOR_NAME:
    {
        OUString val;
        bRet = ( rValue >>= val );
        rConvertedValue = makeAny( val );
        break;
    }
    case PREPARED_STATEMENT_ESCAPE_PROCESSING:
    {
        sal_Bool val;
        bRet = ( rValue >>= val );
        rConvertedValue = makeAny( 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 = makeAny( val );
        break;
    }
    default:
    {
        OUStringBuffer buf(128);
        buf.appendAscii( "pq_statement: Invalid property handle (" );
        buf.append( nHandle );
        buf.appendAscii( ")" );
        throw IllegalArgumentException( buf.makeStringAndClear(), *this, 2 );
    }
    }
    return bRet;
}


void PreparedStatement::setFastPropertyValue_NoBroadcast(
    sal_Int32 nHandle,const Any& rValue ) throw (Exception)
{
    m_props[nHandle] = rValue;
}

void PreparedStatement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
{
    rValue = m_props[nHandle];
}

Reference < XPropertySetInfo >  PreparedStatement::getPropertySetInfo()
        throw(RuntimeException)
{
    return OPropertySetHelper::createPropertySetInfo( getPreparedStatementPropertyArrayHelper() );
}

void PreparedStatement::disposing()
{
    close();
}


Reference< XResultSet > PreparedStatement::getResultSet(  )
    throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
{
    return Reference< XResultSet > ( m_lastResultset, com::sun::star::uno::UNO_QUERY );
}
sal_Int32 PreparedStatement::getUpdateCount(  )
    throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
{
    return m_multipleResultUpdateCount;
}
sal_Bool PreparedStatement::getMoreResults(  )
    throw (::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException)
{
    return sal_False;
}

Reference< XResultSet > PreparedStatement::getGeneratedValues(  )
        throw (SQLException, RuntimeException)
{
    osl::MutexGuard guard( m_refMutex->mutex );
    return getGeneratedValuesFromLastInsert(
        m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery );
}


}
