1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/sdbc/ColumnValue.hpp>
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>
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
;
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
;
63 virtual ~OTableContainerListener(){}
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
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();
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
;
98 if ( ::dbtools::getDataSourceSetting(_xConnection
,i_sSetting
,aValue
) )
100 aValue
>>= sSupportService
;
102 return sSupportService
;
104 struct OTableHelperImpl
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 rtl::Reference
<OTableContainerListener
> m_xTablePropertyListener
;
116 ::std::vector
< ColumnDesc
> m_aColumnDesc
;
117 OTableHelperImpl(const Reference
< ::com::sun::star::sdbc::XConnection
>& _xConnection
)
118 : m_xConnection(_xConnection
)
122 m_xMetaData
= m_xConnection
->getMetaData();
123 Reference
<XMultiServiceFactory
> xFac(_xConnection
,UNO_QUERY
);
126 m_xRename
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"TableRenameServiceName")),UNO_QUERY
);
127 m_xAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"TableAlterationServiceName")),UNO_QUERY
);
128 m_xKeyAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"KeyAlterationServiceName")),UNO_QUERY
);
129 m_xIndexAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"IndexAlterationServiceName")),UNO_QUERY
);
132 catch(const Exception
&)
139 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
140 const Reference
< XConnection
>& _xConnection
,
142 :OTable_TYPEDEF(_pTables
,_bCase
)
143 ,m_pImpl(new OTableHelperImpl(_xConnection
))
147 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
148 const Reference
< XConnection
>& _xConnection
,
150 const OUString
& _Name
,
151 const OUString
& _Type
,
152 const OUString
& _Description
,
153 const OUString
& _SchemaName
,
154 const OUString
& _CatalogName
155 ) : OTable_TYPEDEF(_pTables
,
162 ,m_pImpl(new OTableHelperImpl(_xConnection
))
166 OTableHelper::~OTableHelper()
170 void SAL_CALL
OTableHelper::disposing()
172 ::osl::MutexGuard
aGuard(m_aMutex
);
173 if ( m_pImpl
->m_xTablePropertyListener
.is() )
175 m_pTables
->removeContainerListener(m_pImpl
->m_xTablePropertyListener
.get());
176 m_pImpl
->m_xTablePropertyListener
->clear();
177 m_pImpl
->m_xTablePropertyListener
.clear();
179 OTable_TYPEDEF::disposing();
181 m_pImpl
->m_xConnection
= NULL
;
182 m_pImpl
->m_xMetaData
= NULL
;
189 /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns
191 void lcl_collectColumnDescs_throw( const Reference
< XResultSet
>& _rxResult
, ::std::vector
< ColumnDesc
>& _out_rColumns
)
193 Reference
< XRow
> xRow( _rxResult
, UNO_QUERY_THROW
);
195 OrdinalPosition
nOrdinalPosition( 0 );
196 while ( _rxResult
->next() )
198 sName
= xRow
->getString( 4 ); // COLUMN_NAME
199 sal_Int32 nField5
= xRow
->getInt(5);
200 OUString aField6
= xRow
->getString(6);
201 sal_Int32 nField7
= xRow
->getInt(7)
202 , nField9
= xRow
->getInt(9)
203 , nField11
= xRow
->getInt(11);
204 OUString sField12
= xRow
->getString(12)
205 ,sField13
= xRow
->getString(13);
206 nOrdinalPosition
= xRow
->getInt( 17 ); // ORDINAL_POSITION
207 _out_rColumns
.push_back( ColumnDesc( sName
,nField5
,aField6
,nField7
,nField9
,nField11
,sField12
,sField13
, nOrdinalPosition
) );
211 /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not,
212 they will be normalized to be the array index.
214 void lcl_sanitizeColumnDescs( ::std::vector
< ColumnDesc
>& _rColumns
)
216 if ( _rColumns
.empty() )
219 // collect all used ordinals
220 ::std::set
< OrdinalPosition
> aUsedOrdinals
;
221 for ( ::std::vector
< ColumnDesc
>::iterator collect
= _rColumns
.begin();
222 collect
!= _rColumns
.end();
225 aUsedOrdinals
.insert( collect
->nOrdinalPosition
);
227 // we need to have as much different ordinals as we have different columns
228 bool bDuplicates
= aUsedOrdinals
.size() != _rColumns
.size();
229 // and it needs to be a continuous range
230 size_t nOrdinalsRange
= *aUsedOrdinals
.rbegin() - *aUsedOrdinals
.begin() + 1;
231 bool bGaps
= nOrdinalsRange
!= _rColumns
.size();
233 // if that's not the case, normalize it
234 if ( bGaps
|| bDuplicates
)
236 OSL_FAIL( "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" );
238 OrdinalPosition nNormalizedPosition
= 1;
239 for ( ::std::vector
< ColumnDesc
>::iterator normalize
= _rColumns
.begin();
240 normalize
!= _rColumns
.end();
243 normalize
->nOrdinalPosition
= nNormalizedPosition
++;
247 // what's left is that the range might not be from 1 to <column count>, but for instance
248 // 0 to <column count>-1.
249 size_t nOffset
= *aUsedOrdinals
.begin() - 1;
250 for ( ::std::vector
< ColumnDesc
>::iterator offset
= _rColumns
.begin();
251 offset
!= _rColumns
.end();
254 offset
->nOrdinalPosition
-= nOffset
;
259 void OTableHelper::refreshColumns()
261 TStringVector aVector
;
265 if ( !m_CatalogName
.isEmpty() )
266 aCatalog
<<= m_CatalogName
;
268 ::utl::SharedUNOComponent
< XResultSet
> xResult( getMetaData()->getColumns(
275 // collect the column names, together with their ordinal position
276 m_pImpl
->m_aColumnDesc
.clear();
277 lcl_collectColumnDescs_throw( xResult
, m_pImpl
->m_aColumnDesc
);
279 // ensure that the ordinal positions as obtained from the meta data do make sense
280 lcl_sanitizeColumnDescs( m_pImpl
->m_aColumnDesc
);
282 // sort by ordinal position
283 ::std::map
< OrdinalPosition
, OUString
> aSortedColumns
;
284 for ( ::std::vector
< ColumnDesc
>::const_iterator copy
= m_pImpl
->m_aColumnDesc
.begin();
285 copy
!= m_pImpl
->m_aColumnDesc
.end();
288 aSortedColumns
[ copy
->nOrdinalPosition
] = copy
->sName
;
290 // copy them to aVector, now that we have the proper ordering
292 aSortedColumns
.begin(),
293 aSortedColumns
.end(),
294 ::std::insert_iterator
< TStringVector
>( aVector
, aVector
.begin() ),
295 ::o3tl::select2nd
< ::std::map
< OrdinalPosition
, OUString
>::value_type
>()
300 m_pColumns
->reFill(aVector
);
302 m_pColumns
= createColumns(aVector
);
305 const ColumnDesc
* OTableHelper::getColumnDescription(const OUString
& _sName
) const
307 const ColumnDesc
* pRet
= NULL
;
308 ::std::vector
< ColumnDesc
>::const_iterator aEnd
= m_pImpl
->m_aColumnDesc
.end();
309 for (::std::vector
< ColumnDesc
>::const_iterator aIter
= m_pImpl
->m_aColumnDesc
.begin();aIter
!= aEnd
;++aIter
)
311 if ( aIter
->sName
== _sName
)
316 } // for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter)
320 void OTableHelper::refreshPrimaryKeys(TStringVector
& _rNames
)
323 if ( !m_CatalogName
.isEmpty() )
324 aCatalog
<<= m_CatalogName
;
325 Reference
< XResultSet
> xResult
= getMetaData()->getPrimaryKeys(aCatalog
,m_SchemaName
,m_Name
);
329 sdbcx::TKeyProperties
pKeyProps(new sdbcx::KeyProperties(OUString(),KeyType::PRIMARY
,0,0));
331 bool bAlreadyFetched
= false;
332 const Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
333 while ( xResult
->next() )
335 pKeyProps
->m_aKeyColumnNames
.push_back(xRow
->getString(4));
336 if ( !bAlreadyFetched
)
338 aPkName
= xRow
->getString(6);
339 SAL_WARN_IF(xRow
->wasNull(),"connectivity.commontools", "NULL Primary Key name");
340 SAL_WARN_IF(aPkName
.isEmpty(),"connectivity.commontools", "empty Primary Key name");
341 bAlreadyFetched
= true;
347 SAL_WARN_IF(aPkName
.isEmpty(),"connectivity.commontools", "empty Primary Key name");
348 SAL_WARN_IF(pKeyProps
->m_aKeyColumnNames
.size() == 0,"connectivity.commontools", "Primary Key has no columns");
349 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(aPkName
,pKeyProps
));
350 _rNames
.push_back(aPkName
);
352 } // if ( xResult.is() && xResult->next() )
353 ::comphelper::disposeComponent(xResult
);
356 void OTableHelper::refreshForeignKeys(TStringVector
& _rNames
)
359 if ( !m_CatalogName
.isEmpty() )
360 aCatalog
<<= m_CatalogName
;
361 Reference
< XResultSet
> xResult
= getMetaData()->getImportedKeys(aCatalog
,m_SchemaName
,m_Name
);
362 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
366 sdbcx::TKeyProperties pKeyProps
;
367 OUString aName
,sCatalog
,aSchema
,sOldFKName
;
368 while( xResult
->next() )
370 // this must be outsid the "if" because we have to call in a right order
371 sCatalog
= xRow
->getString(1);
372 if ( xRow
->wasNull() )
374 aSchema
= xRow
->getString(2);
375 aName
= xRow
->getString(3);
377 const OUString sForeignKeyColumn
= xRow
->getString(8);
378 const sal_Int32 nUpdateRule
= xRow
->getInt(10);
379 const sal_Int32 nDeleteRule
= xRow
->getInt(11);
380 const OUString sFkName
= xRow
->getString(12);
382 if ( pKeyProps
.get() )
387 if ( !sFkName
.isEmpty() && !xRow
->wasNull() )
389 if ( sOldFKName
!= sFkName
)
391 if ( pKeyProps
.get() )
392 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(sOldFKName
,pKeyProps
));
394 const OUString sReferencedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,aSchema
,aName
,false,::dbtools::eInDataManipulation
);
395 pKeyProps
.reset(new sdbcx::KeyProperties(sReferencedName
,KeyType::FOREIGN
,nUpdateRule
,nDeleteRule
));
396 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
397 _rNames
.push_back(sFkName
);
398 if ( m_pTables
->hasByName(sReferencedName
) )
400 if ( !m_pImpl
->m_xTablePropertyListener
.is() )
401 m_pImpl
->m_xTablePropertyListener
= new OTableContainerListener(this);
402 m_pTables
->addContainerListener(m_pImpl
->m_xTablePropertyListener
.get());
403 m_pImpl
->m_xTablePropertyListener
->add(sReferencedName
);
404 } // if ( m_pTables->hasByName(sReferencedName) )
405 sOldFKName
= sFkName
;
406 } // if ( sOldFKName != sFkName )
407 else if ( pKeyProps
.get() )
409 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
412 } // while( xResult->next() )
413 if ( pKeyProps
.get() )
414 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(sOldFKName
,pKeyProps
));
415 ::comphelper::disposeComponent(xResult
);
419 void OTableHelper::refreshKeys()
421 m_pImpl
->m_aKeys
.clear();
423 TStringVector aNames
;
427 refreshPrimaryKeys(aNames
);
428 refreshForeignKeys(aNames
);
429 m_pKeys
= createKeys(aNames
);
432 m_pKeys
= createKeys(aNames
);
434 m_pKeys->reFill(aVector);
439 void OTableHelper::refreshIndexes()
441 TStringVector aVector
;
446 if ( !m_CatalogName
.isEmpty() )
447 aCatalog
<<= m_CatalogName
;
448 Reference
< XResultSet
> xResult
= getMetaData()->getIndexInfo(aCatalog
,m_SchemaName
,m_Name
,sal_False
,sal_False
);
452 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
454 OUString sCatalogSep
= getMetaData()->getCatalogSeparator();
455 OUString sPreviousRoundName
;
456 while( xResult
->next() )
458 aName
= xRow
->getString(5);
460 aName
+= sCatalogSep
;
461 aName
+= xRow
->getString(6);
462 if ( !aName
.isEmpty() )
464 // don't insert the name if the last one we inserted was the same
465 if (sPreviousRoundName
!= aName
)
466 aVector
.push_back(aName
);
468 sPreviousRoundName
= aName
;
470 ::comphelper::disposeComponent(xResult
);
475 m_pIndexes
->reFill(aVector
);
477 m_pIndexes
= createIndexes(aVector
);
480 OUString
OTableHelper::getRenameStart() const
482 OUString
sSql("RENAME ");
483 if ( m_Type
== "VIEW" )
492 void SAL_CALL
OTableHelper::rename( const OUString
& newName
) throw(SQLException
, ElementExistException
, RuntimeException
, std::exception
)
494 ::osl::MutexGuard
aGuard(m_aMutex
);
497 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
505 if ( m_pImpl
->m_xRename
.is() )
507 m_pImpl
->m_xRename
->rename(this,newName
);
511 OUString sSql
= getRenameStart();
513 OUString sCatalog
,sSchema
,sTable
;
514 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,sCatalog
,sSchema
,sTable
,::dbtools::eInDataManipulation
);
516 OUString sComposedName
;
517 sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,true,::dbtools::eInDataManipulation
);
518 sSql
+= sComposedName
520 sComposedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,sSchema
,sTable
,true,::dbtools::eInDataManipulation
);
521 sSql
+= sComposedName
;
523 Reference
< XStatement
> xStmt
= m_pImpl
->m_xConnection
->createStatement( );
526 xStmt
->execute(sSql
);
527 ::comphelper::disposeComponent(xStmt
);
531 OTable_TYPEDEF::rename(newName
);
534 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,m_CatalogName
,m_SchemaName
,m_Name
,::dbtools::eInTableDefinitions
);
537 Reference
< XDatabaseMetaData
> OTableHelper::getMetaData() const
539 return m_pImpl
->m_xMetaData
;
542 void SAL_CALL
OTableHelper::alterColumnByIndex( sal_Int32 index
, const Reference
< XPropertySet
>& descriptor
) throw(SQLException
, ::com::sun::star::lang::IndexOutOfBoundsException
, RuntimeException
, std::exception
)
544 ::osl::MutexGuard
aGuard(m_aMutex
);
547 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
553 Reference
< XPropertySet
> xOld(
554 m_pColumns
->getByIndex(index
), css::uno::UNO_QUERY
);
556 alterColumnByName(getString(xOld
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME
))),descriptor
);
560 OUString SAL_CALL
OTableHelper::getName() throw(RuntimeException
, std::exception
)
562 OUString sComposedName
;
563 sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,false,::dbtools::eInDataManipulation
);
564 return sComposedName
;
567 void SAL_CALL
OTableHelper::acquire() throw()
569 OTable_TYPEDEF::acquire();
572 void SAL_CALL
OTableHelper::release() throw()
574 OTable_TYPEDEF::release();
577 sdbcx::TKeyProperties
OTableHelper::getKeyProperties(const OUString
& _sName
) const
579 sdbcx::TKeyProperties pKeyProps
;
580 TKeyMap::const_iterator aFind
= m_pImpl
->m_aKeys
.find(_sName
);
581 if ( aFind
!= m_pImpl
->m_aKeys
.end() )
583 pKeyProps
= aFind
->second
;
585 else // only a fall back
587 OSL_FAIL("No key with the given name found");
588 pKeyProps
.reset(new sdbcx::KeyProperties());
594 void OTableHelper::addKey(const OUString
& _sName
,const sdbcx::TKeyProperties
& _aKeyProperties
)
596 m_pImpl
->m_aKeys
.insert(TKeyMap::value_type(_sName
,_aKeyProperties
));
599 OUString
OTableHelper::getTypeCreatePattern() const
604 Reference
< XConnection
> OTableHelper::getConnection() const
606 return m_pImpl
->m_xConnection
;
609 Reference
< ::com::sun::star::sdb::tools::XTableRename
> OTableHelper::getRenameService() const
611 return m_pImpl
->m_xRename
;
614 Reference
< ::com::sun::star::sdb::tools::XTableAlteration
> OTableHelper::getAlterService() const
616 return m_pImpl
->m_xAlter
;
619 Reference
< ::com::sun::star::sdb::tools::XKeyAlteration
> OTableHelper::getKeyService() const
621 return m_pImpl
->m_xKeyAlter
;
624 Reference
< ::com::sun::star::sdb::tools::XIndexAlteration
> OTableHelper::getIndexService() const
626 return m_pImpl
->m_xIndexAlter
;
630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */