Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/connectivity/source/commontools/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 21 kB image not shown  

Quelle  TTableHelper.cxx   Sprache: C

 
/* -*- 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 <sal/config.h>
#include <sal/log.hxx>

#include <connectivity/TTableHelper.hxx>
#include <com/sun/star/sdb/tools/XTableRename.hpp>
#include <com/sun/star/sdb/tools/XTableAlteration.hpp>
#include <com/sun/star/sdb/tools/XKeyAlteration.hpp>
#include <com/sun/star/sdb/tools/XIndexAlteration.hpp>
#include <sdbcx/VKey.hxx>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbcx/KeyType.hpp>
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <comphelper/types.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sdbcx/VCollection.hxx>
#include <unotools/sharedunocomponent.hxx>
#include <TConnection.hxx>

#include <o3tl/functional.hxx>

#include <iterator>
#include <set>

using namespace ::comphelper;
using namespace connectivity;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::lang;

namespace
{
    /// helper class for column property change events which holds the OComponentDefinition weak
class OTableContainerListener:
    public ::cppu::WeakImplHelper< XContainerListener >
{
    OTableHelper* m_pComponent;
    std::map< OUString,bool> m_aRefNames;

protected:
    virtual ~OTableContainerListener() override {}
public:
    explicit OTableContainerListener(OTableHelper* _pComponent) : m_pComponent(_pComponent){}
    // noncopyable
    OTableContainerListener(const OTableContainerListener&) = delete;
    const OTableContainerListener& operator=(const OTableContainerListener&) = delete;
    virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& /*Event*/ ) override
    {
    }
    virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override
    {
        // tdf#137745, perhaps connectivity::OTableHelper::disposing() has been called
        // which called OTableContainerListener::clear(), so m_pComponent may be null
        if (m_pComponent == nullptr)
            return;

        OUString sName;
        Event.Accessor  >>= sName;
        if ( m_aRefNames.find(sName) != m_aRefNames.end() )
            m_pComponent->refreshKeys();
    }
    virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override
    {
        OUString sOldComposedName,sNewComposedName;
        Event.ReplacedElement   >>= sOldComposedName;
        Event.Accessor          >>= sNewComposedName;
        if ( sOldComposedName != sNewComposedName && m_aRefNames.find(sOldComposedName) != m_aRefNames.end() )
            m_pComponent->refreshKeys();
    }
    // XEventListener
    virtual void SAL_CALL disposing( const EventObject& /*_rSource*/ ) override
    {
    }
    void clear() { m_pComponent = nullptr; }
    void add(const OUString& _sRefName) { m_aRefNames.emplace(_sRefName,true); }
};
}
namespace connectivity
{
    static OUString lcl_getServiceNameForSetting(const Reference< css::sdbc::XConnection >&&nbsp;_xConnection,const OUString& i_sSetting)
    {
        OUString sSupportService;
        Any aValue;
        if ( ::dbtools::getDataSourceSetting(_xConnection,i_sSetting,aValue) )
        {
            aValue >>= sSupportService;
        }
        return sSupportService;
    }
    struct OTableHelperImpl
    {
        TKeyMap  m_aKeys;
        // helper services which can be provided by extensions
        Reference< css::sdb::tools::XTableRename>      m_xRename;
        Reference< css::sdb::tools::XTableAlteration>  m_xAlter;
        Reference< css::sdb::tools::XKeyAlteration>    m_xKeyAlter;
        Reference< css::sdb::tools::XIndexAlteration>  m_xIndexAlter;

        Reference< css::sdbc::XDatabaseMetaData >      m_xMetaData;
        Reference< css::sdbc::XConnection >            m_xConnection;
        rtl::Reference<OTableContainerListener>        m_xTablePropertyListener;
        std::vector< ColumnDesc >                    m_aColumnDesc;
        explicit OTableHelperImpl(const Reference< css::sdbc::XConnection >& _xConnection)
            : m_xConnection(_xConnection)
        {
            try
            {
                m_xMetaData = m_xConnection->getMetaData();
                Reference<XMultiServiceFactory> xFac(_xConnection,UNO_QUERY);
                if ( xFac.is() )
                {
                    m_xRename.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,u"TableRenameServiceName"_ustr)),UNO_QUERY);
                    m_xAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,u"TableAlterationServiceName"_ustr)),UNO_QUERY);
                    m_xKeyAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,u"KeyAlterationServiceName"_ustr)),UNO_QUERY);
                    m_xIndexAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,u"IndexAlterationServiceName"_ustr)),UNO_QUERY);
                }
            }
            catch(const Exception&)
            {
            }
        }
    };
}

