LibreOffice Module dbaccess (master) 1
RelationController.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <map>
23
24#include <strings.hrc>
25#include <strings.hxx>
26#include <vcl/svapp.hxx>
27#include <vcl/weld.hxx>
28#include <browserids.hxx>
29#include <comphelper/types.hxx>
30#include <core_resource.hxx>
32#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
33#include <com/sun/star/sdbcx/KeyType.hpp>
34#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
35#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
36#include <com/sun/star/sdbc/SQLException.hpp>
37#include <com/sun/star/sdbc/XRow.hpp>
40#include <sqlmessage.hxx>
42#include <TableWindowData.hxx>
43#include <UITools.hxx>
45#include <RelationTableView.hxx>
48#include <osl/thread.hxx>
49#include <osl/mutex.hxx>
50
51#define MAX_THREADS 10
52
53extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
55 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
56{
57 return cppu::acquire(new ::dbaui::ORelationController(context));
58}
59
60using namespace ::com::sun::star::uno;
61using namespace ::com::sun::star::io;
62using namespace ::com::sun::star::beans;
63using namespace ::com::sun::star::frame;
64using namespace ::com::sun::star::lang;
65using namespace ::com::sun::star::container;
66using namespace ::com::sun::star::sdbcx;
67using namespace ::com::sun::star::sdbc;
68using namespace ::com::sun::star::sdb;
69using namespace ::com::sun::star::ui::dialogs;
70using namespace ::com::sun::star::util;
71using namespace ::dbtools;
72using namespace ::dbaui;
73using namespace ::comphelper;
74using namespace ::osl;
75
76OUString SAL_CALL ORelationController::getImplementationName()
77{
78 return "org.openoffice.comp.dbu.ORelationDesign";
79}
80
81Sequence< OUString> SAL_CALL ORelationController::getSupportedServiceNames()
82{
83 return { "com.sun.star.sdb.RelationDesign" };
84}
85
86ORelationController::ORelationController(const Reference< XComponentContext >& _rM)
87 : OJoinController(_rM)
88 ,m_nThreadEvent(0)
89 ,m_bRelationsPossible(true)
90{
91 InvalidateAll();
92}
93
95{
96}
97
99{
100 FeatureState aReturn;
102 switch (_nId)
103 {
104 case SID_RELATION_ADD_RELATION:
105 aReturn.bEnabled = !m_vTableData.empty() && isConnected() && isEditable();
106 aReturn.bChecked = false;
107 break;
109 aReturn.bEnabled = haveDataSource() && impl_isModified();
110 break;
111 default:
112 aReturn = OJoinController::GetState(_nId);
113 break;
114 }
115 return aReturn;
116}
117
119{
120 switch(_nId)
121 {
123 {
124 OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!");
125 if(!::dbaui::checkDataSourceAvailable(::comphelper::getString(getDataSource()->getPropertyValue(PROPERTY_NAME)), getORB()))
126 {
127 OUString aMessage(DBA_RES(STR_DATASOURCE_DELETED));
128 OSQLWarningBox aWarning(getFrameWeld(), aMessage);
129 aWarning.run();
130 }
131 else
132 {
133 // now we save the layout information
134 // create the output stream
135 try
136 {
137 if ( haveDataSource() && getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) )
138 {
140 saveTableWindows( aWindowsData );
141 getDataSource()->setPropertyValue( PROPERTY_LAYOUTINFORMATION, Any( aWindowsData.getPropertyValues() ) );
142 setModified(false);
143 }
144 }
145 catch ( const Exception& )
146 {
147 DBG_UNHANDLED_EXCEPTION("dbaccess");
148 }
149 }
150 }
151 break;
152 case SID_RELATION_ADD_RELATION:
153 static_cast<ORelationTableView*>(static_cast<ORelationDesignView*>( getView() )->getTableView())->AddNewRelation();
154 break;
155 default:
156 OJoinController::Execute(_nId,aArgs);
157 return;
158 }
159 InvalidateFeature(_nId);
160}
161
163{
164 OJoinController::impl_initialize();
165
166 if( !getSdbMetaData().supportsRelations() )
167 {// check if this database supports relations
168
169 setEditable(false);
170 m_bRelationsPossible = false;
171 {
172 OUString sTitle(DBA_RES(STR_RELATIONDESIGN));
173 sTitle = sTitle.copy(3);
174 OSQLMessageBox aDlg(getFrameWeld(), sTitle, DBA_RES(STR_RELATIONDESIGN_NOT_AVAILABLE));
175 aDlg.run();
176 }
177 disconnect();
178 throw SQLException();
179 }
180
182 InvalidateAll();
183
184 // we need a datasource
185 OSL_ENSURE(haveDataSource(),"ORelationController::initialize: need a datasource!");
186
188 OSL_ENSURE(xSup.is(),"Connection isn't a XTablesSupplier!");
189 if(xSup.is())
190 m_xTables = xSup->getTables();
191 // load the layoutInformation
193 try
194 {
195 loadData();
196 if ( !m_nThreadEvent )
197 Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished));
198 }
199 catch( const Exception& )
200 {
201 DBG_UNHANDLED_EXCEPTION("dbaccess");
202 }
203
204}
205
207{
208 OUString sName = getDataSourceName();
210}
211
213{
214 setView( VclPtr<ORelationDesignView>::Create( pParent, *this, getORB() ) );
215 OJoinController::Construct(pParent);
216 return true;
217}
218
220{
221 short nSaved = RET_YES;
222 if(haveDataSource() && isModified())
223 {
224 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/designsavemodifieddialog.ui"));
225 std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("DesignSaveModifiedDialog"));
226 nSaved = xQuery->run();
227 if(nSaved == RET_YES)
229 }
230 return nSaved;
231}
232
234{
236 implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION, CommandGroup::EDIT );
237}
238
239namespace
240{
241 class RelationLoader : public ::osl::Thread
242 {
243 typedef std::map<OUString, std::shared_ptr<OTableWindowData>, ::comphelper::UStringMixLess> TTableDataHelper;
244 TTableDataHelper m_aTableData;
245 TTableConnectionData m_vTableConnectionData;
247 ORelationController* m_pParent;
248 const Reference< XDatabaseMetaData> m_xMetaData;
249 const Reference< XNameAccess > m_xTables;
250 const sal_Int32 m_nStartIndex;
251 const sal_Int32 m_nEndIndex;
252
253 public:
254 RelationLoader(ORelationController* _pParent
255 ,const Reference< XDatabaseMetaData>& _xMetaData
256 ,const Reference< XNameAccess >& _xTables
257 ,const Sequence< OUString>& _aTableList
258 ,const sal_Int32 _nStartIndex
259 ,const sal_Int32 _nEndIndex)
260 :m_aTableData(_xMetaData.is() && _xMetaData->supportsMixedCaseQuotedIdentifiers())
261 ,m_aTableList(_aTableList)
262 ,m_pParent(_pParent)
263 ,m_xMetaData(_xMetaData)
264 ,m_xTables(_xTables)
265 ,m_nStartIndex(_nStartIndex)
266 ,m_nEndIndex(_nEndIndex)
267 {
268 }
269
271 virtual void SAL_CALL run() override;
272 virtual void SAL_CALL onTerminated() override;
273 protected:
274 virtual ~RelationLoader() override {}
275
276 void loadTableData(const Any& _aTable);
277 };
278
279 void SAL_CALL RelationLoader::run()
280 {
281 osl_setThreadName("RelationLoader");
282
283 for(sal_Int32 i = m_nStartIndex; i < m_nEndIndex; ++i)
284 {
285 OUString sCatalog,sSchema,sTable;
286 ::dbtools::qualifiedNameComponents(m_xMetaData,
287 m_aTableList[i],
288 sCatalog,
289 sSchema,
290 sTable,
291 ::dbtools::EComposeRule::InDataManipulation);
292 Any aCatalog;
293 if ( !sCatalog.isEmpty() )
294 aCatalog <<= sCatalog;
295
296 try
297 {
298 Reference< XResultSet > xResult = m_xMetaData->getImportedKeys(aCatalog, sSchema,sTable);
299 if ( xResult.is() && xResult->next() )
300 {
301 ::comphelper::disposeComponent(xResult);
302 loadTableData(m_xTables->getByName(m_aTableList[i]));
303 }
304 }
305 catch( const Exception& )
306 {
307 DBG_UNHANDLED_EXCEPTION("dbaccess");
308 }
309 }
310 }
311 void SAL_CALL RelationLoader::onTerminated()
312 {
313 m_pParent->mergeData(m_vTableConnectionData);
314 delete this;
315 }
316
317 void RelationLoader::loadTableData(const Any& _aTable)
318 {
319 Reference<XPropertySet> xTableProp(_aTable,UNO_QUERY);
320 const OUString sSourceName = ::dbtools::composeTableName( m_xMetaData, xTableProp, ::dbtools::EComposeRule::InTableDefinitions, false );
321 TTableDataHelper::const_iterator aFind = m_aTableData.find(sSourceName);
322 if ( aFind == m_aTableData.end() )
323 {
324 aFind = m_aTableData.emplace(sSourceName,std::make_shared<OTableWindowData>(xTableProp,sSourceName, sSourceName, OUString())).first;
325 aFind->second->ShowAll(false);
326 }
327 TTableWindowData::value_type pReferencingTable = aFind->second;
328 Reference<XIndexAccess> xKeys = pReferencingTable->getKeys();
329 const Reference<XKeysSupplier> xKeySup(xTableProp,UNO_QUERY);
330
331 if ( !xKeys.is() && xKeySup.is() )
332 {
333 xKeys = xKeySup->getKeys();
334 }
335
336 if ( !xKeys.is() )
337 return;
338
339 Reference<XPropertySet> xKey;
340 const sal_Int32 nCount = xKeys->getCount();
341 for(sal_Int32 i = 0 ; i < nCount ; ++i)
342 {
343 xKeys->getByIndex(i) >>= xKey;
344 sal_Int32 nKeyType = 0;
345 xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
346 if ( KeyType::FOREIGN == nKeyType )
347 {
348 OUString sReferencedTable;
349 xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
350
351 // insert windows
352 TTableDataHelper::const_iterator aRefFind = m_aTableData.find(sReferencedTable);
353 if ( aRefFind == m_aTableData.end() )
354 {
355 if ( m_xTables->hasByName(sReferencedTable) )
356 {
357 Reference<XPropertySet> xReferencedTable(m_xTables->getByName(sReferencedTable),UNO_QUERY);
358 aRefFind = m_aTableData.emplace(sReferencedTable,std::make_shared<OTableWindowData>(xReferencedTable,sReferencedTable, sReferencedTable, OUString())).first;
359 aRefFind->second->ShowAll(false);
360 }
361 else
362 continue; // table name could not be found so we do not show this table relation
363 }
364 TTableWindowData::value_type pReferencedTable = aRefFind->second;
365
366 OUString sKeyName;
367 xKey->getPropertyValue(PROPERTY_NAME) >>= sKeyName;
368 // insert connection
369 auto xTabConnData = std::make_shared<ORelationTableConnectionData>( pReferencingTable, pReferencedTable, sKeyName );
370 m_vTableConnectionData.push_back(xTabConnData);
371 // insert columns
372 const Reference<XColumnsSupplier> xColsSup(xKey,UNO_QUERY);
373 OSL_ENSURE(xColsSup.is(),"Key is no XColumnsSupplier!");
374 const Reference<XNameAccess> xColumns = xColsSup->getColumns();
375 const Sequence< OUString> aNames = xColumns->getElementNames();
376 OUString sColumnName,sRelatedName;
377 for(sal_Int32 j=0;j<aNames.getLength();++j)
378 {
379 const Reference<XPropertySet> xPropSet(xColumns->getByName(aNames[j]),UNO_QUERY);
380 OSL_ENSURE(xPropSet.is(),"Invalid column found in KeyColumns!");
381 if ( xPropSet.is() )
382 {
383 xPropSet->getPropertyValue(PROPERTY_NAME) >>= sColumnName;
384 xPropSet->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedName;
385 }
386 xTabConnData->SetConnLine( j, sColumnName, sRelatedName );
387 }
388 // set update/del flags
389 sal_Int32 nUpdateRule = 0;
390 sal_Int32 nDeleteRule = 0;
391 xKey->getPropertyValue(PROPERTY_UPDATERULE) >>= nUpdateRule;
392 xKey->getPropertyValue(PROPERTY_DELETERULE) >>= nDeleteRule;
393
394 xTabConnData->SetUpdateRules( nUpdateRule );
395 xTabConnData->SetDeleteRules( nDeleteRule );
396
397 // set cardinality
398 xTabConnData->SetCardinality();
399 }
400 }
401 }
402}
403
405{
406 ::osl::MutexGuard aGuard( getMutex() );
407
408 m_vTableConnectionData.insert( m_vTableConnectionData.end(), _aConnectionData.begin(), _aConnectionData.end() );
409 // here we are finished, so we can collect the table from connection data
410 for (auto const& elem : m_vTableConnectionData)
411 {
412 if ( !existsTable(elem->getReferencingTable()->GetComposedName()) )
413 {
414 m_vTableData.push_back(elem->getReferencingTable());
415 }
416 if ( !existsTable(elem->getReferencedTable()->GetComposedName()) )
417 {
418 m_vTableData.push_back(elem->getReferencedTable());
419 }
420 }
421 if ( m_nThreadEvent )
422 {
424 if ( !m_nThreadEvent )
425 Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished));
426 }
427}
428
429IMPL_LINK_NOARG( ORelationController, OnThreadFinished, void*, void )
430{
431 ::SolarMutexGuard aSolarGuard;
432 ::osl::MutexGuard aGuard( getMutex() );
433 try
434 {
435 getView()->initialize(); // show the windows and fill with our information
436 getView()->Invalidate(InvalidateFlags::NoErase);
437 ClearUndoManager();
438 setModified(false); // and we are not modified yet
439
440 if(m_vTableData.empty())
441 Execute(ID_BROWSER_ADDTABLE,Sequence<PropertyValue>());
442 }
443 catch( const Exception& )
444 {
445 DBG_UNHANDLED_EXCEPTION("dbaccess");
446 }
447 m_xWaitObject.reset();
448}
449
451{
452 m_xWaitObject.reset(new weld::WaitObject(getFrameWeld()));
453 try
454 {
455 if ( !m_xTables.is() )
456 return;
458 // this may take some time
459 const Reference< XDatabaseMetaData> xMetaData = getConnection()->getMetaData();
460 const Sequence< OUString> aNames = m_xTables->getElementNames();
461 const sal_Int32 nCount = aNames.getLength();
462 if ( aMeta.supportsThreads() )
463 {
464 const sal_Int32 nMaxElements = (nCount / MAX_THREADS) +1;
465 sal_Int32 nStart = 0,nEnd = std::min(nMaxElements,nCount);
466 while(nStart != nEnd)
467 {
469 RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,nStart,nEnd);
470 pThread->createSuspended();
471 pThread->setPriority(osl_Thread_PriorityBelowNormal);
472 pThread->resume();
473 nStart = nEnd;
474 nEnd += nMaxElements;
475 nEnd = std::min(nEnd,nCount);
476 }
477 }
478 else
479 {
480 RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,0,nCount);
481 pThread->run();
482 pThread->onTerminated();
483 }
484 }
485 catch(SQLException& e)
486 {
488 }
489 catch(const Exception&)
490 {
491 DBG_UNHANDLED_EXCEPTION("dbaccess");
492 }
493}
494
495TTableWindowData::value_type ORelationController::existsTable(std::u16string_view _rComposedTableName) const
496{
498 for (auto const& elem : m_vTableData)
499 {
500 if(bCase(elem->GetComposedName(),_rComposedTableName))
501 return elem;
502 }
503 return TTableWindowData::value_type();
504}
505
507{
508 try
509 {
510 OSL_ENSURE(haveDataSource(),"We need a datasource from our connection!");
511 if ( haveDataSource() )
512 {
513 if ( getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) )
514 {
516 getDataSource()->getPropertyValue(PROPERTY_LAYOUTINFORMATION) >>= aWindows;
517 loadTableWindows(aWindows);
518 }
519 }
520 }
521 catch(Exception&)
522 {
523 }
524}
525
527{
529 ODataView* pView = getView();
530 OSL_ENSURE(pView,"No current view!");
531 if(pView)
532 {
533 pView->initialize();
534 pView->Invalidate(InvalidateFlags::NoErase);
535 }
536}
537
539{
540 return false;
541}
542
544{
545 return false;
546}
547
548/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sSchema
OptionalString sCatalog
OptionalString sName
std::array< RawFontData, NUM_TAGS > m_aTableList
#define MAX_THREADS
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * org_openoffice_comp_dbu_ORelationDesign_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
#define ID_BROWSER_ADDTABLE
Definition: browserids.hxx:53
#define ID_BROWSER_SAVEDOC
Definition: browserids.hxx:32
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
static std::unique_ptr< weld::Builder > CreateBuilder(weld::Widget *pParent, const OUString &rUIFile, bool bMobile=false, sal_uInt64 nLOKWindowId=0)
css::uno::Sequence< css::beans::PropertyValue > getPropertyValues() const
virtual void initialize()
Definition: dataview.hxx:65
void loadTableWindows(const ::comphelper::NamedValueCollection &i_rViewSettings)
loads the information for the windows.
virtual void Execute(sal_uInt16 nId, const css::uno::Sequence< css::beans::PropertyValue > &aArgs) override
void saveTableWindows(::comphelper::NamedValueCollection &o_rViewSettings) const
saves the TableWindows structure in a sequence of property values
virtual FeatureState GetState(sal_uInt16 nId) const override
virtual void describeSupportedFeatures() override
TTableConnectionData m_vTableConnectionData
TTableWindowData m_vTableData
virtual bool Construct(vcl::Window *pParent) override
void mergeData(const TTableConnectionData &_aConnectionData)
virtual bool allowQueries() const override
determines whether or not it's allowed for queries to participate in the game
virtual void reset() override
virtual void describeSupportedFeatures() override
css::uno::Reference< css::container::XNameAccess > m_xTables
virtual bool allowViews() const override
determines whether or not it's allowed for database views to participate in the game
virtual void impl_initialize() override
std::unique_ptr< weld::WaitObject > m_xWaitObject
virtual ~ORelationController() override
TTableWindowData::value_type existsTable(std::u16string_view _rComposedTableName) const
virtual short saveModified() override
virtual FeatureState GetState(sal_uInt16 nId) const override
virtual OUString getPrivateTitle() const override
virtual void Execute(sal_uInt16 nId, const css::uno::Sequence< css::beans::PropertyValue > &aArgs) override
bool supportsThreads() const
void Invalidate(InvalidateFlags nFlags=InvalidateFlags::NONE)
virtual short run()
#define DBA_RES(id)
int nCount
#define DBG_UNHANDLED_EXCEPTION(...)
def run(arg=None, arg2=-1)
@ Exception
css::uno::Reference< css::uno::XInterface > getDataSource(const css::uno::Reference< css::uno::XInterface > &_rxDependentObject)
IMPL_LINK_NOARG(OApplicationController, OnClipboardChanged, TransferableDataHelper *, void)
std::vector< std::shared_ptr< OTableConnectionData > > TTableConnectionData
bool checkDataSourceAvailable(const OUString &_sDataSourceName, const css::uno::Reference< css::uno::XComponentContext > &_rxContext)
checks if the given name exists in the database context
OUString getStrippedDatabaseName(const css::uno::Reference< css::beans::XPropertySet > &_xDataSource, OUString &_rsDatabaseName)
returns the stripped database name.
Reference< XConnection > getConnection(const Reference< XRowSet > &_rxRowSet)
void showError(const SQLExceptionInfo &_rInfo, const Reference< XWindow > &_xParent, const Reference< XComponentContext > &_rxContext)
::osl::Mutex & getMutex()
int i
SwView * getView(const uno::Reference< frame::XModel > &xModel)
bool getPropertyValue(ValueType &rValue, css::uno::Reference< css::beans::XPropertySet > const &xPropSet, OUString const &propName)
const ::avmedia::MediaItem * Execute(const SdrMarkView *pSdrView, SfxRequest const &rReq)
constexpr OUStringLiteral PROPERTY_UPDATERULE(u"UpdateRule")
constexpr OUStringLiteral PROPERTY_DELETERULE(u"DeleteRule")
constexpr OUStringLiteral PROPERTY_RELATEDCOLUMN(u"RelatedColumn")
constexpr OUStringLiteral PROPERTY_TYPE(u"Type")
constexpr OUStringLiteral PROPERTY_REFERENCEDTABLE(u"ReferencedTable")
constexpr OUStringLiteral PROPERTY_NAME(u"Name")
constexpr OUStringLiteral PROPERTY_LAYOUTINFORMATION(u"LayoutInformation")
describes the state of a feature
std::optional< bool > bChecked
RET_YES