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>
21 #include <sal/log.hxx>
23 #include <connectivity/TTableHelper.hxx>
24 #include <com/sun/star/sdb/tools/XTableRename.hpp>
25 #include <com/sun/star/sdb/tools/XTableAlteration.hpp>
26 #include <com/sun/star/sdb/tools/XKeyAlteration.hpp>
27 #include <com/sun/star/sdb/tools/XIndexAlteration.hpp>
28 #include <connectivity/sdbcx/VKey.hxx>
29 #include <com/sun/star/sdbc/XRow.hpp>
30 #include <com/sun/star/sdbc/XResultSet.hpp>
31 #include <com/sun/star/sdbcx/KeyType.hpp>
32 #include <com/sun/star/sdbc/KeyRule.hpp>
33 #include <cppuhelper/implbase.hxx>
34 #include <cppuhelper/typeprovider.hxx>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37 #include <com/sun/star/sdbc/ColumnValue.hpp>
38 #include <comphelper/types.hxx>
39 #include <connectivity/dbtools.hxx>
40 #include <connectivity/sdbcx/VCollection.hxx>
41 #include <unotools/sharedunocomponent.hxx>
42 #include <TConnection.hxx>
44 #include <o3tl/functional.hxx>
49 using namespace ::comphelper
;
50 using namespace connectivity
;
51 using namespace ::com::sun::star::uno
;
52 using namespace ::com::sun::star::beans
;
53 using namespace ::com::sun::star::sdbcx
;
54 using namespace ::com::sun::star::sdbc
;
55 using namespace ::com::sun::star::container
;
56 using namespace ::com::sun::star::lang
;
60 /// helper class for column property change events which holds the OComponentDefinition weak
61 class OTableContainerListener
:
62 public ::cppu::WeakImplHelper
< XContainerListener
>
64 OTableHelper
* m_pComponent
;
65 std::map
< OUString
,bool> m_aRefNames
;
68 virtual ~OTableContainerListener() override
{}
70 explicit OTableContainerListener(OTableHelper
* _pComponent
) : m_pComponent(_pComponent
){}
72 OTableContainerListener(const OTableContainerListener
&) = delete;
73 const OTableContainerListener
& operator=(const OTableContainerListener
&) = delete;
74 virtual void SAL_CALL
elementInserted( const css::container::ContainerEvent
& /*Event*/ ) override
77 virtual void SAL_CALL
elementRemoved( const css::container::ContainerEvent
& Event
) override
80 Event
.Accessor
>>= sName
;
81 if ( m_aRefNames
.find(sName
) != m_aRefNames
.end() )
82 m_pComponent
->refreshKeys();
84 virtual void SAL_CALL
elementReplaced( const css::container::ContainerEvent
& Event
) override
86 OUString sOldComposedName
,sNewComposedName
;
87 Event
.ReplacedElement
>>= sOldComposedName
;
88 Event
.Accessor
>>= sNewComposedName
;
89 if ( sOldComposedName
!= sNewComposedName
&& m_aRefNames
.find(sOldComposedName
) != m_aRefNames
.end() )
90 m_pComponent
->refreshKeys();
93 virtual void SAL_CALL
disposing( const EventObject
& /*_rSource*/ ) override
96 void clear() { m_pComponent
= nullptr; }
97 void add(const OUString
& _sRefName
) { m_aRefNames
.emplace(_sRefName
,true); }
100 namespace connectivity
102 static OUString
lcl_getServiceNameForSetting(const Reference
< css::sdbc::XConnection
>& _xConnection
,const OUString
& i_sSetting
)
104 OUString sSupportService
;
106 if ( ::dbtools::getDataSourceSetting(_xConnection
,i_sSetting
,aValue
) )
108 aValue
>>= sSupportService
;
110 return sSupportService
;
112 struct OTableHelperImpl
115 // helper services which can be provided by extensions
116 Reference
< css::sdb::tools::XTableRename
> m_xRename
;
117 Reference
< css::sdb::tools::XTableAlteration
> m_xAlter
;
118 Reference
< css::sdb::tools::XKeyAlteration
> m_xKeyAlter
;
119 Reference
< css::sdb::tools::XIndexAlteration
> m_xIndexAlter
;
121 Reference
< css::sdbc::XDatabaseMetaData
> m_xMetaData
;
122 Reference
< css::sdbc::XConnection
> m_xConnection
;
123 rtl::Reference
<OTableContainerListener
> m_xTablePropertyListener
;
124 std::vector
< ColumnDesc
> m_aColumnDesc
;
125 explicit OTableHelperImpl(const Reference
< css::sdbc::XConnection
>& _xConnection
)
126 : m_xConnection(_xConnection
)
130 m_xMetaData
= m_xConnection
->getMetaData();
131 Reference
<XMultiServiceFactory
> xFac(_xConnection
,UNO_QUERY
);
134 m_xRename
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"TableRenameServiceName")),UNO_QUERY
);
135 m_xAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"TableAlterationServiceName")),UNO_QUERY
);
136 m_xKeyAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"KeyAlterationServiceName")),UNO_QUERY
);
137 m_xIndexAlter
.set(xFac
->createInstance(lcl_getServiceNameForSetting(m_xConnection
,"IndexAlterationServiceName")),UNO_QUERY
);
140 catch(const Exception
&)
147 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
148 const Reference
< XConnection
>& _xConnection
,
150 :OTable_TYPEDEF(_pTables
,_bCase
)
151 ,m_pImpl(new OTableHelperImpl(_xConnection
))
155 OTableHelper::OTableHelper( sdbcx::OCollection
* _pTables
,
156 const Reference
< XConnection
>& _xConnection
,
158 const OUString
& Name
,
159 const OUString
& Type
,
160 const OUString
& Description
,
161 const OUString
& SchemaName
,
162 const OUString
& CatalogName
163 ) : OTable_TYPEDEF(_pTables
,
170 ,m_pImpl(new OTableHelperImpl(_xConnection
))
174 OTableHelper::~OTableHelper()
178 void SAL_CALL
OTableHelper::disposing()
180 ::osl::MutexGuard
aGuard(m_aMutex
);
181 if ( m_pImpl
->m_xTablePropertyListener
.is() )
183 m_pTables
->removeContainerListener(m_pImpl
->m_xTablePropertyListener
.get());
184 m_pImpl
->m_xTablePropertyListener
->clear();
185 m_pImpl
->m_xTablePropertyListener
.clear();
187 OTable_TYPEDEF::disposing();
189 m_pImpl
->m_xConnection
= nullptr;
190 m_pImpl
->m_xMetaData
= nullptr;
197 /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns
199 void lcl_collectColumnDescs_throw( const Reference
< XResultSet
>& _rxResult
, std::vector
< ColumnDesc
>& _out_rColumns
)
201 Reference
< XRow
> xRow( _rxResult
, UNO_QUERY_THROW
);
203 OrdinalPosition
nOrdinalPosition( 0 );
204 while ( _rxResult
->next() )
206 sName
= xRow
->getString( 4 ); // COLUMN_NAME
207 sal_Int32 nField5
= xRow
->getInt(5);
208 OUString aField6
= xRow
->getString(6);
209 sal_Int32 nField7
= xRow
->getInt(7)
210 , nField9
= xRow
->getInt(9)
211 , nField11
= xRow
->getInt(11);
212 OUString sField12
= xRow
->getString(12)
213 ,sField13
= xRow
->getString(13);
214 nOrdinalPosition
= xRow
->getInt( 17 ); // ORDINAL_POSITION
215 _out_rColumns
.push_back( ColumnDesc( sName
,nField5
,aField6
,nField7
,nField9
,nField11
,sField12
,sField13
, nOrdinalPosition
) );
219 /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not,
220 they will be normalized to be the array index.
222 void lcl_sanitizeColumnDescs( std::vector
< ColumnDesc
>& _rColumns
)
224 if ( _rColumns
.empty() )
227 // collect all used ordinals
228 std::set
< OrdinalPosition
> aUsedOrdinals
;
229 for ( const auto& collect
: _rColumns
)
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 ( auto& normalize
: _rColumns
)
245 normalize
.nOrdinalPosition
= nNormalizedPosition
++;
249 // what's left is that the range might not be from 1 to <column count>, but for instance
250 // 0 to <column count>-1.
251 size_t nOffset
= *aUsedOrdinals
.begin() - 1;
252 for ( auto& offset
: _rColumns
)
253 offset
.nOrdinalPosition
-= nOffset
;
258 void OTableHelper::refreshColumns()
260 ::std::vector
< OUString
> aVector
;
264 if ( !m_CatalogName
.isEmpty() )
265 aCatalog
<<= m_CatalogName
;
267 ::utl::SharedUNOComponent
< XResultSet
> xResult( getMetaData()->getColumns(
274 // collect the column names, together with their ordinal position
275 m_pImpl
->m_aColumnDesc
.clear();
276 lcl_collectColumnDescs_throw( xResult
, m_pImpl
->m_aColumnDesc
);
278 // ensure that the ordinal positions as obtained from the meta data do make sense
279 lcl_sanitizeColumnDescs( m_pImpl
->m_aColumnDesc
);
281 // sort by ordinal position
282 std::map
< OrdinalPosition
, OUString
> aSortedColumns
;
283 for (const auto& copy
: m_pImpl
->m_aColumnDesc
)
284 aSortedColumns
[ copy
.nOrdinalPosition
] = copy
.sName
;
286 // copy them to aVector, now that we have the proper ordering
288 aSortedColumns
.begin(),
289 aSortedColumns
.end(),
290 std::insert_iterator
< ::std::vector
< OUString
> >( aVector
, aVector
.begin() ),
291 ::o3tl::select2nd
< std::map
< OrdinalPosition
, OUString
>::value_type
>()
296 m_xColumns
->reFill(aVector
);
298 m_xColumns
= createColumns(aVector
);
301 const ColumnDesc
* OTableHelper::getColumnDescription(const OUString
& _sName
) const
303 const ColumnDesc
* pRet
= nullptr;
304 auto aIter
= std::find_if(m_pImpl
->m_aColumnDesc
.begin(), m_pImpl
->m_aColumnDesc
.end(),
305 [&_sName
](const ColumnDesc
& rColumnDesc
) { return rColumnDesc
.sName
== _sName
; });
306 if (aIter
!= m_pImpl
->m_aColumnDesc
.end())
311 void OTableHelper::refreshPrimaryKeys(::std::vector
< OUString
>& _rNames
)
314 if ( !m_CatalogName
.isEmpty() )
315 aCatalog
<<= m_CatalogName
;
316 Reference
< XResultSet
> xResult
= getMetaData()->getPrimaryKeys(aCatalog
,m_SchemaName
,m_Name
);
320 std::shared_ptr
<sdbcx::KeyProperties
> pKeyProps(new sdbcx::KeyProperties(OUString(),KeyType::PRIMARY
,0,0));
322 bool bAlreadyFetched
= false;
323 const Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
324 while ( xResult
->next() )
326 pKeyProps
->m_aKeyColumnNames
.push_back(xRow
->getString(4));
327 if ( !bAlreadyFetched
)
329 aPkName
= xRow
->getString(6);
330 SAL_WARN_IF(xRow
->wasNull(),"connectivity.commontools", "NULL Primary Key name");
331 SAL_WARN_IF(aPkName
.isEmpty(),"connectivity.commontools", "empty Primary Key name");
332 bAlreadyFetched
= true;
338 SAL_WARN_IF(aPkName
.isEmpty(),"connectivity.commontools", "empty Primary Key name");
339 SAL_WARN_IF(pKeyProps
->m_aKeyColumnNames
.empty(),"connectivity.commontools", "Primary Key has no columns");
340 m_pImpl
->m_aKeys
.emplace(aPkName
,pKeyProps
);
341 _rNames
.push_back(aPkName
);
343 } // if ( xResult.is() && xResult->next() )
344 ::comphelper::disposeComponent(xResult
);
347 void OTableHelper::refreshForeignKeys(::std::vector
< OUString
>& _rNames
)
350 if ( !m_CatalogName
.isEmpty() )
351 aCatalog
<<= m_CatalogName
;
352 Reference
< XResultSet
> xResult
= getMetaData()->getImportedKeys(aCatalog
,m_SchemaName
,m_Name
);
353 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
357 std::shared_ptr
<sdbcx::KeyProperties
> pKeyProps
;
358 OUString aName
,sCatalog
,aSchema
,sOldFKName
;
359 while( xResult
->next() )
361 // this must be outside the "if" because we have to call in a right order
362 sCatalog
= xRow
->getString(1);
363 if ( xRow
->wasNull() )
365 aSchema
= xRow
->getString(2);
366 aName
= xRow
->getString(3);
368 const OUString sForeignKeyColumn
= xRow
->getString(8);
369 const sal_Int32 nUpdateRule
= xRow
->getInt(10);
370 const sal_Int32 nDeleteRule
= xRow
->getInt(11);
371 const OUString sFkName
= xRow
->getString(12);
373 if ( !sFkName
.isEmpty() && !xRow
->wasNull() )
375 if ( sOldFKName
!= sFkName
)
377 if ( pKeyProps
.get() )
378 m_pImpl
->m_aKeys
.emplace(sOldFKName
,pKeyProps
);
380 const OUString sReferencedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,aSchema
,aName
,false,::dbtools::EComposeRule::InDataManipulation
);
381 pKeyProps
.reset(new sdbcx::KeyProperties(sReferencedName
,KeyType::FOREIGN
,nUpdateRule
,nDeleteRule
));
382 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
383 _rNames
.push_back(sFkName
);
384 if ( m_pTables
->hasByName(sReferencedName
) )
386 if ( !m_pImpl
->m_xTablePropertyListener
.is() )
387 m_pImpl
->m_xTablePropertyListener
= new OTableContainerListener(this);
388 m_pTables
->addContainerListener(m_pImpl
->m_xTablePropertyListener
.get());
389 m_pImpl
->m_xTablePropertyListener
->add(sReferencedName
);
390 } // if ( m_pTables->hasByName(sReferencedName) )
391 sOldFKName
= sFkName
;
392 } // if ( sOldFKName != sFkName )
393 else if ( pKeyProps
.get() )
395 pKeyProps
->m_aKeyColumnNames
.push_back(sForeignKeyColumn
);
398 } // while( xResult->next() )
399 if ( pKeyProps
.get() )
400 m_pImpl
->m_aKeys
.emplace(sOldFKName
,pKeyProps
);
401 ::comphelper::disposeComponent(xResult
);
405 void OTableHelper::refreshKeys()
407 m_pImpl
->m_aKeys
.clear();
409 ::std::vector
< OUString
> aNames
;
413 refreshPrimaryKeys(aNames
);
414 refreshForeignKeys(aNames
);
415 m_xKeys
= createKeys(aNames
);
418 m_xKeys
= createKeys(aNames
);
420 m_pKeys->reFill(aVector);
425 void OTableHelper::refreshIndexes()
427 ::std::vector
< OUString
> aVector
;
432 if ( !m_CatalogName
.isEmpty() )
433 aCatalog
<<= m_CatalogName
;
434 Reference
< XResultSet
> xResult
= getMetaData()->getIndexInfo(aCatalog
,m_SchemaName
,m_Name
,false,false);
438 Reference
< XRow
> xRow(xResult
,UNO_QUERY
);
439 OUString sCatalogSep
= getMetaData()->getCatalogSeparator();
440 OUString sPreviousRoundName
;
441 while( xResult
->next() )
443 OUString aName
= xRow
->getString(5);
445 aName
+= sCatalogSep
;
446 aName
+= xRow
->getString(6);
447 if ( !aName
.isEmpty() )
449 // don't insert the name if the last one we inserted was the same
450 if (sPreviousRoundName
!= aName
)
451 aVector
.push_back(aName
);
453 sPreviousRoundName
= aName
;
455 ::comphelper::disposeComponent(xResult
);
460 m_xIndexes
->reFill(aVector
);
462 m_xIndexes
= createIndexes(aVector
);
465 OUString
OTableHelper::getRenameStart() const
467 OUString
sSql("RENAME ");
468 if ( m_Type
== "VIEW" )
477 void SAL_CALL
OTableHelper::rename( const OUString
& newName
)
479 ::osl::MutexGuard
aGuard(m_aMutex
);
482 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
490 if ( m_pImpl
->m_xRename
.is() )
492 m_pImpl
->m_xRename
->rename(this,newName
);
496 OUString sSql
= getRenameStart();
498 OUString sCatalog
,sSchema
,sTable
;
499 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,sCatalog
,sSchema
,sTable
,::dbtools::EComposeRule::InDataManipulation
);
501 OUString sComposedName
;
502 sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,true,::dbtools::EComposeRule::InDataManipulation
);
503 sSql
+= sComposedName
505 sComposedName
= ::dbtools::composeTableName(getMetaData(),sCatalog
,sSchema
,sTable
,true,::dbtools::EComposeRule::InDataManipulation
);
506 sSql
+= sComposedName
;
508 Reference
< XStatement
> xStmt
= m_pImpl
->m_xConnection
->createStatement( );
511 xStmt
->execute(sSql
);
512 ::comphelper::disposeComponent(xStmt
);
516 OTable_TYPEDEF::rename(newName
);
519 ::dbtools::qualifiedNameComponents(getMetaData(),newName
,m_CatalogName
,m_SchemaName
,m_Name
,::dbtools::EComposeRule::InTableDefinitions
);
522 Reference
< XDatabaseMetaData
> OTableHelper::getMetaData() const
524 return m_pImpl
->m_xMetaData
;
527 void SAL_CALL
OTableHelper::alterColumnByIndex( sal_Int32 index
, const Reference
< XPropertySet
>& descriptor
)
529 ::osl::MutexGuard
aGuard(m_aMutex
);
532 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper
.bDisposed
538 Reference
< XPropertySet
> xOld(
539 m_xColumns
->getByIndex(index
), css::uno::UNO_QUERY
);
541 alterColumnByName(getString(xOld
->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME
))),descriptor
);
545 OUString SAL_CALL
OTableHelper::getName()
547 OUString sComposedName
= ::dbtools::composeTableName(getMetaData(),m_CatalogName
,m_SchemaName
,m_Name
,false,::dbtools::EComposeRule::InDataManipulation
);
548 return sComposedName
;
551 std::shared_ptr
<sdbcx::KeyProperties
> OTableHelper::getKeyProperties(const OUString
& _sName
) const
553 std::shared_ptr
<sdbcx::KeyProperties
> pKeyProps
;
554 TKeyMap::const_iterator aFind
= m_pImpl
->m_aKeys
.find(_sName
);
555 if ( aFind
!= m_pImpl
->m_aKeys
.end() )
557 pKeyProps
= aFind
->second
;
559 else // only a fall back
561 OSL_FAIL("No key with the given name found");
562 pKeyProps
.reset(new sdbcx::KeyProperties());
568 void OTableHelper::addKey(const OUString
& _sName
,const std::shared_ptr
<sdbcx::KeyProperties
>& _aKeyProperties
)
570 m_pImpl
->m_aKeys
.emplace(_sName
,_aKeyProperties
);
573 OUString
OTableHelper::getTypeCreatePattern() const
578 Reference
< XConnection
> const & OTableHelper::getConnection() const
580 return m_pImpl
->m_xConnection
;
583 Reference
< css::sdb::tools::XTableRename
> const & OTableHelper::getRenameService() const
585 return m_pImpl
->m_xRename
;
588 Reference
< css::sdb::tools::XTableAlteration
> const & OTableHelper::getAlterService() const
590 return m_pImpl
->m_xAlter
;
593 Reference
< css::sdb::tools::XKeyAlteration
> const & OTableHelper::getKeyService() const
595 return m_pImpl
->m_xKeyAlter
;
598 Reference
< css::sdb::tools::XIndexAlteration
> const & OTableHelper::getIndexService() const
600 return m_pImpl
->m_xIndexAlter
;
604 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */