/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <connectivity/CommonTools.hxx>
#include <TConnection.hxx>
#include <ParameterCont.hxx>

#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdb/BooleanComparisonMode.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/ErrorMessageDialog.hpp>
#include <com/sun/star/sdb/ParametersRequest.hpp>
#include <com/sun/star/sdb/RowSetVetoException.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdb/XCompletedConnection.hpp>
#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp>
#include <com/sun/star/sdb/XParametersSupplier.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
#include <com/sun/star/sdbc/ConnectionPool.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbc/XDataSource.hpp>
#include <com/sun/star/sdbc/XParameters.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sdbc/XRowUpdate.hpp>
#include <com/sun/star/sdbcx/KeyType.hpp>
#include <com/sun/star/sdbcx/Privilege.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/util/NumberFormat.hpp>
#include <com/sun/star/util/NumberFormatsSupplier.hpp>
#include <com/sun/star/util/XNumberFormatTypes.hpp>

#include <comphelper/extract.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/property.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/types.hxx>
#include <connectivity/conncleanup.hxx>
#include <connectivity/dbconversion.hxx>
#include <connectivity/dbexception.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/FValue.hxx>
#include <connectivity/statementcomposer.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/stream.hxx>
#include <cppuhelper/implbase.hxx>
#include <strings.hrc>
#include <resource/sharedresources.hxx>

#include <algorithm>
#include <iterator>
#include <set>

using namespace ::comphelper;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::ui::dialogs;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::form;
using namespace connectivity;

namespace dbtools
{

namespace
{
    typedef sal_Bool (SAL_CALL XDatabaseMetaData::*FMetaDataSupport)();
}

sal_Int32 getDefaultNumberFormat(const Reference< XPropertySet >& _xColumn,
                                 const Reference< XNumberFormatTypes >& _xTypes,
                                 const Locale& _rLocale)
{
    OSL_ENSURE(_xTypes.is() && _xColumn.is(), "dbtools::getDefaultNumberFormat: invalid arg !");
    if (!_xTypes.is() || !_xColumn.is())
        return NumberFormat::UNDEFINED;

    sal_Int32 nDataType = 0;
    sal_Int32 nScale = 0;
    try
    {
        // determine the datatype of the column
        _xColumn->getPropertyValue(u"Type"_ustr) >>= nDataType;

        if (DataType::NUMERIC == nDataType || DataType::DECIMAL == nDataType)
            _xColumn->getPropertyValue(u"Scale"_ustr) >>= nScale;
    }
    catch (Exception&)
    {
        return NumberFormat::UNDEFINED;
    }
    return getDefaultNumberFormat(nDataType,
                    nScale,
                    ::cppu::any2bool(_xColumn->getPropertyValue(u"IsCurrency"_ustr)),
                    _xTypes,
                    _rLocale);
}

sal_Int32 getDefaultNumberFormat(sal_Int32 _nDataType,
                                 sal_Int32 _nScale,
                                 bool _bIsCurrency,
                                 const Reference< XNumberFormatTypes >& _xTypes,
                                 const Locale& _rLocale)
{
    OSL_ENSURE(_xTypes.is() , "dbtools::getDefaultNumberFormat: invalid arg !");
    if (!_xTypes.is())
        return NumberFormat::UNDEFINED;

    sal_Int32 nFormat = 0;
    sal_Int32 nNumberType   = _bIsCurrency ? NumberFormat::CURRENCY : NumberFormat::NUMBER;
    switch (_nDataType)
    {
        case DataType::BIT:
        case DataType::BOOLEAN:
            nFormat = _xTypes->getStandardFormat(NumberFormat::LOGICAL, _rLocale);
            break;
        case DataType::TINYINT:
        case DataType::SMALLINT:
        case DataType::INTEGER:
        case DataType::BIGINT:
        case DataType::FLOAT:
        case DataType::REAL:
        case DataType::DOUBLE:
        case DataType::NUMERIC:
        case DataType::DECIMAL:
        {
            try
            {
                nFormat = _xTypes->getStandardFormat(static_cast<sal_Int16>(nNumberType), _rLocale);
                if(_nScale > 0)
                {
                    // generate a new format if necessary
                    Reference< XNumberFormats > xFormats(_xTypes, UNO_QUERY);
                    OUString sNewFormat = xFormats->generateFormat( 0, _rLocale, false, false, static_cast<sal_Int16>(_nScale), 1);

                    // and add it to the formatter if necessary
                    nFormat = xFormats->queryKey(sNewFormat, _rLocale, false);
                    if (nFormat == sal_Int32(-1))
                        nFormat = xFormats->addNew(sNewFormat, _rLocale);
                }
            }
            catch (Exception&)
            {
                nFormat = _xTypes->getStandardFormat(static_cast<sal_Int16>(nNumberType), _rLocale);
            }
        }   break;
        case DataType::CHAR:
        case DataType::VARCHAR:
        case DataType::LONGVARCHAR:
        case DataType::CLOB:
            nFormat = _xTypes->getStandardFormat(NumberFormat::TEXT, _rLocale);
            break;
        case DataType::DATE:
            nFormat = _xTypes->getStandardFormat(NumberFormat::DATE, _rLocale);
            break;
        case DataType::TIME:
            nFormat = _xTypes->getStandardFormat(NumberFormat::TIME, _rLocale);
            break;
        case DataType::TIMESTAMP:
            nFormat = _xTypes->getStandardFormat(NumberFormat::DATETIME, _rLocale);
            break;
        case DataType::BINARY:
        case DataType::VARBINARY:
        case DataType::LONGVARBINARY:
        case DataType::SQLNULL:
        case DataType::OTHER:
        case DataType::OBJECT:
        case DataType::DISTINCT:
        case DataType::STRUCT:
        case DataType::ARRAY:
        case DataType::BLOB:
        case DataType::REF:
        default:
            nFormat = _xTypes->getStandardFormat(NumberFormat::UNDEFINED, _rLocale);
    }
    return nFormat;
}

static Reference< XConnection> findConnection(const Reference< XInterface >& xParent)
{
    Reference< XConnection> xConnection(xParent, UNO_QUERY);
    if (!xConnection.is())
    {
        Reference< XChild> xChild(xParent, UNO_QUERY);
        if (xChild.is())
            xConnection = findConnection(xChild->getParent());
    }
    return xConnection;
}

static Reference< XDataSource> getDataSource_allowException(
            const OUString& _rsTitleOrPath,
            const Reference< XComponentContext >& _rxContext )
{
    ENSURE_OR_RETURN( !_rsTitleOrPath.isEmpty(), "getDataSource_allowException: invalid arg !", nullptr );

    Reference< XDatabaseContext> xDatabaseContext = DatabaseContext::create(_rxContext);

    return Reference< XDataSource >( xDatabaseContext->getByName( _rsTitleOrPath ), UNO_QUERY );
}

Reference< XDataSource > getDataSource(
            const OUString& _rsTitleOrPath,
            const Reference< XComponentContext >& _rxContext )
{
    Reference< XDataSource > xDS;
    try
    {
        xDS = getDataSource_allowException( _rsTitleOrPath, _rxContext );
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
    }

    return xDS;
}

static Reference< XConnection > getConnection_allowException(
            const OUString& _rsTitleOrPath,
            const OUString& _rsUser,
            const OUString& _rsPwd,
            const Reference< XComponentContext>& _rxContext,
            const Reference< XWindow >& _rxParent)
{
    Reference< XDataSource> xDataSource( getDataSource_allowException(_rsTitleOrPath, _rxContext) );
    Reference<XConnection> xConnection;
    if (xDataSource.is())
    {

        //set ParentWindow for dialog, but just for the duration of this
        //call, undo at end of scope
        Reference<XInitialization> xIni(xDataSource, UNO_QUERY);
        if (xIni.is())
        {
            Sequence< Any > aArgs{ Any(NamedValue( u"ParentWindow"_ustr, Any(_rxParent) )) };
            xIni->initialize(aArgs);
        }

        // do it with interaction handler
        if(_rsUser.isEmpty() || _rsPwd.isEmpty())
        {
            Reference<XPropertySet> xProp(xDataSource,UNO_QUERY);
            OUString sPwd, sUser;
            bool bPwdReq = false;
            try
            {
                xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd;
                bPwdReq = ::cppu::any2bool(xProp->getPropertyValue(u"IsPasswordRequired"_ustr));
                xProp->getPropertyValue(u"User"_ustr) >>= sUser;
            }
            catch(Exception&)
            {
                OSL_FAIL("dbtools::getConnection: error while retrieving data source properties!");
            }
            if(bPwdReq && sPwd.isEmpty())
            {   // password required, but empty -> connect using an interaction handler
                Reference<XCompletedConnection> xConnectionCompletion(xProp, UNO_QUERY);
                if (xConnectionCompletion.is())
                {   // instantiate the default SDB interaction handler
                    Reference< XInteractionHandler > xHandler =
                        InteractionHandler::createWithParent(_rxContext, _rxParent);
                    xConnection = xConnectionCompletion->connectWithCompletion(xHandler);
                }
            }
            else
                xConnection = xDataSource->getConnection(sUser, sPwd);
        }
        if(!xConnection.is()) // try to get one if not already have one, just to make sure
            xConnection = xDataSource->getConnection(_rsUser, _rsPwd);

        if (xIni.is())
        {
            Sequence< Any > aArgs{ Any(NamedValue( u"ParentWindow"_ustr, Any(Reference<XWindow>()) )) };
            xIni->initialize(aArgs);
        }

    }
    return xConnection;
}

Reference< XConnection> getConnection_withFeedback(const OUString& _rDataSourceName,
        const OUString& _rUser, const OUString& _rPwd, const Reference< XComponentContext>& _rxContext,
        const Reference< XWindow >& _rxParent)
{
    Reference< XConnection > xReturn;
    try
    {
        xReturn = getConnection_allowException(_rDataSourceName, _rUser, _rPwd, _rxContext, _rxParent);
    }
    catch(SQLException&)
    {
        // allowed to pass
        throw;
    }
    catch(Exception&)
    {
        TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::dbtools::getConnection_withFeedback: unexpected (non-SQL) exception caught!");
    }
    return xReturn;
}

Reference< XConnection> getConnection(const Reference< XRowSet>& _rxRowSet)
{
    Reference< XConnection> xReturn;
    Reference< XPropertySet> xRowSetProps(_rxRowSet, UNO_QUERY);
    if (xRowSetProps.is())
        xRowSetProps->getPropertyValue(u"ActiveConnection"_ustr) >>= xReturn;
    return xReturn;
}

// helper function which allows to implement both the connectRowset and the ensureRowSetConnection semantics
// if connectRowset (which is deprecated) is removed, this function and one of its parameters are
// not needed anymore, the whole implementation can be moved into ensureRowSetConnection then)
static SharedConnection lcl_connectRowSet(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext >& _rxContext,
        bool _bAttachAutoDisposer, const Reference< XWindow >& _rxParent)
{
    SharedConnection xConnection;

    do
    {
        Reference< XPropertySet> xRowSetProps(_rxRowSet, UNO_QUERY);
        if ( !xRowSetProps.is() )
            break;

        // 1. already connected?
        Reference< XConnection > xExistingConn(
            xRowSetProps->getPropertyValue(u"ActiveConnection"_ustr),
            UNO_QUERY );

        if  (   xExistingConn.is()
            // 2. embedded in a database?
            ||  isEmbeddedInDatabase( _rxRowSet, xExistingConn )
            // 3. is there a connection in the parent hierarchy?
            ||  ( xExistingConn = findConnection( _rxRowSet ) ).is()
            )
        {
            xRowSetProps->setPropertyValue(u"ActiveConnection"_ustr, Any( xExistingConn ) );
            // no auto disposer needed, since we did not create the connection

            xConnection.reset( xExistingConn, SharedConnection::NoTakeOwnership );
            break;
        }

        // build a connection with its current settings (4. data source name, or 5. URL)

        static constexpr OUString sUserProp( u"User"_ustr );
        OUString sDataSourceName;
        xRowSetProps->getPropertyValue(u"DataSourceName"_ustr) >>= sDataSourceName;
        OUString sURL;
        xRowSetProps->getPropertyValue(u"URL"_ustr) >>= sURL;

        Reference< XConnection > xPureConnection;
        if (!sDataSourceName.isEmpty())
        {   // the row set's data source property is set
            // -> try to connect, get user and pwd setting for that
            OUString sUser, sPwd;

            if (hasProperty(sUserProp, xRowSetProps))
                xRowSetProps->getPropertyValue(sUserProp) >>= sUser;
            if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), xRowSetProps))
                xRowSetProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd;

