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