diff --git a/kexi/core/KexiView.h b/kexi/core/KexiView.h --- a/kexi/core/KexiView.h +++ b/kexi/core/KexiView.h @@ -35,6 +35,7 @@ namespace KexiDB { +class Cursor; class SchemaData; } @@ -148,6 +149,9 @@ Q_DECLARE_FLAGS(StoreNewDataOptions, StoreNewDataOption) public Q_SLOTS: + /*! Sets data. Only works for db-aware view. */ + bool setData(KexiDB::Cursor *cursor); + virtual void setFocus(); /*! Call this in your view's implementation whenever current property set @@ -176,6 +180,11 @@ void focus(bool in); protected: + /*! Sets data to the widget. Default implementation just calls KexiDB::Cursor::open(). + Used internally by setData(KexiDB::Cursor*). + @return true on success. */ + virtual bool setWidgetData(KexiDB::Cursor *cursor); + virtual bool eventFilter(QObject *o, QEvent *e); /*! called by KexiWindow::switchToViewMode() right before window is switched to new mode diff --git a/kexi/core/KexiView.cpp b/kexi/core/KexiView.cpp --- a/kexi/core/KexiView.cpp +++ b/kexi/core/KexiView.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -555,6 +556,32 @@ window()->removeView(mode); } +bool KexiView::setWidgetData(KexiDB::Cursor *cursor) +{ + return cursor->open(); +} + +bool KexiView::setData(KexiDB::Cursor *cursor) +{ + bool ok = setWidgetData(cursor); + if (!ok && cursor) { + // A simple workaround needed because the cursor will be destroyed before + // the error message is built. Kexi 3 would have cleaner solution. + // See https://bugs.kde.org/show_bug.cgi?id=356888 + QString msg = cursor->errorMsg(); + QString desc; + if (!cursor->serverErrorMsg().isEmpty()) { + if (msg.isEmpty()) { + msg = cursor->serverErrorMsg(); + } else { + desc = cursor->serverErrorMsg(); + } + } + window()->setStatus(msg, desc, cursor->recentSQLString()); + } + return ok; +} + void KexiView::setFocus() { if (!d->lastFocusedChildBeforeFocusOut.isNull()) { diff --git a/kexi/core/kexi.h b/kexi/core/kexi.h --- a/kexi/core/kexi.h +++ b/kexi/core/kexi.h @@ -110,7 +110,8 @@ bool error() const; - void setStatus(const QString& message, const QString& description); + void setStatus(const QString& message, const QString& description, + const QString& errorSql = QString()); //! Note: for safety, \a dbObject needs to be derived from QObject, //! otherwise it won't be assigned @@ -137,7 +138,9 @@ //! by receiving a message operator KexiDB::MessageHandler*(); - QString message, description; + QString message; + QString description; + QString errorSql; //!< Recently executed SQL, overrides information from dbObject() protected: QPointer dbObj; //! This is in fact KexiDB::Object KexiDB::MessageHandler* msgHandler; diff --git a/kexi/core/kexi.cpp b/kexi/core/kexi.cpp --- a/kexi/core/kexi.cpp +++ b/kexi/core/kexi.cpp @@ -198,11 +198,12 @@ || (dynamic_cast((QObject*)dbObj) && dynamic_cast((QObject*)dbObj)->error()); } -void ObjectStatus::setStatus(const QString& message, const QString& description) +void ObjectStatus::setStatus(const QString& message, const QString& description, const QString& errorSql) { this->dbObj = 0; this->message = message; this->description = description; + this->errorSql = errorSql; } void ObjectStatus::setStatus(KexiDB::Object* dbObject, const QString& message, const QString& description) diff --git a/kexi/core/kexiproject.cpp b/kexi/core/kexiproject.cpp --- a/kexi/core/kexiproject.cpp +++ b/kexi/core/kexiproject.cpp @@ -861,10 +861,12 @@ return 0; KexiWindow *window = part->openInstance(parent, item, viewMode, staticObjectArgs); if (!window) { - if (part->lastOperationStatus().error()) + if (part->lastOperationStatus().error()) { + m_sql = part->lastOperationStatus().errorSql; // pass proper SQL error setError(i18n("Opening object \"%1\" failed.", item.name()) + "
" + part->lastOperationStatus().message, part->lastOperationStatus().description); + } return 0; } return window; diff --git a/kexi/plugins/forms/kexiformview.h b/kexi/plugins/forms/kexiformview.h --- a/kexi/plugins/forms/kexiformview.h +++ b/kexi/plugins/forms/kexiformview.h @@ -151,7 +151,7 @@ //! Redirects to Container::eventFilter(). virtual void contextMenuEvent(QContextMenuEvent *e); - void initDataSource(); + bool initDataSource(); virtual void setFocusInternal(); /*! Called after loading the form contents (before showing it). */ diff --git a/kexi/plugins/forms/kexiformview.cpp b/kexi/plugins/forms/kexiformview.cpp --- a/kexi/plugins/forms/kexiformview.cpp +++ b/kexi/plugins/forms/kexiformview.cpp @@ -540,7 +540,9 @@ if (viewMode() == Kexi::DataViewMode) { //TMP!! - initDataSource(); + if (!initDataSource()) { + return false; + } //handle events for this form d->scrollView->setMainWidgetForEventHandling(d->dbform); @@ -596,7 +598,7 @@ return dynamic_cast(part()); } -void KexiFormView::initDataSource() +bool KexiFormView::initDataSource() { deleteQuery(); //! @todo also handle anonymous (not stored) queries provided as statements here @@ -700,11 +702,15 @@ KexiUtils::WaitCursorRemover remover; params = KexiQueryParameters::getParameters(this, *conn->driver(), *d->query, ok); } - if (ok) //input cancelled - d->cursor = conn->executeQuery(*d->query, params); + if (ok) { //input cancelled + d->cursor = conn->prepareQuery(*d->query, params); + } } d->scrollView->invalidateDataSources(invalidSources, d->query); ok = d->cursor != 0; + if (ok) { + ok = setData(d->cursor); + } } if (!invalidSources.isEmpty()) @@ -716,7 +722,7 @@ KexiDB::TableViewData* data = new KexiDB::TableViewData(d->cursor); if (forceReadOnlyDataSource) data->setReadOnly(true); - data->preloadAllRows(); + ok = data->preloadAllRows(); ///*! @todo few backends return result count for free! - no need to reopen() */ // int resultCount = -1; @@ -727,11 +733,16 @@ // if (ok) // ok = ! (!d->cursor->moveFirst() && d->cursor->error()); - d->scrollView->setData(data, true /*owner*/); + if (ok) { + d->scrollView->setData(data, true /*owner*/); + } else { + delete data; + } } else { d->scrollView->setData(0, false); } + return ok; } void KexiFormView::setFormModified() diff --git a/kexi/plugins/queries/kexiqueryview.cpp b/kexi/plugins/queries/kexiqueryview.cpp --- a/kexi/plugins/queries/kexiqueryview.cpp +++ b/kexi/plugins/queries/kexiqueryview.cpp @@ -88,21 +88,28 @@ if (!ok) {//input cancelled return cancelled; } - d->cursor = conn->executeQuery(*query, d->currentParams); + d->cursor = conn->prepareQuery(*query, d->currentParams); if (!d->cursor) { window()->setStatus( conn, i18n("Query executing failed.")); //! @todo also provide server result and sql statement return false; } - setData(d->cursor); + ok = setData(d->cursor); //! @todo remove close() when dynamic cursors arrive - d->cursor->close(); - - if (oldCursor) - oldCursor->connection()->deleteCursor(oldCursor); + if (!d->cursor->close()) { + ok = false; + } + if (oldCursor) { + conn->deleteCursor(oldCursor); + } + if (!ok) { + conn->deleteCursor(d->cursor); + d->cursor = 0; + return false; + } //! @todo maybe allow writing and inserting for single-table relations? tableView()->setReadOnly(true); diff --git a/kexi/plugins/reports/kexidbreportdata.h b/kexi/plugins/reports/kexidbreportdata.h --- a/kexi/plugins/reports/kexidbreportdata.h +++ b/kexi/plugins/reports/kexidbreportdata.h @@ -1,6 +1,7 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2009 by Adam Pigg (adam@piggz.co.uk) +* Copyright (C) 2015 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,22 +28,24 @@ #include +class KexiReportView; + /** */ class KexiDBReportData : public KoReportData { public: - KexiDBReportData(const QString &objectName, KexiDB::Connection *conn); + KexiDBReportData(const QString &objectName, KexiDB::Connection *conn, KexiReportView *view); /*! * @a partClass specifies @a objectName type: a table or query. * Types accepted: * -"org.kexi-project.table" * -"org.kexi-project.query" * -empty QString() - attempt to resolve @a objectName */ - KexiDBReportData(const QString &objectName, const QString& partClass, KexiDB::Connection *conn); + KexiDBReportData(const QString &objectName, const QString& partClass, KexiDB::Connection *conn, KexiReportView *view); virtual ~KexiDBReportData(); virtual QStringList fieldNames() const; diff --git a/kexi/plugins/reports/kexidbreportdata.cpp b/kexi/plugins/reports/kexidbreportdata.cpp --- a/kexi/plugins/reports/kexidbreportdata.cpp +++ b/kexi/plugins/reports/kexidbreportdata.cpp @@ -1,6 +1,7 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2009 by Adam Pigg (adam@piggz.co.uk) +* Copyright (C) 2015 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,17 +18,19 @@ */ #include "kexidbreportdata.h" -#include +#include "kexireportview.h" #include #include -#include +#include + +#include class KexiDBReportData::Private { public: - explicit Private(KexiDB::Connection *pDb) - : cursor(0), connection(pDb), originalSchema(0), copySchema(0) + explicit Private(KexiDB::Connection *pDb, KexiReportView *v) + : cursor(0), connection(pDb), view(v), originalSchema(0), copySchema(0) { } ~Private() @@ -37,27 +40,26 @@ delete cursor; } - QString objectName; - KexiDB::Cursor *cursor; - KexiDB::Connection *connection; + KexiDB::Connection * const connection; + KexiReportView * const view; KexiDB::QuerySchema *originalSchema; KexiDB::QuerySchema *copySchema; }; KexiDBReportData::KexiDBReportData (const QString &objectName, - KexiDB::Connection * pDb) - : d(new Private(pDb)) + KexiDB::Connection * pDb, KexiReportView *view) + : d(new Private(pDb, view)) { d->objectName = objectName; getSchema(); } KexiDBReportData::KexiDBReportData(const QString& objectName, const QString& partClass, - KexiDB::Connection* pDb) - : d(new Private(pDb)) + KexiDB::Connection* pDb, KexiReportView *view) + : d(new Private(pDb, view)) { d->objectName = objectName; getSchema(partClass); @@ -102,22 +104,24 @@ { if ( d->objectName.isEmpty() ) { - d->cursor = d->connection->executeQuery ( "SELECT '' AS expr1 FROM kexi__db WHERE kexi__db.db_property = 'kexidb_major_ver'" ); + d->cursor = d->connection->prepareQuery("SELECT '' AS expr1 FROM kexi__db WHERE kexi__db.db_property = 'kexidb_major_ver'"); } else if ( d->copySchema) { kDebug() << "Opening cursor.." << d->copySchema->debugString(); - d->cursor = d->connection->executeQuery ( *d->copySchema, 1 ); + d->cursor = d->connection->prepareQuery(*d->copySchema, KexiDB::Cursor::Buffered); } - - if ( d->cursor ) - { - kDebug() << "Moving to first record.."; - return d->cursor->moveFirst(); + if (d->cursor) { + bool ok = d->view->setData(d->cursor); + if (ok) { + kDebug() << "Moving to first record.."; + if (!d->cursor->moveFirst()) { + ok = !d->cursor->error(); + } + } + return ok; } - else - return false; } return false; } @@ -396,5 +400,5 @@ KoReportData* KexiDBReportData::create(const QString& source) { - return new KexiDBReportData(source, d->connection); + return new KexiDBReportData(source, d->connection, d->view); } diff --git a/kexi/plugins/reports/kexireportview.cpp b/kexi/plugins/reports/kexireportview.cpp --- a/kexi/plugins/reports/kexireportview.cpp +++ b/kexi/plugins/reports/kexireportview.cpp @@ -388,6 +388,8 @@ Q_UNUSED(mode); if (tempData()->reportSchemaChangedInPreviousView) { + tempData()->reportSchemaChangedInPreviousView = false; + kDebug() << "Schema changed"; delete m_preRenderer; @@ -399,7 +401,7 @@ reportData = sourceData(tempData()->connectionDefinition); } if (!reportData) { - reportData = new KexiDBReportData(QString(), KexiMainWindowIface::global()->project()->dbConnection()); + reportData = new KexiDBReportData(QString(), KexiMainWindowIface::global()->project()->dbConnection(), this); } m_preRenderer->setSourceData(reportData); @@ -421,28 +423,24 @@ delete m_reportDocument; // prev document m_reportDocument = m_preRenderer->generate(); - if (m_reportDocument) { - m_pageCount = m_reportDocument->pages(); + if (!m_reportDocument) { + return false; + } + m_pageCount = m_reportDocument->pages(); #ifndef KEXI_MOBILE - m_pageSelector->setRecordCount(m_pageCount); - m_pageSelector->setCurrentRecordNumber(1); + m_pageSelector->setRecordCount(m_pageCount); + m_pageSelector->setCurrentRecordNumber(1); #endif - } - m_reportPage = new KoReportPage(this, m_reportDocument); m_reportPage->setObjectName("KexiReportPage"); m_reportScene->setSceneRect(0,0,m_reportPage->rect().width() + 40, m_reportPage->rect().height() + 40); m_reportScene->addItem(m_reportPage); m_reportPage->setPos(20,20); m_reportView->centerOn(0,0); - } else { KMessageBox::error(this, i18n("Report schema appears to be invalid or corrupt"), i18n("Opening failed")); } - - - tempData()->reportSchemaChangedInPreviousView = false; } return true; } @@ -452,7 +450,9 @@ KoReportData *kodata = 0; if (e.attribute("type") == "internal") { - kodata = new KexiDBReportData(e.attribute("source"), KexiMainWindowIface::global()->project()->dbConnection()); + kodata = new KexiDBReportData(e.attribute("source"), + KexiMainWindowIface::global()->project()->dbConnection(), + this); } #ifndef KEXI_MOBILE if (e.attribute("type") == "external") { diff --git a/kexi/plugins/reports/kexisourceselector.cpp b/kexi/plugins/reports/kexisourceselector.cpp --- a/kexi/plugins/reports/kexisourceselector.cpp +++ b/kexi/plugins/reports/kexisourceselector.cpp @@ -17,16 +17,19 @@ */ #include "kexisourceselector.h" +#include "kexireportview.h" +#include +#include "KexiDataSourceComboBox.h" +#include +#include #include #include - -#include #include #include + #include -#include "KexiDataSourceComboBox.h" -#include +#include //#define NO_EXTERNAL_SOURCES @@ -178,8 +181,17 @@ //!@TODO Fix when enable external data #ifndef NO_EXTERNAL_SOURCES + KexiReportView *view = 0; + if (KexiMainWindowIface::global()->currentWindow()) { + view = qobject_cast(KexiMainWindowIface::global()->currentWindow()->selectedView()); + if (!view) { + return 0; + } + } if (d->sourceType->itemData(d->sourceType->currentIndex()).toString() == "internal" && d->internalSource->isSelectionValid()) { - d->kexiDBData = new KexiDBReportData(d->internalSource->selectedName(), d->internalSource->selectedPartClass(), d->conn); + d->kexiDBData = new KexiDBReportData(d->internalSource->selectedName(), + d->internalSource->selectedPartClass(), + d->conn, view); return d->kexiDBData; } diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.cpp b/kexi/plugins/tables/kexitabledesigner_dataview.cpp --- a/kexi/plugins/tables/kexitabledesigner_dataview.cpp +++ b/kexi/plugins/tables/kexitabledesigner_dataview.cpp @@ -76,12 +76,15 @@ if (tempData()->tableSchemaChangedInPreviousView) { KexiUtils::WaitCursor wait; - KexiDB::Cursor *c - = KexiMainWindowIface::global()->project()->dbConnection()->prepareQuery( - *tempData()->table); - if (!c) + KexiDB::Connection *conn = KexiMainWindowIface::global()->project()->dbConnection(); + KexiDB::Cursor *cursor = conn->prepareQuery(*tempData()->table); + if (!cursor) { return false; - setData(c); + } + if (!setData(cursor)) { + conn->deleteCursor(cursor); + return false; + } tempData()->tableSchemaChangedInPreviousView = false; } return true; diff --git a/kexi/widget/tableview/KexiDataTableScrollArea.cpp b/kexi/widget/tableview/KexiDataTableScrollArea.cpp --- a/kexi/widget/tableview/KexiDataTableScrollArea.cpp +++ b/kexi/widget/tableview/KexiDataTableScrollArea.cpp @@ -70,6 +70,7 @@ kWarning() << "Cursor should have query schema defined!\n--aborting setData().\n"; m_cursor->debug(); clearColumns(); + m_cursor = 0; return false; } @@ -82,6 +83,7 @@ kWarning() << "Cannot open cursor\n--aborting setData(). \n" << m_cursor->serverErrorMsg(); m_cursor->debug(); clearColumns(); + m_cursor = 0; return false; } @@ -98,8 +100,11 @@ setWindowTitle(windowTitle); //PRIMITIVE!! data setting: - tv_data->preloadAllRows(); - + if (!tv_data->preloadAllRows()) { + delete tv_data; + clearColumns(); + m_cursor = 0; + } KexiTableScrollArea::setData(tv_data); return true; } diff --git a/kexi/widget/tableview/KexiDataTableView.h b/kexi/widget/tableview/KexiDataTableView.h --- a/kexi/widget/tableview/KexiDataTableView.h +++ b/kexi/widget/tableview/KexiDataTableView.h @@ -66,9 +66,6 @@ bool loadTableViewSettings(KexiDB::TableViewData* data); public Q_SLOTS: - /*! Sets data. Only works for db-aware table. */ - void setData(KexiDB::Cursor *cursor); - /*! Saves settings for the view. Implemented for KexiView. */ virtual bool saveSettings(); @@ -79,6 +76,8 @@ protected: void init(); + virtual bool setWidgetData(KexiDB::Cursor *cursor); + class Private; Private * const d; }; diff --git a/kexi/widget/tableview/KexiDataTableView.cpp b/kexi/widget/tableview/KexiDataTableView.cpp --- a/kexi/widget/tableview/KexiDataTableView.cpp +++ b/kexi/widget/tableview/KexiDataTableView.cpp @@ -110,12 +110,13 @@ return true; } -void -KexiDataTableView::setData(KexiDB::Cursor *c) +bool KexiDataTableView::setWidgetData(KexiDB::Cursor *cursor) { - if (!dynamic_cast(mainWidget())) - return; - dynamic_cast(mainWidget())->setData(c); + bool ok = dynamic_cast(mainWidget()); + if (!ok) { + return false; + } + return dynamic_cast(mainWidget())->setData(cursor); } void KexiDataTableView::filter() diff --git a/libs/koreport/renderer/KoReportPage.cpp b/libs/koreport/renderer/KoReportPage.cpp --- a/libs/koreport/renderer/KoReportPage.cpp +++ b/libs/koreport/renderer/KoReportPage.cpp @@ -31,38 +31,33 @@ KoReportPage::KoReportPage(QWidget *parent, ORODocument *document) : QObject(parent), QGraphicsRectItem() + , m_reportDocument(document) + , m_page(0) { - //TODO setAttribute(Qt::WA_NoBackground); - //kDebug() << "CREATED PAGE"; - m_reportDocument = document; - m_page = 0; - int pageWidth = 0; - int pageHeight = 0; + Q_ASSERT(m_reportDocument); - if (m_reportDocument) { - QString pageSize = m_reportDocument->pageOptions().getPageSize(); - - - if (pageSize == "Custom") { - // if this is custom sized sheet of paper we will just use those values - pageWidth = (int)(m_reportDocument->pageOptions().getCustomWidth()); - pageHeight = (int)(m_reportDocument->pageOptions().getCustomHeight()); - } else { - // lookup the correct size information for the specified size paper - pageWidth = m_reportDocument->pageOptions().widthPx(); - pageHeight = m_reportDocument->pageOptions().heightPx(); - } + int pageWidth; + int pageHeight; + const QString pageSize = m_reportDocument->pageOptions().getPageSize(); + if (pageSize == "Custom") { + // if this is custom sized sheet of paper we will just use those values + pageWidth = (int)(m_reportDocument->pageOptions().getCustomWidth()); + pageHeight = (int)(m_reportDocument->pageOptions().getCustomHeight()); + } else { + // lookup the correct size information for the specified size paper + pageWidth = m_reportDocument->pageOptions().widthPx(); + pageHeight = m_reportDocument->pageOptions().heightPx(); } - setRect(0,0,pageWidth, pageHeight); - //kDebug() << "PAGE IS " << pageWidth << "x" << pageHeight; + setRect(0, 0, pageWidth, pageHeight); + m_pixmap = new QPixmap(pageWidth, pageHeight); m_renderer = m_factory.createInstance("screen"); connect(m_reportDocument, SIGNAL(updated(int)), this, SLOT(pageUpdated(int))); m_renderTimer = new QTimer(this); m_renderTimer->setSingleShot(true); connect(m_renderTimer, SIGNAL(timeout()), this, SLOT(renderCurrentPage())); - + renderPage(1); } @@ -107,5 +102,4 @@ renderPage(m_page + 1); } - #include "KoReportPage.moc" diff --git a/libs/koreport/renderer/KoReportPreRenderer.cpp b/libs/koreport/renderer/KoReportPreRenderer.cpp --- a/libs/koreport/renderer/KoReportPreRenderer.cpp +++ b/libs/koreport/renderer/KoReportPreRenderer.cpp @@ -470,7 +470,7 @@ } //kDebug() << "Creating Document"; - d->m_document = new ORODocument(d->m_reportData->m_title); + QScopedPointer document(new ORODocument(d->m_reportData->m_title)); d->m_pageCounter = 0; d->m_yOffset = 0.0; @@ -526,9 +526,11 @@ //kDebug() << "Page Size:" << d->m_maxWidth << d->m_maxHeight; - d->m_document->setPageOptions(rpo); + document->setPageOptions(rpo); d->m_kodata->setSorting(d->m_reportData->m_detailSection->m_sortedFields); - d->m_kodata->open(); + if (!d->m_kodata->open()) { + return 0; + } d->initEngine(); //Loop through all abjects that have been registered, and register them with the script handler @@ -579,7 +581,9 @@ KoReportData *mydata = d->m_kodata; if (mydata && mydata->recordCount() > 0) { /* && !((query = orqThis->getQuery())->eof()))*/ - mydata->moveFirst(); + if (!mydata->moveFirst()) { + return 0; + } int row = 0; int col = 0; do { @@ -625,7 +629,7 @@ // _postProcText contains those text boxes that need to be updated // with information that wasn't available at the time it was added to the document - d->m_scriptHandler->setPageTotal(d->m_document->pages()); + d->m_scriptHandler->setPageTotal(document->pages()); for (int i = 0; i < d->m_postProcText.size(); i++) { OROTextBox * tb = d->m_postProcText.at(i); @@ -639,11 +643,14 @@ d->m_scriptHandler->displayErrors(); - d->m_kodata->close(); + if (!d->m_kodata->close()) { + return 0; + } delete d->m_scriptHandler; delete d->m_kodata; d->m_postProcText.clear(); + d->m_document = document.take(); return d->m_document; }