OTableHelper::OTableHelper( sdbcx::OCollection* _pTables,
                           const Reference< XConnection >& _xConnection,
                           bool _bCase)
    :OTable_TYPEDEF(_pTables,_bCase)
    ,m_pImpl(new OTableHelperImpl(_xConnection))
{
}

OTableHelper::OTableHelper( sdbcx::OCollection* _pTables,
                            const Reference< XConnection >& _xConnection,
                            bool _bCase,
                            const OUString& Name,
                            const OUString& Type,
                            const OUString& Description ,
                            const OUString& SchemaName,
                            const OUString& CatalogName
                        ) : OTable_TYPEDEF(_pTables,
                                            _bCase,
                                            Name,
                                          Type,
                                          Description,
                                          SchemaName,
                                          CatalogName)
                        ,m_pImpl(new OTableHelperImpl(_xConnection))
{
}

OTableHelper::~OTableHelper()
{
}

void SAL_CALL OTableHelper::disposing()
{
    ::osl::MutexGuard aGuard(m_aMutex);
    if ( m_pImpl->m_xTablePropertyListener.is() )
    {
        m_pTables->removeContainerListener(m_pImpl->m_xTablePropertyListener);
        m_pImpl->m_xTablePropertyListener->clear();
        m_pImpl->m_xTablePropertyListener.clear();
    }
    OTable_TYPEDEF::disposing();

    m_pImpl->m_xConnection  = nullptr;
    m_pImpl->m_xMetaData    = nullptr;

}


namespace
{
    /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns
    */

    void lcl_collectColumnDescs_throw( const Reference< XResultSet >& _rxResult, std::vector< ColumnDesc >& _out_rColumns )
    {
        Reference< XRow > xRow( _rxResult, UNO_QUERY_THROW );
        while ( _rxResult->next() )
        {
            // tdf#162227: ODBC SQLGetData requires that data must be retrieved in increasing
            // column number order, unless the driver supports SQL_GD_ANY_ORDER extension (see
            // https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdata-function).
            // We can't emplace_back(getString(4), getInt(5), ..., getString(12), getInt(17)),
            // because MSVC would reorder calls into getInt(17) -> getString(12) -> ..., and then
            // MS SQL Server ODBC driver will give error on access of column 12 after column 17.
            OUString sName = xRow->getString(4); // COLUMN_NAME
            sal_Int32 nField5 = xRow->getInt(5);
            OUString aField6 = xRow->getString(6);
            sal_Int32 nField7 = xRow->getInt(7);
            sal_Int32 nField9 = xRow->getInt(9);
            sal_Int32 nField11 = xRow->getInt(11);
            OUString sField12 = xRow->getString(12);
            OUString sField13 = xRow->getString(13);
            OrdinalPosition nOrdinalPosition = xRow->getInt(17); // ORDINAL_POSITION
            _out_rColumns.emplace_back(sName, nField5, aField6, nField7, nField9, nField11,
                                       sField12, sField13, nOrdinalPosition);
        }
    }

    /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not,
        they will be normalized to be the array index.
    */

    void lcl_sanitizeColumnDescs( std::vector< ColumnDesc >& _rColumns )
    {
        if ( _rColumns.empty() )
            return;

        // collect all used ordinals
        std::set< OrdinalPosition > aUsedOrdinals;
        for ( const auto& collect : _rColumns )
            aUsedOrdinals.insert( collect.nOrdinalPosition );

        // we need to have as much different ordinals as we have different columns
        bool bDuplicates = aUsedOrdinals.size() != _rColumns.size();
        // and it needs to be a continuous range
        size_t nOrdinalsRange = *aUsedOrdinals.rbegin() - *aUsedOrdinals.begin() + 1;
        bool bGaps = nOrdinalsRange != _rColumns.size();

        // if that's not the case, normalize it
        if ( bGaps || bDuplicates )
        {
            OSL_FAIL( "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" );

            OrdinalPosition nNormalizedPosition = 1;
            for ( auto& normalize : _rColumns )
                normalize.nOrdinalPosition = nNormalizedPosition++;
            return;
        }

        // what's left is that the range might not be from 1 to <column count>, but for instance
        // 0 to <column count>-1.
        size_t nOffset = *aUsedOrdinals.begin() - 1;
        for ( auto& offset : _rColumns )
            offset.nOrdinalPosition -= nOffset;
    }
}


