bump product version to 6.3.0.0.beta1
[LibreOffice.git] / connectivity / source / commontools / TTableHelper.cxx
blobf9e91f18076309f5d14e36c651e2e76de8f424ef
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>
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>
46 #include <iterator>
47 #include <set>
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;
58 namespace
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;
67 protected:
68 virtual ~OTableContainerListener() override {}
69 public:
70 explicit OTableContainerListener(OTableHelper* _pComponent) : m_pComponent(_pComponent){}
71 // noncopyable
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
79 OUString sName;
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();
92 // XEventListener
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;
105 Any aValue;
106 if ( ::dbtools::getDataSourceSetting(_xConnection,i_sSetting,aValue) )
108 aValue >>= sSupportService;
110 return sSupportService;
112 struct OTableHelperImpl
114 TKeyMap m_aKeys;
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);
132 if ( xFac.is() )
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,
149 bool _bCase)
150 :OTable_TYPEDEF(_pTables,_bCase)
151 ,m_pImpl(new OTableHelperImpl(_xConnection))
155 OTableHelper::OTableHelper( sdbcx::OCollection* _pTables,
156 const Reference< XConnection >& _xConnection,
157 bool _bCase,
158 const OUString& Name,
159 const OUString& Type,
160 const OUString& Description ,
161 const OUString& SchemaName,
162 const OUString& CatalogName
163 ) : OTable_TYPEDEF(_pTables,
164 _bCase,
165 Name,
166 Type,
167 Description,
168 SchemaName,
169 CatalogName)
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;
195 namespace
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 );
202 OUString sName;
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() )
225 return;
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++;
246 return;
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;
261 if(!isNew())
263 Any aCatalog;
264 if ( !m_CatalogName.isEmpty() )
265 aCatalog <<= m_CatalogName;
267 ::utl::SharedUNOComponent< XResultSet > xResult( getMetaData()->getColumns(
268 aCatalog,
269 m_SchemaName,
270 m_Name,
272 ) );
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
287 std::transform(
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 >()
295 if(m_xColumns)
296 m_xColumns->reFill(aVector);
297 else
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())
307 pRet = &*aIter;
308 return pRet;
311 void OTableHelper::refreshPrimaryKeys(::std::vector< OUString>& _rNames)
313 Any aCatalog;
314 if ( !m_CatalogName.isEmpty() )
315 aCatalog <<= m_CatalogName;
316 Reference< XResultSet > xResult = getMetaData()->getPrimaryKeys(aCatalog,m_SchemaName,m_Name);
318 if ( xResult.is() )
320 std::shared_ptr<sdbcx::KeyProperties> pKeyProps(new sdbcx::KeyProperties(OUString(),KeyType::PRIMARY,0,0));
321 OUString aPkName;
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;
336 if(bAlreadyFetched)
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)
349 Any aCatalog;
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);
355 if ( xRow.is() )
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() )
364 sCatalog.clear();
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;
411 if(!isNew())
413 refreshPrimaryKeys(aNames);
414 refreshForeignKeys(aNames);
415 m_xKeys = createKeys(aNames);
416 } // if(!isNew())
417 else if (!m_xKeys )
418 m_xKeys = createKeys(aNames);
419 /*if(m_pKeys)
420 m_pKeys->reFill(aVector);
421 else*/
425 void OTableHelper::refreshIndexes()
427 ::std::vector< OUString> aVector;
428 if(!isNew())
430 // fill indexes
431 Any aCatalog;
432 if ( !m_CatalogName.isEmpty() )
433 aCatalog <<= m_CatalogName;
434 Reference< XResultSet > xResult = getMetaData()->getIndexInfo(aCatalog,m_SchemaName,m_Name,false,false);
436 if(xResult.is())
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);
444 if(!aName.isEmpty())
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);
459 if(m_xIndexes)
460 m_xIndexes->reFill(aVector);
461 else
462 m_xIndexes = createIndexes(aVector);
465 OUString OTableHelper::getRenameStart() const
467 OUString sSql("RENAME ");
468 if ( m_Type == "VIEW" )
469 sSql += " VIEW ";
470 else
471 sSql += " TABLE ";
473 return sSql;
476 // XRename
477 void SAL_CALL OTableHelper::rename( const OUString& newName )
479 ::osl::MutexGuard aGuard(m_aMutex);
480 checkDisposed(
481 #ifdef __GNUC__
482 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
483 #else
484 rBHelper.bDisposed
485 #endif
488 if(!isNew())
490 if ( m_pImpl->m_xRename.is() )
492 m_pImpl->m_xRename->rename(this,newName);
494 else
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
504 + " TO ";
505 sComposedName = ::dbtools::composeTableName(getMetaData(),sCatalog,sSchema,sTable,true,::dbtools::EComposeRule::InDataManipulation);
506 sSql += sComposedName;
508 Reference< XStatement > xStmt = m_pImpl->m_xConnection->createStatement( );
509 if ( xStmt.is() )
511 xStmt->execute(sSql);
512 ::comphelper::disposeComponent(xStmt);
516 OTable_TYPEDEF::rename(newName);
518 else
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);
530 checkDisposed(
531 #ifdef __GNUC__
532 ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed
533 #else
534 rBHelper.bDisposed
535 #endif
538 Reference< XPropertySet > xOld(
539 m_xColumns->getByIndex(index), css::uno::UNO_QUERY);
540 if(xOld.is())
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());
565 return pKeyProps;
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
575 return OUString();
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: */