LibreOffice Module dbaccess (master) 1
directsql.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <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>
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
36namespace 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
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(m_xBuilder->weld_scrolled_window("scrolledwindow", true)))
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
84 {
85 ::osl::MutexGuard aGuard(m_aMutex);
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
110 }
111
113 {
114 #ifdef DBG_UTIL
115 {
116 const char* pError = impl_CheckInvariants();
117 if (pError)
118 SAL_WARN("dbaccess.ui", "DirectSQLDialog::getHistorySize: " << pError);
119 }
120 #endif
121 return m_aStatementHistory.size();
122 }
123
125 {
126 #ifdef DBG_UTIL
127 {
128 const char* pError = impl_CheckInvariants();
129 if (pError)
130 SAL_WARN("dbaccess.ui", "DirectSQLDialog::implEnsureHistoryLimit: " << pError);
131 }
132 #endif
133
135 // nothing to do
136 return;
137
138 sal_Int32 nRemoveEntries = getHistorySize() - g_nHistoryLimit;
139 while (nRemoveEntries--)
140 {
141 m_aStatementHistory.pop_front();
142 m_aNormalizedHistory.pop_front();
143 m_xSQLHistory->remove(0);
144 }
145 }
146
147 void DirectSQLDialog::implAddToStatementHistory(const OUString& _rStatement)
148 {
149 #ifdef DBG_UTIL
150 {
151 const char* pError = impl_CheckInvariants();
152 if (pError)
153 SAL_WARN("dbaccess.ui", "DirectSQLDialog::implAddToStatementHistor: " << pError);
154 }
155 #endif
156
157 // add the statement to the history
158 m_aStatementHistory.push_back(_rStatement);
159
160 // normalize the statement, and remember the normalized form, too
161 OUString sNormalized = _rStatement.replaceAll("\n", " ");
162 m_aNormalizedHistory.push_back(sNormalized);
163
164 // add the normalized version to the list box
165 m_xSQLHistory->append_text(sNormalized);
166
167 // ensure that we don't exceed the history limit
169 }
170
171#ifdef DBG_UTIL
173 {
174 if (m_aStatementHistory.size() != m_aNormalizedHistory.size())
175 return "statement history is inconsistent!";
176
177 if (!m_xSQLHistory)
178 return "invalid listbox!";
179
180 if (m_aStatementHistory.size() != static_cast<size_t>(m_xSQLHistory->get_count()))
181 return "invalid listbox entry count!";
182
183 if (!m_xConnection.is())
184 return "have no connection!";
185
186 return nullptr;
187 }
188#endif
189
190 void DirectSQLDialog::implExecuteStatement(const OUString& _rStatement)
191 {
192 #ifdef DBG_UTIL
193 {
194 const char* pError = impl_CheckInvariants();
195 if (pError)
196 SAL_WARN("dbaccess.ui", "DirectSQLDialog::implExecuteStatement: " << pError);
197 }
198 #endif
199
200 ::osl::MutexGuard aGuard(m_aMutex);
201
202 OUString sStatus;
203
204 // clear the output box
205 m_xOutput->set_text(OUString());
206 try
207 {
208 // create a statement
209 Reference< XStatement > xStatement = m_xConnection->createStatement();
210
211 if (m_xDirectSQL->get_active())
212 {
213 Reference< com::sun::star::beans::XPropertySet > xStatementProps(xStatement, UNO_QUERY_THROW);
214 try
215 {
216 xStatementProps->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, Any(false));
217 }
218 catch( const Exception& )
219 {
220 DBG_UNHANDLED_EXCEPTION("dbaccess");
221 }
222 }
223
224 Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData();
225 css::uno::Reference< css::sdbc::XMultipleResults > xMR ( xStatement, UNO_QUERY );
226
227 if (xMeta.is() && xMeta->supportsMultipleResultSets() && xMR.is())
228 {
229 bool hasRS = xStatement->execute(_rStatement);
230 if(hasRS)
231 {
232 css::uno::Reference< css::sdbc::XResultSet > xRS (xMR->getResultSet());
233 if (m_xShowOutput->get_active())
234 display(xRS);
235 }
236 else
238 Concat2View(OUString::number(xMR->getUpdateCount()) + " rows updated\n"));
239 for (;;)
240 {
241 hasRS = xMR->getMoreResults();
242 if (!hasRS && xMR->getUpdateCount() == -1)
243 break;
244 if(hasRS)
245 {
246 css::uno::Reference< css::sdbc::XResultSet > xRS (xMR->getResultSet());
247 if (m_xShowOutput->get_active())
248 display(xRS);
249 }
250 }
251 }
252 else
253 {
254 const OUString upperStatement = _rStatement.toAsciiUpperCase();
255 if (upperStatement.startsWith("UPDATE"))
256 {
257 sal_Int32 resultCount = xStatement->executeUpdate(_rStatement);
258 addOutputText(Concat2View(OUString::number(resultCount) + " rows updated\n"));
259 }
260 else if (upperStatement.startsWith("INSERT"))
261 {
262 sal_Int32 resultCount = xStatement->executeUpdate(_rStatement);
263 addOutputText(Concat2View(OUString::number(resultCount) + " rows inserted\n"));
264 }
265 else if (upperStatement.startsWith("DELETE"))
266 {
267 sal_Int32 resultCount = xStatement->executeUpdate(_rStatement);
268 addOutputText(Concat2View(OUString::number(resultCount) + " rows deleted\n"));
269 }
270 else if (upperStatement.startsWith("CREATE"))
271 {
272 xStatement->executeUpdate(_rStatement);
273 addOutputText(u"Command executed\n");
274 }
275 else if (upperStatement.startsWith("SELECT") || m_xShowOutput->get_active())
276 {
277 css::uno::Reference< css::sdbc::XResultSet > xRS = xStatement->executeQuery(_rStatement);
278 if (m_xShowOutput->get_active())
279 display(xRS);
280 }
281 else
282 {
283 sal_Int32 resultCount = xStatement->executeUpdate(_rStatement);
284 addOutputText(Concat2View(OUString::number(resultCount) + " rows updated\n"));
285 }
286 }
287 // successful
288 sStatus = DBA_RES(STR_COMMAND_EXECUTED_SUCCESSFULLY);
289
290 // dispose the statement
291 ::comphelper::disposeComponent(xStatement);
292 }
293 catch(const SQLException& e)
294 {
295 sStatus = e.Message;
296 }
297 catch( const Exception& )
298 {
299 DBG_UNHANDLED_EXCEPTION("dbaccess");
300 }
301
302 // add the status text
303 addStatusText(sStatus);
304 }
305
306 void DirectSQLDialog::display(const css::uno::Reference< css::sdbc::XResultSet >& xRS)
307 {
308 // get a handle for the rows
309 css::uno::Reference< css::sdbc::XRow > xRow( xRS, css::uno::UNO_QUERY );
310 // work through each of the rows
311 while (xRS->next())
312 {
313 // initialise the output line for each row
314 OUStringBuffer out;
315 // work along the columns until that are none left
316 try
317 {
318 int i = 1;
319 for (;;)
320 {
321 // be dumb, treat everything as a string unless below
322 // tdf#153317, at least "Bit" type in Mysql/MariaDB gives: "\000" or "\001"
323 // so retrieve Sequence from getBytes, test if it has a length of 1 (so we avoid BLOB/CLOB or other complex types)
324 // and test if the value of first byte is one of those.
325 // In this case, there's a good chance it's a "Bit" field
326 // remark: for unknown reason, getByte(i) gives "\000" even if the bit is at 1.
327 auto seq = xRow->getBytes(i);
328 if ((seq.getLength() == 1) && (seq[0] >= 0) && (seq[0] <= 1))
329 {
330 out.append(OUString::number(static_cast<int>(seq[0])) + ",");
331 }
332 else
333 {
334 out.append(xRow->getString(i) + ",");
335 }
336 i++;
337 }
338 }
339 // trap for when we fall off the end of the row
340 catch (const SQLException&)
341 {
342 }
343 // report the output
344 addOutputText(out);
345 }
346 }
347
348 void DirectSQLDialog::addStatusText(std::u16string_view _rMessage)
349 {
350 OUString sAppendMessage = OUString::number(m_nStatusCount++) + ": " + _rMessage + "\n\n";
351
352 OUString sCompleteMessage = m_xStatus->get_text() + sAppendMessage;
353 m_xStatus->set_text(sCompleteMessage);
354
355 m_xStatus->select_region(sCompleteMessage.getLength(), sCompleteMessage.getLength());
356 }
357
358 void DirectSQLDialog::addOutputText(std::u16string_view _rMessage)
359 {
360 OUString sAppendMessage = OUString::Concat(_rMessage) + "\n";
361
362 OUString sCompleteMessage = m_xOutput->get_text() + sAppendMessage;
363 m_xOutput->set_text(sCompleteMessage);
364 }
365
367 {
368 #ifdef DBG_UTIL
369 {
370 const char* pError = impl_CheckInvariants();
371 if (pError)
372 SAL_WARN("dbaccess.ui", "DirectSQLDialog::executeCurrent: " << pError);
373 }
374 #endif
375
376 OUString sStatement = m_xSQL->GetText();
377
378 // execute
379 implExecuteStatement(sStatement);
380
381 // add the statement to the history
382 implAddToStatementHistory(sStatement);
383
384 m_xSQL->GrabFocus();
385 }
386
387 void DirectSQLDialog::switchToHistory(sal_Int32 _nHistoryPos)
388 {
389 #ifdef DBG_UTIL
390 {
391 const char* pError = impl_CheckInvariants();
392 if (pError)
393 SAL_WARN("dbaccess.ui", "DirectSQLDialog::switchToHistory: " << pError);
394 }
395 #endif
396
397 if ((_nHistoryPos >= 0) && (_nHistoryPos < getHistorySize()))
398 {
399 // set the text in the statement editor
400 OUString sStatement = m_aStatementHistory[_nHistoryPos];
401 m_xSQL->SetTextAndUpdate(sStatement);
402 OnStatementModified(nullptr);
403
404 m_xSQL->GrabFocus();
405 }
406 else
407 OSL_FAIL("DirectSQLDialog::switchToHistory: invalid position!");
408 }
409
410 IMPL_LINK_NOARG( DirectSQLDialog, OnStatementModified, LinkParamNone*, void )
411 {
412 m_xExecute->set_sensitive(!m_xSQL->GetText().isEmpty());
413 }
414
416 {
417 m_xDialog->response(RET_OK);
418 }
419
420 IMPL_LINK_NOARG( DirectSQLDialog, OnClose, void*, void )
421 {
422 assert(m_pClosingEvent);
423 Application::RemoveUserEvent(m_pClosingEvent);
424 m_pClosingEvent = nullptr;
425
426 m_xDialog->response(RET_OK);
427 }
428
430 {
431 executeCurrent();
432 }
433
434 IMPL_LINK_NOARG( DirectSQLDialog, OnListEntrySelected, weld::ComboBox&, void )
435 {
436 const sal_Int32 nSelected = m_xSQLHistory->get_active();
437 if (nSelected != -1)
438 switchToHistory(nSelected);
439 }
440
441} // namespace dbaui
442
443/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XExecutableDialog > m_xDialog
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
static void RemoveUserEvent(ImplSVEvent *nUserEvent)
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, const ILibreOfficeKitNotifier *pNotifier=nullptr)
std::unique_ptr< weld::TextView > m_xStatus
Definition: directsql.hxx:47
virtual void _disposing(const css::lang::EventObject &_rSource) override
Definition: directsql.cxx:91
void addStatusText(std::u16string_view _rMessage)
adds a status text to the status list
Definition: directsql.cxx:348
void implAddToStatementHistory(const OUString &_rStatement)
adds a statement to the statement history
Definition: directsql.cxx:147
StringQueue m_aStatementHistory
Definition: directsql.hxx:56
ImplSVEvent * m_pClosingEvent
Definition: directsql.hxx:64
StringQueue m_aNormalizedHistory
Definition: directsql.hxx:57
std::unique_ptr< weld::Button > m_xClose
Definition: directsql.hxx:51
const char * impl_CheckInvariants() const
Definition: directsql.cxx:172
void implEnsureHistoryLimit()
ensures that our history has at most m_nHistoryLimit entries
Definition: directsql.cxx:124
::osl::Mutex m_aMutex
Definition: directsql.hxx:43
std::unique_ptr< weld::CheckButton > m_xDirectSQL
Definition: directsql.hxx:48
std::unique_ptr< SQLEditView > m_xSQL
Definition: directsql.hxx:52
std::unique_ptr< weld::Button > m_xExecute
Definition: directsql.hxx:45
void display(const css::uno::Reference< css::sdbc::XResultSet > &xRS)
displays resultset
Definition: directsql.cxx:306
css::uno::Reference< css::sdbc::XConnection > m_xConnection
Definition: directsql.hxx:62
std::unique_ptr< weld::CustomWeld > m_xSQLEd
Definition: directsql.hxx:53
void addOutputText(std::u16string_view _rMessage)
adds a status text to the output list
Definition: directsql.cxx:358
void switchToHistory(sal_Int32 _nHistoryPos)
Definition: directsql.cxx:387
void implExecuteStatement(const OUString &_rStatement)
executes the statement given, adds the status to the status list
Definition: directsql.cxx:190
std::unique_ptr< weld::TextView > m_xOutput
Definition: directsql.hxx:50
virtual ~DirectSQLDialog() override
Definition: directsql.cxx:83
std::unique_ptr< weld::CheckButton > m_xShowOutput
Definition: directsql.hxx:49
DirectSQLDialog(weld::Window *_pParent, const css::uno::Reference< css::sdbc::XConnection > &_rxConn)
Definition: directsql.cxx:45
sal_Int32 m_nStatusCount
Definition: directsql.hxx:59
std::unique_ptr< weld::ComboBox > m_xSQLHistory
Definition: directsql.hxx:46
sal_Int32 getHistorySize() const
number of history entries
Definition: directsql.cxx:112
void startComponentListening(const css::uno::Reference< css::lang::XComponent > &_rxComp)
std::shared_ptr< weld::Dialog > m_xDialog
#define DBA_RES(id)
#define DBG_UNHANDLED_EXCEPTION(...)
float u
#define SAL_WARN(area, stream)
@ Exception
IMPL_LINK_NOARG(OApplicationController, OnClipboardChanged, TransferableDataHelper *, void)
constexpr sal_Int32 g_nHistoryLimit
Definition: directsql.cxx:42
int i
Reference< XConnection > m_xConnection
Definition: objectnames.cxx:79
uno::Reference< io::XOutputStream > m_xOutput
OUString sMessage
Definition: sqlmessage.cxx:159
constexpr OUStringLiteral PROPERTY_ESCAPE_PROCESSING(u"EscapeProcessing")
RET_OK