            xPureConnection = getConnection_allowException( sDataSourceName, sUser, sPwd, _rxContext, _rxParent );
        }
        else if (!sURL.isEmpty())
        {   // the row set has no data source, but a connection url set
            // -> try to connection with that url
            Reference< XConnectionPool > xDriverManager;
            try {
                xDriverManager = ConnectionPool::create( _rxContext );
            } catch( const Exception& ) {  }
            if (xDriverManager.is())
            {
                OUString sUser, sPwd;
                if (hasProperty(sUserProp, xRowSetProps))
                    xRowSetProps->getPropertyValue(sUserProp) >>= sUser;
                if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), xRowSetProps))
                    xRowSetProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd;
                if (!sUser.isEmpty())
                {   // use user and pwd together with the url
                    auto aInfo(::comphelper::InitPropertySequence({
                        { "user", Any(sUser) },
                        { "password", Any(sPwd) }
                    }));
                    xPureConnection = xDriverManager->getConnectionWithInfo( sURL, aInfo );
                }
                else
                    // just use the url
                    xPureConnection = xDriverManager->getConnection( sURL );
            }
        }
        xConnection.reset(
            xPureConnection,
            _bAttachAutoDisposer ? SharedConnection::NoTakeOwnership : SharedConnection::TakeOwnership
            /* take ownership if and only if we're *not* going to auto-dispose the connection */
        );

        // now if we created a connection, forward it to the row set
        if ( xConnection.is() )
        {
            try
            {
                if ( _bAttachAutoDisposer )
                {
                    new OAutoConnectionDisposer( _rxRowSet, xConnection );
                }
                else
                    xRowSetProps->setPropertyValue(
                        u"ActiveConnection"_ustr,
                        Any( xConnection.getTyped() )
                    );
            }
            catch(Exception&)
            {
                TOOLS_WARN_EXCEPTION( "connectivity.commontools", "EXception when we set the new active connection!");
            }
        }
    }
    while ( false );

    return xConnection;
}

Reference< XConnection> connectRowset(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext >& _rxContext, const Reference< XWindow >& _rxParent)
{
    SharedConnection xConnection = lcl_connectRowSet( _rxRowSet, _rxContext, true, _rxParent );
    return xConnection.getTyped();
}

SharedConnection ensureRowSetConnection(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext>& _rxContext, const Reference< XWindow >& _rxParent)
{
    return lcl_connectRowSet( _rxRowSet, _rxContext, false/*bUseAutoConnectionDisposer*/, _rxParent );
}

Reference< XNameAccess> getTableFields(const Reference< XConnection>& _rxConn,const OUString& _rName)
{
    Reference< XComponent > xDummy;
    return getFieldsByCommandDescriptor( _rxConn, CommandType::TABLE, _rName, xDummy );
}

Reference< XNameAccess> getPrimaryKeyColumns_throw(const Any& i_aTable)
{
    const Reference< XPropertySet > xTable(i_aTable,UNO_QUERY_THROW);
    return getPrimaryKeyColumns_throw(xTable);
}

Reference< XNameAccess> getPrimaryKeyColumns_throw(const Reference< XPropertySet >& i_xTable)
{
    Reference<XNameAccess> xKeyColumns;
    const Reference<XKeysSupplier> xKeySup(i_xTable,UNO_QUERY);
    if ( xKeySup.is() )
    {
        const Reference<XIndexAccess> xKeys = xKeySup->getKeys();
        if ( xKeys.is() )
        {
            ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
            const OUString& sPropName = rPropMap.getNameByIndex(PROPERTY_ID_TYPE);
            Reference<XPropertySet> xProp;
            const sal_Int32 nCount = xKeys->getCount();
            for(sal_Int32 i = 0;i< nCount;++i)
            {
                xProp.set(xKeys->getByIndex(i),UNO_QUERY_THROW);
                sal_Int32 nKeyType = 0;
                xProp->getPropertyValue(sPropName) >>= nKeyType;
                if(KeyType::PRIMARY == nKeyType)
                {
                    const Reference<XColumnsSupplier> xKeyColsSup(xProp,UNO_QUERY_THROW);
                    xKeyColumns = xKeyColsSup->getColumns();
                    break;
                }
            }
        }
    }

    return xKeyColumns;
}

namespace
{
    enum FieldLookupState
    {
        HANDLE_TABLE, HANDLE_QUERY, HANDLE_SQL, RETRIEVE_OBJECT, RETRIEVE_COLUMNS, DONE, FAILED
    };
}

Reference< XNameAccess > getFieldsByCommandDescriptor( const Reference< XConnection >& _rxConnection,
    const sal_Int32 _nCommandType, const OUString& _rCommand,
    Reference< XComponent >& _rxKeepFieldsAlive, SQLExceptionInfo* _pErrorInfo )
{
    OSL_PRECOND( _rxConnection.is(), "::dbtools::getFieldsByCommandDescriptor: invalid connection!" );
    OSL_PRECOND( ( CommandType::TABLE == _nCommandType ) || ( CommandType::QUERY == _nCommandType ) || ( CommandType::COMMAND == _nCommandType ),
        "::dbtools::getFieldsByCommandDescriptor: invalid command type!" );
    OSL_PRECOND( !_rCommand.isEmpty(), "::dbtools::getFieldsByCommandDescriptor: invalid command (empty)!" );

    Reference< XNameAccess > xFields;

    // reset the error
    if ( _pErrorInfo )
        *_pErrorInfo = SQLExceptionInfo();
    // reset the ownership holder
    _rxKeepFieldsAlive.clear();

    // go for the fields
    try
    {
        // some kind of state machine to ease the sharing of code
        FieldLookupState eState = FAILED;
        switch ( _nCommandType )
        {
            case CommandType::TABLE:
                eState = HANDLE_TABLE;
                break;
            case CommandType::QUERY:
                eState = HANDLE_QUERY;
                break;
            case CommandType::COMMAND:
                eState = HANDLE_SQL;
                break;
        }

        // needed in various states:
        Reference< XNameAccess > xObjectCollection;
        Reference< XColumnsSupplier > xSupplyColumns;

        // go!
        while ( ( DONE != eState ) && ( FAILED != eState ) )
        {
            switch ( eState )
            {
                case HANDLE_TABLE:
                {
                    // initial state for handling the tables

                    // get the table objects
                    Reference< XTablesSupplier > xSupplyTables( _rxConnection, UNO_QUERY );
                    if ( xSupplyTables.is() )
                        xObjectCollection = xSupplyTables->getTables();
                    // if something went wrong 'til here, then this will be handled in the next state

                    // next state: get the object
                    eState = RETRIEVE_OBJECT;
                }
                break;

                case HANDLE_QUERY:
                {
                    // initial state for handling the tables

                    // get the table objects
                    Reference< XQueriesSupplier > xSupplyQueries( _rxConnection, UNO_QUERY );
                    if ( xSupplyQueries.is() )
                        xObjectCollection = xSupplyQueries->getQueries();
                    // if something went wrong 'til here, then this will be handled in the next state

                    // next state: get the object
                    eState = RETRIEVE_OBJECT;
                }
                break;

                case RETRIEVE_OBJECT:
                    // here we should have an object (aka query or table) collection, and are going
                    // to retrieve the desired object

                    // next state: default to FAILED
                    eState = FAILED;

                    OSL_ENSURE( xObjectCollection.is(), "::dbtools::getFieldsByCommandDescriptor: invalid connection (no sdb.Connection, or no Tables-/QueriesSupplier)!");
                    if ( xObjectCollection.is() && xObjectCollection->hasByName( _rCommand ) )
                    {
                        xObjectCollection->getByName( _rCommand ) >>= xSupplyColumns;
                            // (xSupplyColumns being NULL will be handled in the next state)

                        // next: go for the columns
                        eState = RETRIEVE_COLUMNS;
                    }
                    break;

                case RETRIEVE_COLUMNS:
                    OSL_ENSURE( xSupplyColumns.is(), "::dbtools::getFieldsByCommandDescriptor: could not retrieve the columns supplier!" );

                    // next state: default to FAILED
                    eState = FAILED;

                    if ( xSupplyColumns.is() )
                    {
                        xFields = xSupplyColumns->getColumns();
                        // that's it
                        eState = DONE;
                    }
                    break;

                case HANDLE_SQL:
                {
                    OUString sStatementToExecute( _rCommand );

                    // well, the main problem here is to handle statements which contain a parameter
                    // If we would simply execute a parametrized statement, then this will fail because
                    // we cannot supply any parameter values.
                    // Thus, we try to analyze the statement, and to append a WHERE 0=1 filter criterion
                    // This should cause every driver to not really execute the statement, but to return
                    // an empty result set with the proper structure. We then can use this result set
                    // to retrieve the columns.

                    try
                    {
                        Reference< XMultiServiceFactory > xComposerFac( _rxConnection, UNO_QUERY );

                        if ( xComposerFac.is() )
                        {
                            Reference< XSingleSelectQueryComposer > xComposer(xComposerFac->createInstance(u"com.sun.star.sdb.SingleSelectQueryComposer"_ustr),UNO_QUERY);
                            if ( xComposer.is() )
                            {
                                xComposer->setQuery( sStatementToExecute );

                                // Now set the filter to a dummy restriction which will result in an empty
                                // result set.
                                xComposer->setFilter( u"0=1"_ustr );
                                sStatementToExecute = xComposer->getQuery( );
                            }
                        }
                    }
                    catch( const Exception& )
                    {
                        // silent this error, this was just a try. If we're here, we did not change sStatementToExecute,
                        // so it will still be _rCommand, which then will be executed without being touched
                    }

                    // now execute
                    Reference< XPreparedStatement > xStatement = _rxConnection->prepareStatement( sStatementToExecute );
                    // transfer ownership of this temporary object to the caller
                    _rxKeepFieldsAlive.set(xStatement, css::uno::UNO_QUERY);

                    // set the "MaxRows" to 0. This is just in case our attempt to append a 0=1 filter
                    // failed - in this case, the MaxRows restriction should at least ensure that there
                    // is no data returned (which would be potentially expensive)
                    Reference< XPropertySet > xStatementProps( xStatement,UNO_QUERY );
                    try
                    {
                        if ( xStatementProps.is() )
                            xStatementProps->setPropertyValue( u"MaxRows"_ustr,  Any( sal_Int32( 0 ) ) );
                    }
                    catch( const Exception& )
                    {
                        OSL_FAIL( "::dbtools::getFieldsByCommandDescriptor: could not set the MaxRows!" );
                        // oh damn. Not much of a chance to recover, we will no retrieve the complete
                        // full blown result set
                    }

                    xSupplyColumns.set(xStatement->executeQuery(), css::uno::UNO_QUERY);
                    // this should have given us a result set which does not contain any data, but
                    // the structural information we need

                    // so the next state is to get the columns
                    eState = RETRIEVE_COLUMNS;
                }
                break;

                default:
                    OSL_FAIL( "::dbtools::getFieldsByCommandDescriptor: oops! unhandled state here!" );
                    eState = FAILED;
            }
        }
    }
    catch( const SQLContext& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); }
    catch( const SQLWarning& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); }
    catch( const SQLException& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); }
    catch( const Exception& )
    {
        TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::dbtools::getFieldsByCommandDescriptor: caught an exception while retrieving the fields!" );
    }

    return xFields;
}

Sequence< OUString > getFieldNamesByCommandDescriptor( const Reference< XConnection >& _rxConnection,
    const sal_Int32 _nCommandType, const OUString& _rCommand,
    SQLExceptionInfo* _pErrorInfo )
{
    // get the container for the fields
    Reference< XComponent > xKeepFieldsAlive;
    Reference< XNameAccess > xFieldContainer = getFieldsByCommandDescriptor( _rxConnection, _nCommandType, _rCommand, xKeepFieldsAlive, _pErrorInfo );

    // get the names of the fields
    Sequence< OUString > aNames;
    if ( xFieldContainer.is() )
        aNames = xFieldContainer->getElementNames();

    // clean up any temporary objects which have been created
    disposeComponent( xKeepFieldsAlive );

    // outta here
    return aNames;
}

SQLException prependErrorInfo( const SQLException& _rChainedException, const Reference< XInterface >& _rxContext,
    const OUString& _rAdditionalError, const StandardSQLState _eSQLState )
{
    return SQLException( _rAdditionalError, _rxContext,
        _eSQLState == StandardSQLState::ERROR_UNSPECIFIED ? OUString() : getStandardSQLState( _eSQLState ),
        0, Any( _rChainedException ) );
}

namespace
{
    struct NameComponentSupport
    {
        const bool  bCatalogs;
        const bool  bSchemas;

        NameComponentSupport( const bool _bCatalogs, const bool _bSchemas )
            :bCatalogs( _bCatalogs )
            ,bSchemas( _bSchemas )
        {
        }
    };

    NameComponentSupport lcl_getNameComponentSupport( const Reference< XDatabaseMetaData >& _rxMetaData, EComposeRule _eComposeRule )
    {
        OSL_PRECOND( _rxMetaData.is(), "lcl_getNameComponentSupport: invalid meta data!" );

        FMetaDataSupport pCatalogCall = &XDatabaseMetaData::supportsCatalogsInDataManipulation;
        FMetaDataSupport pSchemaCall = &XDatabaseMetaData::supportsSchemasInDataManipulation;
        bool bIgnoreMetaData = false;

        switch ( _eComposeRule )
        {
            case EComposeRule::InTableDefinitions:
                pCatalogCall = &XDatabaseMetaData::supportsCatalogsInTableDefinitions;
                pSchemaCall = &XDatabaseMetaData::supportsSchemasInTableDefinitions;
                break;
            case EComposeRule::InIndexDefinitions:
                pCatalogCall = &XDatabaseMetaData::supportsCatalogsInIndexDefinitions;
                pSchemaCall = &XDatabaseMetaData::supportsSchemasInIndexDefinitions;
                break;
            case EComposeRule::InProcedureCalls:
                pCatalogCall = &XDatabaseMetaData::supportsCatalogsInProcedureCalls;
                pSchemaCall = &XDatabaseMetaData::supportsSchemasInProcedureCalls;
                break;
            case EComposeRule::InPrivilegeDefinitions:
                pCatalogCall = &XDatabaseMetaData::supportsCatalogsInPrivilegeDefinitions;
                pSchemaCall = &XDatabaseMetaData::supportsSchemasInPrivilegeDefinitions;
                break;
            case EComposeRule::Complete:
                bIgnoreMetaData = true;
                break;
            case EComposeRule::InDataManipulation:
                // already properly set above
                break;
        }
        return NameComponentSupport(
            bIgnoreMetaData || (_rxMetaData.get()->*pCatalogCall)(),
            bIgnoreMetaData || (_rxMetaData.get()->*pSchemaCall)()
        );
    }
}

static OUString impl_doComposeTableName( const Reference< XDatabaseMetaData >& _rxMetaData,
                const OUString& _rCatalog, const OUString& _rSchema, const OUString& _rName,
                bool _bQuote, EComposeRule _eComposeRule )
{
    OSL_ENSURE(_rxMetaData.is(), "impl_doComposeTableName : invalid meta data !");
    if ( !_rxMetaData.is() )
        return OUString();
    OSL_ENSURE(!_rName.isEmpty(), "impl_doComposeTableName : at least the name should be non-empty !");

    const OUString sQuoteString = _rxMetaData->getIdentifierQuoteString();
    const NameComponentSupport aNameComps( lcl_getNameComponentSupport( _rxMetaData, _eComposeRule ) );

    OUStringBuffer aComposedName;

    OUString sCatalogSep;
    bool bCatalogAtStart = true;
    if ( !_rCatalog.isEmpty() && aNameComps.bCatalogs )
    {
        sCatalogSep     = _rxMetaData->getCatalogSeparator();
        bCatalogAtStart  = _rxMetaData->isCatalogAtStart();

        if ( bCatalogAtStart && !sCatalogSep.isEmpty())
        {
            aComposedName.append( _bQuote ? quoteName( sQuoteString, _rCatalog ) : _rCatalog );
            aComposedName.append( sCatalogSep );
        }
    }

    if ( !_rSchema.isEmpty() && aNameComps.bSchemas )
    {
        aComposedName.append(
            (_bQuote ? quoteName( sQuoteString, _rSchema ) : _rSchema )
            + "." );
    }

    aComposedName.append( _bQuote ? quoteName( sQuoteString, _rName ) : _rName );

    if  (   !_rCatalog.isEmpty()
        &&  !bCatalogAtStart
        &&  !sCatalogSep.isEmpty()
        &&  aNameComps.bCatalogs
        )
    {
        aComposedName.append( sCatalogSep );
        aComposedName.append( _bQuote ? quoteName( sQuoteString, _rCatalog ) : _rCatalog );
    }

    return aComposedName.makeStringAndClear();
}

