/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: TTableHelper.cxx,v $ * $Revision: 1.10 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_connectivity.hxx" #include "connectivity/TTableHelper.hxx" #include #include #include #include #include #include #include #include #include #include #include #include "connectivity/dbtools.hxx" #include "connectivity/sdbcx/VCollection.hxx" #include #include "TConnection.hxx" 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 typedef ::cppu::WeakImplHelper1 < XContainerListener > OTableContainerListener_BASE; class OTableContainerListener : public OTableContainerListener_BASE { OTableHelper* m_pComponent; ::std::map< ::rtl::OUString,bool> m_aRefNames; OTableContainerListener(const OTableContainerListener&); void operator =(const OTableContainerListener&); protected: virtual ~OTableContainerListener(){} public: OTableContainerListener(OTableHelper* _pComponent) : m_pComponent(_pComponent){} virtual void SAL_CALL elementInserted( const ::com::sun::star::container::ContainerEvent& /*Event*/ ) throw (RuntimeException) { } virtual void SAL_CALL elementRemoved( const ::com::sun::star::container::ContainerEvent& Event ) throw (RuntimeException) { ::rtl::OUString sName; Event.Accessor >>= sName; if ( m_aRefNames.find(sName) != m_aRefNames.end() ) m_pComponent->refreshKeys(); } virtual void SAL_CALL elementReplaced( const ::com::sun::star::container::ContainerEvent& Event ) throw (RuntimeException) { ::rtl::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*/ ) throw (RuntimeException) { } void clear() { m_pComponent = NULL; } inline void add(const ::rtl::OUString& _sRefName) { m_aRefNames.insert(::std::map< ::rtl::OUString,bool>::value_type(_sRefName,true)); } }; } namespace connectivity { struct OTableHelperImpl { TKeyMap m_aKeys; Reference< ::com::sun::star::sdbc::XDatabaseMetaData > m_xMetaData; Reference< ::com::sun::star::sdbc::XConnection > m_xConnection; ::comphelper::ImplementationReference< OTableContainerListener,XContainerListener> m_xTablePropertyListener; ::std::vector< ColumnDesc > m_aColumnDesc; }; } OTableHelper::OTableHelper( sdbcx::OCollection* _pTables, const Reference< XConnection >& _xConnection, sal_Bool _bCase) :OTable_TYPEDEF(_pTables,_bCase) ,m_pImpl(new OTableHelperImpl) { try { m_pImpl->m_xConnection = _xConnection; m_pImpl->m_xMetaData = m_pImpl->m_xConnection->getMetaData(); } catch(const Exception&) { } } // ------------------------------------------------------------------------- OTableHelper::OTableHelper( sdbcx::OCollection* _pTables, const Reference< XConnection >& _xConnection, sal_Bool _bCase, const ::rtl::OUString& _Name, const ::rtl::OUString& _Type, const ::rtl::OUString& _Description , const ::rtl::OUString& _SchemaName, const ::rtl::OUString& _CatalogName ) : OTable_TYPEDEF(_pTables, _bCase, _Name, _Type, _Description, _SchemaName, _CatalogName) ,m_pImpl(new OTableHelperImpl) { try { m_pImpl->m_xConnection = _xConnection; m_pImpl->m_xMetaData = m_pImpl->m_xConnection->getMetaData(); } catch(const Exception&) { } } // ----------------------------------------------------------------------------- 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.getRef()); m_pImpl->m_xTablePropertyListener->clear(); m_pImpl->m_xTablePropertyListener.dispose(); } OTable_TYPEDEF::disposing(); m_pImpl->m_xConnection = NULL; m_pImpl->m_xMetaData = NULL; } // ------------------------------------------------------------------------- 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 ); ::rtl::OUString sName; OrdinalPosition nOrdinalPosition( 0 ); while ( _rxResult->next() ) { sName = xRow->getString( 4 ); // COLUMN_NAME sal_Int32 nField5 = xRow->getInt(5); ::rtl::OUString aField6 = xRow->getString(6); sal_Int32 nField7 = xRow->getInt(7) , nField9 = xRow->getInt(9) , nField11= xRow->getInt(11); ::rtl::OUString sField13 = xRow->getString(13); nOrdinalPosition = xRow->getInt( 17 ); // ORDINAL_POSITION _out_rColumns.push_back( ColumnDesc( sName,nField5,aField6,nField7,nField9,nField11,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 ( ::std::vector< ColumnDesc >::iterator collect = _rColumns.begin(); collect != _rColumns.end(); ++collect ) 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_ENSURE( false, "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" ); OrdinalPosition nNormalizedPosition = 1; for ( ::std::vector< ColumnDesc >::iterator normalize = _rColumns.begin(); normalize != _rColumns.end(); ++normalize ) normalize->nOrdinalPosition = nNormalizedPosition++; return; } // what's left is that the range might not be from 1 to , but for instance // 0 to -1. size_t nOffset = *aUsedOrdinals.begin() - 1; for ( ::std::vector< ColumnDesc >::iterator offset = _rColumns.begin(); offset != _rColumns.end(); ++offset ) offset->nOrdinalPosition -= nOffset; } } // ------------------------------------------------------------------------- void OTableHelper::refreshColumns() { TStringVector aVector; if(!isNew()) { Any aCatalog; if ( m_CatalogName.getLength() ) aCatalog <<= m_CatalogName; ::utl::SharedUNOComponent< XResultSet > xResult( getMetaData()->getColumns( aCatalog, m_SchemaName, m_Name, ::rtl::OUString::createFromAscii("%") ) ); // 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, ::rtl::OUString > aSortedColumns; for ( ::std::vector< ColumnDesc >::const_iterator copy = m_pImpl->m_aColumnDesc.begin(); copy != m_pImpl->m_aColumnDesc.end(); ++copy ) 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< TStringVector >( aVector, aVector.begin() ), ::std::select2nd< ::std::map< OrdinalPosition, ::rtl::OUString >::value_type >() ); } if(m_pColumns) m_pColumns->reFill(aVector); else m_pColumns = createColumns(aVector); } // ----------------------------------------------------------------------------- const ColumnDesc* OTableHelper::getColumnDescription(const ::rtl::OUString& _sName) const { const ColumnDesc* pRet = NULL; ::std::vector< ColumnDesc >::const_iterator aEnd = m_pImpl->m_aColumnDesc.end(); for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter) { if ( aIter->sName == _sName ) { pRet = &*aIter; break; } } // for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter) return pRet; } // ------------------------------------------------------------------------- void OTableHelper::refreshPrimaryKeys(TStringVector& _rNames) { Any aCatalog; if ( m_CatalogName.getLength() ) aCatalog <<= m_CatalogName; Reference< XResultSet > xResult = getMetaData()->getPrimaryKeys(aCatalog,m_SchemaName,m_Name); if ( xResult.is() ) { sdbcx::TKeyProperties pKeyProps(new sdbcx::KeyProperties(::rtl::OUString(),KeyType::PRIMARY,0,0)); ::rtl::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); bAlreadyFetched = true; } } m_pImpl->m_aKeys.insert(TKeyMap::value_type(aPkName,pKeyProps)); _rNames.push_back(aPkName); } // if ( xResult.is() && xResult->next() ) ::comphelper::disposeComponent(xResult); } // ------------------------------------------------------------------------- void OTableHelper::refreshForgeinKeys(TStringVector& _rNames) { Any aCatalog; if ( m_CatalogName.getLength() ) aCatalog <<= m_CatalogName; Reference< XResultSet > xResult = getMetaData()->getImportedKeys(aCatalog,m_SchemaName,m_Name); Reference< XRow > xRow(xResult,UNO_QUERY); if ( xRow.is() ) { sdbcx::TKeyProperties pKeyProps; ::rtl::OUString aName,sCatalog,aSchema,sOldFKName; while( xResult->next() ) { // this must be outsid the "if" because we have to call in a right order sCatalog = xRow->getString(1); if ( xRow->wasNull() ) sCatalog = ::rtl::OUString(); aSchema = xRow->getString(2); aName = xRow->getString(3); const ::rtl::OUString sForeignKeyColumn = xRow->getString(8); const sal_Int32 nUpdateRule = xRow->getInt(10); const sal_Int32 nDeleteRule = xRow->getInt(11); const ::rtl::OUString sFkName = xRow->getString(12); if ( pKeyProps.get() ) { } if ( sFkName.getLength() && !xRow->wasNull() ) { if ( sOldFKName != sFkName ) { if ( pKeyProps.get() ) m_pImpl->m_aKeys.insert(TKeyMap::value_type(sOldFKName,pKeyProps)); const ::rtl::OUString sReferencedName = ::dbtools::composeTableName(getMetaData(),sCatalog,aSchema,aName,sal_False,::dbtools::eInDataManipulation); pKeyProps.reset(new 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 = ::comphelper::ImplementationReference< OTableContainerListener,XContainerListener>( new OTableContainerListener(this) ); m_pTables->addContainerListener(m_pImpl->m_xTablePropertyListener.getRef()); m_pImpl->m_xTablePropertyListener->add(sReferencedName); } // if ( m_pTables->hasByName(sReferencedName) ) sOldFKName = sFkName; } // if ( sOldFKName != sFkName ) else if ( pKeyProps.get() ) { pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn); } } } // while( xResult->next() ) if ( pKeyProps.get() ) m_pImpl->m_aKeys.insert(TKeyMap::value_type(sOldFKName,pKeyProps)); ::comphelper::disposeComponent(xResult); } } // ------------------------------------------------------------------------- void OTableHelper::refreshKeys() { m_pImpl->m_aKeys.clear(); TStringVector aNames; if(!isNew()) { refreshPrimaryKeys(aNames); refreshForgeinKeys(aNames); m_pKeys = createKeys(aNames); } // if(!isNew()) else if (!m_pKeys ) m_pKeys = createKeys(aNames); /*if(m_pKeys) m_pKeys->reFill(aVector); else*/ } // ------------------------------------------------------------------------- void OTableHelper::refreshIndexes() { TStringVector aVector; if(!isNew()) { // fill indexes Any aCatalog; if ( m_CatalogName.getLength() ) aCatalog <<= m_CatalogName; Reference< XResultSet > xResult = getMetaData()->getIndexInfo(aCatalog,m_SchemaName,m_Name,sal_False,sal_False); if(xResult.is()) { Reference< XRow > xRow(xResult,UNO_QUERY); ::rtl::OUString aName; ::rtl::OUString sCatalogSep = getMetaData()->getCatalogSeparator(); ::rtl::OUString sPreviousRoundName; while( xResult->next() ) { aName = xRow->getString(5); if(aName.getLength()) aName += sCatalogSep; aName += xRow->getString(6); if ( aName.getLength() ) { // 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_pIndexes) m_pIndexes->reFill(aVector); else m_pIndexes = createIndexes(aVector); } // ----------------------------------------------------------------------------- ::rtl::OUString OTableHelper::getRenameStart() const { ::rtl::OUString sSql(RTL_CONSTASCII_USTRINGPARAM("RENAME ")); if ( m_Type == ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("VIEW")) ) sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" VIEW ")); else sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" TABLE ")); return sSql; } // ------------------------------------------------------------------------- // XRename void SAL_CALL OTableHelper::rename( const ::rtl::OUString& newName ) throw(SQLException, ElementExistException, RuntimeException) { ::osl::MutexGuard aGuard(m_aMutex); checkDisposed( #ifdef GCC ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed #else rBHelper.bDisposed #endif ); if(!isNew()) { ::rtl::OUString sSql = getRenameStart(); ::rtl::OUString sQuote = getMetaData()->getIdentifierQuoteString( ); ::rtl::OUString sCatalog,sSchema,sTable; ::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::eInDataManipulation); ::rtl::OUString sComposedName; sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,sal_True,::dbtools::eInDataManipulation); sSql += sComposedName + ::rtl::OUString::createFromAscii(" TO "); sComposedName = ::dbtools::composeTableName(getMetaData(),sCatalog,sSchema,sTable,sal_True,::dbtools::eInDataManipulation); 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::eInTableDefinitions); } // ----------------------------------------------------------------------------- Reference< XDatabaseMetaData> OTableHelper::getMetaData() const { return m_pImpl->m_xMetaData; } // ------------------------------------------------------------------------- void SAL_CALL OTableHelper::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor ) throw(SQLException, ::com::sun::star::lang::IndexOutOfBoundsException, RuntimeException) { ::osl::MutexGuard aGuard(m_aMutex); checkDisposed( #ifdef GCC ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed #else rBHelper.bDisposed #endif ); Reference< XPropertySet > xOld; if(::cppu::extractInterface(xOld,m_pColumns->getByIndex(index)) && xOld.is()) alterColumnByName(getString(xOld->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),descriptor); } // ------------------------------------------------------------------------- ::rtl::OUString SAL_CALL OTableHelper::getName() throw(RuntimeException) { ::rtl::OUString sComposedName; sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,sal_False,::dbtools::eInDataManipulation); return sComposedName; } // ----------------------------------------------------------------------------- void SAL_CALL OTableHelper::acquire() throw() { OTable_TYPEDEF::acquire(); } // ----------------------------------------------------------------------------- void SAL_CALL OTableHelper::release() throw() { OTable_TYPEDEF::release(); } // ----------------------------------------------------------------------------- sdbcx::TKeyProperties OTableHelper::getKeyProperties(const ::rtl::OUString& _sName) const { sdbcx::TKeyProperties 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_ENSURE(0,"No key with the given name found"); pKeyProps.reset(new sdbcx::KeyProperties()); } return pKeyProps; } // ----------------------------------------------------------------------------- void OTableHelper::addKey(const ::rtl::OUString& _sName,const sdbcx::TKeyProperties& _aKeyProperties) { m_pImpl->m_aKeys.insert(TKeyMap::value_type(_sName,_aKeyProperties)); } // ----------------------------------------------------------------------------- ::rtl::OUString OTableHelper::getTypeCreatePattern() const { return ::rtl::OUString(); } // ----------------------------------------------------------------------------- Reference< XConnection> OTableHelper::getConnection() const { return m_pImpl->m_xConnection; }