void OTableHelper::refreshColumns()
{
    ::std::vector< OUString> aVector;
    if(!isNew())
    {
        Any aCatalog;
        if ( !m_CatalogName.isEmpty() )
            aCatalog <<= m_CatalogName;

        ::utl::SharedUNOComponent< XResultSet > xResult( getMetaData()->getColumns(
            aCatalog,
            m_SchemaName,
            m_Name,
            u"%"_ustr
        ) );

        // collect the column names, together with their ordinal position
        m_pImpl->m_aColumnDesc.clear();
        lcl_collectColumnDescs_throw( xResult, m_pImpl->m_aColumnDesc );

        // ensure that the ordinal positions as obtained from the meta data do make sense
        lcl_sanitizeColumnDescs( m_pImpl->m_aColumnDesc );

        // sort by ordinal position
        std::map< OrdinalPosition, OUString > aSortedColumns;
        for (const auto& copy : m_pImpl->m_aColumnDesc)
            aSortedColumns[ copy.nOrdinalPosition ] = copy.sName;

        // copy them to aVector, now that we have the proper ordering
        std::transform(
            aSortedColumns.begin(),
            aSortedColumns.end(),
            std::insert_iterator< ::std::vector< OUString> >( aVector, aVector.begin() ),
            ::o3tl::select2nd< std::map< OrdinalPosition, OUString >::value_type >()
            );
    }

    if(m_xColumns)
        m_xColumns->reFill(aVector);
    else
        m_xColumns.reset(createColumns(aVector));
}

const ColumnDesc* OTableHelper::getColumnDescription(const OUString& _sName) const
{
    const ColumnDesc* pRet = nullptr;
    auto aIter = std::find_if(m_pImpl->m_aColumnDesc.begin(), m_pImpl->m_aColumnDesc.end(),
        [&_sName](const ColumnDesc& rColumnDesc) { return rColumnDesc.sName == _sName; });
    if (aIter != m_pImpl->m_aColumnDesc.end())
        pRet = &*aIter;
    return pRet;
}

void OTableHelper::refreshPrimaryKeys(::std::vector< OUString>& _rNames)
{
    Any aCatalog;
    if ( !m_CatalogName.isEmpty() )
        aCatalog <<= m_CatalogName;
    Reference< XResultSet > xResult = getMetaData()->getPrimaryKeys(aCatalog,m_SchemaName,m_Name);

    if ( xResult.is() )
    {
        auto pKeyProps = std::make_shared<sdbcx::KeyProperties>(OUString(),KeyType::PRIMARY,0,0);
        OUString aPkName;
        bool bAlreadyFetched = false;
        const Reference< XRow > xRow(xResult,UNO_QUERY);
        while ( xResult->next() )
        {
            pKeyProps->m_aKeyColumnNames.push_back(xRow->getString(4));
            if ( !bAlreadyFetched )
            {
                aPkName = xRow->getString(6);
                SAL_WARN_IF(xRow->wasNull(),"connectivity.commontools""NULL Primary Key name");
                SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools""empty Primary Key name");
                bAlreadyFetched = true;
            }
        }

        if(bAlreadyFetched)
        {
            SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools""empty Primary Key name");
            SAL_WARN_IF(pKeyProps->m_aKeyColumnNames.empty(),"connectivity.commontools""Primary Key has no columns");
            m_pImpl->m_aKeys.emplace(aPkName,pKeyProps);
            _rNames.push_back(aPkName);
        }
    } // if ( xResult.is() && xResult->next() )
    ::comphelper::disposeComponent(xResult);
}