OUString quoteTableName(const Reference< XDatabaseMetaData>& _rxMeta
                               , const OUString& _rName
                               , EComposeRule _eComposeRule)
{
    OUString sCatalog, sSchema, sTable;
    qualifiedNameComponents(_rxMeta,_rName,sCatalog,sSchema,sTable,_eComposeRule);
    return impl_doComposeTableName( _rxMeta, sCatalog, sSchema, sTable, true, _eComposeRule );
}

void qualifiedNameComponents(const Reference< XDatabaseMetaData >& _rxConnMetaData, const OUString& _rQualifiedName, OUString& _rCatalog, OUString& _rSchema, OUString& _rName,EComposeRule _eComposeRule)
{
    OSL_ENSURE(_rxConnMetaData.is(), "QualifiedNameComponents : invalid meta data!");

    NameComponentSupport aNameComps( lcl_getNameComponentSupport( _rxConnMetaData, _eComposeRule ) );

    OUString sSeparator = _rxConnMetaData->getCatalogSeparator();

    OUString sName(_rQualifiedName);
    // do we have catalogs?
    if ( aNameComps.bCatalogs )
    {
        if (_rxConnMetaData->isCatalogAtStart())
        {
            // search for the catalog name at the beginning
            sal_Int32 nIndex = sName.indexOf(sSeparator);
            if (-1 != nIndex)
            {
                _rCatalog = sName.copy(0, nIndex);
                sName = sName.copy(nIndex + 1);
            }
        }
        else
        {
            // Catalog name at the end
            sal_Int32 nIndex = sName.lastIndexOf(sSeparator);
            if (-1 != nIndex)
            {
                _rCatalog = sName.copy(nIndex + 1);
                sName = sName.copy(0, nIndex);
            }
        }
    }

    if ( aNameComps.bSchemas )
    {
        sal_Int32 nIndex = sName.indexOf('.');
        //  OSL_ENSURE(-1 != nIndex, "QualifiedNameComponents: no schema separator!");
        if ( nIndex != -1 )
            _rSchema = sName.copy(0, nIndex);
        sName = sName.copy(nIndex + 1);
    }

    _rName = sName;
}

Reference< XNumberFormatsSupplier> getNumberFormats(
            const Reference< XConnection>& _rxConn,
            bool _bAlloweDefault,
            const Reference< XComponentContext>& _rxContext)
{
    // ask the parent of the connection (should be a DatabaseAccess)
    Reference< XNumberFormatsSupplier> xReturn;
    Reference< XChild> xConnAsChild(_rxConn, UNO_QUERY);
    static constexpr OUString sPropFormatsSupplier( u"NumberFormatsSupplier"_ustr );
    if (xConnAsChild.is())
    {
        Reference< XPropertySet> xConnParentProps(xConnAsChild->getParent(), UNO_QUERY);
        if (xConnParentProps.is() && hasProperty(sPropFormatsSupplier, xConnParentProps))
            xConnParentProps->getPropertyValue(sPropFormatsSupplier) >>= xReturn;
    }
    else if(_bAlloweDefault && _rxContext.is())
    {
        xReturn = NumberFormatsSupplier::createWithDefaultLocale( _rxContext );
    }
    return xReturn;
}

void TransferFormComponentProperties(
            const Reference< XPropertySet>& xOldProps,
            const Reference< XPropertySet>& xNewProps,
            const Locale& _rLocale)
{
try
{
    OSL_ENSURE( xOldProps.is() && xNewProps.is(), "TransferFormComponentProperties: invalid source/dest!" );
    if ( !xOldProps.is() || !xNewProps.is() )
        return;

    // First we copy all the Props, that are available in source and target and have the same description
    Reference< XPropertySetInfo> xOldInfo( xOldProps->getPropertySetInfo());
    Reference< XPropertySetInfo> xNewInfo( xNewProps->getPropertySetInfo());

    const Sequence< Property> aOldProperties = xOldInfo->getProperties();
    const Sequence< Property> aNewProperties = xNewInfo->getProperties();

    static constexpr OUString sPropFormatsSupplier(u"FormatsSupplier"_ustr);
    static constexpr OUString sPropCurrencySymbol(u"CurrencySymbol"_ustr);
    static constexpr OUString sPropDecimals(u"Decimals"_ustr);
    static constexpr OUString sPropEffectiveMin(u"EffectiveMin"_ustr);
    static constexpr OUString sPropEffectiveMax(u"EffectiveMax"_ustr);
    static constexpr OUString sPropEffectiveDefault(u"EffectiveDefault"_ustr);
    static constexpr OUString sPropDefaultText(u"DefaultText"_ustr);
    static constexpr OUString sPropDefaultDate(u"DefaultDate"_ustr);
    static constexpr OUString sPropDefaultTime(u"DefaultTime"_ustr);
    static constexpr OUString sPropValueMin(u"ValueMin"_ustr);
    static constexpr OUString sPropValueMax(u"ValueMax"_ustr);
    static constexpr OUString sPropDecimalAccuracy(u"DecimalAccuracy"_ustr);
    static constexpr OUString sPropClassId(u"ClassId"_ustr);
    static constexpr OUString sFormattedServiceName( u"com.sun.star.form.component.FormattedField"_ustr );

    for (const Property& rOldProp : aOldProperties)
    {
        if ( rOldProp.Name != "DefaultControl" && rOldProp.Name != "LabelControl" )
        {
            // binary search
            const Property* pResult = std::lower_bound(
                aNewProperties.begin(), aNewProperties.end(), rOldProp, ::comphelper::PropertyCompareByName());

            if (   ( pResult != aNewProperties.end() )
                && ( pResult->Name == rOldProp.Name )
                && ( (pResult->Attributes & PropertyAttribute::READONLY) == 0 )
                && ( pResult->Type.equals(rOldProp.Type)) )
            {   // Attributes match and the property is not read-only
                try
                {
                    xNewProps->setPropertyValue(pResult->Name, xOldProps->getPropertyValue(pResult->Name));
                }
                catch(IllegalArgumentException const &)
                {
                    TOOLS_WARN_EXCEPTION( "connectivity.commontools", "TransferFormComponentProperties : could not transfer the value for property \""
                                << pResult->Name << "\"");
                }
            }
        }
    }

    // for formatted fields (either old or new) we have some special treatments
    Reference< XServiceInfo > xSI( xOldProps, UNO_QUERY );
    bool bOldIsFormatted = xSI.is() && xSI->supportsService( sFormattedServiceName );
    xSI.set( xNewProps, UNO_QUERY );
    bool bNewIsFormatted = xSI.is() && xSI->supportsService( sFormattedServiceName );

    if (!bOldIsFormatted && !bNewIsFormatted)
        return; // nothing to do

    if (bOldIsFormatted && bNewIsFormatted)
        // if both fields are formatted we do no conversions
        return;

    if (bOldIsFormatted)
    {
        // get some properties from the selected format and put them in the new Set
        Any aFormatKey( xOldProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)) );
        if (aFormatKey.hasValue())
        {
            Reference< XNumberFormatsSupplier> xSupplier;
            xOldProps->getPropertyValue(sPropFormatsSupplier) >>= xSupplier;
            if (xSupplier.is())
            {
                Reference< XNumberFormats> xFormats(xSupplier->getNumberFormats());
                Reference< XPropertySet> xFormat(xFormats->getByKey(getINT32(aFormatKey)));
                if (hasProperty(sPropCurrencySymbol, xFormat))
                {
                    Any aVal( xFormat->getPropertyValue(sPropCurrencySymbol) );
                    if (aVal.hasValue() && hasProperty(sPropCurrencySymbol, xNewProps))
                        // If the source value hasn't been set then don't copy it
                        // so we don't overwrite the default value
                        xNewProps->setPropertyValue(sPropCurrencySymbol, aVal);
                }
                if (hasProperty(sPropDecimals, xFormat) && hasProperty(sPropDecimals, xNewProps))
                    xNewProps->setPropertyValue(sPropDecimals, xFormat->getPropertyValue(sPropDecimals));
            }
        }

        // a potential Min-Max-Conversion
        Any aEffectiveMin( xOldProps->getPropertyValue(sPropEffectiveMin) );
        if (aEffectiveMin.hasValue())
        {   // Unlike the ValueMin the EffectiveMin can be void
            if (hasProperty(sPropValueMin, xNewProps))
            {
                OSL_ENSURE(aEffectiveMin.getValueTypeClass() == TypeClass_DOUBLE,
                    "TransferFormComponentProperties : invalid property type !");
                xNewProps->setPropertyValue(sPropValueMin, aEffectiveMin);
            }
        }
        Any aEffectiveMax( xOldProps->getPropertyValue(sPropEffectiveMax) );
        if (aEffectiveMax.hasValue())
        {   // analog
            if (hasProperty(sPropValueMax, xNewProps))
            {
                OSL_ENSURE(aEffectiveMax.getValueTypeClass() == TypeClass_DOUBLE,
                    "TransferFormComponentProperties : invalid property type !");
                xNewProps->setPropertyValue(sPropValueMax, aEffectiveMax);
            }
        }

        // then we can still convert and copy the default values
        Any aEffectiveDefault( xOldProps->getPropertyValue(sPropEffectiveDefault) );
        if (aEffectiveDefault.hasValue())
        {
            bool bIsString = aEffectiveDefault.getValueTypeClass() == TypeClass_STRING;
            OSL_ENSURE(bIsString || aEffectiveDefault.getValueTypeClass() == TypeClass_DOUBLE,
                "TransferFormComponentProperties : invalid property type !");
                // The Effective-Properties should always be void or string or double...

            if (hasProperty(sPropDefaultDate, xNewProps) && !bIsString)
            {   // (to convert an OUString into a date will not always succeed, because it might be bound to a text-column,
                // but we can work with a double)
                Date aDate = DBTypeConversion::toDate(getDouble(aEffectiveDefault));
                xNewProps->setPropertyValue(sPropDefaultDate, Any(aDate));
            }

            if (hasProperty(sPropDefaultTime, xNewProps) && !bIsString)
            {   // Completely analogous to time
                css::util::Time aTime = DBTypeConversion::toTime(getDouble(aEffectiveDefault));
                xNewProps->setPropertyValue(sPropDefaultTime, Any(aTime));
            }

            if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), xNewProps) && !bIsString)
            {   // Here we can simply pass the double
                xNewProps->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), aEffectiveDefault);
            }

            if (hasProperty(sPropDefaultText, xNewProps) && bIsString)
            {   // and here the OUString
                xNewProps->setPropertyValue(sPropDefaultText, aEffectiveDefault);
            }

            // nyi: The translation between doubles and OUString would offer more alternatives
        }
    }

    // The other direction: the new Control shall be formatted
    if (bNewIsFormatted)
    {
        // first the formatting
        // we can't set a Supplier, so the new Set must bring one in
        Reference< XNumberFormatsSupplier> xSupplier;
        xNewProps->getPropertyValue(sPropFormatsSupplier) >>= xSupplier;
        if (xSupplier.is())
        {
            Reference< XNumberFormats> xFormats(xSupplier->getNumberFormats());

            // Set number of decimals
            sal_Int16 nDecimals = 2;
            if (hasProperty(sPropDecimalAccuracy, xOldProps))
                xOldProps->getPropertyValue(sPropDecimalAccuracy) >>= nDecimals;

            // base format (depending on the ClassId of the old Set)
            sal_Int32 nBaseKey = 0;
            if (hasProperty(sPropClassId, xOldProps))
            {
                Reference< XNumberFormatTypes> xTypeList(xFormats, UNO_QUERY);
                if (xTypeList.is())
                {
                    sal_Int16 nClassId = 0;
                    xOldProps->getPropertyValue(sPropClassId) >>= nClassId;
                    switch (nClassId)
                    {
                        case FormComponentType::DATEFIELD :
                            nBaseKey = xTypeList->getStandardFormat(NumberFormat::DATE, _rLocale);
                            break;

                        case FormComponentType::TIMEFIELD :
                            nBaseKey = xTypeList->getStandardFormat(NumberFormat::TIME, _rLocale);
                            break;

                        case FormComponentType::CURRENCYFIELD :
                            nBaseKey = xTypeList->getStandardFormat(NumberFormat::CURRENCY, _rLocale);
                            break;
                    }
                }
            }

            // With this we can generate a new format ...
            OUString sNewFormat = xFormats->generateFormat(nBaseKey, _rLocale, false, false, nDecimals, 0);
            // No thousands separator, negative numbers are not in red, no leading zeros

            // ... and add at FormatsSupplier (if needed)
            sal_Int32 nKey = xFormats->queryKey(sNewFormat, _rLocale, false);
            if (nKey == sal_Int32(-1))
            {   // not added yet in my formatter ...
                nKey = xFormats->addNew(sNewFormat, _rLocale);
            }

            xNewProps->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY), Any(nKey));
        }

        // min-/max-Value
        Any aNewMin, aNewMax;
        if (hasProperty(sPropValueMin, xOldProps))
            aNewMin = xOldProps->getPropertyValue(sPropValueMin);
        if (hasProperty(sPropValueMax, xOldProps))
            aNewMax = xOldProps->getPropertyValue(sPropValueMax);
        xNewProps->setPropertyValue(sPropEffectiveMin, aNewMin);
        xNewProps->setPropertyValue(sPropEffectiveMax, aNewMax);

        // Default-Value
        Any aNewDefault;
        if (hasProperty(sPropDefaultDate, xOldProps))
        {
            Any aDate( xOldProps->getPropertyValue(sPropDefaultDate) );
            if (aDate.hasValue())
                aNewDefault <<= DBTypeConversion::toDouble(*o3tl::doAccess<Date>(aDate));
        }

        if (hasProperty(sPropDefaultTime, xOldProps))
        {
            Any aTime( xOldProps->getPropertyValue(sPropDefaultTime) );
            if (aTime.hasValue())
                aNewDefault <<= DBTypeConversion::toDouble(*o3tl::doAccess<Time>(aTime));
        }

        // double or OUString will be copied directly
        if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), xOldProps))
            aNewDefault = xOldProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE));
        if (hasProperty(sPropDefaultText, xOldProps))
            aNewDefault = xOldProps->getPropertyValue(sPropDefaultText);

        if (aNewDefault.hasValue())
            xNewProps->setPropertyValue(sPropEffectiveDefault, aNewDefault);
    }
}
catch(const Exception&)
{
    TOOLS_WARN_EXCEPTION( "connectivity.commontools", "TransferFormComponentProperties" );
}
}

bool canInsert(const Reference< XPropertySet>& _rxCursorSet)
{
    return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue(u"Privileges"_ustr)) & Privilege::INSERT) != 0);
}

bool canUpdate(const Reference< XPropertySet>& _rxCursorSet)
{
    return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue(u"Privileges"_ustr)) & Privilege::UPDATE) != 0);
}

bool canDelete(const Reference< XPropertySet>& _rxCursorSet)
{
    return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue(u"Privileges"_ustr)) & Privilege::DELETE) != 0);
}

Reference< XDataSource> findDataSource(const Reference< XInterface >& _xParent)
{
    Reference< XOfficeDatabaseDocument> xDatabaseDocument(_xParent, UNO_QUERY);
    Reference< XDataSource> xDataSource;
    if ( xDatabaseDocument.is() )
        xDataSource = xDatabaseDocument->getDataSource();
    if ( !xDataSource.is() )
        xDataSource.set(_xParent, UNO_QUERY);
    if (!xDataSource.is())
    {
        Reference< XChild> xChild(_xParent, UNO_QUERY);
        if ( xChild.is() )
            xDataSource = findDataSource(xChild->getParent());
    }
    return xDataSource;
}

static Reference< XSingleSelectQueryComposer > getComposedRowSetStatement( const Reference< XPropertySet >& _rxRowSet, const Reference< XComponentContext >& _rxContext, const Reference< XWindow >& _rxParent )
{
    Reference< XSingleSelectQueryComposer > xComposer;
    try
    {
        Reference< XConnection> xConn = connectRowset( Reference< XRowSet >( _rxRowSet, UNO_QUERY ), _rxContext, _rxParent );
        if ( xConn.is() )       // implies _rxRowSet.is()
        {
            // build the statement the row set is based on (can't use the ActiveCommand property of the set
            // as this reflects the status after the last execute, not the currently set properties)

            sal_Int32 nCommandType = CommandType::COMMAND;
            OUString sCommand;
            bool bEscapeProcessing = false;

            OSL_VERIFY( _rxRowSet->getPropertyValue(u"CommandType"_ustr) >>= nCommandType      );
            OSL_VERIFY( _rxRowSet->getPropertyValue(u"Command"_ustr) >>= sCommand          );
            OSL_VERIFY( _rxRowSet->getPropertyValue(u"EscapeProcessing"_ustr) >>= bEscapeProcessing );

            StatementComposer aComposer( xConn, sCommand, nCommandType, bEscapeProcessing );
            // append sort
            aComposer.setOrder( getString( _rxRowSet->getPropertyValue(u"Order"_ustr) ) );

            // append filter
            bool bApplyFilter = true;
            _rxRowSet->getPropertyValue(u"ApplyFilter"_ustr) >>= bApplyFilter;
            if ( bApplyFilter )
            {
                aComposer.setFilter( getString( _rxRowSet->getPropertyValue(u"Filter"_ustr) ) );
                aComposer.setHavingClause( getString( _rxRowSet->getPropertyValue(u"HavingClause"_ustr) ) );
            }

            aComposer.getQuery();

            xComposer = aComposer.getComposer();
            aComposer.setDisposeComposer( false );
        }
    }
    catch( const SQLException& )
    {
        throw;
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
    }

    return xComposer;
}

Reference< XSingleSelectQueryComposer > getCurrentSettingsComposer(
                const Reference< XPropertySet>& _rxRowSetProps,
                const Reference< XComponentContext>& _rxContext,
                const Reference< XWindow >& _rxParent)
{
    Reference< XSingleSelectQueryComposer > xReturn;
    try
    {
        xReturn = getComposedRowSetStatement( _rxRowSetProps, _rxContext, _rxParent );
    }
    catch( const SQLException& )
    {
        throw;
    }
    catch( const Exception& )
    {
        TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::getCurrentSettingsComposer : caught an exception !" );
    }

    return xReturn;
}

OUString composeTableName( const Reference< XDatabaseMetaData >& _rxMetaData,
                        const OUString& _rCatalog,
                        const OUString& _rSchema,
                        const OUString& _rName,
                        bool _bQuote,
                        EComposeRule _eComposeRule)
{
    return impl_doComposeTableName( _rxMetaData, _rCatalog, _rSchema, _rName, _bQuote, _eComposeRule );
}

