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
