xref: /core/dbaccess/source/ui/dlg/directsql.cxx (revision 76f89b00)
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 <core_resource.hxx>
21 #include <directsql.hxx>
22 #include <sqledit.hxx>
23 #include <strings.hxx>
24 #include <strings.hrc>
25 #include <comphelper/types.hxx>
26 #include <osl/mutex.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <tools/diagnose_ex.h>
29 #include <vcl/svapp.hxx>
30 #include <vcl/weld.hxx>
31 #include <com/sun/star/sdbc/SQLException.hpp>
32 #include <com/sun/star/sdbc/XRow.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/sdbc/XMultipleResults.hpp>
35 
36 namespace dbaui
37 {
38     using namespace ::com::sun::star::uno;
39     using namespace ::com::sun::star::sdbc;
40     using namespace ::com::sun::star::lang;
41 
42     constexpr sal_Int32 g_nHistoryLimit = 20;
43 
44     // DirectSQLDialog
45     DirectSQLDialog::DirectSQLDialog(weld::Window* _pParent, const Reference< XConnection >& _rxConn)
46         : GenericDialogController(_pParent, "dbaccess/ui/directsqldialog.ui", "DirectSQLDialog")
47         , m_xExecute(m_xBuilder->weld_button("execute"))
48         , m_xSQLHistory(m_xBuilder->weld_combo_box("sqlhistory"))
49         , m_xStatus(m_xBuilder->weld_text_view("status"))
50         , m_xDirectSQL(m_xBuilder->weld_check_button("directsql"))
51         , m_xShowOutput(m_xBuilder->weld_check_button("showoutput"))
52         , m_xOutput(m_xBuilder->weld_text_view("output"))
53         , m_xClose(m_xBuilder->weld_button("close"))
54         , m_xSQL(new SQLEditView)
55         , m_xSQLEd(new weld::CustomWeld(*m_xBuilder, "sql", *m_xSQL))
56         , m_nStatusCount(1)
57         , m_xConnection(_rxConn)
58         , m_pClosingEvent(nullptr)
59     {
60         int nWidth = m_xStatus->get_approximate_digit_width() * 60;
61         int nHeight = m_xStatus->get_height_rows(7);
62 
63         m_xSQLEd->set_size_request(nWidth, nHeight);
64         m_xStatus->set_size_request(-1, nHeight);
65         m_xOutput->set_size_request(-1, nHeight);
66 
67         m_xSQL->GrabFocus();
68 
69         m_xExecute->connect_clicked(LINK(this, DirectSQLDialog, OnExecute));
70         m_xClose->connect_clicked(LINK(this, DirectSQLDialog, OnCloseClick));
71         m_xSQLHistory->connect_changed(LINK(this, DirectSQLDialog, OnListEntrySelected));
72 
73         // add a dispose listener to the connection
74         Reference< XComponent > xConnComp(m_xConnection, UNO_QUERY);
75         OSL_ENSURE(xConnComp.is(), "DirectSQLDialog::DirectSQLDialog: invalid connection!");
76         if (xConnComp.is())
77             startComponentListening(xConnComp);
78 
79         m_xSQL->SetModifyHdl(LINK(this, DirectSQLDialog, OnStatementModified));
80         OnStatementModified(nullptr);
81     }
82 
83     DirectSQLDialog::~DirectSQLDialog()
84     {
85         ::osl::MutexGuard aGuard(m_aMutex);
86         if (m_pClosingEvent)
87             Application::RemoveUserEvent(m_pClosingEvent);
88         stopAllComponentListening();
89     }
90 
91     void DirectSQLDialog::_disposing( const EventObject& _rSource )
92     {
93         SolarMutexGuard aSolarGuard;
94         ::osl::MutexGuard aGuard(m_aMutex);
95 
96         assert(!m_pClosingEvent);
97 
98         OSL_ENSURE(Reference< XConnection >(_rSource.Source, UNO_QUERY).get() == m_xConnection.get(),
99             "DirectSQLDialog::_disposing: where does this come from?");
100 
101         {
102             OUString sMessage(DBA_RES(STR_DIRECTSQL_CONNECTIONLOST));
103             std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(),
104                                                         VclMessageType::Warning, VclButtonsType::Ok,
105                                                         sMessage));
106             xError->run();
107         }
108 
109         m_pClosingEvent = Application::PostUserEvent(LINK(this, DirectSQLDialog, OnClose));
110     }
111 
112     sal_Int32 DirectSQLDialog::getHistorySize() const
113     {
114         CHECK_INVARIANTS("DirectSQLDialog::getHistorySize");
115         return m_aStatementHistory.size();
116     }
117 
118     void DirectSQLDialog::implEnsureHistoryLimit()
119     {
120         CHECK_INVARIANTS("DirectSQLDialog::implEnsureHistoryLimit");
121 
122         if (getHistorySize() <= g_nHistoryLimit)
123             // nothing to do
124             return;
125 
126         sal_Int32 nRemoveEntries = getHistorySize() - g_nHistoryLimit;
127         while (nRemoveEntries--)
128         {
129             m_aStatementHistory.pop_front();
130             m_aNormalizedHistory.pop_front();
131             m_xSQLHistory->remove(0);
132         }
133     }
134 
135     void DirectSQLDialog::implAddToStatementHistory(const OUString& _rStatement)
136     {
137         CHECK_INVARIANTS("DirectSQLDialog::implAddToStatementHistory");
138 
139         // add the statement to the history
140         m_aStatementHistory.push_back(_rStatement);
141 
142         // normalize the statement, and remember the normalized form, too
143         OUString sNormalized = _rStatement.replaceAll("\n", " ");
144         m_aNormalizedHistory.push_back(sNormalized);
145 
146         // add the normalized version to the list box
147         m_xSQLHistory->append_text(sNormalized);
148 
149         // ensure that we don't exceed the history limit
150         implEnsureHistoryLimit();
151     }
152 
153 #ifdef DBG_UTIL
154     const char* DirectSQLDialog::impl_CheckInvariants() const
155     {
156         if (m_aStatementHistory.size() != m_aNormalizedHistory.size())
157             return "statement history is inconsistent!";
158 
159         if (!m_xSQLHistory)
160             return "invalid listbox!";
161 
162         if (m_aStatementHistory.size() != static_cast<size_t>(m_xSQLHistory->get_count()))
163             return "invalid listbox entry count!";
164 
165         if (!m_xConnection.is())
166             return "have no connection!";
167 
168         return nullptr;
169     }
170 #endif
171 
172     void DirectSQLDialog::implExecuteStatement(const OUString& _rStatement)
173     {
174         CHECK_INVARIANTS("DirectSQLDialog::implExecuteStatement");
175 
176         ::osl::MutexGuard aGuard(m_aMutex);
177 
178         OUString sStatus;
179 
180         // clear the output box
181         m_xOutput->set_text(OUString());
182         try
183         {
184             // create a statement
185             Reference< XStatement > xStatement = m_xConnection->createStatement();
186 
187             if (m_xDirectSQL->get_active())
188             {
189                 Reference< com::sun::star::beans::XPropertySet > xStatementProps(xStatement, UNO_QUERY_THROW);
190                 try
191                 {
192                     xStatementProps->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, makeAny(false));
193                 }
194                 catch( const Exception& )
195                 {
196                     DBG_UNHANDLED_EXCEPTION("dbaccess");
197                 }
198             }
199 
200             Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData();
201             css::uno::Reference< css::sdbc::XMultipleResults > xMR ( xStatement, UNO_QUERY );
202 
203             if (xMeta.is() && xMeta->supportsMultipleResultSets() && xMR.is())
204             {
205                 bool hasRS = xStatement->execute(_rStatement);
206                 if(hasRS)
207                 {
208                     css::uno::Reference< css::sdbc::XResultSet > xRS (xMR->getResultSet());
209                     if (m_xShowOutput->get_active())
210                         display(xRS);
211                 }
212                 else
213                     addOutputText(
214                         OUString(OUString::number(xMR->getUpdateCount()) + " rows updated\n"));
215                 for (;;)
216                 {
217                     hasRS = xMR->getMoreResults();
218                     if (!hasRS && xMR->getUpdateCount() == -1)
219                         break;
220                     if(hasRS)
221                     {
222                         css::uno::Reference< css::sdbc::XResultSet > xRS (xMR->getResultSet());
223                         if (m_xShowOutput->get_active())
224                             display(xRS);
225                     }
226                 }
227             }
228             else
229             {
230                 if (_rStatement.toAsciiUpperCase().startsWith("SELECT"))
231                 {
232                     css::uno::Reference< css::sdbc::XResultSet > xRS = xStatement->executeQuery(_rStatement);
233                     if (m_xShowOutput->get_active())
234                         display(xRS);
235                 }
236                 else
237                 {
238                     sal_Int32 resultCount = xStatement->executeUpdate(_rStatement);
239                     addOutputText(OUString(OUString::number(resultCount) + " rows updated\n"));
240                 }
241             }
242             // successful
243             sStatus = DBA_RES(STR_COMMAND_EXECUTED_SUCCESSFULLY);
244 
245             // dispose the statement
246             ::comphelper::disposeComponent(xStatement);
247         }
248         catch(const SQLException& e)
249         {
250             sStatus = e.Message;
251         }
252         catch( const Exception& )
253         {
254             DBG_UNHANDLED_EXCEPTION("dbaccess");
255         }
256 
257         // add the status text
258         addStatusText(sStatus);
259     }
260 
261     void DirectSQLDialog::display(const css::uno::Reference< css::sdbc::XResultSet >& xRS)
262     {
263         // get a handle for the rows
264         css::uno::Reference< css::sdbc::XRow > xRow( xRS, css::uno::UNO_QUERY );
265         // work through each of the rows
266         while (xRS->next())
267         {
268             // initialise the output line for each row
269             OUStringBuffer out;
270             // work along the columns until that are none left
271             try
272             {
273                 int i = 1;
274                 for (;;)
275                 {
276                     // be dumb, treat everything as a string
277                     out.append(xRow->getString(i) + ",");
278                     i++;
279                 }
280             }
281             // trap for when we fall off the end of the row
282             catch (const SQLException&)
283             {
284             }
285             // report the output
286             addOutputText(out.makeStringAndClear());
287         }
288     }
289 
290     void DirectSQLDialog::addStatusText(std::u16string_view _rMessage)
291     {
292         OUString sAppendMessage = OUString::number(m_nStatusCount++) + ": " + _rMessage + "\n\n";
293 
294         OUString sCompleteMessage = m_xStatus->get_text() + sAppendMessage;
295         m_xStatus->set_text(sCompleteMessage);
296 
297         m_xStatus->select_region(sCompleteMessage.getLength(), sCompleteMessage.getLength());
298     }
299 
300     void DirectSQLDialog::addOutputText(std::u16string_view _rMessage)
301     {
302         OUString sAppendMessage = OUString::Concat(_rMessage) + "\n";
303 
304         OUString sCompleteMessage = m_xOutput->get_text() + sAppendMessage;
305         m_xOutput->set_text(sCompleteMessage);
306     }
307 
308     void DirectSQLDialog::executeCurrent()
309     {
310         CHECK_INVARIANTS("DirectSQLDialog::executeCurrent");
311 
312         OUString sStatement = m_xSQL->GetText();
313 
314         // execute
315         implExecuteStatement(sStatement);
316 
317         // add the statement to the history
318         implAddToStatementHistory(sStatement);
319 
320         m_xSQL->GrabFocus();
321     }
322 
323     void DirectSQLDialog::switchToHistory(sal_Int32 _nHistoryPos)
324     {
325         CHECK_INVARIANTS("DirectSQLDialog::switchToHistory");
326 
327         if ((_nHistoryPos >= 0) && (_nHistoryPos < getHistorySize()))
328         {
329             // set the text in the statement editor
330             OUString sStatement = m_aStatementHistory[_nHistoryPos];
331             m_xSQL->SetTextAndUpdate(sStatement);
332             OnStatementModified(nullptr);
333 
334             m_xSQL->GrabFocus();
335         }
336         else
337             OSL_FAIL("DirectSQLDialog::switchToHistory: invalid position!");
338     }
339 
340     IMPL_LINK_NOARG( DirectSQLDialog, OnStatementModified, LinkParamNone*, void )
341     {
342         m_xExecute->set_sensitive(!m_xSQL->GetText().isEmpty());
343     }
344 
345     IMPL_LINK_NOARG( DirectSQLDialog, OnCloseClick, weld::Button&, void )
346     {
347         m_xDialog->response(RET_OK);
348     }
349 
350     IMPL_LINK_NOARG( DirectSQLDialog, OnClose, void*, void )
351     {
352         assert(m_pClosingEvent);
353         Application::RemoveUserEvent(m_pClosingEvent);
354         m_pClosingEvent = nullptr;
355 
356         m_xDialog->response(RET_OK);
357     }
358 
359     IMPL_LINK_NOARG( DirectSQLDialog, OnExecute, weld::Button&, void )
360     {
361         executeCurrent();
362     }
363 
364     IMPL_LINK_NOARG( DirectSQLDialog, OnListEntrySelected, weld::ComboBox&, void )
365     {
366         const sal_Int32 nSelected = m_xSQLHistory->get_active();
367         if (nSelected != -1)
368             switchToHistory(nSelected);
369     }
370 
371 }   // namespace dbaui
372 
373 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
374