OUString composeTableNameForSelect( const Reference< XConnection >& _rxConnection,
    const OUString& _rCatalog, const OUString& _rSchema, const OUString& _rName )
{
    bool bUseCatalogInSelect = isDataSourcePropertyEnabled( _rxConnection, u"UseCatalogInSelect"_ustr, true );
    bool bUseSchemaInSelect = isDataSourcePropertyEnabled( _rxConnection, u"UseSchemaInSelect"_ustr, true );

    return impl_doComposeTableName(
        _rxConnection->getMetaData(),
        bUseCatalogInSelect ? _rCatalog : OUString(),
        bUseSchemaInSelect ? _rSchema : OUString(),
        _rName,
        true,
        EComposeRule::InDataManipulation
    );
}

namespace
{
    void lcl_getTableNameComponents( const Reference<XPropertySet>& _xTable,
        OUString& _out_rCatalog, OUString& _out_rSchema, OUString& _out_rName )
    {
        ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
        Reference< XPropertySetInfo > xInfo;
        if (_xTable.is())
            xInfo = _xTable->getPropertySetInfo();
        if (    xInfo.is()
            &&  xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) )
        {
            if (    xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))
                &&  xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) )
            {
                _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= _out_rCatalog;
                _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME))  >>= _out_rSchema;
            }
            _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))        >>= _out_rName;
        }
        else
            OSL_FAIL( "::dbtools::lcl_getTableNameComponents: this is no table object!" );
    }
}

OUString composeTableNameForSelect( const Reference< XConnection >& _rxConnection, const Reference<XPropertySet>& _xTable )
{
    OUString sCatalog, sSchema, sName;
    lcl_getTableNameComponents( _xTable, sCatalog, sSchema, sName );

    return composeTableNameForSelect( _rxConnection, sCatalog, sSchema, sName );
}

OUString composeTableName(const Reference<XDatabaseMetaData>& _xMetaData,
                                 const Reference<XPropertySet>& _xTable,
                                 EComposeRule _eComposeRule,
                                 bool _bQuote )
{
    OUString sCatalog, sSchema, sName;
    lcl_getTableNameComponents( _xTable, sCatalog, sSchema, sName );

    return impl_doComposeTableName(
            _xMetaData,
            sCatalog,
            sSchema,
            sName,
            _bQuote,
            _eComposeRule
        );
}

sal_Int32 getSearchColumnFlag( const Reference< XConnection>& _rxConn,sal_Int32 _nDataType)
{
    sal_Int32 nSearchFlag = 0;
    Reference<XResultSet> xSet = _rxConn->getMetaData()->getTypeInfo();
    if(xSet.is())
    {
        Reference<XRow> xRow(xSet,UNO_QUERY);
        while(xSet->next())
        {
            if(xRow->getInt(2) == _nDataType)
            {
                nSearchFlag = xRow->getInt(9);
                break;
            }
        }
    }
    return nSearchFlag;
}

OUString createUniqueName( const Sequence< OUString >& _rNames, const OUString& _rBaseName, bool _bStartWithNumber )
{
    std::set< OUString > aUsedNames(_rNames.begin(), _rNames.end());

    OUString sName( _rBaseName );
    sal_Int32 nPos = 1;
    if ( _bStartWithNumber )
        sName += OUString::number( nPos );

    while ( aUsedNames.contains( sName ) )
    {
        sName = _rBaseName + OUString::number( ++nPos );
    }
    return sName;
}

OUString createUniqueName(const Reference<XNameAccess>& _rxContainer,const OUString& _rBaseName, bool _bStartWithNumber)
{
    Sequence< OUString > aElementNames;

    OSL_ENSURE( _rxContainer.is(), "createUniqueName: invalid container!" );
    if ( _rxContainer.is() )
        aElementNames = _rxContainer->getElementNames();

    return createUniqueName( aElementNames, _rBaseName, _bStartWithNumber );
}

OUString createUniqueName(const Reference<XTablesSupplier>& _rxSupplier,const OUString& _rBaseName, bool _bStartWithNumber)
{
    Reference< XNameAccess > xContainer;
    OSL_ENSURE( _rxSupplier.is(), "createUniqueName: invalid connection!" );
    if ( _rxSupplier.is() )
        xContainer = _rxSupplier->getTables();
    return createUniqueName( xContainer, _rBaseName, _bStartWithNumber );
}

void showError(const SQLExceptionInfo& _rInfo,
               const Reference< XWindow>& _xParent,
               const Reference< XComponentContext >& _rxContext)
{
    if (_rInfo.isValid())
    {
        try
        {
            Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create( _rxContext, u""_ustr, _xParent, _rInfo.get() );
            xErrorDialog->execute();
        }
        catch(const Exception&)
        {
            OSL_FAIL("showError: could not display the error message!");
        }
    }
}

bool implUpdateObject(const Reference< XRowUpdate >& _rxUpdatedObject,
    const sal_Int32 _nColumnIndex, const Any& _rValue)
{
    bool bSuccessfullyReRouted = true;
    switch (_rValue.getValueTypeClass())
    {
        case TypeClass_ANY:
        {
            bSuccessfullyReRouted = implUpdateObject(_rxUpdatedObject, _nColumnIndex, _rValue);
        }
        break;

        case TypeClass_VOID:
            _rxUpdatedObject->updateNull(_nColumnIndex);
            break;

        case TypeClass_STRING:
            _rxUpdatedObject->updateString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue));
            break;

        case TypeClass_BOOLEAN:
            _rxUpdatedObject->updateBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue));
            break;

        case TypeClass_BYTE:
            _rxUpdatedObject->updateByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue));
            break;

        case TypeClass_UNSIGNED_SHORT:
        case TypeClass_SHORT:
            _rxUpdatedObject->updateShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue));
            break;

        case TypeClass_CHAR:
            _rxUpdatedObject->updateString(_nColumnIndex,OUString(*o3tl::forceAccess<sal_Unicode>(_rValue)));
            break;

        case TypeClass_UNSIGNED_LONG:
        case TypeClass_LONG:
            _rxUpdatedObject->updateInt(_nColumnIndex, *o3tl::forceAccess<sal_Int32>(_rValue));
            break;

        case TypeClass_HYPER:
        {
            sal_Int64 nValue = 0;
            OSL_VERIFY( _rValue >>= nValue );
            _rxUpdatedObject->updateLong( _nColumnIndex, nValue );
        }
        break;

        case TypeClass_FLOAT:
            _rxUpdatedObject->updateFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue));
            break;

        case TypeClass_DOUBLE:
            _rxUpdatedObject->updateDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue));
            break;

        case TypeClass_SEQUENCE:
            if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue))
                _rxUpdatedObject->updateBytes(_nColumnIndex, *s);
            else
                bSuccessfullyReRouted = false;
            break;
        case TypeClass_STRUCT:
            if (auto s1 = o3tl::tryAccess<DateTime>(_rValue))
                _rxUpdatedObject->updateTimestamp(_nColumnIndex, *s1);
            else if (auto s2 = o3tl::tryAccess<Date>(_rValue))
                _rxUpdatedObject->updateDate(_nColumnIndex, *s2);
            else if (auto s3 = o3tl::tryAccess<Time>(_rValue))
                _rxUpdatedObject->updateTime(_nColumnIndex, *s3);
            else
                bSuccessfullyReRouted = false;
            break;

        case TypeClass_INTERFACE:
            if (auto xStream = o3tl::tryAccess<Reference<XInputStream>>(_rValue))
            {
                _rxUpdatedObject->updateBinaryStream(_nColumnIndex, *xStream, (*xStream)->available());
                break;
            }
            [[fallthrough]];
        default:
            bSuccessfullyReRouted = false;
    }

    return bSuccessfullyReRouted;
}

bool implSetObject( const Reference< XParameters >& _rxParameters,
                        const sal_Int32 _nColumnIndex, const Any& _rValue)
{
    bool bSuccessfullyReRouted = true;
    switch (_rValue.getValueTypeClass())
    {
        case TypeClass_UNSIGNED_HYPER:
        {
            sal_uInt64 nValue = 0;
            OSL_VERIFY( _rValue >>= nValue );
            _rxParameters->setString(_nColumnIndex, OUString::number(nValue));
        }
        break;

        case TypeClass_UNSIGNED_LONG:
        case TypeClass_HYPER:
        {
            sal_Int64 nValue = 0;
            OSL_VERIFY( _rValue >>= nValue );
            _rxParameters->setLong( _nColumnIndex, nValue );
        }
        break;

        case TypeClass_ANY:
        {
            bSuccessfullyReRouted = implSetObject(_rxParameters, _nColumnIndex, _rValue);
        }
        break;

        case TypeClass_VOID:
            _rxParameters->setNull(_nColumnIndex,DataType::VARCHAR);
            break;

        case TypeClass_STRING:
            _rxParameters->setString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue));
            break;

        case TypeClass_BOOLEAN:
            _rxParameters->setBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue));
            break;

        case TypeClass_BYTE:
            _rxParameters->setByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue));
            break;

        case TypeClass_SHORT:
            _rxParameters->setShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue));
            break;

        case TypeClass_CHAR:
            _rxParameters->setString(_nColumnIndex, OUString(*o3tl::forceAccess<sal_Unicode>(_rValue)));
            break;

        case TypeClass_UNSIGNED_SHORT:
        case TypeClass_LONG:
        {
            sal_Int32 nValue = 0;
            OSL_VERIFY( _rValue >>= nValue );
            _rxParameters->setInt(_nColumnIndex, nValue);
            break;
        }

        case TypeClass_FLOAT:
            _rxParameters->setFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue));
            break;

        case TypeClass_DOUBLE:
            _rxParameters->setDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue));
            break;

        case TypeClass_SEQUENCE:
            if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue))
            {
                _rxParameters->setBytes(_nColumnIndex, *s);
            }
            else
                bSuccessfullyReRouted = false;
            break;
        case TypeClass_STRUCT:
            if (auto s1 = o3tl::tryAccess<DateTime>(_rValue))
                _rxParameters->setTimestamp(_nColumnIndex, *s1);
            else if (auto s2 = o3tl::tryAccess<Date>(_rValue))
                _rxParameters->setDate(_nColumnIndex, *s2);
            else if (auto s3 = o3tl::tryAccess<Time>(_rValue))
                _rxParameters->setTime(_nColumnIndex, *s3);
            else
                bSuccessfullyReRouted = false;
            break;

        case TypeClass_INTERFACE:
            if (Reference<XInputStream> xStream; _rValue >>= xStream)
            {
                _rxParameters->setBinaryStream(_nColumnIndex, xStream, xStream->available());
                break;
            }
            [[fallthrough]];
        default:
            bSuccessfullyReRouted = false;

    }

    return bSuccessfullyReRouted;
}