void OTableHelper::refreshForeignKeys(::std::vector< OUString>& _rNames)
{
    Any aCatalog;
    if ( !m_CatalogName.isEmpty() )
        aCatalog <<= m_CatalogName;
    Reference< XResultSet > xResult = getMetaData()->getImportedKeys(aCatalog,m_SchemaName,m_Name);
    Reference< XRow > xRow(xResult,UNO_QUERY);

    if ( !xRow.is() )
        return;

    std::shared_ptr<sdbcx::KeyProperties> pKeyProps;
    OUString aName,sCatalog,aSchema,sOldFKName;
    while( xResult->next() )
    {
        // this must be outside the "if" because we have to call in a right order
        sCatalog    = xRow->getString(1);
        if ( xRow->wasNull() )
            sCatalog.clear();
        aSchema     = xRow->getString(2);
        aName       = xRow->getString(3);

        const OUString sForeignKeyColumn = xRow->getString(8);
        const sal_Int32 nUpdateRule = xRow->getInt(10);
        const sal_Int32 nDeleteRule = xRow->getInt(11);
        const OUString sFkName = xRow->getString(12);

        if ( !sFkName.isEmpty() && !xRow->wasNull() )
        {
            if ( sOldFKName != sFkName )
            {
                if ( pKeyProps )
                    m_pImpl->m_aKeys.emplace(sOldFKName,pKeyProps);

                const OUString sReferencedName = ::dbtools::composeTableName(getMetaData(),sCatalog,aSchema,aName,false,::dbtools::EComposeRule::InDataManipulation);
                pKeyProps = std::make_shared<sdbcx::KeyProperties>(sReferencedName,KeyType::FOREIGN,nUpdateRule,nDeleteRule);
                pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn);
                _rNames.push_back(sFkName);
                if ( m_pTables->hasByName(sReferencedName) )
                {
                    if ( !m_pImpl->m_xTablePropertyListener.is() )
                        m_pImpl->m_xTablePropertyListener = new OTableContainerListener(this);
                    m_pTables->addContainerListener(m_pImpl->m_xTablePropertyListener);
                    m_pImpl->m_xTablePropertyListener->add(sReferencedName);
                } // if ( m_pTables->hasByName(sReferencedName) )
                sOldFKName = sFkName;
            } // if ( sOldFKName != sFkName )
            else if ( pKeyProps )
            {
                pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn);
            }
        }
    } // while( xResult->next() )
    if ( pKeyProps )
        m_pImpl->m_aKeys.emplace(sOldFKName,pKeyProps);
    ::comphelper::disposeComponent(xResult);
}

void OTableHelper::refreshKeys()
{
    m_pImpl->m_aKeys.clear();

    ::std::vector< OUString> aNames;

    if(!isNew())
    {
        refreshPrimaryKeys(aNames);
        refreshForeignKeys(aNames);
        m_xKeys.reset(createKeys(aNames));
    } // if(!isNew())
    else if (!m_xKeys )
        m_xKeys.reset(createKeys(aNames));
    /*if(m_pKeys)
        m_pKeys->reFill(aVector);
    else*/


}

void OTableHelper::refreshIndexes()
{
    ::std::vector< OUString> aVector;
    if(!isNew())
    {
        // fill indexes
        Any aCatalog;
        if ( !m_CatalogName.isEmpty() )
            aCatalog <<= m_CatalogName;
        Reference< XResultSet > xResult = getMetaData()->getIndexInfo(aCatalog,m_SchemaName,m_Name,false,false);

        if(xResult.is())
        {
            Reference< XRow > xRow(xResult,UNO_QUERY);
            OUString sCatalogSep = getMetaData()->getCatalogSeparator();
            OUString sPreviousRoundName;
            while( xResult->next() )
            {
                OUString aName = xRow->getString(5);
                if(!aName.isEmpty())
                    aName += sCatalogSep;
                aName += xRow->getString(6);
                if ( !aName.isEmpty() )
                {
                    // don't insert the name if the last one we inserted was the same
                    if (sPreviousRoundName != aName)
                        aVector.push_back(aName);
                }
                sPreviousRoundName = aName;
            }
            ::comphelper::disposeComponent(xResult);
        }
    }

    if(m_xIndexes)
        m_xIndexes->reFill(aVector);
    else
        m_xIndexes.reset(createIndexes(aVector));
}

