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 <browserids.hxx>
21 #include <core_resource.hxx>
22 #include <strings.hrc>
23 #include <strings.hxx>
24 #include <query.hrc>
25 #include <stringconstants.hxx>
26 #include <defaultobjectnamecheck.hxx>
27 #include <dlgsave.hxx>
28 #include <querycontainerwindow.hxx>
29 #include <querycontroller.hxx>
30 #include <QueryDesignView.hxx>
31 #include <QueryTableView.hxx>
32 #include <sqlmessage.hxx>
33 #include <TableConnectionData.hxx>
34 #include <TableFieldDescription.hxx>
35 #include <UITools.hxx>
36 #include <QueryPropertiesDialog.hxx>
37
38 #include <com/sun/star/beans/PropertyAttribute.hpp>
39 #include <com/sun/star/container/XNameContainer.hpp>
40 #include <com/sun/star/frame/FrameSearchFlag.hpp>
41 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
42 #include <com/sun/star/sdb/CommandType.hpp>
43 #include <com/sun/star/sdb/SQLContext.hpp>
44 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
45 #include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp>
46 #include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
47 #include <com/sun/star/sdbcx/XAppend.hpp>
48 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
49 #include <com/sun/star/sdbcx/XDrop.hpp>
50 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
51 #include <com/sun/star/sdbcx/XViewsSupplier.hpp>
52 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
53 #include <com/sun/star/util/XCloseable.hpp>
54 #include <com/sun/star/util/VetoException.hpp>
55 #include <com/sun/star/ui/XUIElement.hpp>
56
57 #include <comphelper/propertysequence.hxx>
58 #include <comphelper/property.hxx>
59 #include <comphelper/types.hxx>
60 #include <connectivity/dbexception.hxx>
61 #include <connectivity/dbtools.hxx>
62 #include <cppuhelper/exc_hlp.hxx>
63 #include <svl/undo.hxx>
64 #include <toolkit/helper/vclunohelper.hxx>
65 #include <comphelper/diagnose_ex.hxx>
66 #include <osl/diagnose.h>
67 #include <utility>
68 #include <vcl/stdtext.hxx>
69 #include <vcl/svapp.hxx>
70 #include <vcl/weld.hxx>
71 #include <osl/mutex.hxx>
72 #include <o3tl/string_view.hxx>
73 #include <memory>
74 #include <vector>
75
76 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OQueryDesign_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)77 org_openoffice_comp_dbu_OQueryDesign_get_implementation(
78 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
79 {
80 return cppu::acquire(new ::dbaui::OQueryController(context));
81 }
82
83 namespace dbaui
84 {
85 using namespace ::com::sun::star::uno;
86 using namespace ::com::sun::star::beans;
87 using namespace ::com::sun::star::frame;
88 using namespace ::com::sun::star::util;
89 using namespace ::com::sun::star::lang;
90
91 namespace {
92
93 class OViewController : public OQueryController
94 {
getImplementationName()95 virtual OUString SAL_CALL getImplementationName() override
96 {
97 return u"org.openoffice.comp.dbu.OViewDesign"_ustr;
98 }
getSupportedServiceNames()99 virtual Sequence< OUString> SAL_CALL getSupportedServiceNames() override
100 {
101 return { u"com.sun.star.sdb.ViewDesign"_ustr };
102 }
103
104 public:
OViewController(const Reference<XComponentContext> & _rM)105 explicit OViewController(const Reference< XComponentContext >& _rM) : OQueryController(_rM){}
106 };
107
108 }
109 }
110
111 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OViewDesign_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)112 org_openoffice_comp_dbu_OViewDesign_get_implementation(
113 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
114 {
115 return cppu::acquire(new ::dbaui::OViewController(context));
116 }
117
118 namespace dbaui
119 {
120 using namespace ::connectivity;
121
122 namespace
123 {
lcl_getObjectResourceString(TranslateId pResId,sal_Int32 _nCommandType)124 OUString lcl_getObjectResourceString(TranslateId pResId, sal_Int32 _nCommandType)
125 {
126 OUString sMessageText = DBA_RES(pResId);
127 OUString sObjectType = DBA_RES(RSC_QUERY_OBJECT_TYPE[_nCommandType]);
128 sMessageText = sMessageText.replaceFirst( "$object$", sObjectType );
129 return sMessageText;
130 }
131 }
132
133 using namespace ::com::sun::star::container;
134 using namespace ::com::sun::star::sdbcx;
135 using namespace ::com::sun::star::sdbc;
136 using namespace ::com::sun::star::sdb;
137 using namespace ::com::sun::star::ui;
138 using namespace ::com::sun::star::awt;
139 using namespace ::dbtools;
140
141 using namespace ::comphelper;
142
143 namespace
144 {
ensureToolbars(OQueryController & _rController,bool _bDesign)145 void ensureToolbars( OQueryController& _rController, bool _bDesign )
146 {
147 Reference< css::frame::XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() );
148 if ( !xLayoutManager.is() )
149 return;
150
151 xLayoutManager->lock();
152 static constexpr OUString s_sDesignToolbar = u"private:resource/toolbar/designobjectbar"_ustr;
153 static constexpr OUString s_sSqlToolbar = u"private:resource/toolbar/sqlobjectbar"_ustr;
154 if ( _bDesign )
155 {
156 xLayoutManager->destroyElement( s_sSqlToolbar );
157 xLayoutManager->createElement( s_sDesignToolbar );
158 }
159 else
160 {
161 xLayoutManager->destroyElement( s_sDesignToolbar );
162 xLayoutManager->createElement( s_sSqlToolbar );
163 }
164 xLayoutManager->unlock();
165 xLayoutManager->doLayout();
166 }
167
168 /**
169 * The value of m_nLimit is updated when LimitBox loses its focus
170 * So in those case when execution needs recent data, grab the focus
171 * (e.g. execute SQL statement, change views)
172 */
grabFocusFromLimitBox(OQueryController & _rController)173 void grabFocusFromLimitBox( OQueryController& _rController )
174 {
175 Reference< XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() );
176 Reference< XUIElement > xUIElement = xLayoutManager->getElement(u"private:resource/toolbar/designobjectbar"_ustr);
177 if (xUIElement.is())
178 {
179 Reference< XWindow > xWindow(xUIElement->getRealInterface(), css::uno::UNO_QUERY);
180 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
181 if( pWindow && pWindow->HasChildPathFocus() )
182 {
183 pWindow->GrabFocusToDocument();
184 }
185 }
186 }
187 }
188
getImplementationName()189 OUString SAL_CALL OQueryController::getImplementationName()
190 {
191 return u"org.openoffice.comp.dbu.OQueryDesign"_ustr;
192 }
193
getSupportedServiceNames()194 Sequence< OUString> SAL_CALL OQueryController::getSupportedServiceNames()
195 {
196 return { u"com.sun.star.sdb.QueryDesign"_ustr };
197 }
198
OQueryController(const Reference<XComponentContext> & _rM)199 OQueryController::OQueryController(const Reference< XComponentContext >& _rM)
200 :OJoinController(_rM)
201 ,OQueryController_PBase( getBroadcastHelper() )
202 ,m_pParseContext( new svxform::OSystemParseContext )
203 ,m_aSqlParser( _rM, m_pParseContext.get() )
204 ,m_nLimit(-1)
205 ,m_nVisibleRows(0x400)
206 ,m_nSplitPos(-1)
207 ,m_nCommandType( CommandType::QUERY )
208 ,m_bGraphicalDesign(false)
209 ,m_bDistinct(false)
210 ,m_bEscapeProcessing(true)
211 {
212 InvalidateAll();
213
214 registerProperty( PROPERTY_ACTIVECOMMAND, PROPERTY_ID_ACTIVECOMMAND, PropertyAttribute::READONLY | PropertyAttribute::BOUND,
215 &m_sStatement, cppu::UnoType<decltype(m_sStatement)>::get() );
216 registerProperty( PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::READONLY | PropertyAttribute::BOUND,
217 &m_bEscapeProcessing, cppu::UnoType<decltype(m_bEscapeProcessing)>::get() );
218 }
219
~OQueryController()220 OQueryController::~OQueryController()
221 {
222 if ( !getBroadcastHelper().bDisposed && !getBroadcastHelper().bInDispose )
223 {
224 OSL_FAIL("Please check who doesn't dispose this component!");
225 // increment ref count to prevent double call of Dtor
226 osl_atomic_increment( &m_refCount );
227 dispose();
228 }
229 }
230
IMPLEMENT_FORWARD_XINTERFACE2(OQueryController,OJoinController,OQueryController_PBase)231 IMPLEMENT_FORWARD_XINTERFACE2( OQueryController, OJoinController, OQueryController_PBase )
232 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OQueryController, OJoinController, OQueryController_PBase )
233
234 Reference< XPropertySetInfo > SAL_CALL OQueryController::getPropertySetInfo()
235 {
236 Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
237 return xInfo;
238 }
239
getFastPropertyValue(Any & o_rValue,sal_Int32 i_nHandle) const240 void SAL_CALL OQueryController::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const
241 {
242 switch ( i_nHandle )
243 {
244 case PROPERTY_ID_CURRENT_QUERY_DESIGN:
245 {
246 ::comphelper::NamedValueCollection aCurrentDesign;
247 aCurrentDesign.put( u"GraphicalDesign"_ustr, isGraphicalDesign() );
248 aCurrentDesign.put( PROPERTY_ESCAPE_PROCESSING, m_bEscapeProcessing );
249
250 if ( isGraphicalDesign() )
251 {
252 getContainer()->SaveUIConfig();
253 saveViewSettings( aCurrentDesign, true );
254 aCurrentDesign.put( u"Statement"_ustr, m_sStatement );
255 }
256 else
257 {
258 aCurrentDesign.put( u"Statement"_ustr, getContainer()->getStatement() );
259 }
260
261 o_rValue <<= aCurrentDesign.getPropertyValues();
262 }
263 break;
264
265 default:
266 OPropertyContainer::getFastPropertyValue( o_rValue, i_nHandle );
267 break;
268 }
269 }
270
getInfoHelper()271 ::cppu::IPropertyArrayHelper& OQueryController::getInfoHelper()
272 {
273 return *getArrayHelper();
274 }
275
createArrayHelper() const276 ::cppu::IPropertyArrayHelper* OQueryController::createArrayHelper( ) const
277 {
278 Sequence< Property > aProps;
279 describeProperties( aProps );
280
281 // one additional property:
282 const sal_Int32 nLength = aProps.getLength();
283 aProps.realloc( nLength + 1 );
284 auto pProps = aProps.getArray();
285 pProps[ nLength ] = Property(
286 u"CurrentQueryDesign"_ustr,
287 PROPERTY_ID_CURRENT_QUERY_DESIGN,
288 ::cppu::UnoType< Sequence< PropertyValue > >::get(),
289 PropertyAttribute::READONLY
290 );
291
292 std::sort(
293 pProps,
294 pProps + aProps.getLength(),
295 ::comphelper::PropertyCompareByName()
296 );
297
298 return new ::cppu::OPropertyArrayHelper(aProps);
299 }
300
deleteIterator()301 void OQueryController::deleteIterator()
302 {
303 if(m_pSqlIterator)
304 {
305 delete m_pSqlIterator->getParseTree();
306 m_pSqlIterator->dispose();
307 m_pSqlIterator.reset();
308 }
309 }
310
disposing()311 void OQueryController::disposing()
312 {
313 OQueryController_PBase::disposing();
314
315 deleteIterator();
316
317 m_pParseContext.reset();
318
319 clearFields();
320 OTableFields().swap(m_vUnUsedFieldsDesc);
321
322 ::comphelper::disposeComponent(m_xComposer);
323 OJoinController::disposing();
324 OQueryController_PBase::disposing();
325 }
326
clearFields()327 void OQueryController::clearFields()
328 {
329 OTableFields().swap(m_vTableFieldDesc);
330 }
331
GetState(sal_uInt16 _nId) const332 FeatureState OQueryController::GetState(sal_uInt16 _nId) const
333 {
334 FeatureState aReturn;
335 aReturn.bEnabled = true;
336 // (disabled automatically)
337
338 switch (_nId)
339 {
340 case ID_BROWSER_EDITDOC:
341 if ( editingCommand() )
342 aReturn.bEnabled = false;
343 else if ( editingView() && !m_xAlterView.is() )
344 aReturn.bEnabled = false;
345 else
346 aReturn = OJoinController::GetState( _nId );
347 break;
348
349 case ID_BROWSER_ESCAPEPROCESSING:
350 aReturn.bChecked = !m_bEscapeProcessing;
351 aReturn.bEnabled = ( m_pSqlIterator != nullptr ) && !m_bGraphicalDesign;
352 break;
353 case SID_RELATION_ADD_RELATION:
354 aReturn.bEnabled = isEditable() && m_bGraphicalDesign && m_vTableData.size() > 1;
355 break;
356 case ID_BROWSER_SAVEASDOC:
357 aReturn.bEnabled = !editingCommand() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty()));
358 break;
359 case ID_BROWSER_SAVEDOC:
360 aReturn.bEnabled = isEditable() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty()));
361 break;
362 case SID_PRINTDOCDIRECT:
363 break;
364 case ID_BROWSER_CUT:
365 aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isCutAllowed();
366 break;
367 case ID_BROWSER_COPY:
368 aReturn.bEnabled = getContainer() && getContainer()->isCopyAllowed();
369 break;
370 case ID_BROWSER_PASTE:
371 aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isPasteAllowed();
372 break;
373 case ID_BROWSER_SQL:
374 aReturn.bEnabled = m_bEscapeProcessing && m_pSqlIterator;
375 aReturn.bChecked = m_bGraphicalDesign;
376 break;
377 case SID_BROWSER_CLEAR_QUERY:
378 aReturn.bEnabled = isEditable() && (!m_sStatement.isEmpty() || !m_vTableData.empty());
379 break;
380 case SID_QUERY_VIEW_FUNCTIONS:
381 case SID_QUERY_VIEW_TABLES:
382 case SID_QUERY_VIEW_ALIASES:
383 aReturn.bChecked = getContainer() && getContainer()->isSlotEnabled(_nId);
384 aReturn.bEnabled = m_bGraphicalDesign;
385 break;
386 case SID_QUERY_DISTINCT_VALUES:
387 aReturn.bEnabled = m_bGraphicalDesign && isEditable();
388 aReturn.bChecked = m_bDistinct;
389 break;
390 case SID_QUERY_LIMIT:
391 aReturn.bEnabled = m_bGraphicalDesign;
392 if( aReturn.bEnabled )
393 aReturn.aValue <<= m_nLimit;
394 break;
395 case SID_QUERY_PROP_DLG:
396 aReturn.bEnabled = m_bGraphicalDesign;
397 break;
398 case ID_BROWSER_QUERY_EXECUTE:
399 aReturn.bEnabled = true;
400 break;
401 case SID_DB_QUERY_PREVIEW:
402 aReturn.bEnabled = true;
403 aReturn.bChecked = getContainer() && getContainer()->getPreviewFrame().is();
404 break;
405 #if OSL_DEBUG_LEVEL > 0
406 case ID_EDIT_QUERY_SQL:
407 break;
408 case ID_EDIT_QUERY_DESIGN:
409 break;
410 #endif
411 case ID_BROWSER_ADDTABLE:
412 if ( !m_bGraphicalDesign )
413 {
414 aReturn.bEnabled = false;
415 break;
416 }
417 [[fallthrough]];
418 default:
419 aReturn = OJoinController::GetState(_nId);
420 break;
421 }
422 return aReturn;
423 }
424
Execute(sal_uInt16 _nId,const Sequence<PropertyValue> & aArgs)425 void OQueryController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs)
426 {
427 switch(_nId)
428 {
429 case ID_BROWSER_ESCAPEPROCESSING:
430 setEscapeProcessing_fireEvent( !m_bEscapeProcessing );
431 if ( !editingView() )
432 setModified(true);
433 InvalidateFeature(ID_BROWSER_SQL);
434 break;
435 case ID_BROWSER_SAVEASDOC:
436 case ID_BROWSER_SAVEDOC:
437 grabFocusFromLimitBox(*this);
438 doSaveAsDoc(ID_BROWSER_SAVEASDOC == _nId);
439 break;
440 case SID_RELATION_ADD_RELATION:
441 {
442 OJoinDesignView* pView = getJoinView();
443 if( pView )
444 static_cast<OQueryTableView*>(pView->getTableView())->createNewConnection();
445 }
446 break;
447 case SID_PRINTDOCDIRECT:
448 break;
449 case ID_BROWSER_CUT:
450 getContainer()->cut();
451 break;
452 case ID_BROWSER_COPY:
453 getContainer()->copy();
454 break;
455 case ID_BROWSER_PASTE:
456 getContainer()->paste();
457 break;
458 case ID_BROWSER_SQL:
459 {
460 grabFocusFromLimitBox(*this);
461 if ( !getContainer()->checkStatement() )
462 break;
463 SQLExceptionInfo aError;
464 try
465 {
466 setStatement_fireEvent( getContainer()->getStatement() );
467 if(m_sStatement.isEmpty() && m_pSqlIterator)
468 {
469 // change the view of the data
470 delete m_pSqlIterator->getParseTree();
471 m_pSqlIterator->setParseTree(nullptr);
472 m_bGraphicalDesign = !m_bGraphicalDesign;
473 impl_setViewMode( &aError );
474 }
475 else
476 {
477 OUString aErrorMsg;
478 std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree(aErrorMsg,m_sStatement,m_bGraphicalDesign);
479 if ( pNode )
480 {
481 assert(m_pSqlIterator && "SqlIterator must exist");
482 delete m_pSqlIterator->getParseTree();
483 m_pSqlIterator->setParseTree(pNode.release());
484 m_pSqlIterator->traverseAll();
485
486 if ( m_pSqlIterator->hasErrors() )
487 {
488 aError = m_pSqlIterator->getErrors();
489 }
490 else
491 {
492 const OSQLTables& rTabs = m_pSqlIterator->getTables();
493 if ( m_pSqlIterator->getStatementType() != OSQLStatementType::Select || rTabs.empty() )
494 {
495 aError = SQLException(
496 DBA_RES(STR_QRY_NOSELECT),
497 nullptr,
498 u"S1000"_ustr,
499 1000,
500 Any()
501 );
502 }
503 else
504 {
505 // change the view of the data
506 m_bGraphicalDesign = !m_bGraphicalDesign;
507 OUString sNewStatement;
508 m_pSqlIterator->getParseTree()->parseNodeToStr( sNewStatement, getConnection() );
509 setStatement_fireEvent( sNewStatement );
510 getContainer()->SaveUIConfig();
511 m_vTableConnectionData.clear();
512 impl_setViewMode( &aError );
513 }
514 }
515 }
516 else
517 {
518 aError = SQLException(
519 DBA_RES(STR_QRY_SYNTAX),
520 nullptr,
521 u"S1000"_ustr,
522 1000,
523 Any()
524 );
525 }
526 }
527 }
528 catch(const SQLException&)
529 {
530 aError = ::cppu::getCaughtException();
531 }
532 catch(const Exception&)
533 {
534 DBG_UNHANDLED_EXCEPTION("dbaccess");
535 }
536
537 if ( aError.isValid() )
538 showError( aError );
539
540 if(m_bGraphicalDesign)
541 {
542 InvalidateFeature(ID_BROWSER_ADDTABLE);
543 InvalidateFeature(SID_RELATION_ADD_RELATION);
544 }
545 }
546 break;
547 case SID_BROWSER_CLEAR_QUERY:
548 {
549 GetUndoManager().EnterListAction(DBA_RES(STR_QUERY_UNDO_TABWINDELETE), OUString(), 0, ViewShellId(-1) );
550 getContainer()->clear();
551 GetUndoManager().LeaveListAction();
552
553 setStatement_fireEvent( OUString() );
554 if(m_bGraphicalDesign)
555 InvalidateFeature(ID_BROWSER_ADDTABLE);
556 }
557 break;
558 case SID_QUERY_VIEW_FUNCTIONS:
559 case SID_QUERY_VIEW_TABLES:
560 case SID_QUERY_VIEW_ALIASES:
561 getContainer()->setSlotEnabled(_nId,!getContainer()->isSlotEnabled(_nId));
562 setModified(true);
563 break;
564 case SID_QUERY_DISTINCT_VALUES:
565 m_bDistinct = !m_bDistinct;
566 setModified(true);
567 break;
568 case SID_QUERY_LIMIT:
569 if ( aArgs.hasElements() && aArgs[0].Name == "DBLimit.Value" )
570 {
571 aArgs[0].Value >>= m_nLimit;
572 setModified(true);
573 }
574 break;
575 case SID_QUERY_PROP_DLG:
576 grabFocusFromLimitBox(*this);
577 execute_QueryPropDlg();
578 break;
579 case ID_BROWSER_QUERY_EXECUTE:
580 grabFocusFromLimitBox(*this);
581 if ( getContainer()->checkStatement() )
582 executeQuery();
583 break;
584 case SID_DB_QUERY_PREVIEW:
585 try
586 {
587 Reference< css::util::XCloseable > xCloseFrame( getContainer()->getPreviewFrame(), UNO_QUERY );
588 if ( xCloseFrame.is() )
589 {
590 try
591 {
592 xCloseFrame->close( true );
593 }
594 catch(const Exception&)
595 {
596 OSL_FAIL( "OQueryController::Execute(SID_DB_QUERY_PREVIEW): *nobody* is expected to veto closing the preview frame!" );
597 }
598 }
599 else
600 Execute(ID_BROWSER_QUERY_EXECUTE,Sequence< PropertyValue >());
601 }
602 catch(const Exception&)
603 {
604 }
605 break;
606 default:
607 OJoinController::Execute(_nId,aArgs);
608 return; // else we would invalidate twice
609 }
610 InvalidateFeature(_nId);
611 }
612
impl_showAutoSQLViewError(const css::uno::Any & _rErrorDetails)613 void OQueryController::impl_showAutoSQLViewError( const css::uno::Any& _rErrorDetails )
614 {
615 SQLContext aErrorContext(
616 lcl_getObjectResourceString(STR_ERROR_PARSING_STATEMENT, m_nCommandType), *this, {}, 0,
617 _rErrorDetails, lcl_getObjectResourceString(STR_INFO_OPENING_IN_SQL_VIEW, m_nCommandType));
618 showError( aErrorContext );
619 }
620
impl_setViewMode(::dbtools::SQLExceptionInfo * _pErrorInfo)621 void OQueryController::impl_setViewMode( ::dbtools::SQLExceptionInfo* _pErrorInfo )
622 {
623 OSL_PRECOND( getContainer(), "OQueryController::impl_setViewMode: illegal call!" );
624
625 bool wasModified = isModified();
626
627 SQLExceptionInfo aError;
628 bool bSuccess = getContainer()->switchView( &aError );
629 if ( !bSuccess )
630 {
631 m_bGraphicalDesign = !m_bGraphicalDesign;
632 // restore old state
633 getContainer()->switchView( nullptr );
634 // don't pass &aError here, this would overwrite the error which the first switchView call
635 // returned in this location.
636 if ( _pErrorInfo )
637 *_pErrorInfo = std::move(aError);
638 else
639 showError( aError );
640 }
641 else
642 {
643 ensureToolbars( *this, m_bGraphicalDesign );
644 }
645
646 setModified( wasModified );
647 }
648
impl_initialize(const::comphelper::NamedValueCollection & rArguments)649 void OQueryController::impl_initialize(const ::comphelper::NamedValueCollection& rArguments)
650 {
651 OJoinController::impl_initialize(rArguments);
652
653 OUString sCommand;
654 m_nCommandType = CommandType::QUERY;
655
656 // reading parameters:
657
658 // legacy parameters first (later overwritten by regular parameters)
659 OUString sIndependentSQLCommand;
660 if ( rArguments.get_ensureType( u"IndependentSQLCommand"_ustr, sIndependentSQLCommand ) )
661 {
662 OSL_FAIL( "OQueryController::impl_initialize: IndependentSQLCommand is regognized for compatibility only!" );
663 sCommand = sIndependentSQLCommand;
664 m_nCommandType = CommandType::COMMAND;
665 }
666
667 OUString sCurrentQuery;
668 if ( rArguments.get_ensureType( u"CurrentQuery"_ustr, sCurrentQuery ) )
669 {
670 OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" );
671 sCommand = sCurrentQuery;
672 m_nCommandType = CommandType::QUERY;
673 }
674
675 bool bCreateView( false );
676 if ( rArguments.get_ensureType( u"CreateView"_ustr, bCreateView ) && bCreateView )
677 {
678 OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" );
679 m_nCommandType = CommandType::TABLE;
680 }
681
682 // non-legacy parameters which overwrite the legacy parameters
683 rArguments.get_ensureType( PROPERTY_COMMAND, sCommand );
684 rArguments.get_ensureType( PROPERTY_COMMAND_TYPE, m_nCommandType );
685
686 // translate Command/Type into proper members
687 // TODO/Later: all this (including those members) should be hidden behind some abstract interface,
688 // which is implemented for all the three commands
689 switch ( m_nCommandType )
690 {
691 case CommandType::QUERY:
692 case CommandType::TABLE:
693 m_sName = sCommand;
694 break;
695 case CommandType::COMMAND:
696 setStatement_fireEvent( sCommand );
697 m_sName.clear();
698 break;
699 default:
700 OSL_FAIL( "OQueryController::impl_initialize: logic error in code!" );
701 throw RuntimeException();
702 }
703
704 // more legacy parameters
705 bool bGraphicalDesign( true );
706 if ( rArguments.get_ensureType( PROPERTY_QUERYDESIGNVIEW, bGraphicalDesign ) )
707 {
708 OSL_FAIL( "OQueryController::impl_initialize: QueryDesignView is regognized for compatibility only!" );
709 m_bGraphicalDesign = bGraphicalDesign;
710 }
711
712 // more non-legacy
713 rArguments.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign );
714
715 bool bEscapeProcessing( true );
716 if ( rArguments.get_ensureType( PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing ) )
717 {
718 setEscapeProcessing_fireEvent( bEscapeProcessing );
719
720 OSL_ENSURE( m_bEscapeProcessing || !m_bGraphicalDesign, "OQueryController::impl_initialize: can't do the graphical design without escape processing!" );
721 if ( !m_bEscapeProcessing )
722 m_bGraphicalDesign = false;
723 }
724
725 // initial design
726 bool bForceInitialDesign = false;
727 Sequence< PropertyValue > aCurrentQueryDesignProps;
728 aCurrentQueryDesignProps = rArguments.getOrDefault( u"CurrentQueryDesign"_ustr, aCurrentQueryDesignProps );
729
730 if ( aCurrentQueryDesignProps.hasElements() )
731 {
732 ::comphelper::NamedValueCollection aCurrentQueryDesign( aCurrentQueryDesignProps );
733 if ( aCurrentQueryDesign.has( PROPERTY_GRAPHICAL_DESIGN ) )
734 {
735 aCurrentQueryDesign.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign );
736 }
737 if ( aCurrentQueryDesign.has( PROPERTY_ESCAPE_PROCESSING ) )
738 {
739 aCurrentQueryDesign.get_ensureType( PROPERTY_ESCAPE_PROCESSING, m_bEscapeProcessing );
740 }
741 if ( aCurrentQueryDesign.has( u"Statement"_ustr ) )
742 {
743 OUString sStatement;
744 aCurrentQueryDesign.get_ensureType( u"Statement"_ustr, sStatement );
745 aCurrentQueryDesign.remove( u"Statement"_ustr );
746 setStatement_fireEvent( sStatement );
747 }
748
749 loadViewSettings( aCurrentQueryDesign );
750
751 bForceInitialDesign = true;
752 }
753
754 if ( !ensureConnected() )
755 { // we have no connection so what else should we do
756 m_bGraphicalDesign = false;
757 if ( editingView() )
758 {
759 connectionLostMessage();
760 throw SQLException();
761 }
762 }
763
764 // check the view capabilities
765 if ( isConnected() && editingView() )
766 {
767 Reference< XViewsSupplier > xViewsSup( getConnection(), UNO_QUERY );
768 Reference< XNameAccess > xViews;
769 if ( xViewsSup.is() )
770 xViews = xViewsSup->getViews();
771
772 if ( !xViews.is() )
773 { // we can't create views so we ask if the user wants to create a query instead
774 m_nCommandType = CommandType::QUERY;
775 bool bClose = false;
776 {
777 OUString aTitle(DBA_RES(STR_QUERYDESIGN_NO_VIEW_SUPPORT));
778 OUString aMessage(DBA_RES(STR_QUERYDESIGN_NO_VIEW_ASK));
779 OSQLMessageBox aDlg(getFrameWeld(), aTitle, aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, MessageType::Query);
780 bClose = aDlg.run() == RET_NO;
781 }
782 if ( bClose )
783 throw VetoException();
784 }
785
786 // now if we are to edit an existing view, check whether this is possible
787 if ( !m_sName.isEmpty() )
788 {
789 Any aView( xViews->getByName( m_sName ) );
790 // will throw if there is no such view
791 if ( !( aView >>= m_xAlterView ) )
792 {
793 throw IllegalArgumentException(
794 DBA_RES(STR_NO_ALTER_VIEW_SUPPORT),
795 *this,
796 1
797 );
798 }
799 }
800 }
801
802 OSL_ENSURE(getDataSource().is(),"OQueryController::impl_initialize: need a datasource!");
803
804 try
805 {
806 getContainer()->initialize();
807 impl_reset( bForceInitialDesign );
808
809 SQLExceptionInfo aError;
810 const bool bAttemptedGraphicalDesign = m_bGraphicalDesign;
811
812 if ( bForceInitialDesign )
813 {
814 getContainer()->forceInitialView();
815 }
816 else
817 {
818 impl_setViewMode( &aError );
819 }
820
821 if ( aError.isValid() && bAttemptedGraphicalDesign && !m_bGraphicalDesign )
822 {
823 // we tried initializing the graphical view, this failed, and we were automatically switched to SQL
824 // view => tell this to the user
825 if ( !editingView() )
826 {
827 impl_showAutoSQLViewError( aError.get() );
828 }
829 }
830
831 ClearUndoManager();
832
833 if ( m_bGraphicalDesign
834 && ( ( m_sName.isEmpty() && !editingCommand() )
835 || ( m_sStatement.isEmpty() && editingCommand() )
836 )
837 )
838 {
839 Application::PostUserEvent( LINK( this, OQueryController, OnExecuteAddTable ) );
840 }
841
842 setModified(false);
843 }
844 catch(const SQLException& e)
845 {
846 DBG_UNHANDLED_EXCEPTION("dbaccess");
847 // we caught an exception so we switch to text only mode
848 {
849 m_bGraphicalDesign = false;
850 getContainer()->initialize();
851 OSQLMessageBox aBox(getFrameWeld(), e);
852 aBox.run();
853 }
854 throw;
855 }
856 }
857
onLoadedMenu(const Reference<css::frame::XLayoutManager> &)858 void OQueryController::onLoadedMenu(const Reference< css::frame::XLayoutManager >& /*_xLayoutManager*/)
859 {
860 ensureToolbars( *this, m_bGraphicalDesign );
861 }
862
getPrivateTitle() const863 OUString OQueryController::getPrivateTitle( ) const
864 {
865 if ( m_sName.isEmpty() )
866 {
867 if ( !editingCommand() )
868 {
869 SolarMutexGuard aSolarGuard;
870 ::osl::MutexGuard aGuard( getMutex() );
871 OUString aDefaultName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE);
872 return o3tl::getToken(aDefaultName, 0, ' ') + OUString::number(getCurrentStartNumber());
873 }
874 }
875 return m_sName;
876 }
877
setQueryComposer()878 void OQueryController::setQueryComposer()
879 {
880 if(!isConnected())
881 return;
882
883 Reference< XSQLQueryComposerFactory > xFactory(getConnection(), UNO_QUERY);
884 OSL_ENSURE(xFactory.is(),"Connection doesn't support a querycomposer");
885 if ( !(xFactory.is() && getContainer()) )
886 return;
887
888 try
889 {
890 m_xComposer = xFactory->createQueryComposer();
891 getContainer()->setStatement(m_sStatement);
892 }
893 catch(const Exception&)
894 {
895 m_xComposer = nullptr;
896 }
897 OSL_ENSURE(m_xComposer.is(),"No querycomposer available!");
898 Reference<XTablesSupplier> xTablesSup(getConnection(), UNO_QUERY);
899 deleteIterator();
900 m_pSqlIterator.reset(new ::connectivity::OSQLParseTreeIterator( getConnection(), xTablesSup->getTables(), m_aSqlParser ));
901 }
902
Construct(vcl::Window * pParent)903 bool OQueryController::Construct(vcl::Window* pParent)
904 {
905 // TODO: we have to check if we should create the text view or the design view
906
907 setView( VclPtr<OQueryContainerWindow>::Create( pParent, *this, getORB() ) );
908
909 return OJoinController::Construct(pParent);
910 }
911
getJoinView()912 OJoinDesignView* OQueryController::getJoinView()
913 {
914 return getContainer()->getDesignView();
915 }
916
describeSupportedFeatures()917 void OQueryController::describeSupportedFeatures()
918 {
919 OJoinController::describeSupportedFeatures();
920 implDescribeSupportedFeature( u".uno:SaveAs"_ustr, ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT );
921 implDescribeSupportedFeature( u".uno:SbaNativeSql"_ustr, ID_BROWSER_ESCAPEPROCESSING,CommandGroup::FORMAT );
922 implDescribeSupportedFeature( u".uno:DBViewFunctions"_ustr, SID_QUERY_VIEW_FUNCTIONS, CommandGroup::VIEW );
923 implDescribeSupportedFeature( u".uno:DBViewTableNames"_ustr, SID_QUERY_VIEW_TABLES, CommandGroup::VIEW );
924 implDescribeSupportedFeature( u".uno:DBViewAliases"_ustr, SID_QUERY_VIEW_ALIASES, CommandGroup::VIEW );
925 implDescribeSupportedFeature( u".uno:DBDistinctValues"_ustr, SID_QUERY_DISTINCT_VALUES, CommandGroup::FORMAT );
926 implDescribeSupportedFeature( u".uno:DBChangeDesignMode"_ustr,ID_BROWSER_SQL, CommandGroup::VIEW );
927 implDescribeSupportedFeature( u".uno:DBClearQuery"_ustr, SID_BROWSER_CLEAR_QUERY, CommandGroup::EDIT );
928 implDescribeSupportedFeature( u".uno:SbaExecuteSql"_ustr, ID_BROWSER_QUERY_EXECUTE, CommandGroup::VIEW );
929 implDescribeSupportedFeature( u".uno:DBAddRelation"_ustr, SID_RELATION_ADD_RELATION, CommandGroup::EDIT );
930 implDescribeSupportedFeature( u".uno:DBQueryPreview"_ustr, SID_DB_QUERY_PREVIEW, CommandGroup::VIEW );
931 implDescribeSupportedFeature( u".uno:DBLimit"_ustr, SID_QUERY_LIMIT, CommandGroup::FORMAT );
932 implDescribeSupportedFeature( u".uno:DBQueryPropertiesDialog"_ustr, SID_QUERY_PROP_DLG, CommandGroup::FORMAT );
933
934 #if OSL_DEBUG_LEVEL > 0
935 implDescribeSupportedFeature( u".uno:DBShowParseTree"_ustr, ID_EDIT_QUERY_SQL );
936 implDescribeSupportedFeature( u".uno:DBMakeDisjunct"_ustr, ID_EDIT_QUERY_DESIGN );
937 #endif
938 }
939
impl_onModifyChanged()940 void OQueryController::impl_onModifyChanged()
941 {
942 OJoinController::impl_onModifyChanged();
943 InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
944 InvalidateFeature(ID_BROWSER_SAVEASDOC);
945 InvalidateFeature(ID_BROWSER_QUERY_EXECUTE);
946 }
947
disposing(const EventObject & Source)948 void SAL_CALL OQueryController::disposing( const EventObject& Source )
949 {
950 SolarMutexGuard aGuard;
951
952 if ( getContainer() && Source.Source.is() )
953 {
954 if ( Source.Source == m_aCurrentFrame.getFrame() )
955 { // our frame is being disposed -> close the preview window (if we have one)
956 Reference< XFrame2 > xPreviewFrame( getContainer()->getPreviewFrame() );
957 ::comphelper::disposeComponent( xPreviewFrame );
958 }
959 else if ( Source.Source == getContainer()->getPreviewFrame() )
960 {
961 getContainer()->disposingPreview();
962 }
963 }
964
965 OJoinController_BASE::disposing(Source);
966 }
967
reconnect(bool _bUI)968 void OQueryController::reconnect(bool _bUI)
969 {
970 deleteIterator();
971 ::comphelper::disposeComponent(m_xComposer);
972
973 OJoinController::reconnect( _bUI );
974
975 if (isConnected())
976 {
977 setQueryComposer();
978 }
979 else
980 {
981 if(m_bGraphicalDesign)
982 {
983 m_bGraphicalDesign = false;
984 // don't call Execute(SQL) because this changes the sql statement
985 impl_setViewMode( nullptr );
986 }
987 InvalidateAll();
988 }
989 }
990
saveViewSettings(::comphelper::NamedValueCollection & o_rViewSettings,const bool i_includingCriteria) const991 void OQueryController::saveViewSettings( ::comphelper::NamedValueCollection& o_rViewSettings, const bool i_includingCriteria ) const
992 {
993 saveTableWindows( o_rViewSettings );
994
995 ::comphelper::NamedValueCollection aAllFieldsData;
996 ::comphelper::NamedValueCollection aFieldData;
997 sal_Int32 i = 1;
998 for (auto const& fieldDesc : m_vTableFieldDesc)
999 {
1000 if ( !fieldDesc->IsEmpty() )
1001 {
1002 aFieldData.clear();
1003 fieldDesc->Save( aFieldData, i_includingCriteria );
1004
1005 const OUString sFieldSettingName = "Field" + OUString::number( i );
1006 aAllFieldsData.put( sFieldSettingName, aFieldData.getPropertyValues() );
1007 }
1008 ++i;
1009 }
1010
1011 o_rViewSettings.put( u"Fields"_ustr, aAllFieldsData.getPropertyValues() );
1012 o_rViewSettings.put( u"SplitterPosition"_ustr, m_nSplitPos );
1013 o_rViewSettings.put( u"VisibleRows"_ustr, m_nVisibleRows );
1014 }
1015
loadViewSettings(const::comphelper::NamedValueCollection & o_rViewSettings)1016 void OQueryController::loadViewSettings( const ::comphelper::NamedValueCollection& o_rViewSettings )
1017 {
1018 loadTableWindows( o_rViewSettings );
1019
1020 m_nSplitPos = o_rViewSettings.getOrDefault( u"SplitterPosition"_ustr, m_nSplitPos );
1021 m_nVisibleRows = o_rViewSettings.getOrDefault( u"VisibleRows"_ustr, m_nVisibleRows );
1022 m_aFieldInformation = o_rViewSettings.getOrDefault( u"Fields"_ustr, m_aFieldInformation );
1023 }
1024
execute_QueryPropDlg()1025 void OQueryController::execute_QueryPropDlg()
1026 {
1027 QueryPropertiesDialog aQueryPropDlg(getContainer()->GetFrameWeld(), m_bDistinct, m_nLimit);
1028
1029 if (aQueryPropDlg.run() == RET_OK)
1030 {
1031 m_bDistinct = aQueryPropDlg.getDistinct();
1032 m_nLimit = aQueryPropDlg.getLimit();
1033 InvalidateFeature( SID_QUERY_DISTINCT_VALUES );
1034 InvalidateFeature( SID_QUERY_LIMIT, nullptr, true );
1035 }
1036 }
1037
getColWidth(sal_uInt16 _nColPos) const1038 sal_Int32 OQueryController::getColWidth(sal_uInt16 _nColPos) const
1039 {
1040 if ( _nColPos < m_aFieldInformation.getLength() )
1041 {
1042 rtl::Reference<OTableFieldDesc> pField( new OTableFieldDesc());
1043 pField->Load( m_aFieldInformation[ _nColPos ], false );
1044 return pField->GetColWidth();
1045 }
1046 return 0;
1047 }
1048
getObjectContainer() const1049 Reference<XNameAccess> OQueryController::getObjectContainer() const
1050 {
1051 Reference< XNameAccess > xElements;
1052 if ( editingView() )
1053 {
1054 Reference< XViewsSupplier > xViewsSupp( getConnection(), UNO_QUERY );
1055 if ( xViewsSupp.is() )
1056 xElements = xViewsSupp->getViews();
1057 }
1058 else
1059 {
1060 Reference< XQueriesSupplier > xQueriesSupp( getConnection(), UNO_QUERY );
1061 if ( xQueriesSupp.is() )
1062 xElements = xQueriesSupp->getQueries();
1063 else
1064 {
1065 Reference< XQueryDefinitionsSupplier > xQueryDefsSupp( getDataSource(), UNO_QUERY );
1066 if ( xQueryDefsSupp.is() )
1067 xElements = xQueryDefsSupp->getQueryDefinitions();
1068 }
1069 }
1070
1071 OSL_ENSURE( xElements.is(), "OQueryController::getObjectContainer: unable to obtain the container!" );
1072 return xElements;
1073 }
1074
executeQuery()1075 void OQueryController::executeQuery()
1076 {
1077 // we don't need to check the connection here because we already check the composer
1078 // which can't live without his connection
1079 OUString sTranslatedStmt = translateStatement( false );
1080
1081 OUString sDataSourceName = getDataSourceName();
1082 if ( sDataSourceName.isEmpty() || sTranslatedStmt.isEmpty() )
1083 return;
1084
1085 try
1086 {
1087 getContainer()->showPreview( getFrame() );
1088 InvalidateFeature(SID_DB_QUERY_PREVIEW);
1089
1090 URL aWantToDispatch;
1091 aWantToDispatch.Complete = ".component:DB/DataSourceBrowser";
1092
1093 OUString sFrameName( FRAME_NAME_QUERY_PREVIEW );
1094 sal_Int32 nSearchFlags = FrameSearchFlag::CHILDREN;
1095
1096 Reference< XDispatch> xDisp;
1097 Reference< XDispatchProvider> xProv( getFrame()->findFrame( sFrameName, nSearchFlags ), UNO_QUERY );
1098 if(!xProv.is())
1099 {
1100 xProv.set( getFrame(), UNO_QUERY );
1101 if (xProv.is())
1102 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, nSearchFlags);
1103 }
1104 else
1105 {
1106 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, FrameSearchFlag::SELF);
1107 }
1108 if (xDisp.is())
1109 {
1110 auto aProps(::comphelper::InitPropertySequence(
1111 {
1112 { PROPERTY_DATASOURCENAME, Any(sDataSourceName) },
1113 { PROPERTY_COMMAND_TYPE, Any(CommandType::COMMAND) },
1114 { PROPERTY_COMMAND, Any(sTranslatedStmt) },
1115 { PROPERTY_ENABLE_BROWSER, Any(false) },
1116 { PROPERTY_ACTIVE_CONNECTION, Any(getConnection()) },
1117 { PROPERTY_UPDATE_CATALOGNAME, Any(m_sUpdateCatalogName) },
1118 { PROPERTY_UPDATE_SCHEMANAME, Any(m_sUpdateSchemaName) },
1119 { PROPERTY_UPDATE_TABLENAME, Any(OUString()) },
1120 { PROPERTY_ESCAPE_PROCESSING, Any(m_bEscapeProcessing) }
1121 }));
1122
1123 xDisp->dispatch(aWantToDispatch, aProps);
1124 // check the state of the beamer
1125 // be notified when the beamer frame is closed
1126 Reference< XComponent > xComponent = getFrame()->findFrame( sFrameName, nSearchFlags );
1127 if (xComponent.is())
1128 {
1129 OSL_ENSURE(Reference< XFrame >(xComponent, UNO_QUERY).get() == getContainer()->getPreviewFrame().get(),
1130 "OQueryController::executeQuery: oops ... which window do I have here?");
1131 Reference< XEventListener> xEvtL(static_cast<cppu::OWeakObject*>(this),UNO_QUERY);
1132 xComponent->addEventListener(xEvtL);
1133 }
1134 }
1135 else
1136 {
1137 OSL_FAIL("Couldn't create a beamer window!");
1138 }
1139 }
1140 catch(const Exception&)
1141 {
1142 OSL_FAIL("Couldn't create a beamer window!");
1143 }
1144 }
1145
askForNewName(const Reference<XNameAccess> & _xElements,bool _bSaveAs)1146 bool OQueryController::askForNewName(const Reference<XNameAccess>& _xElements, bool _bSaveAs)
1147 {
1148 OSL_ENSURE( !editingCommand(), "OQueryController::askForNewName: not to be called when designing an independent statement!" );
1149 if ( editingCommand() )
1150 return false;
1151
1152 OSL_PRECOND( _xElements.is(), "OQueryController::askForNewName: invalid container!" );
1153 if ( !_xElements.is() )
1154 return false;
1155
1156 bool bRet = true;
1157 bool bNew = _bSaveAs || !_xElements->hasByName( m_sName );
1158 if(bNew)
1159 {
1160 OUString aDefaultName;
1161 if (!m_sName.isEmpty())
1162 aDefaultName = m_sName;
1163 else
1164 {
1165 OUString sName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE);
1166 aDefaultName = ::dbtools::createUniqueName(_xElements, sName.getToken(0, ' '));
1167 }
1168
1169 DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::QUERY );
1170 OSaveAsDlg aDlg(
1171 getFrameWeld(),
1172 m_nCommandType,
1173 getORB(),
1174 getConnection(),
1175 aDefaultName,
1176 aNameChecker,
1177 SADFlags::NONE );
1178
1179 bRet = ( aDlg.run() == RET_OK );
1180 if ( bRet )
1181 {
1182 m_sName = aDlg.getName();
1183 if ( editingView() )
1184 {
1185 m_sUpdateCatalogName = aDlg.getCatalog();
1186 m_sUpdateSchemaName = aDlg.getSchema();
1187 }
1188 }
1189 }
1190 return bRet;
1191 }
1192
doSaveAsDoc(bool _bSaveAs)1193 bool OQueryController::doSaveAsDoc(bool _bSaveAs)
1194 {
1195 OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!");
1196 if ( !editingCommand() && !haveDataSource() )
1197 {
1198 OUString aMessage(DBA_RES(STR_DATASOURCE_DELETED));
1199 OSQLWarningBox aBox(getFrameWeld(), aMessage);
1200 aBox.run();
1201 return false;
1202 }
1203
1204 Reference< XNameAccess > xElements = getObjectContainer();
1205 if ( !xElements.is() )
1206 return false;
1207
1208 if ( !getContainer()->checkStatement() )
1209 return false;
1210
1211 OUString sTranslatedStmt = translateStatement();
1212 if ( editingCommand() )
1213 {
1214 setModified( false );
1215 // this is all we need to do here. translateStatement implicitly set our m_sStatement, and
1216 // notified it, and that's all
1217 return true;
1218 }
1219
1220 if ( sTranslatedStmt.isEmpty() )
1221 return false;
1222
1223 // first we need a name for our query so ask the user
1224 // did we get a name
1225 OUString sOriginalName( m_sName );
1226 if ( !askForNewName( xElements, _bSaveAs ) || m_sName.isEmpty() )
1227 return false;
1228
1229 SQLExceptionInfo aInfo;
1230 bool bSuccess = false;
1231 bool bNew = false;
1232 try
1233 {
1234 bNew = _bSaveAs
1235 || ( !xElements->hasByName( m_sName ) );
1236
1237 Reference<XPropertySet> xQuery;
1238 if ( bNew ) // just to make sure the query already exists
1239 {
1240 // drop the query, in case it already exists
1241 if ( xElements->hasByName( m_sName ) )
1242 {
1243 Reference< XDrop > xNameCont( xElements, UNO_QUERY );
1244 if ( xNameCont.is() )
1245 xNameCont->dropByName( m_sName );
1246 else
1247 {
1248 Reference< XNameContainer > xCont( xElements, UNO_QUERY );
1249 if ( xCont.is() )
1250 xCont->removeByName( m_sName );
1251 }
1252 }
1253
1254 // create a new (empty, uninitialized) query resp. view
1255 Reference< XDataDescriptorFactory > xFact( xElements, UNO_QUERY );
1256 if ( xFact.is() )
1257 {
1258 xQuery = xFact->createDataDescriptor();
1259 // to set the name is only allowed when the query is new
1260 xQuery->setPropertyValue( PROPERTY_NAME, Any( m_sName ) );
1261 }
1262 else
1263 {
1264 Reference< XSingleServiceFactory > xSingleFac( xElements, UNO_QUERY );
1265 if ( xSingleFac.is() )
1266 xQuery.set(xSingleFac->createInstance(), css::uno::UNO_QUERY);
1267 }
1268 }
1269 else
1270 {
1271 xElements->getByName( m_sName ) >>= xQuery;
1272 }
1273 if ( !xQuery.is() )
1274 throw RuntimeException();
1275
1276 // the new commands
1277 if ( editingView() && !bNew )
1278 {
1279 OSL_ENSURE( xQuery == m_xAlterView, "OQueryController::doSaveAsDoc: already have another alterable view ...!?" );
1280 m_xAlterView.set( xQuery, UNO_QUERY_THROW );
1281 m_xAlterView->alterCommand( sTranslatedStmt );
1282 }
1283 else
1284 { // we're creating a query, or a *new* view
1285 xQuery->setPropertyValue( PROPERTY_COMMAND, Any( sTranslatedStmt ) );
1286
1287 if ( editingView() )
1288 {
1289 xQuery->setPropertyValue( PROPERTY_CATALOGNAME, Any( m_sUpdateCatalogName ) );
1290 xQuery->setPropertyValue( PROPERTY_SCHEMANAME, Any( m_sUpdateSchemaName ) );
1291 }
1292
1293 if ( editingQuery() )
1294 {
1295 xQuery->setPropertyValue( PROPERTY_UPDATE_TABLENAME, Any( OUString() ) );
1296 xQuery->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, css::uno::Any( m_bEscapeProcessing ) );
1297
1298 xQuery->setPropertyValue( PROPERTY_LAYOUTINFORMATION, getViewData() );
1299 }
1300 }
1301
1302 if ( bNew )
1303 {
1304 Reference< XAppend > xAppend( xElements, UNO_QUERY );
1305 if ( xAppend.is() )
1306 {
1307 xAppend->appendByDescriptor( xQuery );
1308 }
1309 else
1310 {
1311 Reference< XNameContainer > xCont( xElements, UNO_QUERY );
1312 if ( xCont.is() )
1313 xCont->insertByName( m_sName, Any( xQuery ) );
1314 }
1315
1316 if ( editingView() )
1317 {
1318 Reference< XPropertySet > xViewProps;
1319 if ( xElements->hasByName( m_sName ) )
1320 xViewProps.set( xElements->getByName( m_sName ), UNO_QUERY );
1321
1322 if ( !xViewProps.is() ) // correct name and try again
1323 m_sName = ::dbtools::composeTableName( getMetaData(), xQuery, ::dbtools::EComposeRule::InDataManipulation, false );
1324
1325 OSL_ENSURE( xElements->hasByName( m_sName ), "OQueryController::doSaveAsDoc: newly created view does not exist!" );
1326
1327 if ( xElements->hasByName( m_sName ) )
1328 m_xAlterView.set( xElements->getByName( m_sName ), UNO_QUERY );
1329
1330 // now check if our datasource has set a tablefilter and if so, append the new table name to it
1331 ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld());
1332 }
1333 Reference< XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY);
1334 if ( xEventListener.is() )
1335 {
1336 TitleChangedEvent aEvent;
1337 xEventListener->titleChanged(aEvent);
1338 }
1339 releaseNumberForComponent();
1340 }
1341
1342 setModified( false );
1343 bSuccess = true;
1344
1345 }
1346 catch(const SQLException&)
1347 {
1348 if ( !bNew )
1349 m_sName = sOriginalName;
1350 aInfo = SQLExceptionInfo( ::cppu::getCaughtException() );
1351 }
1352 catch(const Exception&)
1353 {
1354 DBG_UNHANDLED_EXCEPTION("dbaccess");
1355 if ( !bNew )
1356 m_sName = sOriginalName;
1357 }
1358
1359 showError( aInfo );
1360
1361 // if we successfully saved a view we were creating, then close the designer
1362 if ( bSuccess && editingView() && !m_xAlterView.is() )
1363 {
1364 closeTask();
1365 }
1366
1367 if ( bSuccess && editingView() )
1368 InvalidateFeature( ID_BROWSER_EDITDOC );
1369
1370 return bSuccess;
1371 }
1372
1373 namespace {
1374 struct CommentStrip
1375 {
1376 OUString maComment;
1377 bool mbLastOnLine;
CommentStripdbaui::__anon078316030411::CommentStrip1378 CommentStrip( OUString sComment, bool bLastOnLine )
1379 : maComment(std::move( sComment)), mbLastOnLine( bLastOnLine) {}
1380 };
1381
1382 }
1383
1384 /** Obtain all comments in a query.
1385
1386 See also delComment() implementation for OSQLParser::parseTree().
1387 */
getComment(const OUString & rQuery)1388 static std::vector< CommentStrip > getComment( const OUString& rQuery )
1389 {
1390 std::vector< CommentStrip > aRet;
1391 // First a quick search if there is any "--" or "//" or "/*", if not then
1392 // the whole copying loop is pointless.
1393 if (rQuery.indexOf( "--" ) < 0 && rQuery.indexOf( "//" ) < 0 &&
1394 rQuery.indexOf( "/*" ) < 0)
1395 return aRet;
1396
1397 const sal_Unicode* pCopy = rQuery.getStr();
1398 const sal_Int32 nQueryLen = rQuery.getLength();
1399 bool bIsText1 = false; // "text"
1400 bool bIsText2 = false; // 'text'
1401 bool bComment2 = false; // /* comment */
1402 bool bComment = false; // -- or // comment
1403 OUStringBuffer aBuf;
1404 for (sal_Int32 i=0; i < nQueryLen; ++i)
1405 {
1406 if (bComment2)
1407 {
1408 aBuf.append( &pCopy[i], 1);
1409 if ((i+1) < nQueryLen)
1410 {
1411 if (pCopy[i]=='*' && pCopy[i+1]=='/')
1412 {
1413 bComment2 = false;
1414 aBuf.append( &pCopy[++i], 1);
1415 aRet.emplace_back( aBuf.makeStringAndClear(), false);
1416 }
1417 }
1418 else
1419 {
1420 // comment can't close anymore, actually an error, but...
1421 aRet.emplace_back( aBuf.makeStringAndClear(), false);
1422 }
1423 continue;
1424 }
1425 if (pCopy[i] == '\n' || i == nQueryLen-1)
1426 {
1427 if (bComment)
1428 {
1429 if (i == nQueryLen-1 && pCopy[i] != '\n')
1430 aBuf.append( &pCopy[i], 1);
1431 aRet.emplace_back( aBuf.makeStringAndClear(), true);
1432 bComment = false;
1433 }
1434 else if (!aRet.empty())
1435 aRet.back().mbLastOnLine = true;
1436 }
1437 else if (!bComment)
1438 {
1439 if (pCopy[i] == '\"' && !bIsText2)
1440 bIsText1 = !bIsText1;
1441 else if (pCopy[i] == '\'' && !bIsText1)
1442 bIsText2 = !bIsText2;
1443 if (!bIsText1 && !bIsText2 && (i+1) < nQueryLen)
1444 {
1445 if ((pCopy[i]=='-' && pCopy[i+1]=='-') || (pCopy[i]=='/' && pCopy[i+1]=='/'))
1446 bComment = true;
1447 else if (pCopy[i]=='/' && pCopy[i+1]=='*')
1448 bComment2 = true;
1449 }
1450 }
1451 if (bComment || bComment2)
1452 aBuf.append( &pCopy[i], 1);
1453 }
1454 return aRet;
1455 }
1456
1457 /** Concat/insert comments that were previously obtained with getComment().
1458
1459 NOTE: The current parser implementation does not preserve newlines, so all
1460 comments are always appended to the entire query, also inline comments
1461 that would need positioning anyway that can't be obtained after
1462 recomposition. This is ugly but at least allows commented queries while
1463 preserving the comments _somehow_.
1464 */
concatComment(const OUString & rQuery,const std::vector<CommentStrip> & rComments)1465 static OUString concatComment( const OUString& rQuery, const std::vector< CommentStrip >& rComments )
1466 {
1467 // No comments => return query.
1468 if (rComments.empty())
1469 return rQuery;
1470
1471 const sal_Unicode* pBeg = rQuery.getStr();
1472 const sal_Int32 nLen = rQuery.getLength();
1473 const size_t nComments = rComments.size();
1474 // Obtaining the needed size once should be faster than reallocating.
1475 // Also add a blank or linefeed for each comment.
1476 sal_Int32 nBufSize = nLen + nComments;
1477 for (auto const& comment : rComments)
1478 nBufSize += comment.maComment.getLength();
1479 OUStringBuffer aBuf( nBufSize );
1480 sal_Int32 nIndBeg = 0;
1481 sal_Int32 nIndLF = rQuery.indexOf('\n');
1482 size_t i = 0;
1483 while (nIndLF >= 0 && i < nComments)
1484 {
1485 aBuf.append( pBeg + nIndBeg, nIndLF - nIndBeg);
1486 do
1487 {
1488 aBuf.append( rComments[i].maComment);
1489 } while (!rComments[i++].mbLastOnLine && i < nComments);
1490 aBuf.append( pBeg + nIndLF, 1); // the LF
1491 nIndBeg = nIndLF + 1;
1492 nIndLF = (nIndBeg < nLen ? rQuery.indexOf( '\n', nIndBeg) : -1);
1493 }
1494 // Append remainder of query.
1495 if (nIndBeg < nLen)
1496 aBuf.append( pBeg + nIndBeg, nLen - nIndBeg);
1497 // Append all remaining comments, preserve lines.
1498 bool bNewLine = false;
1499 for ( ; i < nComments; ++i)
1500 {
1501 if (!bNewLine)
1502 aBuf.append( ' ');
1503 aBuf.append( rComments[i].maComment);
1504 if (rComments[i].mbLastOnLine)
1505 {
1506 aBuf.append( '\n');
1507 bNewLine = true;
1508 }
1509 else
1510 bNewLine = false;
1511 }
1512 return aBuf.makeStringAndClear();
1513 }
1514
translateStatement(bool _bFireStatementChange)1515 OUString OQueryController::translateStatement( bool _bFireStatementChange )
1516 {
1517 // now set the properties
1518 setStatement_fireEvent( getContainer()->getStatement(), _bFireStatementChange );
1519 OUString sTranslatedStmt;
1520 if(!m_sStatement.isEmpty() && m_xComposer.is() && m_bEscapeProcessing)
1521 {
1522 try
1523 {
1524 OUString aErrorMsg;
1525
1526 std::vector< CommentStrip > aComments = getComment( m_sStatement);
1527
1528 std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign );
1529 if(pNode)
1530 {
1531 pNode->parseNodeToStr( sTranslatedStmt, getConnection() );
1532 }
1533
1534 m_xComposer->setQuery(sTranslatedStmt);
1535 sTranslatedStmt = m_xComposer->getComposedQuery();
1536 sTranslatedStmt = concatComment( sTranslatedStmt, aComments);
1537 }
1538 catch(const SQLException& e)
1539 {
1540 ::dbtools::SQLExceptionInfo aInfo(e);
1541 showError(aInfo);
1542 // an error occurred so we clear the statement
1543 sTranslatedStmt.clear();
1544 }
1545 }
1546 else if(m_sStatement.isEmpty())
1547 {
1548 showError(SQLException(DBA_RES(STR_QRY_NOSELECT), nullptr, u"S1000"_ustr, 1000, Any()));
1549 }
1550 else
1551 sTranslatedStmt = m_sStatement;
1552
1553 return sTranslatedStmt;
1554 }
1555
saveModified()1556 short OQueryController::saveModified()
1557 {
1558 SolarMutexGuard aSolarGuard;
1559 ::osl::MutexGuard aGuard( getMutex() );
1560 short nRet = RET_YES;
1561 if ( !isConnected() || !isModified() )
1562 return nRet;
1563
1564 if ( !m_bGraphicalDesign
1565 || ( !m_vTableFieldDesc.empty()
1566 && !m_vTableData.empty()
1567 )
1568 )
1569 {
1570 OUString sMessageText( lcl_getObjectResourceString( STR_QUERY_SAVEMODIFIED, m_nCommandType ) );
1571
1572 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(getFrameWeld(),
1573 VclMessageType::Question, VclButtonsType::YesNo,
1574 sMessageText));
1575 xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
1576 xQueryBox->set_default_response(RET_YES);
1577
1578 nRet = xQueryBox->run();
1579 if ( ( nRet == RET_YES )
1580 && !doSaveAsDoc( false )
1581 )
1582 {
1583 nRet = RET_CANCEL;
1584 }
1585 }
1586 return nRet;
1587 }
1588
impl_reset(const bool i_bForceCurrentControllerSettings)1589 void OQueryController::impl_reset( const bool i_bForceCurrentControllerSettings )
1590 {
1591 bool bValid = false;
1592
1593 Sequence< PropertyValue > aLayoutInformation;
1594 // get command from the query if a query name was supplied
1595 if ( !i_bForceCurrentControllerSettings && !editingCommand() )
1596 {
1597 if ( !m_sName.isEmpty() )
1598 {
1599 Reference< XNameAccess > xQueries = getObjectContainer();
1600 if ( xQueries.is() )
1601 {
1602 Reference< XPropertySet > xProp;
1603 if( xQueries->hasByName( m_sName ) && ( xQueries->getByName( m_sName ) >>= xProp ) && xProp.is() )
1604 {
1605 OUString sNewStatement;
1606 xProp->getPropertyValue( PROPERTY_COMMAND ) >>= sNewStatement;
1607 setStatement_fireEvent( sNewStatement );
1608
1609 if ( editingQuery() )
1610 {
1611 bool bNewEscapeProcessing( true );
1612 xProp->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bNewEscapeProcessing;
1613 setEscapeProcessing_fireEvent( bNewEscapeProcessing );
1614 }
1615
1616 m_bGraphicalDesign = m_bGraphicalDesign && m_bEscapeProcessing;
1617 bValid = true;
1618
1619 try
1620 {
1621 if ( editingQuery() )
1622 xProp->getPropertyValue( PROPERTY_LAYOUTINFORMATION ) >>= aLayoutInformation;
1623 }
1624 catch( const Exception& )
1625 {
1626 OSL_FAIL( "OQueryController::impl_reset: could not retrieve the layout information from the query!" );
1627 }
1628 }
1629 }
1630 }
1631 }
1632 else
1633 {
1634 bValid = true;
1635 // assume that we got all necessary information during initialization
1636 }
1637
1638 if ( bValid )
1639 {
1640 // load the layoutInformation
1641 if ( aLayoutInformation.hasElements() )
1642 {
1643 try
1644 {
1645 loadViewSettings( aLayoutInformation );
1646 }
1647 catch( const Exception& )
1648 {
1649 DBG_UNHANDLED_EXCEPTION("dbaccess");
1650 }
1651 }
1652
1653 if ( !m_sStatement.isEmpty() )
1654 {
1655 setQueryComposer();
1656
1657 bool bError( false );
1658
1659 if ( !m_pSqlIterator )
1660 {
1661 bError = true;
1662 }
1663 else if ( m_bEscapeProcessing )
1664 {
1665 OUString aErrorMsg;
1666 std::unique_ptr< ::connectivity::OSQLParseNode > pNode(
1667 m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign ) );
1668
1669 if (pNode)
1670 {
1671 delete m_pSqlIterator->getParseTree();
1672 m_pSqlIterator->setParseTree( pNode.release() );
1673 m_pSqlIterator->traverseAll();
1674 if ( m_pSqlIterator->hasErrors() )
1675 {
1676 if ( !i_bForceCurrentControllerSettings && m_bGraphicalDesign && !editingView() )
1677 {
1678 impl_showAutoSQLViewError( Any( m_pSqlIterator->getErrors() ) );
1679 }
1680 bError = true;
1681 }
1682 }
1683 else
1684 {
1685 if ( !i_bForceCurrentControllerSettings && !editingView() )
1686 {
1687 OUString aTitle(DBA_RES(STR_SVT_SQL_SYNTAX_ERROR));
1688 OSQLMessageBox aDlg(getFrameWeld(), aTitle, aErrorMsg);
1689 aDlg.run();
1690 }
1691 bError = true;
1692 }
1693 }
1694
1695 if ( bError )
1696 {
1697 m_bGraphicalDesign = false;
1698 if ( editingView() )
1699 // if we're editing a view whose statement could not be parsed, default to "no escape processing"
1700 setEscapeProcessing_fireEvent( false );
1701 }
1702 }
1703 }
1704
1705 if(!m_pSqlIterator)
1706 setQueryComposer();
1707 OSL_ENSURE(m_pSqlIterator,"No SQLIterator set!");
1708
1709 getContainer()->setNoneVisibleRow(m_nVisibleRows);
1710 }
1711
reset()1712 void OQueryController::reset()
1713 {
1714 impl_reset();
1715 getContainer()->reset();
1716 ClearUndoManager();
1717 }
1718
setStatement_fireEvent(const OUString & _rNewStatement,bool _bFireStatementChange)1719 void OQueryController::setStatement_fireEvent( const OUString& _rNewStatement, bool _bFireStatementChange )
1720 {
1721 Any aOldValue( m_sStatement );
1722 m_sStatement = _rNewStatement;
1723 Any aNewValue( m_sStatement );
1724
1725 sal_Int32 nHandle = PROPERTY_ID_ACTIVECOMMAND;
1726 if ( _bFireStatementChange )
1727 fire( &nHandle, &aNewValue, &aOldValue, 1, false );
1728 }
1729
setEscapeProcessing_fireEvent(const bool _bEscapeProcessing)1730 void OQueryController::setEscapeProcessing_fireEvent( const bool _bEscapeProcessing )
1731 {
1732 if ( _bEscapeProcessing == m_bEscapeProcessing )
1733 return;
1734
1735 Any aOldValue( m_bEscapeProcessing );
1736 m_bEscapeProcessing = _bEscapeProcessing;
1737 Any aNewValue( m_bEscapeProcessing );
1738
1739 sal_Int32 nHandle = PROPERTY_ID_ESCAPE_PROCESSING;
1740 fire( &nHandle, &aNewValue, &aOldValue, 1, false );
1741 }
1742
IMPL_LINK_NOARG(OQueryController,OnExecuteAddTable,void *,void)1743 IMPL_LINK_NOARG( OQueryController, OnExecuteAddTable, void*, void )
1744 {
1745 Execute( ID_BROWSER_ADDTABLE,Sequence<PropertyValue>() );
1746 }
1747
allowViews() const1748 bool OQueryController::allowViews() const
1749 {
1750 return true;
1751 }
1752
allowQueries() const1753 bool OQueryController::allowQueries() const
1754 {
1755 OSL_ENSURE( getSdbMetaData().isConnected(), "OQueryController::allowQueries: illegal call!" );
1756 if ( !getSdbMetaData().supportsSubqueriesInFrom() )
1757 return false;
1758
1759 bool bCreatingView = ( m_nCommandType == CommandType::TABLE );
1760 return !bCreatingView;
1761 }
1762
getViewData()1763 Any SAL_CALL OQueryController::getViewData()
1764 {
1765 ::osl::MutexGuard aGuard( getMutex() );
1766
1767 getContainer()->SaveUIConfig();
1768
1769 ::comphelper::NamedValueCollection aViewSettings;
1770 saveViewSettings( aViewSettings, false );
1771
1772 return Any( aViewSettings.getPropertyValues() );
1773 }
1774
restoreViewData(const Any &)1775 void SAL_CALL OQueryController::restoreViewData(const Any& /*Data*/)
1776 {
1777 // TODO
1778 }
1779
1780 } // namespace dbaui
1781
1782 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1783