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 <FieldDescriptions.hxx>
21 #include "TEditControl.hxx"
22 #include <TableController.hxx>
23 #include <TableDesignView.hxx>
24 #include <TableRow.hxx>
25 #include <TypeInfo.hxx>
26 #include <UITools.hxx>
27 #include <browserids.hxx>
28 #include <core_resource.hxx>
29 #include <strings.hrc>
30 #include <strings.hxx>
31 #include <defaultobjectnamecheck.hxx>
32 #include <dlgsave.hxx>
33 #include <indexdialog.hxx>
34 #include <sqlmessage.hxx>
35
36 #include <com/sun/star/frame/XTitleChangeListener.hpp>
37 #include <com/sun/star/sdb/CommandType.hpp>
38 #include <com/sun/star/sdb/SQLContext.hpp>
39 #include <com/sun/star/sdbc/ColumnValue.hpp>
40 #include <com/sun/star/sdbc/SQLWarning.hpp>
41 #include <com/sun/star/sdbcx/KeyType.hpp>
42 #include <com/sun/star/sdbcx/XAlterTable.hpp>
43 #include <com/sun/star/sdbcx/XAppend.hpp>
44 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
45 #include <com/sun/star/sdbcx/XDrop.hpp>
46 #include <com/sun/star/sdbcx/XIndexesSupplier.hpp>
47 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
48
49 #include <connectivity/dbexception.hxx>
50 #include <connectivity/dbtools.hxx>
51 #include <connectivity/dbmetadata.hxx>
52 #include <cppuhelper/exc_hlp.hxx>
53 #include <comphelper/diagnose_ex.hxx>
54 #include <vcl/svapp.hxx>
55 #include <vcl/weld.hxx>
56 #include <o3tl/string_view.hxx>
57
58 #include <algorithm>
59 #include <functional>
60 #include <set>
61
62 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OTableDesign_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)63 org_openoffice_comp_dbu_OTableDesign_get_implementation(
64 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
65 {
66 return cppu::acquire(new ::dbaui::OTableController(context));
67 }
68
69 using namespace ::com::sun::star;
70 using namespace ::com::sun::star::uno;
71 using namespace ::com::sun::star::beans;
72 using namespace ::com::sun::star::frame;
73 using namespace ::com::sun::star::lang;
74 using namespace ::com::sun::star::container;
75 using namespace ::com::sun::star::sdbcx;
76 using namespace ::com::sun::star::sdbc;
77 using namespace ::com::sun::star::sdb;
78 using namespace ::com::sun::star::util;
79 using namespace ::dbtools;
80 using namespace ::dbaui;
81 using namespace ::comphelper;
82
83 // number of columns when creating it from scratch
84 #define NEWCOLS 128
85
86 namespace
87 {
dropTable(const Reference<XNameAccess> & _rxTable,const OUString & _sTableName)88 void dropTable(const Reference<XNameAccess>& _rxTable,const OUString& _sTableName)
89 {
90 if ( _rxTable->hasByName(_sTableName) )
91 {
92 Reference<XDrop> xNameCont(_rxTable,UNO_QUERY);
93 OSL_ENSURE(xNameCont.is(),"No drop interface for tables!");
94 if ( xNameCont.is() )
95 xNameCont->dropByName(_sTableName);
96 }
97 }
98 }
99
getImplementationName()100 OUString SAL_CALL OTableController::getImplementationName()
101 {
102 return u"org.openoffice.comp.dbu.OTableDesign"_ustr;
103 }
104
getSupportedServiceNames()105 Sequence< OUString> OTableController::getSupportedServiceNames()
106 {
107 return { u"com.sun.star.sdb.TableDesign"_ustr };
108 }
109
OTableController(const Reference<XComponentContext> & _rM)110 OTableController::OTableController(const Reference< XComponentContext >& _rM) : OTableController_BASE(_rM)
111 ,m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
112 ,m_bAllowAutoIncrementValue(false)
113 ,m_bNew(true)
114 {
115
116 InvalidateAll();
117 m_pTypeInfo = std::make_shared<OTypeInfo>();
118 m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';');
119 }
120
~OTableController()121 OTableController::~OTableController()
122 {
123 m_aTypeInfoIndex.clear();
124 m_aTypeInfo.clear();
125
126 }
127
startTableListening()128 void OTableController::startTableListening()
129 {
130 Reference< XComponent > xComponent(m_xTable, UNO_QUERY);
131 if (xComponent.is())
132 xComponent->addEventListener(static_cast<XModifyListener*>(this));
133 }
134
stopTableListening()135 void OTableController::stopTableListening()
136 {
137 Reference< XComponent > xComponent(m_xTable, UNO_QUERY);
138 if (xComponent.is())
139 xComponent->removeEventListener(static_cast<XModifyListener*>(this));
140 }
141
disposing()142 void OTableController::disposing()
143 {
144 OTableController_BASE::disposing();
145 clearView();
146
147 m_vRowList.clear();
148 }
149
GetState(sal_uInt16 _nId) const150 FeatureState OTableController::GetState(sal_uInt16 _nId) const
151 {
152 FeatureState aReturn;
153 // disabled automatically
154
155 switch (_nId)
156 {
157 case ID_BROWSER_CLOSE:
158 aReturn.bEnabled = true;
159 break;
160 case ID_BROWSER_EDITDOC:
161 aReturn.bChecked = isEditable();
162 aReturn.bEnabled = true;
163 break;
164 case ID_BROWSER_SAVEDOC:
165 aReturn.bEnabled = isEditable() && std::any_of(m_vRowList.begin(),m_vRowList.end(),std::mem_fn(&OTableRow::isValid));
166 break;
167 case ID_BROWSER_SAVEASDOC:
168 aReturn.bEnabled = isConnected() && isEditable();
169 if ( aReturn.bEnabled )
170 {
171 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
172 std::mem_fn(&OTableRow::isValid));
173 }
174 break;
175
176 case ID_BROWSER_CUT:
177 aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isCutAllowed();
178 break;
179 case ID_BROWSER_COPY:
180 aReturn.bEnabled = getView() && static_cast<OTableDesignView*>(getView())->isCopyAllowed();
181 break;
182 case ID_BROWSER_PASTE:
183 aReturn.bEnabled = isEditable() && getView() && static_cast<OTableDesignView*>(getView())->isPasteAllowed();
184 break;
185 case SID_INDEXDESIGN:
186 aReturn.bEnabled =
187 ( ( ((!m_bNew && impl_isModified()) || impl_isModified())
188 || Reference< XIndexesSupplier >(m_xTable, UNO_QUERY).is()
189 )
190 && isConnected()
191 );
192 if ( aReturn.bEnabled )
193 {
194 aReturn.bEnabled = std::any_of(m_vRowList.begin(),m_vRowList.end(),
195 std::mem_fn(&OTableRow::isValid));
196 }
197 break;
198 default:
199 aReturn = OTableController_BASE::GetState(_nId);
200 }
201 return aReturn;
202 }
203
Execute(sal_uInt16 _nId,const Sequence<PropertyValue> & aArgs)204 void OTableController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs)
205 {
206 switch(_nId)
207 {
208 case ID_BROWSER_EDITDOC:
209 setEditable(!isEditable());
210 static_cast<OTableDesignView*>(getView())->setReadOnly(!isEditable());
211 InvalidateFeature(ID_BROWSER_SAVEDOC);
212 InvalidateFeature(ID_BROWSER_PASTE);
213 InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
214 break;
215 case ID_BROWSER_SAVEASDOC:
216 doSaveDoc(true);
217 break;
218 case ID_BROWSER_SAVEDOC:
219 static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->SaveCurRow();
220 doSaveDoc(false);
221 break;
222 case ID_BROWSER_CUT:
223 static_cast<OTableDesignView*>(getView())->cut();
224 break;
225 case ID_BROWSER_COPY:
226 static_cast<OTableDesignView*>(getView())->copy();
227 break;
228 case ID_BROWSER_PASTE:
229 static_cast<OTableDesignView*>(getView())->paste();
230 break;
231 case SID_INDEXDESIGN:
232 doEditIndexes();
233 break;
234 default:
235 OTableController_BASE::Execute(_nId,aArgs);
236 }
237 InvalidateFeature(_nId);
238 }
239
doSaveDoc(bool _bSaveAs)240 bool OTableController::doSaveDoc(bool _bSaveAs)
241 {
242 if (!isConnected())
243 reconnect(true); // ask the user for a new connection
244 Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
245
246 if (!xTablesSup.is())
247 {
248 OUString aMessage(DBA_RES(STR_TABLEDESIGN_CONNECTION_MISSING));
249 OSQLWarningBox aWarning(getFrameWeld(), aMessage);
250 aWarning.run();
251 return false;
252 }
253
254 // check if a column exists
255 // TODO
256
257 Reference<XNameAccess> xTables;
258 OUString sCatalog, sSchema;
259
260 bool bNew = m_sName.isEmpty();
261 bNew = bNew || m_bNew || _bSaveAs;
262
263 try
264 {
265 xTables = xTablesSup->getTables();
266 OSL_ENSURE(xTables.is(),"The tables can't be null!");
267 bNew = bNew || (xTables.is() && !xTables->hasByName(m_sName));
268
269 // first we need a name for our query so ask the user
270 if(bNew)
271 {
272 OUString aName = DBA_RES(STR_TBL_TITLE);
273 OUString aDefaultName = aName.getToken(0,' ');
274 aDefaultName = ::dbtools::createUniqueName(xTables,aDefaultName);
275
276 DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::TABLE );
277 OSaveAsDlg aDlg(getFrameWeld(), CommandType::TABLE, getORB(), getConnection(), aDefaultName, aNameChecker, SADFlags::NONE);
278 if (aDlg.run() != RET_OK)
279 return false;
280
281 m_sName = aDlg.getName();
282 sCatalog = aDlg.getCatalog();
283 sSchema = aDlg.getSchema();
284 }
285
286 // did we get a name
287 if(m_sName.isEmpty())
288 return false;
289 }
290 catch(Exception&)
291 {
292 OSL_FAIL("OTableController::doSaveDoc: nothing is expected to happen here!");
293 }
294
295 bool bAlter = false;
296 bool bError = false;
297 SQLExceptionInfo aInfo;
298 try
299 {
300 // check the columns for double names
301 if(!checkColumns(bNew || !xTables->hasByName(m_sName)))
302 {
303 return false;
304 }
305
306 Reference<XPropertySet> xTable;
307 if(bNew || !xTables->hasByName(m_sName)) // just to make sure the table already exists
308 {
309 dropTable(xTables,m_sName);
310
311 Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY);
312 OSL_ENSURE(xFact.is(),"OTableController::doSaveDoc: No XDataDescriptorFactory available!");
313 xTable = xFact->createDataDescriptor();
314 OSL_ENSURE(xTable.is(),"OTableController::doSaveDoc: Create query failed!");
315 // to set the name is only allowed when the query is new
316 xTable->setPropertyValue(PROPERTY_CATALOGNAME,Any(sCatalog));
317 xTable->setPropertyValue(PROPERTY_SCHEMANAME,Any(sSchema));
318 xTable->setPropertyValue(PROPERTY_NAME,Any(m_sName));
319
320 // now append the columns
321 Reference<XColumnsSupplier> xColSup(xTable,UNO_QUERY);
322 appendColumns(xColSup,bNew);
323 // now append the primary key
324 Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY);
325 appendPrimaryKey(xKeySup,bNew);
326 }
327 // now set the properties
328 if(bNew)
329 {
330 Reference<XAppend> xAppend(xTables,UNO_QUERY);
331 OSL_ENSURE(xAppend.is(),"OTableController::doSaveDoc: No XAppend Interface!");
332 xAppend->appendByDescriptor(xTable);
333
334 assignTable();
335 if(!m_xTable.is()) // correct name and try again
336 {
337 // it can be that someone inserted new data for us
338 m_sName = ::dbtools::composeTableName( getConnection()->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false );
339 assignTable();
340 }
341 // now check if our datasource has set a tablefilter and if append the new table name to it
342 ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld()); // we are not interested in the return value
343 Reference< frame::XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY);
344 if ( xEventListener.is() )
345 {
346 frame::TitleChangedEvent aEvent;
347 xEventListener->titleChanged(aEvent);
348 }
349 releaseNumberForComponent();
350 }
351 else if(m_xTable.is())
352 {
353 bAlter = true;
354 alterColumns();
355 }
356 reSyncRows();
357 }
358 catch(const SQLContext& e)
359 {
360 aInfo = SQLExceptionInfo(e);
361 }
362 catch(const SQLWarning& e)
363 {
364 aInfo = SQLExceptionInfo(e);
365 }
366 catch(const SQLException& e)
367 {
368 aInfo = SQLExceptionInfo(e);
369 }
370 catch(const ElementExistException& )
371 {
372 OUString sText( DBA_RES( STR_NAME_ALREADY_EXISTS ) );
373 sText = sText.replaceFirst( "#" , m_sName);
374 OSQLMessageBox aDlg(getFrameWeld(), DBA_RES( STR_ERROR_DURING_CREATION ), sText, MessBoxStyle::Ok, MessageType::Error);
375 aDlg.run();
376 bError = true;
377 }
378 catch( const Exception& )
379 {
380 DBG_UNHANDLED_EXCEPTION("dbaccess");
381 bError = true;
382 }
383
384 if ( aInfo.isValid() )
385 aInfo.prepend( DBA_RES( STR_TABLEDESIGN_SAVE_ERROR ) );
386 showError(aInfo);
387
388 if (aInfo.isValid() || bError)
389 {
390 if(!bAlter || bNew)
391 {
392 m_sName.clear();
393 stopTableListening();
394 m_xTable = nullptr;
395 }
396 }
397 return ! (aInfo.isValid() || bError);
398 }
399
doEditIndexes()400 void OTableController::doEditIndexes()
401 {
402 // table needs to be saved before editing indexes
403 if (m_bNew || isModified())
404 {
405 std::unique_ptr<weld::MessageDialog> xAsk(Application::CreateMessageDialog(getFrameWeld(),
406 VclMessageType::Question, VclButtonsType::YesNo,
407 DBA_RES(STR_QUERY_SAVE_TABLE_EDIT_INDEXES)));
408 if (RET_YES != xAsk->run())
409 return;
410
411 if (!doSaveDoc(false))
412 return;
413
414 OSL_ENSURE(!m_bNew && !isModified(), "OTableController::doEditIndexes: what the hell did doSaveDoc do?");
415 }
416
417 Reference< XNameAccess > xIndexes; // will be the keys of the table
418 Sequence< OUString > aFieldNames; // will be the column names of the table
419 try
420 {
421 // get the keys
422 Reference< XIndexesSupplier > xIndexesSupp(m_xTable, UNO_QUERY);
423 if (xIndexesSupp.is())
424 {
425 xIndexes = xIndexesSupp->getIndexes();
426 OSL_ENSURE(xIndexes.is(), "OTableController::doEditIndexes: no keys got from the indexes supplier!");
427 }
428 else
429 OSL_FAIL("OTableController::doEditIndexes: should never have reached this (no indexes supplier)!");
430
431 // get the field names
432 Reference< XColumnsSupplier > xColSupp(m_xTable, UNO_QUERY);
433 OSL_ENSURE(xColSupp.is(), "OTableController::doEditIndexes: no columns supplier!");
434 if (xColSupp.is())
435 {
436 Reference< XNameAccess > xCols = xColSupp->getColumns();
437 OSL_ENSURE(xCols.is(), "OTableController::doEditIndexes: no columns!");
438 if (xCols.is())
439 aFieldNames = xCols->getElementNames();
440 }
441 }
442 catch( const Exception& )
443 {
444 DBG_UNHANDLED_EXCEPTION("dbaccess");
445 }
446
447 if (!xIndexes.is())
448 return;
449
450 DbaIndexDialog aDialog(getFrameWeld(), aFieldNames, xIndexes, getConnection(), getORB());
451 if (RET_OK != aDialog.run())
452 return;
453
454 }
455
impl_initialize(const::comphelper::NamedValueCollection & rArguments)456 void OTableController::impl_initialize(const ::comphelper::NamedValueCollection& rArguments)
457 {
458 try
459 {
460 OTableController_BASE::impl_initialize(rArguments);
461
462 rArguments.get_ensureType( PROPERTY_CURRENTTABLE, m_sName );
463
464 // read autoincrement value set in the datasource
465 ::dbaui::fillAutoIncrementValue(getDataSource(),m_bAllowAutoIncrementValue,m_sAutoIncrementValue);
466
467 assignTable();
468 }
469 catch( const Exception& )
470 {
471 DBG_UNHANDLED_EXCEPTION("dbaccess");
472 }
473
474 try
475 {
476 ::dbaui::fillTypeInfo(getConnection(),m_sTypeNames,m_aTypeInfo,m_aTypeInfoIndex); // fill the needed type information
477 }
478 catch(const SQLException&)
479 {
480 OSQLWarningBox aWarning(getFrameWeld(), DBA_RES( STR_NO_TYPE_INFO_AVAILABLE));
481 aWarning.run();
482 throw;
483 }
484 try
485 {
486 loadData(); // fill the column information from the table
487 getView()->initialize(); // show the windows and fill with our information
488 ClearUndoManager();
489 setModified(false); // and we are not modified yet
490 }
491 catch( const Exception& )
492 {
493 DBG_UNHANDLED_EXCEPTION("dbaccess");
494 }
495 }
496
Construct(vcl::Window * pParent)497 bool OTableController::Construct(vcl::Window* pParent)
498 {
499 setView( VclPtr<OTableDesignView>::Create( pParent, getORB(), *this ) );
500 OTableController_BASE::Construct(pParent);
501 return true;
502 }
503
suspend(sal_Bool)504 sal_Bool SAL_CALL OTableController::suspend(sal_Bool /*_bSuspend*/)
505 {
506 if ( getBroadcastHelper().bInDispose || getBroadcastHelper().bDisposed )
507 return true;
508
509 SolarMutexGuard aSolarGuard;
510 ::osl::MutexGuard aGuard( getMutex() );
511 if ( getView() && getView()->IsInModalMode() )
512 return false;
513 if ( getView() )
514 static_cast<OTableDesignView*>(getView())->GrabFocus();
515 bool bCheck = true;
516 if ( isModified() )
517 {
518 if ( std::any_of(m_vRowList.begin(),m_vRowList.end(),
519 std::mem_fn(&OTableRow::isValid)) )
520 {
521 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), u"dbaccess/ui/tabledesignsavemodifieddialog.ui"_ustr));
522 std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog(u"TableDesignSaveModifiedDialog"_ustr));
523 switch (xQuery->run())
524 {
525 case RET_YES:
526 Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>());
527 if ( isModified() )
528 bCheck = false; // when we save the table this must be false else some press cancel
529 break;
530 case RET_CANCEL:
531 bCheck = false;
532 break;
533 default:
534 break;
535 }
536 }
537 else if ( !m_bNew )
538 {
539 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), u"dbaccess/ui/deleteallrowsdialog.ui"_ustr));
540 std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog(u"DeleteAllRowsDialog"_ustr));
541 switch (xQuery->run())
542 {
543 case RET_YES:
544 {
545 try
546 {
547 Reference<XTablesSupplier> xTablesSup(getConnection(),UNO_QUERY);
548 Reference<XNameAccess> xTables = xTablesSup->getTables();
549 dropTable(xTables,m_sName);
550 }
551 catch(const Exception&)
552 {
553 OSL_FAIL("OTableController::suspend: nothing is expected to happen here!");
554 }
555
556 }
557 break;
558 case RET_CANCEL:
559 bCheck = false;
560 break;
561 default:
562 break;
563 }
564 }
565 }
566
567 return bCheck;
568 }
569
describeSupportedFeatures()570 void OTableController::describeSupportedFeatures()
571 {
572 OSingleDocumentController::describeSupportedFeatures();
573
574 implDescribeSupportedFeature( u".uno:Redo"_ustr, ID_BROWSER_REDO, CommandGroup::EDIT );
575 implDescribeSupportedFeature( u".uno:Save"_ustr, ID_BROWSER_SAVEDOC, CommandGroup::EDIT );
576 implDescribeSupportedFeature( u".uno:Undo"_ustr, ID_BROWSER_UNDO, CommandGroup::EDIT );
577 implDescribeSupportedFeature( u".uno:NewDoc"_ustr, SID_NEWDOC, CommandGroup::DOCUMENT );
578 implDescribeSupportedFeature( u".uno:SaveAs"_ustr, ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT );
579 implDescribeSupportedFeature( u".uno:DBIndexDesign"_ustr, SID_INDEXDESIGN, CommandGroup::APPLICATION );
580 implDescribeSupportedFeature( u".uno:EditDoc"_ustr, ID_BROWSER_EDITDOC, CommandGroup::EDIT );
581 implDescribeSupportedFeature( u".uno:GetUndoStrings"_ustr, SID_GETUNDOSTRINGS );
582 implDescribeSupportedFeature( u".uno:GetRedoStrings"_ustr, SID_GETREDOSTRINGS );
583 }
584
impl_onModifyChanged()585 void OTableController::impl_onModifyChanged()
586 {
587 OSingleDocumentController::impl_onModifyChanged();
588 InvalidateFeature( SID_INDEXDESIGN );
589 }
590
disposing(const EventObject & _rSource)591 void SAL_CALL OTableController::disposing( const EventObject& _rSource )
592 {
593 if ( _rSource.Source == m_xTable )
594 { // some deleted our table so we have a new one
595 stopTableListening();
596 m_xTable = nullptr;
597 m_bNew = true;
598 setModified(true);
599 }
600 else
601 OTableController_BASE::disposing( _rSource );
602 }
603
losingConnection()604 void OTableController::losingConnection( )
605 {
606 // let the base class do its reconnect
607 OTableController_BASE::losingConnection( );
608
609 // remove from the table
610 Reference< XComponent > xComponent(m_xTable, UNO_QUERY);
611 if (xComponent.is())
612 {
613 Reference<XEventListener> xEvtL( static_cast< ::cppu::OWeakObject*>(this), UNO_QUERY);
614 xComponent->removeEventListener(xEvtL);
615 }
616 stopTableListening();
617 m_xTable = nullptr;
618 assignTable();
619 if(!m_xTable.is())
620 {
621 m_bNew = true;
622 setModified(true);
623 }
624 InvalidateAll();
625 }
626
getTypeInfoByType(sal_Int32 _nDataType) const627 TOTypeInfoSP OTableController::getTypeInfoByType(sal_Int32 _nDataType) const
628 {
629 return queryTypeInfoByType(_nDataType,m_aTypeInfo);
630 }
631
appendColumns(Reference<XColumnsSupplier> const & _rxColSup,bool _bNew,bool _bKeyColumns)632 void OTableController::appendColumns(Reference<XColumnsSupplier> const & _rxColSup, bool _bNew, bool _bKeyColumns)
633 {
634 try
635 {
636 // now append the columns
637 OSL_ENSURE(_rxColSup.is(),"No columns supplier");
638 if(!_rxColSup.is())
639 return;
640 Reference<XNameAccess> xColumns = _rxColSup->getColumns();
641 OSL_ENSURE(xColumns.is(),"No columns");
642 Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY);
643
644 Reference<XAppend> xAppend(xColumns,UNO_QUERY);
645 OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
646
647 for (auto const& row : m_vRowList)
648 {
649 OSL_ENSURE(row,"OTableRow is null!");
650 OFieldDescription* pField = row->GetActFieldDescr();
651 if ( !pField || (!_bNew && row->IsReadOnly() && !_bKeyColumns) )
652 continue;
653
654 Reference<XPropertySet> xColumn;
655 if(pField->IsPrimaryKey() || !_bKeyColumns)
656 xColumn = xColumnFactory->createDataDescriptor();
657 if(xColumn.is())
658 {
659 if(!_bKeyColumns)
660 ::dbaui::setColumnProperties(xColumn,pField);
661 else
662 xColumn->setPropertyValue(PROPERTY_NAME,Any(pField->GetName()));
663
664 xAppend->appendByDescriptor(xColumn);
665 xColumn = nullptr;
666 // now only the settings are missing
667 if(xColumns->hasByName(pField->GetName()))
668 {
669 xColumns->getByName(pField->GetName()) >>= xColumn;
670 if(xColumn.is())
671 pField->copyColumnSettingsTo(xColumn);
672 }
673 else
674 {
675 OSL_FAIL("OTableController::appendColumns: invalid field name!");
676 }
677
678 }
679 }
680 }
681 catch(const SQLException& )
682 {
683 showError( SQLExceptionInfo( ::cppu::getCaughtException() ) );
684 }
685 catch( const Exception& )
686 {
687 DBG_UNHANDLED_EXCEPTION("dbaccess");
688 }
689 }
690
appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup,bool _bNew)691 void OTableController::appendPrimaryKey(Reference<XKeysSupplier> const & _rxSup, bool _bNew)
692 {
693 if(!_rxSup.is())
694 return; // the database doesn't support keys
695
696 OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!");
697 Reference<XIndexAccess> xKeys = _rxSup->getKeys();
698 Reference<XPropertySet> xProp;
699 if (!xKeys.is())
700 return;
701 const sal_Int32 nCount = xKeys->getCount();
702 for(sal_Int32 i=0;i< nCount ;++i)
703 {
704 xKeys->getByIndex(i) >>= xProp;
705 sal_Int32 nKeyType = 0;
706 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
707 if(KeyType::PRIMARY == nKeyType)
708 {
709 return; // primary key already exists after appending a column
710 }
711 }
712 Reference<XDataDescriptorFactory> xKeyFactory(xKeys,UNO_QUERY);
713 OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!");
714 if ( !xKeyFactory.is() )
715 return;
716 Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY);
717 OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
718
719 Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor();
720 OSL_ENSURE(xKey.is(),"Key is null!");
721 xKey->setPropertyValue(PROPERTY_TYPE,Any(KeyType::PRIMARY));
722
723 Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY);
724 if(xColSup.is())
725 {
726 appendColumns(xColSup,_bNew,true);
727 Reference<XNameAccess> xColumns = xColSup->getColumns();
728 if(xColumns->hasElements())
729 xAppend->appendByDescriptor(xKey);
730 }
731 }
732
loadData()733 void OTableController::loadData()
734 {
735 // if the data structure already exists, empty it
736 m_vRowList.clear();
737
738 std::shared_ptr<OTableRow> pTabEdRow;
739 Reference< XDatabaseMetaData> xMetaData = getMetaData( );
740 // fill data structure with data from DataDefinitionObject
741 if(m_xTable.is() && xMetaData.is())
742 {
743 Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY);
744 OSL_ENSURE(xColSup.is(),"No XColumnsSupplier!");
745 Reference<XNameAccess> xColumns = xColSup->getColumns();
746 // ReadOnly-Flag
747 // For Drop no row may be editable
748 // For Add only the empty rows may be editable
749 // For Add and Drop all rows can be edited
750 // sal_Bool bReadOldRow = xMetaData->supportsAlterTableWithAddColumn() && xMetaData->supportsAlterTableWithDropColumn();
751 bool bIsAlterAllowed = isAlterAllowed();
752
753 const Sequence<OUString> aColNames = xColumns->getElementNames();
754 for(const OUString& rColumn : aColNames)
755 {
756 Reference<XPropertySet> xColumn;
757 xColumns->getByName(rColumn) >>= xColumn;
758 sal_Int32 nType = 0;
759 sal_Int32 nScale = 0;
760 sal_Int32 nPrecision = 0;
761 sal_Int32 nNullable = 0;
762 sal_Int32 nFormatKey = 0;
763 sal_Int32 nAlign = 0;
764
765 bool bIsAutoIncrement = false, bIsCurrency = false;
766 OUString sName,sDescription,sTypeName,sHelpText;
767 Any aControlDefault;
768
769 // get the properties from the column
770 xColumn->getPropertyValue(PROPERTY_NAME) >>= sName;
771 xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName;
772 xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
773 xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bIsAutoIncrement;
774 xColumn->getPropertyValue(PROPERTY_ISCURRENCY) >>= bIsCurrency;
775 xColumn->getPropertyValue(PROPERTY_TYPE) >>= nType;
776 xColumn->getPropertyValue(PROPERTY_SCALE) >>= nScale;
777 xColumn->getPropertyValue(PROPERTY_PRECISION) >>= nPrecision;
778 xColumn->getPropertyValue(PROPERTY_DESCRIPTION) >>= sDescription;
779
780 if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_HELPTEXT))
781 xColumn->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
782
783 if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
784 aControlDefault = xColumn->getPropertyValue(PROPERTY_CONTROLDEFAULT);
785 if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_FORMATKEY))
786 xColumn->getPropertyValue(PROPERTY_FORMATKEY) >>= nFormatKey;
787 if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_ALIGN))
788 xColumn->getPropertyValue(PROPERTY_ALIGN) >>= nAlign;
789
790 pTabEdRow = std::make_shared<OTableRow>();
791 pTabEdRow->SetReadOnly(!bIsAlterAllowed);
792 // search for type
793 bool bForce;
794 TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,u"x"_ustr,nPrecision,nScale,bIsAutoIncrement,bForce);
795 if ( !pTypeInfo )
796 pTypeInfo = m_pTypeInfo;
797 pTabEdRow->SetFieldType( pTypeInfo, bForce );
798
799 OFieldDescription* pActFieldDescr = pTabEdRow->GetActFieldDescr();
800 OSL_ENSURE(pActFieldDescr, "OTableController::loadData: invalid field description generated by the table row!");
801 if ( pActFieldDescr )
802 {
803 pActFieldDescr->SetName(sName);
804 pActFieldDescr->SetFormatKey(nFormatKey);
805 pActFieldDescr->SetDescription(sDescription);
806 pActFieldDescr->SetHelpText(sHelpText);
807 pActFieldDescr->SetAutoIncrement(bIsAutoIncrement);
808 pActFieldDescr->SetHorJustify(dbaui::mapTextJustify(nAlign));
809 pActFieldDescr->SetCurrency(bIsCurrency);
810
811 // special data
812 pActFieldDescr->SetIsNullable(nNullable);
813 pActFieldDescr->SetControlDefault(aControlDefault);
814 pActFieldDescr->SetPrecision(nPrecision);
815 pActFieldDescr->SetScale(nScale);
816 }
817 m_vRowList.push_back( pTabEdRow);
818 }
819 // fill the primary key information
820 Reference<XNameAccess> xKeyColumns = getKeyColumns();
821 if(xKeyColumns.is())
822 {
823 const Sequence<OUString> aKeyColumnNames = xKeyColumns->getElementNames();
824 for(const OUString& rKeyColumn : aKeyColumnNames)
825 {
826 for(std::shared_ptr<OTableRow> const& pRow : m_vRowList)
827 {
828 if(pRow->GetActFieldDescr()->GetName() == rKeyColumn)
829 {
830 pRow->SetPrimaryKey(true);
831 break;
832 }
833 }
834 }
835 }
836 }
837
838 // fill empty rows
839
840 OTypeInfoMap::const_iterator aTypeIter = m_aTypeInfo.find(DataType::VARCHAR);
841 if(aTypeIter == m_aTypeInfo.end())
842 aTypeIter = m_aTypeInfo.begin();
843
844 OSL_ENSURE(aTypeIter != m_aTypeInfo.end(),"We have no type information!");
845
846 bool bReadRow = !isAddAllowed();
847 for(sal_Int32 i=m_vRowList.size(); i < NEWCOLS; i++ )
848 {
849 pTabEdRow = std::make_shared<OTableRow>();
850 pTabEdRow->SetReadOnly(bReadRow);
851 m_vRowList.push_back( pTabEdRow);
852 }
853 }
854
getKeyColumns() const855 Reference<XNameAccess> OTableController::getKeyColumns() const
856 {
857 return getPrimaryKeyColumns_throw(m_xTable);
858 }
859
checkColumns(bool _bNew)860 bool OTableController::checkColumns(bool _bNew)
861 {
862 bool bOk = true;
863 bool bFoundPKey = false;
864 Reference< XDatabaseMetaData > xMetaData = getMetaData( );
865 DatabaseMetaData aMetaData( getConnection() );
866
867 ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
868 std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
869 std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
870 for(;aIter != aEnd;++aIter)
871 {
872 OFieldDescription* pFieldDesc = (*aIter)->GetActFieldDescr();
873 if (pFieldDesc && !pFieldDesc->GetName().isEmpty())
874 {
875 bFoundPKey |= (*aIter)->IsPrimaryKey();
876 // first check for duplicate names
877 bool bDuplicateNameFound = std::any_of(aIter+1, aEnd,
878 [&bCase, &pFieldDesc](const std::shared_ptr<OTableRow>& rxRow) {
879 OFieldDescription* pCompareDesc = rxRow->GetActFieldDescr();
880 return pCompareDesc && bCase(pCompareDesc->GetName(),pFieldDesc->GetName());
881 });
882 if (bDuplicateNameFound)
883 {
884 OUString strMessage = DBA_RES(STR_TABLEDESIGN_DUPLICATE_NAME);
885 strMessage = strMessage.replaceFirst("$column$", pFieldDesc->GetName());
886 OSQLWarningBox aWarning(getFrameWeld(), strMessage);
887 aWarning.run();
888 return false;
889 }
890 }
891 }
892 if ( _bNew && !bFoundPKey && aMetaData.supportsPrimaryKeys() )
893 {
894 OUString sTitle(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY_HEAD));
895 OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY));
896 OSQLMessageBox aBox(getFrameWeld(), sTitle,sMsg, MessBoxStyle::YesNoCancel | MessBoxStyle::DefaultYes);
897
898 switch (aBox.run())
899 {
900 case RET_YES:
901 {
902 TOTypeInfoSP pTypeInfo = ::dbaui::queryPrimaryKeyType(m_aTypeInfo);
903 if ( !pTypeInfo )
904 break;
905
906 auto pNewRow = std::make_shared<OTableRow>();
907 pNewRow->SetFieldType( pTypeInfo );
908 OFieldDescription* pActFieldDescr = pNewRow->GetActFieldDescr();
909
910 pActFieldDescr->SetAutoIncrement(pTypeInfo->bAutoIncrement);
911 pActFieldDescr->SetIsNullable(ColumnValue::NO_NULLS);
912
913 pActFieldDescr->SetName( createUniqueName(u"ID"_ustr ));
914 pActFieldDescr->SetPrimaryKey( true );
915 m_vRowList.insert(m_vRowList.begin(),pNewRow);
916
917 static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->Invalidate();
918 static_cast<OTableDesignView*>(getView())->GetEditorCtrl()->RowInserted(0);
919 }
920 break;
921 case RET_CANCEL:
922 bOk = false;
923 break;
924 }
925 }
926 return bOk;
927 }
928
alterColumns()929 void OTableController::alterColumns()
930 {
931 Reference<XColumnsSupplier> xColSup(m_xTable,UNO_QUERY_THROW);
932
933 Reference<XNameAccess> xColumns = xColSup->getColumns();
934 Reference<XIndexAccess> xIdxColumns(xColumns,UNO_QUERY_THROW);
935 OSL_ENSURE(xColumns.is(),"No columns");
936 if ( !xColumns.is() )
937 return;
938 Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY); // can be null
939
940 sal_Int32 nColumnCount = xIdxColumns->getCount();
941 Reference<XDrop> xDrop(xColumns,UNO_QUERY); // can be null
942 Reference<XAppend> xAppend(xColumns,UNO_QUERY); // can be null
943 Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); // can be null
944
945 bool bReload = false; // refresh the data
946
947 // contains all columns names which are already handled those which are not in the list will be deleted
948 Reference< XDatabaseMetaData> xMetaData = getMetaData( );
949
950 std::set<OUString, comphelper::UStringMixLess> aColumns(
951 comphelper::UStringMixLess(
952 !xMetaData.is()
953 || xMetaData->supportsMixedCaseQuotedIdentifiers()));
954 std::vector< std::shared_ptr<OTableRow> >::const_iterator aIter = m_vRowList.begin();
955 std::vector< std::shared_ptr<OTableRow> >::const_iterator aEnd = m_vRowList.end();
956 // first look for columns where something other than the name changed
957 for(sal_Int32 nPos = 0;aIter != aEnd;++aIter,++nPos)
958 {
959 OSL_ENSURE(*aIter,"OTableRow is null!");
960 OFieldDescription* pField = (*aIter)->GetActFieldDescr();
961 if ( !pField )
962 continue;
963 if ( (*aIter)->IsReadOnly() )
964 {
965 aColumns.insert(pField->GetName());
966 continue;
967 }
968
969 Reference<XPropertySet> xColumn;
970 if ( xColumns->hasByName(pField->GetName()) )
971 {
972 aColumns.insert(pField->GetName());
973 xColumns->getByName(pField->GetName()) >>= xColumn;
974 OSL_ENSURE(xColumn.is(),"Column is null!");
975
976 sal_Int32 nType=0,nPrecision=0,nScale=0,nNullable=0;
977 bool bAutoIncrement = false;
978 OUString sTypeName,sDescription;
979
980 xColumn->getPropertyValue(PROPERTY_TYPE) >>= nType;
981 xColumn->getPropertyValue(PROPERTY_PRECISION) >>= nPrecision;
982 xColumn->getPropertyValue(PROPERTY_SCALE) >>= nScale;
983 xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
984 xColumn->getPropertyValue(PROPERTY_ISAUTOINCREMENT) >>= bAutoIncrement;
985 xColumn->getPropertyValue(PROPERTY_DESCRIPTION) >>= sDescription;
986
987 try { xColumn->getPropertyValue(PROPERTY_TYPENAME) >>= sTypeName; }
988 catch( const Exception& )
989 {
990 OSL_FAIL( "no TypeName property?!" );
991 // since this is a last minute fix for #i41785#, I want to be on the safe side,
992 // and catch errors here as early as possible (instead of the whole process of altering
993 // the columns failing)
994 // Normally, sdbcx::Column objects are expected to have a TypeName property
995 }
996
997 // check if something changed
998 if((nType != pField->GetType() ||
999 sTypeName != pField->GetTypeName() ||
1000 (nPrecision != pField->GetPrecision() && nPrecision ) ||
1001 nScale != pField->GetScale() ||
1002 nNullable != pField->GetIsNullable() ||
1003 sDescription != pField->GetDescription() ||
1004 bAutoIncrement != pField->IsAutoIncrement())&&
1005 xColumnFactory.is())
1006 {
1007 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1008 ::dbaui::setColumnProperties(xNewColumn,pField);
1009 // first try to alter the column
1010 bool bNotOk = false;
1011 try
1012 {
1013 // first try if we can alter the column
1014 if(xAlter.is())
1015 xAlter->alterColumnByName(pField->GetName(),xNewColumn);
1016 }
1017 catch(const SQLException&)
1018 {
1019 if(xDrop.is() && xAppend.is())
1020 {
1021 OUString aMessage( DBA_RES( STR_TABLEDESIGN_ALTER_ERROR ) );
1022 aMessage = aMessage.replaceFirst( "$column$", pField->GetName() );
1023
1024 SQLExceptionInfo aError( ::cppu::getCaughtException() );
1025 OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes , &aError);
1026 bNotOk = aMsg.run() == RET_YES;
1027 }
1028 else
1029 throw;
1030 }
1031 // if something went wrong or we can't alter columns
1032 // drop and append a new one
1033 if((!xAlter.is() || bNotOk) && xDrop.is() && xAppend.is())
1034 {
1035 xDrop->dropByName(pField->GetName());
1036 try
1037 {
1038 xAppend->appendByDescriptor(xNewColumn);
1039 }
1040 catch(const SQLException&)
1041 { // an error occurred so we try to reactivate the old one
1042 xAppend->appendByDescriptor(xColumn);
1043 throw;
1044 }
1045 }
1046 // exceptions are caught outside
1047 xNewColumn = nullptr;
1048 if(xColumns->hasByName(pField->GetName()))
1049 xColumns->getByName(pField->GetName()) >>= xColumn;
1050 bReload = true;
1051 }
1052
1053 }
1054 else if(xColumnFactory.is() && xAlter.is() && nPos < nColumnCount)
1055 { // we can't find the column so we could try it with the index before we drop and append a new column
1056 try
1057 {
1058 Reference<XPropertySet> xNewColumn = xColumnFactory->createDataDescriptor();
1059 ::dbaui::setColumnProperties(xNewColumn,pField);
1060 xAlter->alterColumnByIndex(nPos,xNewColumn);
1061 if(xColumns->hasByName(pField->GetName()))
1062 { // ask for the append by name
1063 aColumns.insert(pField->GetName());
1064 xColumns->getByName(pField->GetName()) >>= xColumn;
1065 if(xColumn.is())
1066 pField->copyColumnSettingsTo(xColumn);
1067 }
1068 else
1069 {
1070 OSL_FAIL("OTableController::alterColumns: invalid column (2)!");
1071 }
1072 }
1073 catch(const SQLException&)
1074 { // we couldn't alter the column so we have to add new columns
1075 SQLExceptionInfo aError( ::cppu::getCaughtException() );
1076 bReload = true;
1077 if(xDrop.is() && xAppend.is())
1078 {
1079 OUString aMessage(DBA_RES(STR_TABLEDESIGN_ALTER_ERROR));
1080 aMessage = aMessage.replaceFirst("$column$",pField->GetName());
1081 OSQLWarningBox aMsg(getFrameWeld(), aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, &aError);
1082 if (aMsg.run() != RET_YES)
1083 {
1084 Reference<XPropertySet> xNewColumn(xIdxColumns->getByIndex(nPos),UNO_QUERY_THROW);
1085 OUString sName;
1086 xNewColumn->getPropertyValue(PROPERTY_NAME) >>= sName;
1087 aColumns.insert(sName);
1088 aColumns.insert(pField->GetName());
1089 continue;
1090 }
1091 }
1092 else
1093 throw;
1094 }
1095 }
1096 else
1097 bReload = true;
1098 }
1099 // alter column settings
1100
1101 // first look for columns where something other than the name changed
1102 for (auto const& row : m_vRowList)
1103 {
1104 OSL_ENSURE(row,"OTableRow is null!");
1105 OFieldDescription* pField = row->GetActFieldDescr();
1106 if ( !pField )
1107 continue;
1108 if ( row->IsReadOnly() )
1109 {
1110 aColumns.insert(pField->GetName());
1111 continue;
1112 }
1113
1114 Reference<XPropertySet> xColumn;
1115 if ( xColumns->hasByName(pField->GetName()) )
1116 {
1117 xColumns->getByName(pField->GetName()) >>= xColumn;
1118 Reference<XPropertySetInfo> xInfo = xColumn->getPropertySetInfo();
1119 if ( xInfo->hasPropertyByName(PROPERTY_HELPTEXT) )
1120 xColumn->setPropertyValue(PROPERTY_HELPTEXT,Any(pField->GetHelpText()));
1121
1122 if(xInfo->hasPropertyByName(PROPERTY_CONTROLDEFAULT))
1123 xColumn->setPropertyValue(PROPERTY_CONTROLDEFAULT,pField->GetControlDefault());
1124 if(xInfo->hasPropertyByName(PROPERTY_FORMATKEY))
1125 xColumn->setPropertyValue(PROPERTY_FORMATKEY,Any(pField->GetFormatKey()));
1126 if(xInfo->hasPropertyByName(PROPERTY_ALIGN))
1127 xColumn->setPropertyValue(PROPERTY_ALIGN,Any(dbaui::mapTextAlign(pField->GetHorJustify())));
1128 }
1129 }
1130 // second drop all columns which could be found by name
1131 Reference<XNameAccess> xKeyColumns = getKeyColumns();
1132 // now we have to look for the columns who could be deleted
1133 if ( xDrop.is() )
1134 {
1135 const Sequence<OUString> aColNames = xColumns->getElementNames();
1136 for(const OUString& rColumnName : aColNames)
1137 {
1138 if(aColumns.find(rColumnName) == aColumns.end()) // found a column to delete
1139 {
1140 if(xKeyColumns.is() && xKeyColumns->hasByName(rColumnName)) // check if this column is a member of the primary key
1141 {
1142 OUString aMsgT(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN));
1143 aMsgT = aMsgT.replaceFirst("$column$",rColumnName);
1144 OUString aTitle(DBA_RES(STR_TBL_COLUMN_IS_KEYCOLUMN_TITLE));
1145 OSQLMessageBox aMsg(getFrameWeld(), aTitle, aMsgT, MessBoxStyle::YesNo| MessBoxStyle::DefaultYes);
1146 if (aMsg.run() == RET_YES)
1147 {
1148 xKeyColumns = nullptr;
1149 dropPrimaryKey();
1150 }
1151 else
1152 {
1153 bReload = true;
1154 continue;
1155 }
1156 }
1157 try
1158 {
1159 xDrop->dropByName(rColumnName);
1160 }
1161 catch (const SQLException&)
1162 {
1163 const auto caughtException = ::cppu::getCaughtException();
1164 OUString sError( DBA_RES( STR_TABLEDESIGN_COULD_NOT_DROP_COL ) );
1165 sError = sError.replaceFirst( "$column$", rColumnName );
1166
1167 throw SQLException(sError, {}, u"S1000"_ustr, 0, caughtException);
1168 }
1169 }
1170 }
1171 }
1172
1173 // third append the new columns
1174 for(const auto& rxRow : m_vRowList)
1175 {
1176 OSL_ENSURE(rxRow,"OTableRow is null!");
1177 OFieldDescription* pField = rxRow->GetActFieldDescr();
1178 if ( !pField || rxRow->IsReadOnly() || aColumns.find(pField->GetName()) != aColumns.end() )
1179 continue;
1180
1181 Reference<XPropertySet> xColumn;
1182 if(!xColumns->hasByName(pField->GetName()))
1183 {
1184 if(xColumnFactory.is() && xAppend.is())
1185 {// column not found by its name so we assume it is new
1186 // Column is new
1187 xColumn = xColumnFactory->createDataDescriptor();
1188 ::dbaui::setColumnProperties(xColumn,pField);
1189 xAppend->appendByDescriptor(xColumn);
1190 if(xColumns->hasByName(pField->GetName()))
1191 { // ask for the append by name
1192 aColumns.insert(pField->GetName());
1193 xColumns->getByName(pField->GetName()) >>= xColumn;
1194 if(xColumn.is())
1195 pField->copyColumnSettingsTo(xColumn);
1196 }
1197 else
1198 {
1199 OSL_FAIL("OTableController::alterColumns: invalid column!");
1200 }
1201 }
1202 }
1203 }
1204
1205 // check if we have to do something with the primary key
1206 bool bNeedDropKey = false;
1207 bool bNeedAppendKey = false;
1208 if ( xKeyColumns.is() )
1209 {
1210 for(const auto& rxRow : m_vRowList)
1211 {
1212 OSL_ENSURE(rxRow,"OTableRow is null!");
1213 OFieldDescription* pField = rxRow->GetActFieldDescr();
1214 if ( !pField )
1215 continue;
1216
1217 if ( pField->IsPrimaryKey()
1218 && !xKeyColumns->hasByName( pField->GetName() )
1219 )
1220 { // new primary key column inserted which isn't already in the columns selection
1221 bNeedDropKey = bNeedAppendKey = true;
1222 break;
1223 }
1224 else if ( !pField->IsPrimaryKey()
1225 && xKeyColumns->hasByName( pField->GetName() )
1226 )
1227 { // found a column which currently is in the primary key, but is marked not to be anymore
1228 bNeedDropKey = bNeedAppendKey = true;
1229 break;
1230 }
1231 }
1232 }
1233 else
1234 { // no primary key available so we check if we should create one
1235 bNeedAppendKey = true;
1236 }
1237
1238 if ( bNeedDropKey && xKeyColumns.is() && xKeyColumns->getElementNames().hasElements() )
1239 dropPrimaryKey();
1240
1241 if ( bNeedAppendKey )
1242 {
1243 Reference< XKeysSupplier > xKeySup( m_xTable, UNO_QUERY );
1244 appendPrimaryKey( xKeySup ,false);
1245 }
1246
1247 reSyncRows();
1248
1249 if ( bReload )
1250 reload();
1251 }
1252
dropPrimaryKey()1253 void OTableController::dropPrimaryKey()
1254 {
1255 SQLExceptionInfo aInfo;
1256 try
1257 {
1258 Reference<XKeysSupplier> xKeySup(m_xTable,UNO_QUERY);
1259 Reference<XIndexAccess> xKeys;
1260 if(xKeySup.is())
1261 xKeys = xKeySup->getKeys();
1262
1263 if(xKeys.is())
1264 {
1265 Reference<XPropertySet> xProp;
1266 for(sal_Int32 i=0;i< xKeys->getCount();++i)
1267 {
1268 xProp.set(xKeys->getByIndex(i),UNO_QUERY);
1269 sal_Int32 nKeyType = 0;
1270 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
1271 if(KeyType::PRIMARY == nKeyType)
1272 {
1273 Reference<XDrop> xDrop(xKeys,UNO_QUERY);
1274 xDrop->dropByIndex(i); // delete the key
1275 break;
1276 }
1277 }
1278 }
1279 }
1280 catch(const SQLContext& e)
1281 {
1282 aInfo = SQLExceptionInfo(e);
1283 }
1284 catch(const SQLWarning& e)
1285 {
1286 aInfo = SQLExceptionInfo(e);
1287 }
1288 catch(const SQLException& e)
1289 {
1290 aInfo = SQLExceptionInfo(e);
1291 }
1292 catch( const Exception& )
1293 {
1294 DBG_UNHANDLED_EXCEPTION("dbaccess");
1295 }
1296
1297 showError(aInfo);
1298 }
1299
assignTable()1300 void OTableController::assignTable()
1301 {
1302 // get the table
1303 if(m_sName.isEmpty())
1304 return;
1305
1306 Reference<XNameAccess> xNameAccess;
1307 Reference<XTablesSupplier> xSup(getConnection(),UNO_QUERY);
1308 if(!xSup.is())
1309 return;
1310
1311 xNameAccess = xSup->getTables();
1312 OSL_ENSURE(xNameAccess.is(),"no nameaccess for the queries!");
1313
1314 if(!xNameAccess->hasByName(m_sName))
1315 return;
1316
1317 Reference<XPropertySet> xProp(xNameAccess->getByName(m_sName), css::uno::UNO_QUERY);
1318 if (!xProp.is())
1319 return;
1320
1321 m_xTable = std::move(xProp);
1322 startTableListening();
1323
1324 // check if we set the table editable
1325 Reference<XDatabaseMetaData> xMeta = getConnection()->getMetaData();
1326 setEditable( xMeta.is() && !xMeta->isReadOnly() && (isAlterAllowed() || isDropAllowed() || isAddAllowed()) );
1327 if(!isEditable())
1328 {
1329 for( const auto& rTableRow : m_vRowList )
1330 {
1331 rTableRow->SetReadOnly();
1332 }
1333 }
1334 m_bNew = false;
1335 // be notified when the table is in disposing
1336 InvalidateAll();
1337 }
1338
isAddAllowed() const1339 bool OTableController::isAddAllowed() const
1340 {
1341 Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1342 bool bAddAllowed = !m_xTable.is();
1343 if(xColsSup.is())
1344 bAddAllowed = Reference<XAppend>(xColsSup->getColumns(),UNO_QUERY).is();
1345
1346 try
1347 {
1348 Reference< XDatabaseMetaData > xMetaData = getMetaData( );
1349 bAddAllowed = bAddAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithAddColumn());
1350 }
1351 catch(Exception&)
1352 {
1353 DBG_UNHANDLED_EXCEPTION("dbaccess");
1354 bAddAllowed = false;
1355 }
1356
1357 return bAddAllowed;
1358 }
1359
isDropAllowed() const1360 bool OTableController::isDropAllowed() const
1361 {
1362 Reference<XColumnsSupplier> xColsSup(m_xTable,UNO_QUERY);
1363 bool bDropAllowed = !m_xTable.is();
1364 if(xColsSup.is())
1365 {
1366 Reference<XNameAccess> xNameAccess = xColsSup->getColumns();
1367 bDropAllowed = Reference<XDrop>(xNameAccess,UNO_QUERY).is() && xNameAccess->hasElements();
1368 }
1369
1370 Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1371 bDropAllowed = bDropAllowed || ( xMetaData.is() && xMetaData->supportsAlterTableWithDropColumn());
1372
1373 return bDropAllowed;
1374 }
1375
isAlterAllowed() const1376 bool OTableController::isAlterAllowed() const
1377 {
1378 bool bAllowed(!m_xTable.is() || Reference<XAlterTable>(m_xTable,UNO_QUERY).is());
1379 return bAllowed;
1380 }
1381
reSyncRows()1382 void OTableController::reSyncRows()
1383 {
1384 bool bAlterAllowed = isAlterAllowed();
1385 bool bAddAllowed = isAddAllowed();
1386 for (auto const& row : m_vRowList)
1387 {
1388 OSL_ENSURE(row,"OTableRow is null!");
1389 OFieldDescription* pField = row->GetActFieldDescr();
1390 if ( pField )
1391 row->SetReadOnly(!bAlterAllowed);
1392 else
1393 row->SetReadOnly(!bAddAllowed);
1394
1395 }
1396 static_cast<OTableDesignView*>(getView())->reSync(); // show the windows and fill with our information
1397
1398 ClearUndoManager();
1399 setModified(false); // and we are not modified yet
1400 }
1401
createUniqueName(const OUString & _rName)1402 OUString OTableController::createUniqueName(const OUString& _rName)
1403 {
1404 OUString sName = _rName;
1405 Reference< XDatabaseMetaData> xMetaData = getMetaData( );
1406
1407 ::comphelper::UStringMixEqual bCase(!xMetaData.is() || xMetaData->supportsMixedCaseQuotedIdentifiers());
1408
1409 auto lHasName = [&bCase, &sName](const std::shared_ptr<OTableRow>& rxRow) {
1410 OFieldDescription* pFieldDesc = rxRow->GetActFieldDescr();
1411 return pFieldDesc && !pFieldDesc->GetName().isEmpty() && bCase(sName, pFieldDesc->GetName());
1412 };
1413
1414 sal_Int32 i = 0;
1415 while(std::any_of(m_vRowList.begin(), m_vRowList.end(), lHasName))
1416 {
1417 // found a second name of _rName so we need another
1418 sName = _rName + OUString::number(++i);
1419 }
1420 return sName;
1421 }
1422
getPrivateTitle() const1423 OUString OTableController::getPrivateTitle() const
1424 {
1425 OUString sTitle;
1426 try
1427 {
1428 // get the table
1429 if ( !m_sName.isEmpty() && getConnection().is() )
1430 {
1431 if ( m_xTable.is() )
1432 sTitle = ::dbtools::composeTableName( getConnection()->getMetaData(), m_xTable, ::dbtools::EComposeRule::InDataManipulation, false );
1433 else
1434 sTitle = m_sName;
1435 }
1436 if ( sTitle.isEmpty() )
1437 {
1438 OUString aName = DBA_RES(STR_TBL_TITLE);
1439 sTitle = o3tl::getToken(aName,0,' ') + OUString::number(getCurrentStartNumber());
1440 }
1441 }
1442 catch( const Exception& )
1443 {
1444 DBG_UNHANDLED_EXCEPTION("dbaccess");
1445 }
1446 return sTitle;
1447 }
1448
reload()1449 void OTableController::reload()
1450 {
1451 loadData(); // fill the column information from the table
1452 static_cast<OTableDesignView*>(getView())->reSync(); // show the windows and fill with our information
1453 ClearUndoManager();
1454 setModified(false); // and we are not modified yet
1455 static_cast<OTableDesignView*>(getView())->Invalidate();
1456 }
1457
getFirstEmptyRowPosition()1458 sal_Int32 OTableController::getFirstEmptyRowPosition()
1459 {
1460 sal_Int32 nRet = 0;
1461 bool bFoundElem = false;
1462 for (auto const& row : m_vRowList)
1463 {
1464 if ( !row || !row->GetActFieldDescr() || row->GetActFieldDescr()->GetName().isEmpty() )
1465 {
1466 bFoundElem = true;
1467 break;
1468 }
1469 ++nRet;
1470 }
1471 if (!bFoundElem)
1472 {
1473 bool bReadRow = !isAddAllowed();
1474 auto pTabEdRow = std::make_shared<OTableRow>();
1475 pTabEdRow->SetReadOnly(bReadRow);
1476 nRet = m_vRowList.size();
1477 m_vRowList.push_back(std::move(pTabEdRow));
1478 }
1479 return nRet;
1480 }
1481
isAutoIncrementPrimaryKey() const1482 bool OTableController::isAutoIncrementPrimaryKey() const
1483 {
1484 return getSdbMetaData().isAutoIncrementPrimaryKey();
1485 }
1486
1487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1488