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/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>
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 ::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
);
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
,
147 :OTable_TYPEDEF(_pTables
,_bCase
)
148 ,m_pImpl(new OTableHelperImpl(_xConnection
))
152 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
153 const Reference
< XConnection
>& _xConnection
,
155 const OUString
& _Name
,
156 const OUString
& _Type
,
157 const OUString
& _Description
,
158 const OUString
& _SchemaName
,
159 const OUString
& _CatalogName
160 ) : OTable_TYPEDEF(_pTables
,
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
;
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
);
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() )
224 // collect all used ordinals
225 ::std::set
< OrdinalPosition
> aUsedOrdinals
;
226 for ( ::std::vector
< ColumnDesc
>::iterator collect
= _rColumns
.begin();
227 collect
!= _rColumns
.end();
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();
248 normalize
->nOrdinalPosition
= nNormalizedPosition
++;
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();
259 offset
->nOrdinalPosition
-= nOffset
;
264 void OTableHelper::refreshColumns()
266 TStringVector aVector
;
270 if ( !m_CatalogName
.isEmpty() )
271 aCatalog
<<= m_CatalogName
;
273 ::utl::SharedUNOComponent
< XResultSet
> xResult( getMetaData()->getColumns(
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();
293 aSortedColumns
[ copy
->nOrdinalPosition
] = copy
->sName
;
295 // copy them to aVector, now that we have the proper ordering
297 aSortedColumns
.begin(),
298 aSortedColumns
.end(),
299 ::std::insert_iterator
< TStringVector
>( aVector
, aVector
.begin() ),
300 ::o3tl::select2nd
< ::std::map
< OrdinalPosition
, OUString
>::value_type
>()
305 m_pColumns
->reFill(aVector
);
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
)
321 } // for (::std::vector< ColumnDesc >::const_iterator aIter = m_pImpl->m_aColumnDesc.begin();aIter != aEnd;++aIter)
325 void OTableHelper::refreshPrimaryKeys(TStringVector
& _rNames
)
328 if ( !m_CatalogName
.isEmpty() )
329 aCatalog
<<= m_CatalogName
;
330 Reference
< XResultSet
> xResult
= getMetaData()->getPrimaryKeys(aCatalog
,m_SchemaName
,m_Name
);
334 sdbcx::TKeyProperties
pKeyProps(new sdbcx::KeyProperties(OUString(),KeyType::PRIMARY
,0,0));
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;
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
)
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
);
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
;
432 refreshPrimaryKeys(aNames
);
433 refreshForeignKeys(aNames
);
434 m_pKeys
= createKeys(aNames
);
437 m_pKeys
= createKeys(aNames
);
439 m_pKeys->reFill(aVector);
444 void OTableHelper::refreshIndexes()
446 TStringVector aVector
;
451 if ( !m_CatalogName
.isEmpty() )
452 aCatalog
<<= m_CatalogName
;
453 Reference
< XResultSet
> xResult
= getMetaData()->getIndexInfo(aCatalog
,m_SchemaName
,m_Name
,sal_False
,sal_False
);
457 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
459 OUString sCatalogSep
= getMetaData()->getCatalogSeparator();
460 OUString sPreviousRoundName
;
461 while( xResult
->next() )
463 aName
= xRow
->getString(5);
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
);
480 m_pIndexes
->reFill(aVector
);
482 m_pIndexes
= createIndexes(aVector
);
485 OUString
OTableHelper::getRenameStart() const
487 OUString
sSql("RENAME ");
488 if ( m_Type
== "VIEW" )
497 void SAL_CALL
OTableHelper::rename( const OUString
& newName
) throw(SQLException
, ElementExistException
, RuntimeException
, std::exception
)
499 ::osl::MutexGuard
aGuard(m_aMutex
);
502 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
510 if ( m_pImpl
->m_xRename
.is() )
512 m_pImpl
->m_xRename
->rename(this,newName
);
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
525 sComposedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,sSchema
,sTable
,true,::dbtools::eInDataManipulation
);
526 sSql
+= sComposedName
;
528 Reference
< XStatement
> xStmt
= m_pImpl
->m_xConnection
->createStatement( );
531 xStmt
->execute(sSql
);
532 ::comphelper::disposeComponent(xStmt
);
536 OTable_TYPEDEF::rename(newName
);
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
);
552 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
558 Reference
< XPropertySet
> xOld(
559 m_pColumns
->getByIndex(index
), css::uno::UNO_QUERY
);
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());
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
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: */