namespace
{
    class OParameterWrapper : public ::cppu::WeakImplHelper< XIndexAccess >
    {
        std::vector<bool, std::allocator<bool> >       m_aSet;
        Reference<XIndexAccess> m_xSource;
    public:
        OParameterWrapper(std::vector<bool, std::allocator<bool> >&& _aSet,const Reference<XIndexAccess>& _xSource)
            : m_aSet(std::move(_aSet)), m_xSource(_xSource) {}
    private:
        // css::container::XElementAccess
        virtual Type SAL_CALL getElementType() override
        {
            return m_xSource->getElementType();
        }
        virtual sal_Bool SAL_CALL hasElements(  ) override
        {
            if ( m_aSet.empty() )
                return m_xSource->hasElements();
            return std::count(m_aSet.begin(),m_aSet.end(),false) != 0;
        }
        // css::container::XIndexAccess
        virtual sal_Int32 SAL_CALL getCount(  ) override
        {
            if ( m_aSet.empty() )
                return m_xSource->getCount();
            return std::count(m_aSet.begin(),m_aSet.end(),false);
        }
        virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override
        {
            if ( m_aSet.empty() )
                return m_xSource->getByIndex(Index);
            if ( Index < 0 || m_aSet.size() < o3tl::make_unsigned(Index) )
                throw IndexOutOfBoundsException();

            std::vector<bool, std::allocator<bool> >::const_iterator aIter = m_aSet.begin();
            std::vector<bool, std::allocator<bool> >::const_iterator aEnd = m_aSet.end();
            sal_Int32 i = 0;
            for(; aIter != aEnd && i <= Index; ++aIter)
            {
                if ( !*aIter )
                {
                    ++i;
                }
            }
            auto nParamPos = static_cast<sal_Int32>(std::distance(m_aSet.cbegin(), aIter)) - 1;
            return m_xSource->getByIndex(nParamPos);
        }
    };
}

void askForParameters(const Reference< XSingleSelectQueryComposer >& _xComposer,
                      const Reference<XParameters>& _xParameters,
                      const Reference< XConnection>& _xConnection,
                      const Reference< XInteractionHandler >& _rxHandler,
                      const std::vector<bool, std::allocator<bool> >& _aParametersSet)
{
    OSL_ENSURE(_xComposer.is(),"dbtools::askForParameters XSQLQueryComposer is null!");
    OSL_ENSURE(_xParameters.is(),"dbtools::askForParameters XParameters is null!");
    OSL_ENSURE(_xConnection.is(),"dbtools::askForParameters XConnection is null!");
    OSL_ENSURE(_rxHandler.is(),"dbtools::askForParameters XInteractionHandler is null!");

    // we have to set this here again because getCurrentSettingsComposer can force a setpropertyvalue
    Reference<XParametersSupplier>  xParameters(_xComposer, UNO_QUERY);

    Reference<XIndexAccess>  xParamsAsIndicies = xParameters.is() ? xParameters->getParameters() : Reference<XIndexAccess>();
    sal_Int32 nParamCount = xParamsAsIndicies.is() ? xParamsAsIndicies->getCount() : 0;
    std::vector<bool, std::allocator<bool> > aNewParameterSet( _aParametersSet );
    if ( !(nParamCount && std::count(aNewParameterSet.begin(),aNewParameterSet.end(),true) != nParamCount) )
        return;

    static const OUString PROPERTY_NAME(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME));
    aNewParameterSet.resize(nParamCount ,false);
    typedef std::map< OUString, std::vector<sal_Int32> > TParameterPositions;
    TParameterPositions aParameterNames;
    for(sal_Int32 i = 0; i < nParamCount; ++i)
    {
        Reference<XPropertySet> xParam(xParamsAsIndicies->getByIndex(i),UNO_QUERY);
        OUString sName;
        xParam->getPropertyValue(PROPERTY_NAME) >>= sName;

        if ( aParameterNames.contains(sName) )
            aNewParameterSet[i] = true;
        aParameterNames[sName].push_back(i+1);
    }
    // build an interaction request
    // two continuations (Ok and Cancel)
    rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
    rtl::Reference<OParameterContinuation> pParams = new OParameterContinuation;
    // the request
    ParametersRequest aRequest;
    Reference<XIndexAccess> xWrappedParameters = new OParameterWrapper(std::move(aNewParameterSet),xParamsAsIndicies);
    aRequest.Parameters = xWrappedParameters;
    aRequest.Connection = _xConnection;
    rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest));
    // some knittings
    pRequest->addContinuation(pAbort);
    pRequest->addContinuation(pParams);

    // execute the request
    _rxHandler->handle(pRequest);

    if (!pParams->wasSelected())
    {
        // canceled by the user (i.e. (s)he canceled the dialog)
        RowSetVetoException e;
        e.ErrorCode = ParameterInteractionCancelled;
        throw e;
    }

    // now transfer the values from the continuation object to the parameter columns
    Sequence< PropertyValue > aFinalValues = pParams->getValues();
    for (sal_Int32 i = 0; i < aFinalValues.getLength(); ++i)
    {
        Reference< XPropertySet > xParamColumn(xWrappedParameters->getByIndex(i),UNO_QUERY);
        if (xParamColumn.is())
        {
            OUString sName;
            xParamColumn->getPropertyValue(PROPERTY_NAME) >>= sName;
            OSL_ENSURE(sName == aFinalValues[i].Name, "::dbaui::askForParameters: inconsistent parameter names!");

            // determine the field type and ...
            sal_Int32 nParamType = 0;
            xParamColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nParamType;
            // ... the scale of the parameter column
            sal_Int32 nScale = 0;
            if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE), xParamColumn))
                xParamColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale;
                // (the index of the parameters is one-based)
            TParameterPositions::const_iterator aFind = aParameterNames.find(aFinalValues[i].Name);
            for(const auto& rItem : aFind->second)
            {
                if ( _aParametersSet.empty() || !_aParametersSet[rItem-1] )
                {
                    _xParameters->setObjectWithInfo(rItem, aFinalValues[i].Value, nParamType, nScale);
                }
            }
        }
    }
}

void setObjectWithInfo(const Reference<XParameters>& _xParams,
                       sal_Int32 parameterIndex,
                       const Any& x,
                       sal_Int32 sqlType,
                       sal_Int32 scale)
{
    ORowSetValue aVal;
    aVal.fill(x);
    setObjectWithInfo(_xParams,parameterIndex,aVal,sqlType,scale);
}

void setObjectWithInfo(const Reference<XParameters>& _xParams,
                       sal_Int32 parameterIndex,
                       const ::connectivity::ORowSetValue& _rValue,
                       sal_Int32 sqlType,
                       sal_Int32 scale)
{
    if ( _rValue.isNull() )
        _xParams->setNull(parameterIndex,sqlType);
    else
    {
        switch(sqlType)
        {
            case DataType::DECIMAL:
            case DataType::NUMERIC:
                _xParams->setObjectWithInfo(parameterIndex,_rValue.makeAny(),sqlType,scale);
                break;
            case DataType::CHAR:
            case DataType::VARCHAR:
            case DataType::LONGVARCHAR:
                _xParams->setString(parameterIndex,_rValue.getString());
                break;
            case DataType::CLOB:
                {
                    Any x(_rValue.makeAny());
                    OUString sValue;
                    if ( x >>= sValue )
                        _xParams->setString(parameterIndex,sValue);
                    else
                    {
                        Reference< XClob > xClob;
                        if(x >>= xClob)
                            _xParams->setClob(parameterIndex,xClob);
                        else
                        {
                            Reference< css::io::XInputStream > xStream;
                            if(x >>= xStream)
                                _xParams->setCharacterStream(parameterIndex,xStream,xStream->available());
                        }
                    }
                }
                break;
            case DataType::BIGINT:
                if ( _rValue.isSigned() )
                    _xParams->setLong(parameterIndex,_rValue.getLong());
                else
                    _xParams->setString(parameterIndex,_rValue.getString());
                break;

            case DataType::FLOAT:
                _xParams->setFloat(parameterIndex,_rValue.getFloat());
                break;
            case DataType::REAL:
            case DataType::DOUBLE:
                _xParams->setDouble(parameterIndex,_rValue.getDouble());
                break;
            case DataType::DATE:
                _xParams->setDate(parameterIndex,_rValue.getDate());
                break;
            case DataType::TIME:
                _xParams->setTime(parameterIndex,_rValue.getTime());
                break;
            case DataType::TIMESTAMP:
                _xParams->setTimestamp(parameterIndex,_rValue.getDateTime());
                break;
            case DataType::BINARY:
            case DataType::VARBINARY:
            case DataType::LONGVARBINARY:
            case DataType::BLOB:
                {
                    Any x(_rValue.makeAny());
                    Sequence< sal_Int8> aBytes;
                    if(x >>= aBytes)
                        _xParams->setBytes(parameterIndex,aBytes);
                    else
                    {
                        Reference< XBlob > xBlob;
                        if(x >>= xBlob)
                            _xParams->setBlob(parameterIndex,xBlob);
                        else
                        {
                            Reference< XClob > xClob;
                            if(x >>= xClob)
                                _xParams->setClob(parameterIndex,xClob);
                            else
                            {
                                Reference< css::io::XInputStream > xBinStream;
                                if(x >>= xBinStream)
                                    _xParams->setBinaryStream(parameterIndex,xBinStream,xBinStream->available());
                            }
                        }
                    }
                }
                break;
            case DataType::BIT:
            case DataType::BOOLEAN:
                _xParams->setBoolean(parameterIndex,_rValue.getBool());
                break;
            case DataType::TINYINT:
                if ( _rValue.isSigned() )
                    _xParams->setByte(parameterIndex,_rValue.getInt8());
                else
                    _xParams->setShort(parameterIndex,_rValue.getInt16());
                break;
            case DataType::SMALLINT:
                if ( _rValue.isSigned() )
                    _xParams->setShort(parameterIndex,_rValue.getInt16());
                else
                    _xParams->setInt(parameterIndex,_rValue.getInt32());
                break;
            case DataType::INTEGER:
                if ( _rValue.isSigned() )
                    _xParams->setInt(parameterIndex,_rValue.getULong());
                else
                    _xParams->setLong(parameterIndex,_rValue.getLong());
                break;
            default:
                {
                    ::connectivity::SharedResources aResources;
                    const OUString sError( aResources.getResourceStringWithSubstitution(
                            STR_UNKNOWN_PARA_TYPE,
                            "$position$", OUString::number(parameterIndex)
                         ) );
                    ::dbtools::throwGenericSQLException(sError,nullptr);
                }
        }
    }
}

void getBooleanComparisonPredicate( std::u16string_view _rExpression, const bool _bValue, const sal_Int32 _nBooleanComparisonMode,
    OUStringBuffer& _out_rSQLPredicate )
{
    switch ( _nBooleanComparisonMode )
    {
    case BooleanComparisonMode::IS_LITERAL:
        _out_rSQLPredicate.append( _rExpression );
        if ( _bValue )
            _out_rSQLPredicate.append( " IS TRUE" );
        else
            _out_rSQLPredicate.append( " IS FALSE" );
        break;

    case BooleanComparisonMode::EQUAL_LITERAL:
        _out_rSQLPredicate.append( _rExpression );
        _out_rSQLPredicate.appendAscii( _bValue ? " = TRUE" : " = FALSE" );
        break;

    case BooleanComparisonMode::ACCESS_COMPAT:
        if ( _bValue )
        {
            _out_rSQLPredicate.append( " NOT ( ( " );
            _out_rSQLPredicate.append( _rExpression );
            _out_rSQLPredicate.append( " = 0 ) OR ( " );
            _out_rSQLPredicate.append( _rExpression );
            _out_rSQLPredicate.append( " IS NULL ) )" );
        }
        else
        {
            _out_rSQLPredicate.append( _rExpression );
            _out_rSQLPredicate.append( " = 0" );
        }
        break;

    case BooleanComparisonMode::EQUAL_INTEGER:
        // fall through
    default:
        _out_rSQLPredicate.append( _rExpression );
        _out_rSQLPredicate.appendAscii( _bValue ? " = 1" : " = 0" );
        break;
    }
}

}   // namespace dbtools

namespace connectivity
{
void checkDisposed(bool _bThrow)
{
    if (_bThrow)
        throw DisposedException();

}

OSQLColumns::const_iterator find(const OSQLColumns::const_iterator& first,
                                        const OSQLColumns::const_iterator& last,
                                        std::u16string_view _rVal,
                                        const ::comphelper::UStringMixEqual& _rCase)
{
    OUString sName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME);
    return find(first,last,sName,_rVal,_rCase);
}

OSQLColumns::const_iterator findRealName(const OSQLColumns::const_iterator& first,
                                        const OSQLColumns::const_iterator& last,
                                        std::u16string_view _rVal,
                                        const ::comphelper::UStringMixEqual& _rCase)
{
    OUString sRealName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME);
    return find(first,last,sRealName,_rVal,_rCase);
}

OSQLColumns::const_iterator find(OSQLColumns::const_iterator first,
                                        const OSQLColumns::const_iterator& last,
                                        const OUString& _rProp,
                                        std::u16string_view _rVal,
                                        const ::comphelper::UStringMixEqual& _rCase)
{
    while (first != last && !_rCase(getString((*first)->getPropertyValue(_rProp)),_rVal))
        ++first;
    return first;
}

namespace dbase
{
    bool dbfDecodeCharset(rtl_TextEncoding &_out_encoding, sal_uInt8 nType, sal_uInt8 nCodepage)
    {
        switch (nType)
        {
        // dBaseIII header doesn't contain language driver ID
        // See http://dbase.free.fr/tlcharge/structure%20tables.pdf
        case dBaseIII:
        case dBaseIIIMemo:
            break;
        case dBaseIV:
        case dBaseV:
        case VisualFoxPro:
        case VisualFoxProAuto:
        case dBaseFS:
        case dBaseFSMemo:
        case dBaseIVMemoSQL:
        case FoxProMemo:
        {
            if (nCodepage != 0x00)
            {
                auto eEncoding(RTL_TEXTENCODING_DONTKNOW);
                switch(nCodepage)
                {
                case 0x01: eEncoding = RTL_TEXTENCODING_IBM_437; break;       // DOS USA  code page 437
                case 0x02: eEncoding = RTL_TEXTENCODING_IBM_850; break;       // DOS Multilingual code page 850
                case 0x03: eEncoding = RTL_TEXTENCODING_MS_1252; break;       // Windows ANSI code page 1252
                case 0x04: eEncoding = RTL_TEXTENCODING_APPLE_ROMAN; break;   // Standard Macintosh
                case 0x64: eEncoding = RTL_TEXTENCODING_IBM_852; break;       // EE MS-DOS    code page 852
                case 0x65: eEncoding = RTL_TEXTENCODING_IBM_866; break;       // Russian MS-DOS   code page 866
                case 0x66: eEncoding = RTL_TEXTENCODING_IBM_865; break;       // Nordic MS-DOS    code page 865
                case 0x67: eEncoding = RTL_TEXTENCODING_IBM_861; break;       // Icelandic MS-DOS
                case 0x68: eEncoding = RTL_TEXTENCODING_KAMENICKY; break;     // Kamenicky (Czech) MS-DOS
                case 0x69: eEncoding = RTL_TEXTENCODING_MAZOVIA; break;       // Mazovia (Polish) MS-DOS
                case 0x6A: eEncoding = RTL_TEXTENCODING_IBM_737; break;       // Greek MS-DOS (437G)
                case 0x6B: eEncoding = RTL_TEXTENCODING_IBM_857; break;       // Turkish MS-DOS
                case 0x6C: eEncoding = RTL_TEXTENCODING_IBM_863; break;       // MS-DOS, Canada
                case 0x78: eEncoding = RTL_TEXTENCODING_MS_950; break;        // Windows, Traditional Chinese
                case 0x79: eEncoding = RTL_TEXTENCODING_MS_949; break;        // Windows, Korean (Hangul)
                case 0x7A: eEncoding = RTL_TEXTENCODING_MS_936; break;        // Windows, Simplified Chinese
                case 0x7B: eEncoding = RTL_TEXTENCODING_MS_932; break;        // Windows, Japanese (Shift-jis)
                case 0x7C: eEncoding = RTL_TEXTENCODING_MS_874; break;        // Windows, Thai
                case 0x7D: eEncoding = RTL_TEXTENCODING_MS_1255; break;       // Windows, Hebrew
                case 0x7E: eEncoding = RTL_TEXTENCODING_MS_1256; break;       // Windows, Arabic
                case 0x96: eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC; break;    // Russian Macintosh
                case 0x97: eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO; break;    // Eastern European Macintosh
                case 0x98: eEncoding = RTL_TEXTENCODING_APPLE_GREEK; break;   // Greek Macintosh
                case 0xC8: eEncoding = RTL_TEXTENCODING_MS_1250; break;       // Windows EE   code page 1250
                case 0xC9: eEncoding = RTL_TEXTENCODING_MS_1251; break;       // Russian Windows
                case 0xCA: eEncoding = RTL_TEXTENCODING_MS_1254; break;       // Turkish Windows
                case 0xCB: eEncoding = RTL_TEXTENCODING_MS_1253; break;       // Greek Windows
                case 0xCC: eEncoding = RTL_TEXTENCODING_MS_1257; break;       // Windows, Baltic
                }
                if(eEncoding != RTL_TEXTENCODING_DONTKNOW)
                {
                    _out_encoding = eEncoding;
                    return true;
                }
            }
        }
        }
        return false;
    }

    bool dbfReadCharset(rtl_TextEncoding &nCharSet, SvStream* dbf_Stream)
    {
        sal_uInt8 nType=0;
        dbf_Stream->ReadUChar( nType );

        dbf_Stream->Seek(STREAM_SEEK_TO_BEGIN + 29);
        if (dbf_Stream->eof())
        {
            return false;
        }
        else
        {
            sal_uInt8 nEncoding=0;
            dbf_Stream->ReadUChar( nEncoding );
            return dbfDecodeCharset(nCharSet, nType, nEncoding);
        }
    }

}

} //namespace connectivity

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