OUString OTableHelper::getRenameStart() const
{
    OUString sSql(u"RENAME "_ustr);
    if ( m_Type == "VIEW" )
        sSql += " VIEW ";
    else
        sSql += " TABLE ";

    return sSql;
}

// XRename
void SAL_CALL OTableHelper::rename( const OUString& newName )
{
    ::osl::MutexGuard aGuard(m_aMutex);
    checkDisposed(
#ifdef __GNUC__
        ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
#else
        rBHelper.bDisposed
#endif
        );

    if(!isNew())
    {
        if ( m_pImpl->m_xRename.is() )
        {
            m_pImpl->m_xRename->rename(this,newName);
        }
        else
        {
            OUString sSql = getRenameStart();

            OUString sCatalog,sSchema,sTable;
            ::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation);

            OUString sComposedName;
            sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,true,::dbtools::EComposeRule::InDataManipulation);
            sSql += sComposedName
                 + " TO ";
            sComposedName = ::dbtools::composeTableName(getMetaData(),sCatalog,sSchema,sTable,true,::dbtools::EComposeRule::InDataManipulation);
            sSql += sComposedName;

            Reference< XStatement > xStmt = m_pImpl->m_xConnection->createStatement(  );
            if ( xStmt.is() )
            {
                xStmt->execute(sSql);
                ::comphelper::disposeComponent(xStmt);
            }
        }

        OTable_TYPEDEF::rename(newName);
    }
    else
        ::dbtools::qualifiedNameComponents(getMetaData(),newName,m_CatalogName,m_SchemaName,m_Name,::dbtools::EComposeRule::InTableDefinitions);
}

Reference< XDatabaseMetaData> OTableHelper::getMetaData() const
{
    return m_pImpl->m_xMetaData;
}

void SAL_CALL OTableHelper::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor )
{
    ::osl::MutexGuard aGuard(m_aMutex);
    checkDisposed(
#ifdef __GNUC__
        ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
#else
        rBHelper.bDisposed
#endif
        );

    Reference< XPropertySet > xOld(
        m_xColumns->getByIndex(index), css::uno::UNO_QUERY);
    if(xOld.is())
        alterColumnByName(getString(xOld->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),descriptor);
}


OUString SAL_CALL OTableHelper::getName()
{
    OUString sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,false,::dbtools::EComposeRule::InDataManipulation);
    return sComposedName;
}

const OUString & OTableHelper::getTableName()
{
    return m_Name;
}

std::shared_ptr<sdbcx::KeyProperties> OTableHelper::getKeyProperties(const OUString&&nbsp;_sName) const
{
    std::shared_ptr<sdbcx::KeyProperties> pKeyProps;
    TKeyMap::const_iterator aFind = m_pImpl->m_aKeys.find(_sName);
    if ( aFind != m_pImpl->m_aKeys.end() )
    {
        pKeyProps = aFind->second;
    }
    else // only a fall back
    {
        OSL_FAIL("No key with the given name found");
        pKeyProps = std::make_shared<sdbcx::KeyProperties>();
    }

    return pKeyProps;
}

void OTableHelper::addKey(const OUString& _sName,const std::shared_ptr<sdbcx::KeyProperties>& _aKeyProperties)
{
    m_pImpl->m_aKeys.emplace(_sName,_aKeyProperties);
}

OUString OTableHelper::getTypeCreatePattern() const
{
    return OUString();
}

Reference< XConnection> const & OTableHelper::getConnection() const
{
    return m_pImpl->m_xConnection;
}

Reference< css::sdb::tools::XTableRename> const & OTableHelper::getRenameService() const
{
    return m_pImpl->m_xRename;
}

Reference< css::sdb::tools::XTableAlteration> const & OTableHelper::getAlterService() const
{
    return m_pImpl->m_xAlter;
}

Reference< css::sdb::tools::XKeyAlteration> const & OTableHelper::getKeyService() const
{
    return m_pImpl->m_xKeyAlter;
}

Reference< css::sdb::tools::XIndexAlteration> const & OTableHelper::getIndexService() const
{
    return m_pImpl->m_xIndexAlter;
}


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

Messung V0.5
C=96 H=96 G=95

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.