Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / connectivity / source / commontools / TTableHelper.cxx
blobbe7e9a135253936477204da2cced47f6593341aa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <boost/noncopyable.hpp>
23 #include "connectivity/TTableHelper.hxx"
24 #include <com/sun/star/sdbc/XRow.hpp>
25 #include <com/sun/star/sdbc/XResultSet.hpp>
26 #include <com/sun/star/sdbcx/KeyType.hpp>
27 #include <com/sun/star/sdbc/KeyRule.hpp>
28 #include <cppuhelper/typeprovider.hxx>
29 #include <com/sun/star/lang/DisposedException.hpp>
30 #include <com/sun/star/sdbc/ColumnValue.hpp>
31 #include <comphelper/implementationreference.hxx>
32 #include <comphelper/sequence.hxx>
33 #include <comphelper/types.hxx>
34 #include "connectivity/dbtools.hxx"
35 #include "connectivity/sdbcx/VCollection.hxx"
36 #include <unotools/sharedunocomponent.hxx>
37 #include "TConnection.hxx"
39 #include <o3tl/compat_functional.hxx>
41 #include <iterator>
42 #include <set>
44 using namespace ::comphelper;
45 using namespace connectivity;
46 using namespace ::com::sun::star::uno;
47 using namespace ::com::sun::star::beans;
48 using namespace ::com::sun::star::sdbcx;
49 using namespace ::com::sun::star::sdbc;
50 using namespace ::com::sun::star::container;
51 using namespace ::com::sun::star::lang;
52 namespace
54 /// helper class for column property change events which holds the OComponentDefinition weak
55 typedef ::cppu::WeakImplHelper1 < XContainerListener > OTableContainerListener_BASE;
56 class OTableContainerListener:
57 public OTableContainerListener_BASE, private boost::noncopyable
59 OTableHelper* m_pComponent;
60 ::std::map< OUString,bool> m_aRefNames;
62 protected:
63 virtual ~OTableContainerListener(){}
64 public:
65 OTableContainerListener(OTableHelper* _pComponent) : m_pComponent(_pComponent){}
66 virtual void SAL_CALL elementInserted( const ::com::sun::star::container::ContainerEvent& /*Event*/ ) throw (RuntimeException, std::exception) SAL_OVERRIDE
69 virtual void SAL_CALL elementRemoved( const ::com::sun::star::container::ContainerEvent& Event ) throw (RuntimeException, std::exception) SAL_OVERRIDE
71 OUString sName;
72 Event.Accessor >>= sName;
73 if ( m_aRefNames.find(sName) != m_aRefNames.end() )
74 m_pComponent->refreshKeys();
76 virtual void SAL_CALL elementReplaced( const ::com::sun::star::container::ContainerEvent& Event ) throw (RuntimeException, std::exception) SAL_OVERRIDE
78 OUString sOldComposedName,sNewComposedName;
79 Event.ReplacedElement >>= sOldComposedName;
80 Event.Accessor >>= sNewComposedName;
81 if ( sOldComposedName != sNewComposedName && m_aRefNames.find(sOldComposedName) != m_aRefNames.end() )
82 m_pComponent->refreshKeys();
84 // XEventListener
85 virtual void SAL_CALL disposing( const EventObject& /*_rSource*/ ) throw (RuntimeException, std::exception) SAL_OVERRIDE
88 void clear() { m_pComponent = NULL; }
89 inline void add(const OUString& _sRefName) { m_aRefNames.insert(::std::map< OUString,bool>::value_type(_sRefName,true)); }
92 namespace connectivity
94 OUString lcl_getServiceNameForSetting(const Reference< ::com::sun::star::sdbc::XConnection >& _xConnection,const OUString& i_sSetting)
96 OUString sSupportService;
97 Any aValue;
98 if ( ::dbtools::getDataSourceSetting(_xConnection,i_sSetting,aValue) )
100 aValue >>= sSupportService;
102 return sSupportService;
104 struct OTableHelperImpl
106 TKeyMap m_aKeys;
107 // helper services which can be provided by extensions
108 Reference< ::com::sun::star::sdb::tools::XTableRename> m_xRename;
109 Reference< ::com::sun::star::sdb::tools::XTableAlteration> m_xAlter;
110 Reference< ::com::sun::star::sdb::tools::XKeyAlteration> m_xKeyAlter;
111 Reference< ::com::sun::star::sdb::tools::XIndexAlteration> m_xIndexAlter;
113 Reference< ::com::sun::star::sdbc::XDatabaseMetaData > m_xMetaData;
114 Reference< ::com::sun::star::sdbc::XConnection > m_xConnection;
115 ::comphelper::ImplementationReference< OTableContainerListener,XContainerListener>
116 m_xTablePropertyListener;
117 ::std::vector< ColumnDesc > m_aColumnDesc;
118 OTableHelperImpl(const Reference< ::com::sun::star::sdbc::XConnection >& _xConnection)
119 : m_xConnection(_xConnection)
123 m_xMetaData = m_xConnection->getMetaData();
124 Reference<XMultiServiceFactory> xFac(_xConnection,UNO_QUERY);
125 if ( xFac.is() )
127 static const OUString s_sTableRename("TableRenameServiceName");
128 m_xRename.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,s_sTableRename)),UNO_QUERY);
129 static const OUString s_sTableAlteration("TableAlterationServiceName");
130 m_xAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,s_sTableAlteration)),UNO_QUERY);
131 static const OUString s_sKeyAlteration("KeyAlterationServiceName");
132 m_xKeyAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,s_sKeyAlteration)),UNO_QUERY);
133 static const OUString s_sIndexAlteration("IndexAlterationServiceName");
134 m_xIndexAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,s_sIndexAlteration)),UNO_QUERY);
137 catch(const Exception&)
144 OTableHelper::OTableHelper( sdbcx::OCollection* _pTables,
145 const Reference< XConnection >& _xConnection,
146 bool _bCase)
147 :OTable_TYPEDEF(_pTables,_bCase)
148 ,m_pImpl(new OTableHelperImpl(_xConnection))
152 OTableHelper::OTableHelper( sdbcx::OCollection* _pTables,
153 const Reference< XConnection >& _xConnection,
154 bool _bCase,
155 const OUString& _Name,
156 const OUString& _Type,
157 const OUString& _Description ,
158 const OUString& _SchemaName,
159 const OUString& _CatalogName
160 ) : OTable_TYPEDEF(_pTables,
161 _bCase,
162 _Name,
163 _Type,
164 _Description,
165 _SchemaName,
166 _CatalogName)
167 ,m_pImpl(new OTableHelperImpl(_xConnection))
171 OTableHelper::~OTableHelper()
175 void SAL_CALL OTableHelper::disposing()
177 ::osl::MutexGuard aGuard(m_aMutex);
178 if ( m_pImpl->m_xTablePropertyListener.is() )
180 m_pTables->removeContainerListener(m_pImpl->m_xTablePropertyListener.getRef());
181 m_pImpl->m_xTablePropertyListener->clear();
182 m_pImpl->m_xTablePropertyListener.dispose();
184 OTable_TYPEDEF::disposing();
186 m_pImpl->m_xConnection = NULL;
187 m_pImpl->m_xMetaData = NULL;
192 namespace
194 /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns
196 void lcl_collectColumnDescs_throw( const Reference< XResultSet >& _rxResult, ::std::vector< ColumnDesc >& _out_rColumns )
198 Reference< XRow > xRow( _rxResult, UNO_QUERY_THROW );
199 OUString sName;
200 OrdinalPosition nOrdinalPosition( 0 );
201 while ( _rxResult->next() )
203 sName = xRow->getString( 4 ); // COLUMN_NAME
204 sal_Int32 nField5 = xRow->getInt(5);
205 OUString aField6 = xRow->getString(6);
206 sal_Int32 nField7 = xRow->getInt(7)
207 , nField9 = xRow->getInt(9)
208 , nField11= xRow->getInt(11);
209 OUString sField12 = xRow->getString(12)
210 ,sField13 = xRow->getString(13);
211 nOrdinalPosition = xRow->getInt( 17 ); // ORDINAL_POSITION
212 _out_rColumns.push_back( ColumnDesc( sName,nField5,aField6,nField7,nField9,nField11,sField12,sField13, nOrdinalPosition ) );
216 /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not,
217 they will be normalized to be the array index.
219 void lcl_sanitizeColumnDescs( ::std::vector< ColumnDesc >& _rColumns )
221 if ( _rColumns.empty() )
222 return;
224 // collect all used ordinals
225 ::std::set< OrdinalPosition > aUsedOrdinals;
226 for ( ::std::vector< ColumnDesc >::iterator collect = _rColumns.begin();
227 collect != _rColumns.end();
228 ++collect
230 aUsedOrdinals.insert( collect->nOrdinalPosition );
232 // we need to have as much different ordinals as we have different columns
233 bool bDuplicates = aUsedOrdinals.size() != _rColumns.size();
234 // and it needs to be a continuous range
235 size_t nOrdinalsRange = *aUsedOrdinals.rbegin() - *aUsedOrdinals.begin() + 1;
236 bool bGaps = nOrdinalsRange != _rColumns.size();
238 // if that's not the case, normalize it
239 if ( bGaps || bDuplicates )
241 OSL_FAIL( "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" );
243 OrdinalPosition nNormalizedPosition = 1;
244 for ( ::std::vector< ColumnDesc >::iterator normalize = _rColumns.begin();
245 normalize != _rColumns.end();
246 ++normalize
248 normalize->nOrdinalPosition = nNormalizedPosition++;
249 return;
252 // what's left is that the range might not be from 1 to <column count>, but for instance
253 // 0 to <column count>-1.
254 size_t nOffset = *aUsedOrdinals.begin() - 1;
255 for ( ::std::vector< ColumnDesc >::iterator offset = _rColumns.begin();
256 offset != _rColumns.end();
257 ++offset
259 offset->nOrdinalPosition -= nOffset;
264 void OTableHelper::refreshColumns()
266 TStringVector aVector;
267 if(!isNew())
269 Any aCatalog;
270 if ( !m_CatalogName.isEmpty() )
271 aCatalog <<= m_CatalogName;
273 ::utl::SharedUNOComponent< XResultSet > xResult( getMetaData()->getColumns(
274 aCatalog,
275 m_SchemaName,
276 m_Name,
277 OUString("%")
278 ) );
280 // collect the column names, together with their ordinal position
281 m_pImpl->m_aColumnDesc.clear();
282 lcl_collectColumnDescs_throw( xResult, m_pImpl->m_aColumnDesc );
284 // ensure that the ordinal positions as obtained from the meta data do make sense
285 lcl_sanitizeColumnDescs( m_pImpl->m_aColumnDesc );
287 // sort by ordinal position
288 ::std::map< OrdinalPosition, OUString > aSortedColumns;
289 for ( ::std::vector< ColumnDesc >::const_iterator copy = m_pImpl->m_aColumnDesc.begin();
290 copy != m_pImpl->m_aColumnDesc.end();
291 ++copy
293 aSortedColumns[ copy->nOrdinalPosition ] = copy->sName;
295 // copy them to aVector, now that we have the proper ordering
296 ::std::transform(
297 aSortedColumns.begin(),
298 aSortedColumns.end(),
299 ::std::insert_iterator< TStringVector >( aVector, aVector.begin() ),
300 ::o3tl::select2nd< ::std::map< OrdinalPosition, OUString >::value_type >()
304 if(m_pColumns)
305 m_pColumns->reFill(aVector);
306 else
307 m_pColumns = createColumns(aVector);
310 const ColumnDesc* OTableHelper::getColumnDescription(const OUString& _sName) const
312 const ColumnDesc* pRet = NULL;
313 ::std::vector< ColumnDesc >::const_iterator aEnd = m_pImpl->m_aColumnDesc.end();
314 for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter)
316 if ( aIter->sName == _sName )
318 pRet = &*aIter;
319 break;
321 } // for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter)
322 return pRet;
325 void OTableHelper::refreshPrimaryKeys(TStringVector& _rNames)
327 Any aCatalog;
328 if ( !m_CatalogName.isEmpty() )
329 aCatalog <<= m_CatalogName;
330 Reference< XResultSet > xResult = getMetaData()->getPrimaryKeys(aCatalog,m_SchemaName,m_Name);
332 if ( xResult.is() )
334 sdbcx::TKeyProperties pKeyProps(new sdbcx::KeyProperties(OUString(),KeyType::PRIMARY,0,0));
335 OUString aPkName;
336 bool bAlreadyFetched = false;
337 const Reference< XRow > xRow(xResult,UNO_QUERY);
338 while ( xResult->next() )
340 pKeyProps->m_aKeyColumnNames.push_back(xRow->getString(4));
341 if ( !bAlreadyFetched )
343 aPkName = xRow->getString(6);
344 SAL_WARN_IF(xRow->wasNull(),"connectivity.commontools", "NULL Primary Key name");
345 SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools", "empty Primary Key name");
346 bAlreadyFetched = true;
350 if(bAlreadyFetched)
352 SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools", "empty Primary Key name");
353 SAL_WARN_IF(pKeyProps->m_aKeyColumnNames.size() == 0,"connectivity.commontools", "Primary Key has no columns");
354 m_pImpl->m_aKeys.insert(TKeyMap::value_type(aPkName,pKeyProps));
355 _rNames.push_back(aPkName);
357 } // if ( xResult.is() && xResult->next() )
358 ::comphelper::disposeComponent(xResult);
361 void OTableHelper::refreshForeignKeys(TStringVector& _rNames)
363 Any aCatalog;
364 if ( !m_CatalogName.isEmpty() )
365 aCatalog <<= m_CatalogName;
366 Reference< XResultSet > xResult = getMetaData()->getImportedKeys(aCatalog,m_SchemaName,m_Name);
367 Reference< XRow > xRow(xResult,UNO_QUERY);
369 if ( xRow.is() )
371 sdbcx::TKeyProperties pKeyProps;
372 OUString aName,sCatalog,aSchema,sOldFKName;
373 while( xResult->next() )
375 // this must be outsid the "if" because we have to call in a right order
376 sCatalog = xRow->getString(1);
377 if ( xRow->wasNull() )
378 sCatalog = OUString();
379 aSchema = xRow->getString(2);
380 aName = xRow->getString(3);
382 const OUString sForeignKeyColumn = xRow->getString(8);
383 const sal_Int32 nUpdateRule = xRow->getInt(10);
384 const sal_Int32 nDeleteRule = xRow->getInt(11);
385 const OUString sFkName = xRow->getString(12);
387 if ( pKeyProps.get() )
392 if ( !sFkName.isEmpty() && !xRow->wasNull() )
394 if ( sOldFKName != sFkName )
396 if ( pKeyProps.get() )
397 m_pImpl->m_aKeys.insert(TKeyMap::value_type(sOldFKName,pKeyProps));
399 const OUString sReferencedName = ::dbtools::composeTableName(getMetaData(),sCatalog,aSchema,aName,false,::dbtools::eInDataManipulation);
400 pKeyProps.reset(new sdbcx::KeyProperties(sReferencedName,KeyType::FOREIGN,nUpdateRule,nDeleteRule));
401 pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn);
402 _rNames.push_back(sFkName);
403 if ( m_pTables->hasByName(sReferencedName) )
405 if ( !m_pImpl->m_xTablePropertyListener.is() )
406 m_pImpl->m_xTablePropertyListener = ::comphelper::ImplementationReference< OTableContainerListener,XContainerListener>( new OTableContainerListener(this) );
407 m_pTables->addContainerListener(m_pImpl->m_xTablePropertyListener.getRef());
408 m_pImpl->m_xTablePropertyListener->add(sReferencedName);
409 } // if ( m_pTables->hasByName(sReferencedName) )
410 sOldFKName = sFkName;
411 } // if ( sOldFKName != sFkName )
412 else if ( pKeyProps.get() )
414 pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn);
417 } // while( xResult->next() )
418 if ( pKeyProps.get() )
419 m_pImpl->m_aKeys.insert(TKeyMap::value_type(sOldFKName,pKeyProps));
420 ::comphelper::disposeComponent(xResult);
424 void OTableHelper::refreshKeys()
426 m_pImpl->m_aKeys.clear();
428 TStringVector aNames;
430 if(!isNew())
432 refreshPrimaryKeys(aNames);
433 refreshForeignKeys(aNames);
434 m_pKeys = createKeys(aNames);
435 } // if(!isNew())
436 else if (!m_pKeys )
437 m_pKeys = createKeys(aNames);
438 /*if(m_pKeys)
439 m_pKeys->reFill(aVector);
440 else*/
444 void OTableHelper::refreshIndexes()
446 TStringVector aVector;
447 if(!isNew())
449 // fill indexes
450 Any aCatalog;
451 if ( !m_CatalogName.isEmpty() )
452 aCatalog <<= m_CatalogName;
453 Reference< XResultSet > xResult = getMetaData()->getIndexInfo(aCatalog,m_SchemaName,m_Name,sal_False,sal_False);
455 if(xResult.is())
457 Reference< XRow > xRow(xResult,UNO_QUERY);
458 OUString aName;
459 OUString sCatalogSep = getMetaData()->getCatalogSeparator();
460 OUString sPreviousRoundName;
461 while( xResult->next() )
463 aName = xRow->getString(5);
464 if(!aName.isEmpty())
465 aName += sCatalogSep;
466 aName += xRow->getString(6);
467 if ( !aName.isEmpty() )
469 // don't insert the name if the last one we inserted was the same
470 if (sPreviousRoundName != aName)
471 aVector.push_back(aName);
473 sPreviousRoundName = aName;
475 ::comphelper::disposeComponent(xResult);
479 if(m_pIndexes)
480 m_pIndexes->reFill(aVector);
481 else
482 m_pIndexes = createIndexes(aVector);
485 OUString OTableHelper::getRenameStart() const
487 OUString sSql("RENAME ");
488 if ( m_Type == "VIEW" )
489 sSql += " VIEW ";
490 else
491 sSql += " TABLE ";
493 return sSql;
496 // XRename
497 void SAL_CALL OTableHelper::rename( const OUString& newName ) throw(SQLException, ElementExistException, RuntimeException, std::exception)
499 ::osl::MutexGuard aGuard(m_aMutex);
500 checkDisposed(
501 #ifdef __GNUC__
502 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
503 #else
504 rBHelper.bDisposed
505 #endif
508 if(!isNew())
510 if ( m_pImpl->m_xRename.is() )
512 m_pImpl->m_xRename->rename(this,newName);
514 else
516 OUString sSql = getRenameStart();
518 OUString sCatalog,sSchema,sTable;
519 ::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::eInDataManipulation);
521 OUString sComposedName;
522 sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,true,::dbtools::eInDataManipulation);
523 sSql += sComposedName
524 + " TO ";
525 sComposedName = ::dbtools::composeTableName(getMetaData(),sCatalog,sSchema,sTable,true,::dbtools::eInDataManipulation);
526 sSql += sComposedName;
528 Reference< XStatement > xStmt = m_pImpl->m_xConnection->createStatement( );
529 if ( xStmt.is() )
531 xStmt->execute(sSql);
532 ::comphelper::disposeComponent(xStmt);
536 OTable_TYPEDEF::rename(newName);
538 else
539 ::dbtools::qualifiedNameComponents(getMetaData(),newName,m_CatalogName,m_SchemaName,m_Name,::dbtools::eInTableDefinitions);
542 Reference< XDatabaseMetaData> OTableHelper::getMetaData() const
544 return m_pImpl->m_xMetaData;
547 void SAL_CALL OTableHelper::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor ) throw(SQLException, ::com::sun::star::lang::IndexOutOfBoundsException, RuntimeException, std::exception)
549 ::osl::MutexGuard aGuard(m_aMutex);
550 checkDisposed(
551 #ifdef __GNUC__
552 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
553 #else
554 rBHelper.bDisposed
555 #endif
558 Reference< XPropertySet > xOld(
559 m_pColumns->getByIndex(index), css::uno::UNO_QUERY);
560 if(xOld.is())
561 alterColumnByName(getString(xOld->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),descriptor);
565 OUString SAL_CALL OTableHelper::getName() throw(RuntimeException, std::exception)
567 OUString sComposedName;
568 sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,false,::dbtools::eInDataManipulation);
569 return sComposedName;
572 void SAL_CALL OTableHelper::acquire() throw()
574 OTable_TYPEDEF::acquire();
577 void SAL_CALL OTableHelper::release() throw()
579 OTable_TYPEDEF::release();
582 sdbcx::TKeyProperties OTableHelper::getKeyProperties(const OUString& _sName) const
584 sdbcx::TKeyProperties pKeyProps;
585 TKeyMap::const_iterator aFind = m_pImpl->m_aKeys.find(_sName);
586 if ( aFind != m_pImpl->m_aKeys.end() )
588 pKeyProps = aFind->second;
590 else // only a fall back
592 OSL_FAIL("No key with the given name found");
593 pKeyProps.reset(new sdbcx::KeyProperties());
596 return pKeyProps;
599 void OTableHelper::addKey(const OUString& _sName,const sdbcx::TKeyProperties& _aKeyProperties)
601 m_pImpl->m_aKeys.insert(TKeyMap::value_type(_sName,_aKeyProperties));
604 OUString OTableHelper::getTypeCreatePattern() const
606 return OUString();
609 Reference< XConnection> OTableHelper::getConnection() const
611 return m_pImpl->m_xConnection;
614 Reference< ::com::sun::star::sdb::tools::XTableRename> OTableHelper::getRenameService() const
616 return m_pImpl->m_xRename;
619 Reference< ::com::sun::star::sdb::tools::XTableAlteration> OTableHelper::getAlterService() const
621 return m_pImpl->m_xAlter;
624 Reference< ::com::sun::star::sdb::tools::XKeyAlteration> OTableHelper::getKeyService() const
626 return m_pImpl->m_xKeyAlter;
629 Reference< ::com::sun::star::sdb::tools::XIndexAlteration> OTableHelper::getIndexService() const
631 return m_pImpl->m_xIndexAlter;
635 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */