From 0108041c68913f89471c9f7b8d7683e0f7d4efab Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Oct 2021 09:09:00 +1000 Subject: [PATCH 01/24] Add query log --- .../core/auto_generated/qgsapplication.sip.in | 7 + .../core/auto_generated/qgsdbquerylog.sip.in | 102 ++++++++ python/core/core_auto.sip | 1 + src/app/CMakeLists.txt | 4 + .../querylogger/qgsappquerylogger.cpp | 244 ++++++++++++++++++ .../devtools/querylogger/qgsappquerylogger.h | 147 +++++++++++ .../querylogger/qgsqueryloggernode.cpp | 201 +++++++++++++++ .../devtools/querylogger/qgsqueryloggernode.h | 197 ++++++++++++++ .../querylogger/qgsqueryloggerpanelwidget.cpp | 205 +++++++++++++++ .../querylogger/qgsqueryloggerpanelwidget.h | 84 ++++++ .../qgsqueryloggerwidgetfactory.cpp | 29 +++ .../querylogger/qgsqueryloggerwidgetfactory.h | 35 +++ src/app/qgisapp.cpp | 7 + src/app/qgisapp.h | 3 + src/core/CMakeLists.txt | 2 + src/core/qgsapplication.cpp | 13 + src/core/qgsapplication.h | 9 + src/core/qgsdbquerylog.cpp | 68 +++++ src/core/qgsdbquerylog.h | 170 ++++++++++++ src/providers/postgres/qgspostgresconn.cpp | 108 ++++++-- src/providers/postgres/qgspostgresconn.h | 3 +- .../postgres/qgspostgresdataitems.cpp | 20 +- .../postgres/qgspostgresfeatureiterator.cpp | 13 +- src/ui/qgsqueryloggerpanelbase.ui | 117 +++++++++ 24 files changed, 1766 insertions(+), 23 deletions(-) create mode 100644 python/core/auto_generated/qgsdbquerylog.sip.in create mode 100644 src/app/devtools/querylogger/qgsappquerylogger.cpp create mode 100644 src/app/devtools/querylogger/qgsappquerylogger.h create mode 100644 src/app/devtools/querylogger/qgsqueryloggernode.cpp create mode 100644 src/app/devtools/querylogger/qgsqueryloggernode.h create mode 100644 src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp create mode 100644 src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h create mode 100644 src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp create mode 100644 src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h create mode 100644 src/core/qgsdbquerylog.cpp create mode 100644 src/core/qgsdbquerylog.h create mode 100644 src/ui/qgsqueryloggerpanelbase.ui diff --git a/python/core/auto_generated/qgsapplication.sip.in b/python/core/auto_generated/qgsapplication.sip.in index f16bbd66913..106a2bef57c 100644 --- a/python/core/auto_generated/qgsapplication.sip.in +++ b/python/core/auto_generated/qgsapplication.sip.in @@ -854,6 +854,13 @@ Returns the application's bookmark manager, used for storing installation-wide b Returns the handler for recently used style items. .. versionadded:: 3.22 +%End + + static QgsDatabaseQueryLog *databaseQueryLog() /KeepReference/; +%Docstring +Returns the database query log. + +.. versionadded:: 3.24 %End static QgsStyleModel *defaultStyleModel(); diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in new file mode 100644 index 00000000000..8f2efcb28d5 --- /dev/null +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -0,0 +1,102 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsdbquerylog.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsDatabaseQueryLogEntry +{ +%Docstring(signature="appended") +Encapsulates a logged database query. + +.. versionadded:: 3.24 +%End + +%TypeHeaderCode +#include "qgsdbquerylog.h" +%End + public: + + QgsDatabaseQueryLogEntry( const QString &query = QString() ); +%Docstring +Constructor for QgsDatabaseQueryLogEntry. +%End + + QString query; + + QString initiatorClass; + + QString origin; +}; + + + +class QgsDatabaseQueryLog: QObject +{ +%Docstring(signature="appended") +Handles logging of database queries. + +:py:class:`QgsDatabaseQueryLog` is not usually directly created, but rather accessed through +:py:func:`QgsApplication.databaseQueryLog()`. Generally, clients should only access the +static :py:func:`~QgsDatabaseQueryLogEntry.log` method to register their queries. + +Example +------- + +.. code-block:: python + + # Log a database query + QgsDatabaseQueryLogger.log('SELECT * FROM my_table') + +.. versionadded:: 3.24 +%End + +%TypeHeaderCode +#include "qgsdbquerylog.h" +%End + public: + + QgsDatabaseQueryLog( QObject *parent = 0 ); +%Docstring +Creates a new query logger. + +:py:class:`QgsDatabaseQueryLogger` is not usually directly created, but rather accessed through +:py:func:`QgsApplication.queryLogger()`. +%End + + static void log( const QString &query ); +%Docstring +Logs a database query. + +Consider using the variant with a :py:class:`QgsDatabaseQueryLogEntry` argument instead, as that +method allows more context for logged queries. + +This method can be safely called from any thread. +%End + + static void log( const QgsDatabaseQueryLogEntry &entry ); +%Docstring +Logs a database query ``entry``. + +This method can be safely called from any thread. +%End + + public slots: + + + signals: + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsdbquerylog.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 914ecb7793e..f149af7e2f9 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -38,6 +38,7 @@ %Include auto_generated/qgsdatasourceuri.sip %Include auto_generated/qgsdatetimestatisticalsummary.sip %Include auto_generated/qgsdbfilterproxymodel.sip +%Include auto_generated/qgsdbquerylog.sip %Include auto_generated/qgsdefaultvalue.sip %Include auto_generated/qgsdiagramrenderer.sip %Include auto_generated/qgsdistancearea.sip diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index afb2228fbac..4d3833a045c 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -169,6 +169,10 @@ set(QGIS_APP_SRCS devtools/networklogger/qgsnetworkloggerwidgetfactory.cpp devtools/profiler/qgsprofilerpanelwidget.cpp devtools/profiler/qgsprofilerwidgetfactory.cpp + devtools/querylogger/qgsappquerylogger.cpp + devtools/querylogger/qgsqueryloggernode.cpp + devtools/querylogger/qgsqueryloggerpanelwidget.cpp + devtools/querylogger/qgsqueryloggerwidgetfactory.cpp dwg/qgsdwgimportdialog.cpp dwg/qgsdwgimporter.cpp diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp new file mode 100644 index 00000000000..0b412391178 --- /dev/null +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -0,0 +1,244 @@ +/*************************************************************************** + qgsappquerylogger.cpp + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsappquerylogger.h" +#include "qgsqueryloggernode.h" +#include "qgsapplication.h" +#include "qgssettings.h" +#include "qgis.h" +#include +#include +#include + +QgsAppQueryLogger::QgsAppQueryLogger( QObject *parent ) + : QAbstractItemModel( parent ) + , mRootNode( std::make_unique< QgsDatabaseQueryLoggerRootNode >() ) +{ + // logger must be created on the main thread + Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() ); + + if ( QgsSettings().value( QStringLiteral( "logQueries" ), false, QgsSettings::App ).toBool() ) + enableLogging( true ); +} + +bool QgsAppQueryLogger::isLogging() const +{ + return mIsLogging; +} + +QgsAppQueryLogger::~QgsAppQueryLogger() = default; + +void QgsAppQueryLogger::enableLogging( bool enabled ) +{ + if ( enabled ) + { + connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged, Qt::UniqueConnection ); + } + else + { + disconnect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged ); + } + mIsLogging = enabled; +} + +void QgsAppQueryLogger::clear() +{ + beginResetModel(); + mRequestGroups.clear(); + mRootNode->clear(); + endResetModel(); +} + +void QgsAppQueryLogger::queryLogged( const QgsDatabaseQueryLogEntry &query ) +{ + const int childCount = mRootNode->childCount(); + + beginInsertRows( QModelIndex(), childCount, childCount ); + + std::unique_ptr< QgsDatabaseQueryLoggerGroup > group = std::make_unique< QgsDatabaseQueryLoggerGroup >( query.query ); +// mRequestGroups.insert( parameters.requestId(), group.get() ); + mRootNode->addChild( std::move( group ) ); + endInsertRows(); +} + +QgsDatabaseQueryLoggerNode *QgsAppQueryLogger::index2node( const QModelIndex &index ) const +{ + if ( !index.isValid() ) + return mRootNode.get(); + + return reinterpret_cast( index.internalPointer() ); +} + +QList QgsAppQueryLogger::actions( const QModelIndex &index, QObject *parent ) +{ + QgsDatabaseQueryLoggerNode *node = index2node( index ); + if ( !node ) + return QList< QAction * >(); + + return node->actions( parent ); +} + +QModelIndex QgsAppQueryLogger::node2index( QgsDatabaseQueryLoggerNode *node ) const +{ + if ( !node || !node->parent() ) + return QModelIndex(); // this is the only root item -> invalid index + + QModelIndex parentIndex = node2index( node->parent() ); + + int row = node->parent()->indexOf( node ); + Q_ASSERT( row >= 0 ); + return index( row, 0, parentIndex ); +} + +QModelIndex QgsAppQueryLogger::indexOfParentLayerTreeNode( QgsDatabaseQueryLoggerNode *parentNode ) const +{ + Q_ASSERT( parentNode ); + + QgsDatabaseQueryLoggerGroup *grandParentNode = parentNode->parent(); + if ( !grandParentNode ) + return QModelIndex(); // root node -> invalid index + + int row = grandParentNode->indexOf( parentNode ); + Q_ASSERT( row >= 0 ); + + return createIndex( row, 0, parentNode ); +} + +void QgsAppQueryLogger::removeRequestRows( const QList &rows ) +{ + QList< int > res = rows; + std::sort( res.begin(), res.end(), std::greater< int >() ); + + for ( int row : std::as_const( res ) ) + { + int popId = data( index( row, 0, QModelIndex() ), QgsDatabaseQueryLoggerNode::RoleId ).toInt(); + mRequestGroups.remove( popId ); + + beginRemoveRows( QModelIndex(), row, row ); + mRootNode->removeRow( row ); + endRemoveRows(); + } +} + +QgsDatabaseQueryLoggerRootNode *QgsAppQueryLogger::rootGroup() +{ + return mRootNode.get(); +} + +int QgsAppQueryLogger::rowCount( const QModelIndex &parent ) const +{ + QgsDatabaseQueryLoggerNode *n = index2node( parent ); + if ( !n ) + return 0; + + return n->childCount(); +} + +int QgsAppQueryLogger::columnCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return 1; +} + +QModelIndex QgsAppQueryLogger::index( int row, int column, const QModelIndex &parent ) const +{ + if ( column < 0 || column >= columnCount( parent ) || + row < 0 || row >= rowCount( parent ) ) + return QModelIndex(); + + QgsDatabaseQueryLoggerGroup *n = dynamic_cast< QgsDatabaseQueryLoggerGroup * >( index2node( parent ) ); + if ( !n ) + return QModelIndex(); // have no children + + return createIndex( row, column, n->childAt( row ) ); +} + +QModelIndex QgsAppQueryLogger::parent( const QModelIndex &child ) const +{ + if ( !child.isValid() ) + return QModelIndex(); + + if ( QgsDatabaseQueryLoggerNode *n = index2node( child ) ) + { + return indexOfParentLayerTreeNode( n->parent() ); // must not be null + } + else + { + Q_ASSERT( false ); + return QModelIndex(); + } +} + +QVariant QgsAppQueryLogger::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid() || index.column() > 1 ) + return QVariant(); + + QgsDatabaseQueryLoggerNode *node = index2node( index ); + if ( !node ) + return QVariant(); + + return node->data( role ); +} + +Qt::ItemFlags QgsAppQueryLogger::flags( const QModelIndex &index ) const +{ + if ( !index.isValid() ) + { + Qt::ItemFlags rootFlags = Qt::ItemFlags(); + return rootFlags; + } + + Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return f; +} + +QVariant QgsAppQueryLogger::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole ) + return tr( "Requests" ); + return QVariant(); +} + + +// +// QgsDatabaseQueryLoggerProxyModel +// + +QgsDatabaseQueryLoggerProxyModel::QgsDatabaseQueryLoggerProxyModel( QgsAppQueryLogger *logger, QObject *parent ) + : QSortFilterProxyModel( parent ) + , mLogger( logger ) +{ + setSourceModel( mLogger ); +} + +void QgsDatabaseQueryLoggerProxyModel::setFilterString( const QString &string ) +{ + mFilterString = string; + invalidateFilter(); +} + +bool QgsDatabaseQueryLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const +{ + QgsDatabaseQueryLoggerNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); +#if 0 + if ( QgsDatabaseQueryLoggerRequestGroup *request = dynamic_cast< QgsDatabaseQueryLoggerRequestGroup * >( node ) ) + { + return mFilterString.isEmpty() || request->url().url().contains( mFilterString, Qt::CaseInsensitive ); + } +#endif + + return true; +} diff --git a/src/app/devtools/querylogger/qgsappquerylogger.h b/src/app/devtools/querylogger/qgsappquerylogger.h new file mode 100644 index 00000000000..ede6e92a692 --- /dev/null +++ b/src/app/devtools/querylogger/qgsappquerylogger.h @@ -0,0 +1,147 @@ +/*************************************************************************** + qgsappquerylogger.h + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSAPPQUERYLOGGER_H +#define QGSAPPQUERYLOGGER_H + +#include +#include +#include +#include "qgsdbquerylog.h" +#include + +class QgsDatabaseQueryLoggerNode; +class QgsDatabaseQueryLoggerRequestGroup; +class QgsDatabaseQueryLoggerRootNode; +class QAction; + +/** + * \ingroup app + * \class QgsAppQueryLogger + * \brief Logs sql queries, converting them + * to a QAbstractItemModel representing the request and response details. + */ +class QgsAppQueryLogger : public QAbstractItemModel +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsAppQueryLogger, logging requests from the specified \a manager. + * + * \warning QgsAppQueryLogger must be created on the main thread. + */ + QgsAppQueryLogger( QObject *parent ); + ~QgsAppQueryLogger() override; + + /** + * Returns TRUE if the logger is currently logging activity. + */ + bool isLogging() const; + + // Implementation of virtual functions from QAbstractItemModel + + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + int columnCount( const QModelIndex &parent = QModelIndex() ) const override; + QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const override; + QModelIndex parent( const QModelIndex &child ) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + Qt::ItemFlags flags( const QModelIndex &index ) const override; + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + + /** + * Returns node for given index. Returns root node for invalid index. + */ + QgsDatabaseQueryLoggerNode *index2node( const QModelIndex &index ) const; + + /** + * Returns a list of actions corresponding to the item at the specified \a index. + * + * The actions should be parented to \a parent. + */ + QList< QAction * > actions( const QModelIndex &index, QObject *parent ); + + /** + * Removes a list of request \a rows from the log. + */ + void removeRequestRows( const QList< int > &rows ); + + /** + * Returns the root node of the log. + */ + QgsDatabaseQueryLoggerRootNode *rootGroup(); + + static constexpr int MAX_LOGGED_REQUESTS = 1000; + + public slots: + + /** + * Enables or disables logging, depending on the value of \a enabled. + */ + void enableLogging( bool enabled ); + + /** + * Clears all logged entries. + */ + void clear(); + + private slots: + void queryLogged( const QgsDatabaseQueryLogEntry &query ); + + private: + + //! Returns index for a given node + QModelIndex node2index( QgsDatabaseQueryLoggerNode *node ) const; + QModelIndex indexOfParentLayerTreeNode( QgsDatabaseQueryLoggerNode *parentNode ) const; + + bool mIsLogging = false; + + std::unique_ptr< QgsDatabaseQueryLoggerRootNode > mRootNode; + + QHash< int, QgsDatabaseQueryLoggerRequestGroup * > mRequestGroups; + +}; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerProxyModel + * \brief A proxy model for filtering QgsNetworkLogger models by url string subsets + * or request status. + */ +class QgsDatabaseQueryLoggerProxyModel : public QSortFilterProxyModel +{ + public: + + /** + * Constructor for QgsDatabaseQueryLoggerProxyModel, filtering the specified network \a logger. + */ + QgsDatabaseQueryLoggerProxyModel( QgsAppQueryLogger *logger, QObject *parent ); + + /** + * Sets a filter \a string to apply to request queries. + */ + void setFilterString( const QString &string ); + + protected: + bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const override; + + private: + + QgsAppQueryLogger *mLogger = nullptr; + + QString mFilterString; +}; + +#endif // QGSAPPQUERYLOGGER_H diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp new file mode 100644 index 00000000000..ed25255682c --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** + QgsDatabaseQueryLoggernode.cpp + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsqueryloggernode.h" +#include "qgis.h" +#include "qgsjsonutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// QgsDatabaseQueryLoggerNode +// + +QgsDatabaseQueryLoggerNode::QgsDatabaseQueryLoggerNode() = default; +QgsDatabaseQueryLoggerNode::~QgsDatabaseQueryLoggerNode() = default; + + +// +// QgsDatabaseQueryLoggerGroup +// + +QgsDatabaseQueryLoggerGroup::QgsDatabaseQueryLoggerGroup( const QString &title ) + : mGroupTitle( title ) +{ +} + +void QgsDatabaseQueryLoggerGroup::addChild( std::unique_ptr child ) +{ + if ( !child ) + return; + + Q_ASSERT( !child->mParent ); + child->mParent = this; + + mChildren.emplace_back( std::move( child ) ); +} + +int QgsDatabaseQueryLoggerGroup::indexOf( QgsDatabaseQueryLoggerNode *child ) const +{ + Q_ASSERT( child->mParent == this ); + auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr &p ) + { + return p.get() == child; + } ); + if ( it != mChildren.end() ) + return std::distance( mChildren.begin(), it ); + return -1; +} + +QgsDatabaseQueryLoggerNode *QgsDatabaseQueryLoggerGroup::childAt( int index ) +{ + Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() ); + return mChildren[ index ].get(); +} + +void QgsDatabaseQueryLoggerGroup::clear() +{ + mChildren.clear(); +} + +QVariant QgsDatabaseQueryLoggerGroup::data( int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + return mGroupTitle; + + default: + break; + } + return QVariant(); +} + +QVariant QgsDatabaseQueryLoggerGroup::toVariant() const +{ + QVariantMap res; + for ( const std::unique_ptr< QgsDatabaseQueryLoggerNode > &child : mChildren ) + { + if ( const QgsDatabaseQueryLoggerValueNode *valueNode = dynamic_cast< const QgsDatabaseQueryLoggerValueNode *>( child.get() ) ) + { + res.insert( valueNode->key(), valueNode->value() ); + } + } + return res; +} + +// +// QgsDatabaseQueryLoggerRootNode +// + +QgsDatabaseQueryLoggerRootNode::QgsDatabaseQueryLoggerRootNode() + : QgsDatabaseQueryLoggerGroup( QString() ) +{ + +} + +QVariant QgsDatabaseQueryLoggerRootNode::data( int ) const +{ + return QVariant(); +} + +void QgsDatabaseQueryLoggerRootNode::removeRow( int row ) +{ + mChildren.erase( mChildren.begin() + row ); +} + +QVariant QgsDatabaseQueryLoggerRootNode::toVariant() const +{ + QVariantList res; + for ( const std::unique_ptr< QgsDatabaseQueryLoggerNode > &child : mChildren ) + res << child->toVariant(); + return res; +} + + +// +// QgsDatabaseQueryLoggerValueNode +// +QgsDatabaseQueryLoggerValueNode::QgsDatabaseQueryLoggerValueNode( const QString &key, const QString &value, const QColor &color ) + : mKey( key ) + , mValue( value ) + , mColor( color ) +{ + +} + +QVariant QgsDatabaseQueryLoggerValueNode::data( int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + case Qt::ToolTipRole: + { + return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue ); + } + + case Qt::ForegroundRole: + { + if ( mColor.isValid() ) + return QBrush( mColor ); + break; + } + default: + break; + } + return QVariant(); +} + +QList QgsDatabaseQueryLoggerValueNode::actions( QObject *parent ) +{ + QList< QAction * > res; + + QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent ); + QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ] + { + QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) ); + } ); + + res << copyAction; + + return res; +} + +// +// QgsDatabaseQueryLoggerGroup +// + +void QgsDatabaseQueryLoggerGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color ) +{ + addChild( std::make_unique< QgsDatabaseQueryLoggerValueNode >( key, value, color ) ); +} + + +QList QgsDatabaseQueryLoggerNode::actions( QObject * ) +{ + return QList< QAction * >(); +} + +QVariant QgsDatabaseQueryLoggerNode::toVariant() const +{ + return QVariant(); +} diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h new file mode 100644 index 00000000000..494fd4c2b1f --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -0,0 +1,197 @@ +/*************************************************************************** + QgsDatabaseQueryLoggernode.h + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QgsDatabaseQueryLoggerNODE_H +#define QgsDatabaseQueryLoggerNODE_H + +#include +#include +#include +#include +#include +#include + +class QAction; +class QgsDatabaseQueryLoggerGroup; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerNode + * \brief Base class for nodes in the query logger model. + */ +class QgsDatabaseQueryLoggerNode +{ + public: + + //! Custom node data roles + enum Roles + { + RoleStatus = Qt::UserRole + 1, //!< Request status role + RoleId, //!< Request ID role + }; + + virtual ~QgsDatabaseQueryLoggerNode(); + + /** + * Returns the node's parent node. + * + * If parent is NULLPTR, the node is a root node + */ + QgsDatabaseQueryLoggerGroup *parent() { return mParent; } + + /** + * Returns the node's data for the specified model \a role. + */ + virtual QVariant data( int role = Qt::DisplayRole ) const = 0; + + /** + * Returns the number of child nodes owned by this node. + */ + virtual int childCount() const = 0; + + /** + * Returns a list of actions relating to the node. + * + * The actions should be parented to \a parent. + */ + virtual QList< QAction * > actions( QObject *parent ); + + /** + * Converts the node's contents to a variant. + */ + virtual QVariant toVariant() const; + + protected: + + QgsDatabaseQueryLoggerNode(); + + private: + + QgsDatabaseQueryLoggerGroup *mParent = nullptr; + friend class QgsDatabaseQueryLoggerGroup; +}; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerGroup + * \brief Base class for query logger model "group" nodes, which contain children of their own. + */ +class QgsDatabaseQueryLoggerGroup : public QgsDatabaseQueryLoggerNode +{ + public: + + /** + * Adds a \a child node to this node. + */ + void addChild( std::unique_ptr< QgsDatabaseQueryLoggerNode > child ); + + /** + * Returns the index of the specified \a child node. + * + * \warning \a child must be a valid child of this node. + */ + int indexOf( QgsDatabaseQueryLoggerNode *child ) const; + + /** + * Returns the child at the specified \a index. + */ + QgsDatabaseQueryLoggerNode *childAt( int index ); + + /** + * Clears the group, removing all its children. + */ + void clear(); + + int childCount() const override final { return mChildren.size(); } + QVariant data( int role = Qt::DisplayRole ) const override; + QVariant toVariant() const override; + +// protected: + + /** + * Constructor for a QgsDatabaseQueryLoggerGroup, with the specified \a title. + */ + QgsDatabaseQueryLoggerGroup( const QString &title ); + + /** + * Adds a simple \a key: \a value node to the group. + */ + void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); + + private: + + std::deque< std::unique_ptr< QgsDatabaseQueryLoggerNode > > mChildren; + QString mGroupTitle; + friend class QgsDatabaseQueryLoggerRootNode; + friend class QgsAppQueryLogger; + +}; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerValueNode + * \brief A "key: value" style node for the query logger model. + */ +class QgsDatabaseQueryLoggerValueNode : public QgsDatabaseQueryLoggerNode +{ + public: + + /** + * Constructor for QgsDatabaseQueryLoggerValueNode, with the specified \a key (usually translated) and \a value. + */ + QgsDatabaseQueryLoggerValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); + + /** + * Returns the node's key. + */ + QString key() const { return mKey; } + + /** + * Returns the node's value. + */ + QString value() const { return mValue; } + + QVariant data( int role = Qt::DisplayRole ) const override final; + int childCount() const override final { return 0; } + QList< QAction * > actions( QObject *parent ) override final; + + private: + + QString mKey; + QString mValue; + QColor mColor; +}; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerRootNode + * \brief Root node for the query logger model. + */ +class QgsDatabaseQueryLoggerRootNode final : public QgsDatabaseQueryLoggerGroup +{ + public: + + QgsDatabaseQueryLoggerRootNode(); + QVariant data( int role = Qt::DisplayRole ) const override final; + + /** + * Removes a \a row from the root group. + */ + void removeRow( int row ); + + QVariant toVariant() const override; +}; + + +#endif // QgsDatabaseQueryLoggerNODE_H diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp new file mode 100644 index 00000000000..37c8b8580b6 --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** + QgsDatabaseQueryLoggerpanelwidget.cpp + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsapplication.h" +#include "qgsguiutils.h" +#include "qgsjsonutils.h" +#include "qgsqueryloggerpanelwidget.h" +#include "qgsqueryloggernode.h" +#include "qgsappquerylogger.h" +#include "qgssettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// +// QgsDatabaseQueryLoggerTreeView +// + +QgsDatabaseQueryLoggerTreeView::QgsDatabaseQueryLoggerTreeView( QgsAppQueryLogger *logger, QWidget *parent ) + : QTreeView( parent ) + , mLogger( logger ) +{ + connect( this, &QTreeView::expanded, this, &QgsDatabaseQueryLoggerTreeView::itemExpanded ); + + setFont( QFontDatabase::systemFont( QFontDatabase::FixedFont ) ); + + mProxyModel = new QgsDatabaseQueryLoggerProxyModel( mLogger, this ); + setModel( mProxyModel ); + + setContextMenuPolicy( Qt::CustomContextMenu ); + connect( this, &QgsDatabaseQueryLoggerTreeView::customContextMenuRequested, this, &QgsDatabaseQueryLoggerTreeView::contextMenu ); + + connect( verticalScrollBar(), &QAbstractSlider::sliderMoved, this, [this]( int value ) + { + if ( value == verticalScrollBar()->maximum() ) + mAutoScroll = true; + else + mAutoScroll = false; + } ); + + connect( mLogger, &QAbstractItemModel::rowsInserted, this, [ = ] + { + if ( mLogger->rowCount() > ( QgsAppQueryLogger::MAX_LOGGED_REQUESTS * 1.2 ) ) // 20 % more as buffer + { + // never trim expanded nodes + const int toTrim = mLogger->rowCount() - QgsAppQueryLogger::MAX_LOGGED_REQUESTS; + int trimmed = 0; + QList< int > rowsToTrim; + rowsToTrim.reserve( toTrim ); + for ( int i = 0; i < mLogger->rowCount(); ++i ) + { + const QModelIndex proxyIndex = mProxyModel->mapFromSource( mLogger->index( i, 0 ) ); + if ( !proxyIndex.isValid() || !isExpanded( proxyIndex ) ) + { + rowsToTrim << i; + trimmed++; + } + if ( trimmed == toTrim ) + break; + } + + mLogger->removeRequestRows( rowsToTrim ); + } + + if ( mAutoScroll ) + scrollToBottom(); + } ); + + mMenu = new QMenu( this ); +} + +void QgsDatabaseQueryLoggerTreeView::setFilterString( const QString &string ) +{ + mProxyModel->setFilterString( string ); +} + +void QgsDatabaseQueryLoggerTreeView::itemExpanded( const QModelIndex &index ) +{ + // if the item is a QgsNetworkLoggerRequestGroup item, open all children (show ALL info of it) + // we want to scroll to last request + + // only expand all children on QgsNetworkLoggerRequestGroup nodes (which don't have a valid parent!) + if ( !index.parent().isValid() ) + expandChildren( index ); + + // make ALL request information visible by scrolling view to it + scrollTo( index ); +} + +void QgsDatabaseQueryLoggerTreeView::contextMenu( QPoint point ) +{ + const QModelIndex viewModelIndex = indexAt( point ); + const QModelIndex modelIndex = mProxyModel->mapToSource( viewModelIndex ); + + if ( modelIndex.isValid() ) + { + mMenu->clear(); + + const QList< QAction * > actions = mLogger->actions( modelIndex, mMenu ); + mMenu->addActions( actions ); + if ( !mMenu->actions().empty() ) + { + mMenu->exec( viewport()->mapToGlobal( point ) ); + } + } +} + +void QgsDatabaseQueryLoggerTreeView::expandChildren( const QModelIndex &index ) +{ + if ( !index.isValid() ) + return; + + const int count = model()->rowCount( index ); + for ( int i = 0; i < count; ++i ) + { + const QModelIndex childIndex = model()->index( i, 0, index ); + expandChildren( childIndex ); + } + if ( !isExpanded( index ) ) + expand( index ); +} + + +// +// QgsDatabaseQueryLoggerPanelWidget +// + +QgsDatabaseQueryLoggerPanelWidget::QgsDatabaseQueryLoggerPanelWidget( QgsAppQueryLogger *logger, QWidget *parent ) + : QgsDevToolWidget( parent ) + , mLogger( logger ) +{ + setupUi( this ); + + mTreeView = new QgsDatabaseQueryLoggerTreeView( mLogger ); + verticalLayout->addWidget( mTreeView ); + mToolbar->setIconSize( QgsGuiUtils::iconSize( true ) ); + + mFilterLineEdit->setShowClearButton( true ); + mFilterLineEdit->setShowSearchIcon( true ); + mFilterLineEdit->setPlaceholderText( tr( "Filter requests" ) ); + + mActionRecord->setChecked( mLogger->isLogging() ); + + connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, mTreeView, &QgsDatabaseQueryLoggerTreeView::setFilterString ); + connect( mActionClear, &QAction::triggered, mLogger, &QgsAppQueryLogger::clear ); + connect( mActionRecord, &QAction::toggled, this, [ = ]( bool enabled ) + { + QgsSettings().setValue( QStringLiteral( "logNetworkRequests" ), enabled, QgsSettings::App ); + mLogger->enableLogging( enabled ); + } ); + connect( mActionSaveLog, &QAction::triggered, this, [ = ]() + { + if ( QMessageBox::warning( this, tr( "Save Network Log" ), + tr( "Security warning: network logs may contain sensitive data including usernames or passwords. Treat this log as confidential and be careful who you share it with. Continue?" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No ) + return; + + const QString saveFilePath = QFileDialog::getSaveFileName( this, tr( "Save Network Log" ), QDir::homePath(), tr( "Log files" ) + " (*.json)" ); + if ( saveFilePath.isEmpty() ) + { + return; + } + + QFile exportFile( saveFilePath ); + if ( !exportFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) + { + return; + } + QTextStream fout( &exportFile ); + + const QVariant value = mLogger->rootGroup()->toVariant(); + const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( value ).dump( 2 ) ); + + fout << json; + } ); + + + QMenu *settingsMenu = new QMenu( this ); + QToolButton *settingsButton = new QToolButton(); + settingsButton->setAutoRaise( true ); + settingsButton->setToolTip( tr( "Settings" ) ); + settingsButton->setMenu( settingsMenu ); + settingsButton->setPopupMode( QToolButton::InstantPopup ); + settingsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOptions.svg" ) ) ); + mToolbar->addWidget( settingsButton ); +} diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h new file mode 100644 index 00000000000..90ed89dd7ae --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h @@ -0,0 +1,84 @@ +/*************************************************************************** + QgsDatabaseQueryLoggerpanelwidget.h + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QgsDatabaseQueryLoggerPANELWIDGET_H +#define QgsDatabaseQueryLoggerPANELWIDGET_H + +#include "qgsdevtoolwidget.h" +#include "ui_qgsqueryloggerpanelbase.h" +#include + +class QgsAppQueryLogger; +class QgsDatabaseQueryLoggerProxyModel; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerTreeView + * \brief A custom QTreeView subclass for showing logged database queries. + */ +class QgsDatabaseQueryLoggerTreeView: public QTreeView +{ + Q_OBJECT + public: + + /** + * Constructor for QgsDatabaseQueryLoggerTreeView, attached to the specified \a logger. + */ + QgsDatabaseQueryLoggerTreeView( QgsAppQueryLogger *logger, QWidget *parent = nullptr ); + + public slots: + + /** + * Sets a filter \a string to apply to request URLs. + */ + void setFilterString( const QString &string ); + + private slots: + void itemExpanded( const QModelIndex &index ); + void contextMenu( QPoint point ); + + private: + + void expandChildren( const QModelIndex &index ); + QMenu *mMenu = nullptr; + QgsAppQueryLogger *mLogger = nullptr; + QgsDatabaseQueryLoggerProxyModel *mProxyModel = nullptr; + bool mAutoScroll = true; +}; + + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerPanelWidget + * \brief A panel widget showing logged network requests. + */ +class QgsDatabaseQueryLoggerPanelWidget : public QgsDevToolWidget, private Ui::QgsDatabaseQueryLoggerPanelBase +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsDatabaseQueryLoggerPanelWidget, linked with the specified \a logger. + */ + QgsDatabaseQueryLoggerPanelWidget( QgsAppQueryLogger *logger, QWidget *parent ); + + private: + + QgsDatabaseQueryLoggerTreeView *mTreeView = nullptr; + QgsAppQueryLogger *mLogger = nullptr; +}; + + +#endif // QgsDatabaseQueryLoggerPANELWIDGET_H diff --git a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp new file mode 100644 index 00000000000..134f3059697 --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp @@ -0,0 +1,29 @@ +/*************************************************************************** + QgsDatabaseQueryLoggerwidgetfactory.cpp + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsqueryloggerwidgetfactory.h" +#include "qgsqueryloggerpanelwidget.h" +#include "qgsapplication.h" + +QgsDatabaseQueryLoggerWidgetFactory::QgsDatabaseQueryLoggerWidgetFactory( QgsAppQueryLogger *logger ) + : QgsDevToolWidgetFactory( QObject::tr( "Query Logger" ), QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/network_and_proxy.svg" ) ) ) + , mLogger( logger ) +{ +} + +QgsDevToolWidget *QgsDatabaseQueryLoggerWidgetFactory::createWidget( QWidget *parent ) const +{ + return new QgsDatabaseQueryLoggerPanelWidget( mLogger, parent ); +} diff --git a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h new file mode 100644 index 00000000000..7e46d37c946 --- /dev/null +++ b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h @@ -0,0 +1,35 @@ +/*************************************************************************** + QgsDatabaseQueryLoggerwidgetfactory.h + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QgsDatabaseQueryLoggerWIDGETFACTORY_H +#define QgsDatabaseQueryLoggerWIDGETFACTORY_H + +#include "qgsdevtoolwidgetfactory.h" + +class QgsAppQueryLogger; + +class QgsDatabaseQueryLoggerWidgetFactory: public QgsDevToolWidgetFactory +{ + public: + + QgsDatabaseQueryLoggerWidgetFactory( QgsAppQueryLogger *logger ); + QgsDevToolWidget *createWidget( QWidget *parent = nullptr ) const override; + + private: + + QgsAppQueryLogger *mLogger = nullptr; +}; + + +#endif // QgsDatabaseQueryLoggerWIDGETFACTORY_H diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 01a197e8d3d..130e7e491c1 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -437,6 +437,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgsuserprofilemanager.h" #include "qgsuserprofile.h" #include "qgsnetworkloggerwidgetfactory.h" +#include "devtools/querylogger/qgsappquerylogger.h" +#include "devtools/querylogger/qgsqueryloggerwidgetfactory.h" #include "devtools/profiler/qgsprofilerwidgetfactory.h" #include "qgsabstractdatabaseproviderconnection.h" #include "qgszipitem.h" @@ -989,6 +991,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers mNetworkLogger = new QgsNetworkLogger( QgsNetworkAccessManager::instance(), this ); endProfile(); + startProfile( tr( "Create database query logger" ) ); + mQueryLogger = new QgsAppQueryLogger( this ); + endProfile(); + // load GUI: actions, menus, toolbars startProfile( tr( "Setting up UI" ) ); setupUi( this ); @@ -1829,6 +1835,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers mBearingNumericFormat.reset( QgsLocalDefaultSettings::bearingFormat() ); mNetworkLoggerWidgetFactory.reset( std::make_unique< QgsNetworkLoggerWidgetFactory >( mNetworkLogger ) ); + mQueryLoggerWidgetFactory.reset( std::make_unique< QgsDatabaseQueryLoggerWidgetFactory >( mQueryLogger ) ); // update windows qApp->processEvents(); diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index a623f2c8acd..3ada37a8afa 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -151,6 +151,7 @@ class QgsDevToolsPanelWidget; class QgsDevToolWidgetFactory; class QgsNetworkLogger; class QgsNetworkLoggerWidgetFactory; +class QgsAppQueryLogger; class QgsMapToolCapture; class QgsElevationProfileWidget; @@ -2742,6 +2743,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsNetworkLogger *mNetworkLogger = nullptr; QgsScopedDevToolWidgetFactory mNetworkLoggerWidgetFactory; QgsScopedDevToolWidgetFactory mStartupProfilerWidgetFactory; + QgsAppQueryLogger *mQueryLogger = nullptr; + QgsScopedDevToolWidgetFactory mQueryLoggerWidgetFactory; QgsScopedOptionsWidgetFactory mCodeEditorWidgetFactory; QgsScopedOptionsWidgetFactory mBabelGpsDevicesWidgetFactory; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9b97946218a..79d39f59c82 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -355,6 +355,7 @@ set(QGIS_CORE_SRCS qgsdataprovidertemporalcapabilities.cpp qgsdatetimestatisticalsummary.cpp qgsdbfilterproxymodel.cpp + qgsdbquerylog.cpp qgsdefaultvalue.cpp qgsdiagramrenderer.cpp qgsdistancearea.cpp @@ -1004,6 +1005,7 @@ set(QGIS_CORE_HDRS qgsdatasourceuri.h qgsdatetimestatisticalsummary.h qgsdbfilterproxymodel.h + qgsdbquerylog.h qgsdefaultvalue.h qgsdiagramrenderer.h qgsdistancearea.h diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 9164a1f1e2c..b2c5032a2dc 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -77,6 +77,7 @@ #include "qgslocator.h" #include "qgsreadwritelocker.h" #include "qgsbabelformatregistry.h" +#include "qgsdbquerylog.h" #include "gps/qgsgpsconnectionregistry.h" #include "processing/qgsprocessingregistry.h" @@ -265,6 +266,7 @@ void QgsApplication::init( QString profileFolder ) std::call_once( sMetaTypesRegistered, [] { qRegisterMetaType( "QgsGeometry::Error" ); + qRegisterMetaType( "QgsDatabaseQueryLogEntry" ); qRegisterMetaType( "QgsProcessingFeatureSourceDefinition" ); qRegisterMetaType( "QgsProcessingOutputLayerDefinition" ); qRegisterMetaType( "QgsUnitTypes::LayoutUnit" ); @@ -2433,6 +2435,11 @@ QgsRecentStyleHandler *QgsApplication::recentStyleHandler() return members()->mRecentStyleHandler; } +QgsDatabaseQueryLog *QgsApplication::databaseQueryLog() +{ + return members()->mQueryLogger; +} + QgsStyleModel *QgsApplication::defaultStyleModel() { return members()->mStyleModel; @@ -2512,6 +2519,11 @@ QgsApplication::ApplicationMembers::ApplicationMembers() mMessageLog = new QgsMessageLog(); QgsRuntimeProfiler *profiler = QgsRuntimeProfiler::threadLocalInstance(); + { + profiler->start( tr( "Create query logger" ) ); + mQueryLogger = new QgsDatabaseQueryLog(); + profiler->end(); + } { profiler->start( tr( "Setup coordinate reference system registry" ) ); mCrsRegistry = new QgsCoordinateReferenceSystemRegistry(); @@ -2726,6 +2738,7 @@ QgsApplication::ApplicationMembers::~ApplicationMembers() delete mConnectionRegistry; delete mLocalizedDataPathRegistry; delete mCrsRegistry; + delete mQueryLogger; delete mSettingsRegistryCore; } diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 040dcc2462c..efed5c9d70a 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -72,6 +72,7 @@ class QgsPointCloudRendererRegistry; class QgsTileDownloadManager; class QgsCoordinateReferenceSystemRegistry; class QgsRecentStyleHandler; +class QgsDatabaseQueryLog; /** * \ingroup core @@ -820,6 +821,13 @@ class CORE_EXPORT QgsApplication : public QApplication */ static QgsRecentStyleHandler *recentStyleHandler() SIP_KEEPREFERENCE; + /** + * Returns the database query log. + * + * \since QGIS 3.24 + */ + static QgsDatabaseQueryLog *databaseQueryLog() SIP_KEEPREFERENCE; + /** * Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()). * @@ -1136,6 +1144,7 @@ class CORE_EXPORT QgsApplication : public QApplication QgsTileDownloadManager *mTileDownloadManager = nullptr; QgsStyleModel *mStyleModel = nullptr; QgsRecentStyleHandler *mRecentStyleHandler = nullptr; + QgsDatabaseQueryLog *mQueryLogger = nullptr; QString mNullRepresentation; QStringList mSvgPathCache; bool mSvgPathCacheValid = false; diff --git a/src/core/qgsdbquerylog.cpp b/src/core/qgsdbquerylog.cpp new file mode 100644 index 00000000000..523b6887942 --- /dev/null +++ b/src/core/qgsdbquerylog.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + qgsdbquerylog.cpp + ------------ + Date : October 2021 + Copyright : (C) 2021 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsdbquerylog.h" +#include "qgsapplication.h" +#include + +// +// QgsDatabaseQueryLogEntry +// + +QAtomicInt QgsDatabaseQueryLogEntry::sQueryId = 0; + +QgsDatabaseQueryLogEntry::QgsDatabaseQueryLogEntry( const QString &query ) + : queryId( ++sQueryId ) + , query( query ) + , startedTime( QDateTime::currentMSecsSinceEpoch() ) +{} + + +// +// QgsDatabaseQueryLog +// + +QgsDatabaseQueryLog::QgsDatabaseQueryLog( QObject *parent ) + : QObject( parent ) +{ + +} + +void QgsDatabaseQueryLog::log( const QgsDatabaseQueryLogEntry &query ) +{ + QMetaObject::invokeMethod( QgsApplication::databaseQueryLog(), "queryStartedPrivate", Qt::QueuedConnection, Q_ARG( QgsDatabaseQueryLogEntry, query ) ); +} + +void QgsDatabaseQueryLog::finished( const QgsDatabaseQueryLogEntry &query ) +{ + // record time of completion + QgsDatabaseQueryLogEntry finishedQuery = query; + finishedQuery.finishedTime = QDateTime::currentMSecsSinceEpoch(); + + QMetaObject::invokeMethod( QgsApplication::databaseQueryLog(), "queryFinishedPrivate", Qt::QueuedConnection, Q_ARG( QgsDatabaseQueryLogEntry, finishedQuery ) ); +} + +void QgsDatabaseQueryLog::queryStartedPrivate( const QgsDatabaseQueryLogEntry &query ) +{ + QgsDebugMsg( query.query ); + emit queryStarted( query ); +} + +void QgsDatabaseQueryLog::queryFinishedPrivate( const QgsDatabaseQueryLogEntry &query ) +{ + QgsDebugMsg( query.query ); + emit queryFinished( query ); +} + diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h new file mode 100644 index 00000000000..be10542f030 --- /dev/null +++ b/src/core/qgsdbquerylog.h @@ -0,0 +1,170 @@ +/*************************************************************************** + qgsdbquerylog.h + ------------ + Date : October 2021 + Copyright : (C) 2021 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSDBQUERYLOG_H +#define QGSDBQUERYLOG_H + +#include "qgis_core.h" +#include "qgis.h" +#include +#include + +/** + * \ingroup core + * \class QgsDatabaseQueryLogEntry + * \brief Encapsulates a logged database query. + * + * \since QGIS 3.24 + */ +class CORE_EXPORT QgsDatabaseQueryLogEntry +{ + public: + + /** + * Constructor for QgsDatabaseQueryLogEntry. + */ + QgsDatabaseQueryLogEntry( const QString &query = QString() ); + + /** + * Unique query ID. + * + * This ID will automatically be set on creation of a new QgsDatabaseQueryLogEntry object. + */ + int queryId = 0; + + //! The logged database query (e.g. the SQL query) + QString query; + + /** + * Time when the query started (in milliseconds since epoch). + * + * This will be automatically recorded on creation of a new QgsDatabaseQueryLogEntry object. + */ + quint64 startedTime = 0; + + /** + * Time when the query finished (in milliseconds since epoch), if available. + */ + quint64 finishedTime = 0; + + /** + * The QGIS class which initiated the query. + * + * c++ code can automatically populate this through the QgsSetQueryLogClass macro. + */ + QString initiatorClass; + + /** + * Code file location for the query origin. + * + * c++ code can automatically populate this through the QgsSetQueryLogClass macro. + */ + QString origin; + + private: + + static QAtomicInt sQueryId; +}; + +Q_DECLARE_METATYPE( QgsDatabaseQueryLogEntry ); + +#ifndef SIP_RUN +#include "qgsconfig.h" +constexpr int sQueryLoggerFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); +#define QgsSetQueryLogClass(entry, _class) entry.initiatorClass = _class; entry.origin = QString(QString( __FILE__ ).mid( sQueryLoggerFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")"); +#endif + +/** + * \ingroup core + * \class QgsDatabaseQueryLog + * \brief Handles logging of database queries. + * + * QgsDatabaseQueryLog is not usually directly created, but rather accessed through + * QgsApplication::databaseQueryLog(). Generally, clients should only access the + * static log() method to register their queries. + * + * ### Example + * + * \code{.py} + * # Log a database query + * QgsDatabaseQueryLog.log('SELECT * FROM my_table') + * \endcode + * + * + * \since QGIS 3.24 + */ +class CORE_EXPORT QgsDatabaseQueryLog: public QObject +{ + Q_OBJECT + + public: + + /** + * Creates a new query log. + * + * QgsDatabaseQueryLog is not usually directly created, but rather accessed through + * QgsApplication::databaseQueryLog(). + */ + QgsDatabaseQueryLog( QObject *parent = nullptr ); + + /** + * Logs a database \a query as starting. + * + * This method can be safely called from any thread. + */ + static void log( const QgsDatabaseQueryLogEntry &query ); + + /** + * Records that the database \a query as finished. + * + * This method can be safely called from any thread. + */ + static void finished( const QgsDatabaseQueryLogEntry &query ); + + public slots: + + /** + * Internal slot for logging queries as start. + * + * \note Not available in Python bindings. + */ + void queryStartedPrivate( const QgsDatabaseQueryLogEntry &query ) SIP_SKIP; + + /** + * Internal slot for logging queries as finished. + * + * \note Not available in Python bindings. + */ + void queryFinishedPrivate( const QgsDatabaseQueryLogEntry &query ) SIP_SKIP; + + signals: + + /** + * Emitted whenever a database query is started. + * + * \note Not available in Python bindings + */ + void queryStarted( const QgsDatabaseQueryLogEntry &query ) SIP_SKIP; + + /** + * Emitted whenever a database query has finished executing. + * + * \note Not available in Python bindings + */ + void queryFinished( const QgsDatabaseQueryLogEntry &query ) SIP_SKIP; + +}; + +#endif // QGSDBQUERYLOG_H diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index edd52477421..3f9d40adb71 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -31,6 +31,7 @@ #include "qgspostgresstringutils.h" #include "qgspostgresconnpool.h" #include "qgsvariantutils.h" +#include "qgsdbquerylog.h" #include #include @@ -49,6 +50,14 @@ const int PG_DEFAULT_TIMEOUT = 30; +#include "qgsconfig.h" +constexpr int sPostgresConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); +#define LoggedPQExecNR(query) { QgsDatabaseQueryLogEntry logEntry( query ); entry.initiatorClass = _class; entry.origin = QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")"); \ + QgsDatabaseQueryLog::log( entry ); \ + PQexecNR( query ); \ + QgsDatabaseQueryLog::finished( entry ); } + + QgsPostgresResult::~QgsPostgresResult() { if ( mRes ) @@ -412,8 +421,17 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s if ( mPostgresqlVersion >= 90000 ) { - PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) ); - PQexecNR( QStringLiteral( "SET extra_float_digits=3" ) ); + QString query = QStringLiteral( "SET application_name='QGIS'" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); + QgsDatabaseQueryLog::log( entry ); + PQexecNR( query ); + + query = QStringLiteral( "SET extra_float_digits=3" ); + entry = QgsDatabaseQueryLogEntry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); + QgsDatabaseQueryLog::log( entry ); + PQexecNR( query ); } PQsetNoticeProcessor( mConn, noticeProcessor, nullptr ); @@ -817,7 +835,11 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined. The error message from the database was:\n%1\n" ) .arg( result.PQresultErrorMessage() ), tr( "PostGIS" ) ); - PQexecNR( QStringLiteral( "COMMIT" ) ); + QString query = QStringLiteral( "COMMIT" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + PQexecNR( query ); return false; } @@ -1021,7 +1043,11 @@ bool QgsPostgresConn::getSchemas( QList &schemas ) result = PQexec( sql, true ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { - PQexecNR( QStringLiteral( "COMMIT" ) ); + QString query = QStringLiteral( "COMMIT" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + PQexecNR( query ); return false; } @@ -1428,7 +1454,7 @@ int QgsPostgresConn::PQCancel() return result; } -bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql ) +bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql, const QgsDatabaseQueryLogEntry &logEntry ) { QMutexLocker locker( &mLock ); // to protect access to mOpenCursors QString preStr; @@ -1442,8 +1468,12 @@ bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql preStr = QStringLiteral( "BEGIN;" ); } QgsDebugMsgLevel( QStringLiteral( "Binary cursor %1 for %2" ).arg( cursorName, sql ), 3 ); - return PQexecNR( QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). - arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ) ); + const QString query = QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). + arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ); + QgsDatabaseQueryLogEntry entry = logEntry; + entry.query = query; + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } bool QgsPostgresConn::closeCursor( const QString &cursorName ) @@ -1457,7 +1487,11 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName ) postStr = QStringLiteral( ";COMMIT" ); } - if ( !PQexecNR( QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) + QString query = QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + if ( !PQexecNR( query ) ) return false; return true; @@ -1495,7 +1529,11 @@ bool QgsPostgresConn::PQexecNR( const QString &query ) if ( PQstatus() == CONNECTION_OK ) { - PQexecNR( QStringLiteral( "ROLLBACK" ) ); + QString query = QStringLiteral( "ROLLBACK" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + PQexecNR( query ); } return false; @@ -1575,11 +1613,19 @@ bool QgsPostgresConn::begin() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); + QString query = QStringLiteral( "SAVEPOINT transaction_savepoint" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } else { - return PQexecNR( QStringLiteral( "BEGIN" ) ); + QString query = QStringLiteral( "BEGIN" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } } @@ -1588,11 +1634,19 @@ bool QgsPostgresConn::commit() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + QString query = QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } else { - return PQexecNR( QStringLiteral( "COMMIT" ) ); + QString query = QStringLiteral( "COMMIT" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } } @@ -1601,12 +1655,29 @@ bool QgsPostgresConn::rollback() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) - && PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + QString query = QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + bool res = false; + if ( PQexecNR( query ) ) + { + query = QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + if ( PQexecNR( query ) ) + res = true; + } + return res; } else { - return PQexecNR( QStringLiteral( "ROLLBACK" ) ); + QString query = QStringLiteral( "ROLLBACK" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + QgsDatabaseQueryLog::log( entry ); + return PQexecNR( query ); } } @@ -1898,7 +1969,10 @@ void QgsPostgresConn::deduceEndian() QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 ); // get the same value using a binary cursor - openCursor( QStringLiteral( "oidcursor" ), QStringLiteral( "select regclass('pg_class')::oid" ) ); + QString query = QStringLiteral( "select regclass('pg_class')::oid" ); + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresConn" ); + openCursor( QStringLiteral( "oidcursor" ), query, entry ); QgsDebugMsgLevel( QStringLiteral( "Fetching a record and attempting to get check endian-ness" ), 2 ); diff --git a/src/providers/postgres/qgspostgresconn.h b/src/providers/postgres/qgspostgresconn.h index 50b25511d84..0eca63ce538 100644 --- a/src/providers/postgres/qgspostgresconn.h +++ b/src/providers/postgres/qgspostgresconn.h @@ -36,6 +36,7 @@ extern "C" } class QgsField; +class QgsDatabaseQueryLogEntry; //! Spatial column types enum QgsPostgresGeometryColumnType @@ -250,7 +251,7 @@ class QgsPostgresConn : public QObject bool PQexecNR( const QString &query ); //! cursor handling - bool openCursor( const QString &cursorName, const QString &declare ); + bool openCursor( const QString &cursorName, const QString &declare, const QgsDatabaseQueryLogEntry &logEntry ); bool closeCursor( const QString &cursorName ); QString uniqueCursorName(); diff --git a/src/providers/postgres/qgspostgresdataitems.cpp b/src/providers/postgres/qgspostgresdataitems.cpp index 66384a75f34..dfaa00e90f7 100644 --- a/src/providers/postgres/qgspostgresdataitems.cpp +++ b/src/providers/postgres/qgspostgresdataitems.cpp @@ -29,6 +29,7 @@ #include "qgsvectorlayerexporter.h" #include "qgsprojectitem.h" #include "qgsfieldsitem.h" +#include "qgsdbquerylog.h" #include #include @@ -58,11 +59,17 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) // handle deletion of views QString sqlViewCheck = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ) .arg( QgsPostgresConn::quotedValue( schemaTableName ) ); + QgsDatabaseQueryLogEntry entry( sqlViewCheck ); + QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); + QgsDatabaseQueryLog::log( entry ); QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) ); QString type = resViewCheck.PQgetvalue( 0, 0 ); if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) ) { QString sql = QStringLiteral( "DROP %1VIEW %2" ).arg( type == QLatin1String( "m" ) ? QStringLiteral( "MATERIALIZED " ) : QString(), schemaTableName ); + QgsDatabaseQueryLogEntry entry( sql ); + QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); + QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { @@ -85,6 +92,9 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) "AND f_table_schema=%1 AND f_table_name=%2" ) .arg( QgsPostgresConn::quotedValue( schemaName ), QgsPostgresConn::quotedValue( tableName ) ); + entry = QgsDatabaseQueryLogEntry( sql ); + QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); + QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -113,6 +123,9 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) QgsPostgresConn::quotedValue( tableName ) ); } + entry = QgsDatabaseQueryLogEntry( sql ); + QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); + QgsDatabaseQueryLog::log( entry ); result = conn->PQexec( sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -147,6 +160,9 @@ bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceU QString sql = QStringLiteral( "DROP SCHEMA %1 %2" ) .arg( schemaName, cascade ? QStringLiteral( "CASCADE" ) : QString() ); + QgsDatabaseQueryLogEntry entry( sql ); + QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); + QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { @@ -262,7 +278,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData *data, const QString &toSc if ( srcLayer->isValid() ) { uri.setDataSource( QString(), u.name, srcLayer->geometryType() != QgsWkbTypes::NullGeometry ? QStringLiteral( "geom" ) : QString() ); - QgsDebugMsgLevel( "URI " + uri.uri( false ), 2 ); + QgsDebugMsgLevel( "URI " + uri.uri( false ), 3 ); if ( !toSchema.isNull() ) { @@ -365,7 +381,7 @@ QString QgsPGLayerItem::createUri() if ( uri.wkbType() != QgsWkbTypes::NoGeometry && mLayerProperty.srids.at( 0 ) != std::numeric_limits::min() ) uri.setSrid( QString::number( mLayerProperty.srids.at( 0 ) ) ); - QgsDebugMsgLevel( QStringLiteral( "layer uri: %1" ).arg( uri.uri( false ) ), 2 ); + QgsDebugMsgLevel( QStringLiteral( "layer uri: %1" ).arg( uri.uri( false ) ), 3 ); return uri.uri( false ); } diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index ee00f330c0e..8247d66c8bc 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -23,7 +23,7 @@ #include "qgssettings.h" #include "qgsexception.h" #include "qgsgeometryengine.h" - +#include "qgsdbquerylog.h" #include #include @@ -422,8 +422,13 @@ bool QgsPostgresFeatureIterator::rewind() return false; // move cursor to first record + const QString query = QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ); + + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); + QgsDatabaseQueryLog::log( entry ); + mConn->PQexecNR( query ); - mConn->PQexecNR( QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) ); mFeatureQueue.clear(); mFetched = 0; mLastFetch = false; @@ -763,7 +768,9 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString &whereClause, long if ( !orderBy.isEmpty() ) query += QStringLiteral( " ORDER BY %1 " ).arg( orderBy ); - if ( !mConn->openCursor( mCursorName, query ) ) + QgsDatabaseQueryLogEntry entry( query ); + QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); + if ( !mConn->openCursor( mCursorName, query, entry ) ) { // reloading the fields might help next time around // TODO how to cleanly force reload of fields? P->loadFields(); diff --git a/src/ui/qgsqueryloggerpanelbase.ui b/src/ui/qgsqueryloggerpanelbase.ui new file mode 100644 index 00000000000..7fff61bf082 --- /dev/null +++ b/src/ui/qgsqueryloggerpanelbase.ui @@ -0,0 +1,117 @@ + + + QgsDatabaseQueryLoggerPanelBase + + + + 0 + 0 + 700 + 629 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 24 + 24 + + + + false + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 6 + + + + + + + + + :/images/themes/default/mActionDeleteSelected.svg:/images/themes/default/mActionDeleteSelected.svg + + + Clear + + + Clear Log + + + + + true + + + + :/images/themes/default/mActionRecord.svg:/images/themes/default/mActionRecord.svg + + + Record Log + + + + + + :/images/themes/default/mActionFileSave.svg:/images/themes/default/mActionFileSave.svg + + + Save Log… + + + + + + QgsPanelWidget + QWidget +
qgspanelwidget.h
+ 1 +
+ + QgsFilterLineEdit + QLineEdit +
qgsfilterlineedit.h
+
+
+ + + + +
From 398ee7803067437a4ce1a8713f03f70d125617fb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 2 Oct 2021 09:27:25 +1000 Subject: [PATCH 02/24] g --- src/providers/postgres/qgspostgresconn.cpp | 107 +++--------------- src/providers/postgres/qgspostgresconn.h | 3 +- .../postgres/qgspostgresdataitems.cpp | 24 +--- .../postgres/qgspostgresfeatureiterator.cpp | 13 +-- 4 files changed, 25 insertions(+), 122 deletions(-) diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index 3f9d40adb71..c4e724d9996 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -50,14 +50,6 @@ const int PG_DEFAULT_TIMEOUT = 30; -#include "qgsconfig.h" -constexpr int sPostgresConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); -#define LoggedPQExecNR(query) { QgsDatabaseQueryLogEntry logEntry( query ); entry.initiatorClass = _class; entry.origin = QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")"); \ - QgsDatabaseQueryLog::log( entry ); \ - PQexecNR( query ); \ - QgsDatabaseQueryLog::finished( entry ); } - - QgsPostgresResult::~QgsPostgresResult() { if ( mRes ) @@ -421,17 +413,8 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s if ( mPostgresqlVersion >= 90000 ) { - QString query = QStringLiteral( "SET application_name='QGIS'" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); - QgsDatabaseQueryLog::log( entry ); - PQexecNR( query ); - - query = QStringLiteral( "SET extra_float_digits=3" ); - entry = QgsDatabaseQueryLogEntry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); - QgsDatabaseQueryLog::log( entry ); - PQexecNR( query ); + PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) ); + PQexecNR( QStringLiteral( "SET extra_float_digits=3" ) ); } PQsetNoticeProcessor( mConn, noticeProcessor, nullptr ); @@ -835,11 +818,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined. The error message from the database was:\n%1\n" ) .arg( result.PQresultErrorMessage() ), tr( "PostGIS" ) ); - QString query = QStringLiteral( "COMMIT" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - PQexecNR( query ); + PQexecNR( QStringLiteral( "COMMIT" ) ); return false; } @@ -1043,11 +1022,7 @@ bool QgsPostgresConn::getSchemas( QList &schemas ) result = PQexec( sql, true ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { - QString query = QStringLiteral( "COMMIT" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - PQexecNR( query ); + PQexecNR( QStringLiteral( "COMMIT" ) ); return false; } @@ -1454,7 +1429,7 @@ int QgsPostgresConn::PQCancel() return result; } -bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql, const QgsDatabaseQueryLogEntry &logEntry ) +bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql ) { QMutexLocker locker( &mLock ); // to protect access to mOpenCursors QString preStr; @@ -1468,12 +1443,8 @@ bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql, preStr = QStringLiteral( "BEGIN;" ); } QgsDebugMsgLevel( QStringLiteral( "Binary cursor %1 for %2" ).arg( cursorName, sql ), 3 ); - const QString query = QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). - arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ); - QgsDatabaseQueryLogEntry entry = logEntry; - entry.query = query; - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). + arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ) ); } bool QgsPostgresConn::closeCursor( const QString &cursorName ) @@ -1487,11 +1458,7 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName ) postStr = QStringLiteral( ";COMMIT" ); } - QString query = QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - if ( !PQexecNR( query ) ) + if ( !PQexecNR( QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) return false; return true; @@ -1529,11 +1496,7 @@ bool QgsPostgresConn::PQexecNR( const QString &query ) if ( PQstatus() == CONNECTION_OK ) { - QString query = QStringLiteral( "ROLLBACK" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - PQexecNR( query ); + PQexecNR( QStringLiteral( "ROLLBACK" ) ); } return false; @@ -1613,19 +1576,11 @@ bool QgsPostgresConn::begin() QMutexLocker locker( &mLock ); if ( mTransaction ) { - QString query = QStringLiteral( "SAVEPOINT transaction_savepoint" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); } else { - QString query = QStringLiteral( "BEGIN" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "BEGIN" ) ); } } @@ -1634,19 +1589,11 @@ bool QgsPostgresConn::commit() QMutexLocker locker( &mLock ); if ( mTransaction ) { - QString query = QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - QString query = QStringLiteral( "COMMIT" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "COMMIT" ) ); } } @@ -1655,29 +1602,12 @@ bool QgsPostgresConn::rollback() QMutexLocker locker( &mLock ); if ( mTransaction ) { - QString query = QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - bool res = false; - if ( PQexecNR( query ) ) - { - query = QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - if ( PQexecNR( query ) ) - res = true; - } - return res; + return PQexecNR( QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) + && PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - QString query = QStringLiteral( "ROLLBACK" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - QgsDatabaseQueryLog::log( entry ); - return PQexecNR( query ); + return PQexecNR( QStringLiteral( "ROLLBACK" ) ); } } @@ -1969,10 +1899,7 @@ void QgsPostgresConn::deduceEndian() QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 ); // get the same value using a binary cursor - QString query = QStringLiteral( "select regclass('pg_class')::oid" ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresConn" ); - openCursor( QStringLiteral( "oidcursor" ), query, entry ); + openCursor( QStringLiteral( "oidcursor" ), QStringLiteral( "select regclass('pg_class')::oid" ) ); QgsDebugMsgLevel( QStringLiteral( "Fetching a record and attempting to get check endian-ness" ), 2 ); diff --git a/src/providers/postgres/qgspostgresconn.h b/src/providers/postgres/qgspostgresconn.h index 0eca63ce538..50b25511d84 100644 --- a/src/providers/postgres/qgspostgresconn.h +++ b/src/providers/postgres/qgspostgresconn.h @@ -36,7 +36,6 @@ extern "C" } class QgsField; -class QgsDatabaseQueryLogEntry; //! Spatial column types enum QgsPostgresGeometryColumnType @@ -251,7 +250,7 @@ class QgsPostgresConn : public QObject bool PQexecNR( const QString &query ); //! cursor handling - bool openCursor( const QString &cursorName, const QString &declare, const QgsDatabaseQueryLogEntry &logEntry ); + bool openCursor( const QString &cursorName, const QString &declare ); bool closeCursor( const QString &cursorName ); QString uniqueCursorName(); diff --git a/src/providers/postgres/qgspostgresdataitems.cpp b/src/providers/postgres/qgspostgresdataitems.cpp index dfaa00e90f7..24b07822eea 100644 --- a/src/providers/postgres/qgspostgresdataitems.cpp +++ b/src/providers/postgres/qgspostgresdataitems.cpp @@ -29,13 +29,12 @@ #include "qgsvectorlayerexporter.h" #include "qgsprojectitem.h" #include "qgsfieldsitem.h" -#include "qgsdbquerylog.h" #include #include bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) { - QgsDebugMsgLevel( "deleting layer " + uri, 2 ); + QgsDebugMsg( "deleting layer " + uri ); QgsDataSourceUri dsUri( uri ); QString schemaName = dsUri.schema(); @@ -59,17 +58,11 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) // handle deletion of views QString sqlViewCheck = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ) .arg( QgsPostgresConn::quotedValue( schemaTableName ) ); - QgsDatabaseQueryLogEntry entry( sqlViewCheck ); - QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); - QgsDatabaseQueryLog::log( entry ); QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) ); QString type = resViewCheck.PQgetvalue( 0, 0 ); if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) ) { QString sql = QStringLiteral( "DROP %1VIEW %2" ).arg( type == QLatin1String( "m" ) ? QStringLiteral( "MATERIALIZED " ) : QString(), schemaTableName ); - QgsDatabaseQueryLogEntry entry( sql ); - QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); - QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { @@ -92,9 +85,6 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) "AND f_table_schema=%1 AND f_table_name=%2" ) .arg( QgsPostgresConn::quotedValue( schemaName ), QgsPostgresConn::quotedValue( tableName ) ); - entry = QgsDatabaseQueryLogEntry( sql ); - QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); - QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -123,9 +113,6 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) QgsPostgresConn::quotedValue( tableName ) ); } - entry = QgsDatabaseQueryLogEntry( sql ); - QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); - QgsDatabaseQueryLog::log( entry ); result = conn->PQexec( sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -142,7 +129,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceUri &uri, QString &errCause, bool cascade ) { - QgsDebugMsgLevel( "deleting schema " + schema, 2 ); + QgsDebugMsg( "deleting schema " + schema ); if ( schema.isEmpty() ) return false; @@ -160,9 +147,6 @@ bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceU QString sql = QStringLiteral( "DROP SCHEMA %1 %2" ) .arg( schemaName, cascade ? QStringLiteral( "CASCADE" ) : QString() ); - QgsDatabaseQueryLogEntry entry( sql ); - QgsSetQueryLogClass( entry, "QgsPostgresUtils" ); - QgsDatabaseQueryLog::log( entry ); QgsPostgresResult result( conn->PQexec( sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { @@ -278,7 +262,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData *data, const QString &toSc if ( srcLayer->isValid() ) { uri.setDataSource( QString(), u.name, srcLayer->geometryType() != QgsWkbTypes::NullGeometry ? QStringLiteral( "geom" ) : QString() ); - QgsDebugMsgLevel( "URI " + uri.uri( false ), 3 ); + QgsDebugMsg( "URI " + uri.uri( false ) ); if ( !toSchema.isNull() ) { @@ -381,7 +365,7 @@ QString QgsPGLayerItem::createUri() if ( uri.wkbType() != QgsWkbTypes::NoGeometry && mLayerProperty.srids.at( 0 ) != std::numeric_limits::min() ) uri.setSrid( QString::number( mLayerProperty.srids.at( 0 ) ) ); - QgsDebugMsgLevel( QStringLiteral( "layer uri: %1" ).arg( uri.uri( false ) ), 3 ); + QgsDebugMsg( QStringLiteral( "layer uri: %1" ).arg( uri.uri( false ) ) ); return uri.uri( false ); } diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index 8247d66c8bc..ee00f330c0e 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -23,7 +23,7 @@ #include "qgssettings.h" #include "qgsexception.h" #include "qgsgeometryengine.h" -#include "qgsdbquerylog.h" + #include #include @@ -422,13 +422,8 @@ bool QgsPostgresFeatureIterator::rewind() return false; // move cursor to first record - const QString query = QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ); - - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); - QgsDatabaseQueryLog::log( entry ); - mConn->PQexecNR( query ); + mConn->PQexecNR( QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) ); mFeatureQueue.clear(); mFetched = 0; mLastFetch = false; @@ -768,9 +763,7 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString &whereClause, long if ( !orderBy.isEmpty() ) query += QStringLiteral( " ORDER BY %1 " ).arg( orderBy ); - QgsDatabaseQueryLogEntry entry( query ); - QgsSetQueryLogClass( entry, "QgsPostgresFeatureIterator" ); - if ( !mConn->openCursor( mCursorName, query, entry ) ) + if ( !mConn->openCursor( mCursorName, query ) ) { // reloading the fields might help next time around // TODO how to cleanly force reload of fields? P->loadFields(); From a0c69216abbaa651b9345f682dd9452794750562 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Oct 2021 09:19:02 +1000 Subject: [PATCH 03/24] Enabled --- .../core/auto_generated/qgsdbquerylog.sip.in | 36 +++++++++++++------ src/core/qgsdbquerylog.cpp | 8 +++++ src/core/qgsdbquerylog.h | 22 ++++++++++++ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in index 8f2efcb28d5..ff26589fd0c 100644 --- a/python/core/auto_generated/qgsdbquerylog.sip.in +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -26,11 +26,18 @@ Encapsulates a logged database query. Constructor for QgsDatabaseQueryLogEntry. %End + int queryId; + QString query; + quint64 startedTime; + + quint64 finishedTime; + QString initiatorClass; QString origin; + }; @@ -50,7 +57,7 @@ Example .. code-block:: python # Log a database query - QgsDatabaseQueryLogger.log('SELECT * FROM my_table') + QgsDatabaseQueryLog.log('SELECT * FROM my_table') .. versionadded:: 3.24 %End @@ -62,25 +69,30 @@ Example QgsDatabaseQueryLog( QObject *parent = 0 ); %Docstring -Creates a new query logger. +Creates a new query log. -:py:class:`QgsDatabaseQueryLogger` is not usually directly created, but rather accessed through -:py:func:`QgsApplication.queryLogger()`. +QgsDatabaseQueryLog is not usually directly created, but rather accessed through +:py:func:`QgsApplication.databaseQueryLog()`. %End - static void log( const QString &query ); -%Docstring -Logs a database query. -Consider using the variant with a :py:class:`QgsDatabaseQueryLogEntry` argument instead, as that -method allows more context for logged queries. + static bool enabled(); +%Docstring +Returns ``True`` if logging is enabled. + +.. seealso:: :py:func:`setEnabled` +%End + + static void log( const QgsDatabaseQueryLogEntry &query ); +%Docstring +Logs a database ``query`` as starting. This method can be safely called from any thread. %End - static void log( const QgsDatabaseQueryLogEntry &entry ); + static void finished( const QgsDatabaseQueryLogEntry &query ); %Docstring -Logs a database query ``entry``. +Records that the database ``query`` as finished. This method can be safely called from any thread. %End @@ -88,9 +100,11 @@ This method can be safely called from any thread. public slots: + signals: + }; /************************************************************************ diff --git a/src/core/qgsdbquerylog.cpp b/src/core/qgsdbquerylog.cpp index 523b6887942..f19107151e9 100644 --- a/src/core/qgsdbquerylog.cpp +++ b/src/core/qgsdbquerylog.cpp @@ -34,6 +34,8 @@ QgsDatabaseQueryLogEntry::QgsDatabaseQueryLogEntry( const QString &query ) // QgsDatabaseQueryLog // +bool QgsDatabaseQueryLog::sEnabled = false; + QgsDatabaseQueryLog::QgsDatabaseQueryLog( QObject *parent ) : QObject( parent ) { @@ -42,11 +44,17 @@ QgsDatabaseQueryLog::QgsDatabaseQueryLog( QObject *parent ) void QgsDatabaseQueryLog::log( const QgsDatabaseQueryLogEntry &query ) { + if ( !sEnabled ) + return; + QMetaObject::invokeMethod( QgsApplication::databaseQueryLog(), "queryStartedPrivate", Qt::QueuedConnection, Q_ARG( QgsDatabaseQueryLogEntry, query ) ); } void QgsDatabaseQueryLog::finished( const QgsDatabaseQueryLogEntry &query ) { + if ( !sEnabled ) + return; + // record time of completion QgsDatabaseQueryLogEntry finishedQuery = query; finishedQuery.finishedTime = QDateTime::currentMSecsSinceEpoch(); diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index be10542f030..ca2714e216b 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -119,6 +119,24 @@ class CORE_EXPORT QgsDatabaseQueryLog: public QObject */ QgsDatabaseQueryLog( QObject *parent = nullptr ); + /** + * Enables query logging. + * + * If disabled, no signals will be emitted by the log. By default the log is disabled, + * and clients must manually enable it. + * + * \note Not available in Python bindings + * \see enabled() + */ + static void setEnabled( bool enabled ) SIP_SKIP { sEnabled = enabled; } + + /** + * Returns TRUE if logging is enabled. + * + * \see setEnabled() + */ + static bool enabled() { return sEnabled; } + /** * Logs a database \a query as starting. * @@ -165,6 +183,10 @@ class CORE_EXPORT QgsDatabaseQueryLog: public QObject */ void queryFinished( const QgsDatabaseQueryLogEntry &query ) SIP_SKIP; + private: + + static bool sEnabled; + }; #endif // QGSDBQUERYLOG_H From 3f959a9c47f06155f79619d4aa6a45eed96025f0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 7 Oct 2021 08:27:07 +1000 Subject: [PATCH 04/24] wip --- src/app/CMakeLists.txt | 1 + .../networklogger/qgsnetworklogger.cpp | 24 +- .../devtools/networklogger/qgsnetworklogger.h | 8 +- .../networklogger/qgsnetworkloggernode.cpp | 171 +---------- .../networklogger/qgsnetworkloggernode.h | 173 +---------- src/app/devtools/qgsdevtoolsmodelnode.cpp | 173 +++++++++++ src/app/devtools/qgsdevtoolsmodelnode.h | 176 +++++++++++ .../querylogger/qgsappquerylogger.cpp | 73 ++--- .../devtools/querylogger/qgsappquerylogger.h | 26 +- .../querylogger/qgsqueryloggernode.cpp | 276 +++++++++++------- .../devtools/querylogger/qgsqueryloggernode.h | 248 ++++++---------- .../querylogger/qgsqueryloggerpanelwidget.cpp | 14 +- src/app/qgisapp.cpp | 8 +- src/core/qgsdbquerylog.h | 8 +- src/providers/postgres/qgspostgresconn.cpp | 91 +++--- src/providers/postgres/qgspostgresconn.h | 9 +- .../qgspostgresdataitemguiprovider.cpp | 12 +- .../postgres/qgspostgresdataitems.cpp | 10 +- .../postgres/qgspostgresfeatureiterator.cpp | 2 +- .../postgres/qgspostgresprovider.cpp | 46 +-- .../qgspostgresproviderconnection.cpp | 2 +- .../postgres/qgspostgrestransaction.cpp | 2 +- 22 files changed, 810 insertions(+), 743 deletions(-) create mode 100644 src/app/devtools/qgsdevtoolsmodelnode.cpp create mode 100644 src/app/devtools/qgsdevtoolsmodelnode.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 4d3833a045c..cccf8de1cc8 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -163,6 +163,7 @@ set(QGIS_APP_SRCS browser/qgsinbuiltdataitemproviders.cpp devtools/qgsappdevtoolutils.cpp + devtools/qgsdevtoolsmodelnode.cpp devtools/networklogger/qgsnetworklogger.cpp devtools/networklogger/qgsnetworkloggernode.cpp devtools/networklogger/qgsnetworkloggerpanelwidget.cpp diff --git a/src/app/devtools/networklogger/qgsnetworklogger.cpp b/src/app/devtools/networklogger/qgsnetworklogger.cpp index 48fc14cfdc0..703b921201b 100644 --- a/src/app/devtools/networklogger/qgsnetworklogger.cpp +++ b/src/app/devtools/networklogger/qgsnetworklogger.cpp @@ -147,24 +147,24 @@ void QgsNetworkLogger::requestEncounteredSslErrors( int requestId, const QList( index.internalPointer() ); + return reinterpret_cast( index.internalPointer() ); } QList QgsNetworkLogger::actions( const QModelIndex &index, QObject *parent ) { - QgsNetworkLoggerNode *node = index2node( index ); + QgsDevToolsModelNode *node = index2node( index ); if ( !node ) return QList< QAction * >(); return node->actions( parent ); } -QModelIndex QgsNetworkLogger::node2index( QgsNetworkLoggerNode *node ) const +QModelIndex QgsNetworkLogger::node2index( QgsDevToolsModelNode *node ) const { if ( !node || !node->parent() ) return QModelIndex(); // this is the only root item -> invalid index @@ -176,11 +176,11 @@ QModelIndex QgsNetworkLogger::node2index( QgsNetworkLoggerNode *node ) const return index( row, 0, parentIndex ); } -QModelIndex QgsNetworkLogger::indexOfParentLayerTreeNode( QgsNetworkLoggerNode *parentNode ) const +QModelIndex QgsNetworkLogger::indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const { Q_ASSERT( parentNode ); - QgsNetworkLoggerGroup *grandParentNode = parentNode->parent(); + QgsDevToolsModelGroup *grandParentNode = parentNode->parent(); if ( !grandParentNode ) return QModelIndex(); // root node -> invalid index @@ -197,7 +197,7 @@ void QgsNetworkLogger::removeRequestRows( const QList &rows ) for ( int row : std::as_const( res ) ) { - int popId = data( index( row, 0, QModelIndex() ), QgsNetworkLoggerNode::RoleId ).toInt(); + int popId = data( index( row, 0, QModelIndex() ), QgsDevToolsModelNode::RoleId ).toInt(); mRequestGroups.remove( popId ); beginRemoveRows( QModelIndex(), row, row ); @@ -213,7 +213,7 @@ QgsNetworkLoggerRootNode *QgsNetworkLogger::rootGroup() int QgsNetworkLogger::rowCount( const QModelIndex &parent ) const { - QgsNetworkLoggerNode *n = index2node( parent ); + QgsDevToolsModelNode *n = index2node( parent ); if ( !n ) return 0; @@ -232,7 +232,7 @@ QModelIndex QgsNetworkLogger::index( int row, int column, const QModelIndex &par row < 0 || row >= rowCount( parent ) ) return QModelIndex(); - QgsNetworkLoggerGroup *n = dynamic_cast< QgsNetworkLoggerGroup * >( index2node( parent ) ); + QgsDevToolsModelGroup *n = dynamic_cast< QgsDevToolsModelGroup * >( index2node( parent ) ); if ( !n ) return QModelIndex(); // have no children @@ -244,7 +244,7 @@ QModelIndex QgsNetworkLogger::parent( const QModelIndex &child ) const if ( !child.isValid() ) return QModelIndex(); - if ( QgsNetworkLoggerNode *n = index2node( child ) ) + if ( QgsDevToolsModelNode *n = index2node( child ) ) { return indexOfParentLayerTreeNode( n->parent() ); // must not be null } @@ -260,7 +260,7 @@ QVariant QgsNetworkLogger::data( const QModelIndex &index, int role ) const if ( !index.isValid() || index.column() > 1 ) return QVariant(); - QgsNetworkLoggerNode *node = index2node( index ); + QgsDevToolsModelNode *node = index2node( index ); if ( !node ) return QVariant(); @@ -324,7 +324,7 @@ void QgsNetworkLoggerProxyModel::setShowCached( bool show ) bool QgsNetworkLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const { - QgsNetworkLoggerNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); + QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); if ( QgsNetworkLoggerRequestGroup *request = dynamic_cast< QgsNetworkLoggerRequestGroup * >( node ) ) { if ( ( request->status() == QgsNetworkLoggerRequestGroup::Status::Complete || request->status() == QgsNetworkLoggerRequestGroup::Status::Canceled ) diff --git a/src/app/devtools/networklogger/qgsnetworklogger.h b/src/app/devtools/networklogger/qgsnetworklogger.h index 2351519712a..792d796ada1 100644 --- a/src/app/devtools/networklogger/qgsnetworklogger.h +++ b/src/app/devtools/networklogger/qgsnetworklogger.h @@ -20,7 +20,7 @@ #include #include "qgsnetworkaccessmanager.h" -class QgsNetworkLoggerNode; +class QgsDevToolsModelNode; class QgsNetworkLoggerRequestGroup; class QgsNetworkLoggerRootNode; class QAction; @@ -66,7 +66,7 @@ class QgsNetworkLogger : public QAbstractItemModel /** * Returns node for given index. Returns root node for invalid index. */ - QgsNetworkLoggerNode *index2node( const QModelIndex &index ) const; + QgsDevToolsModelNode *index2node( const QModelIndex &index ) const; /** * Returns a list of actions corresponding to the item at the specified \a index. @@ -109,8 +109,8 @@ class QgsNetworkLogger : public QAbstractItemModel private: //! Returns index for a given node - QModelIndex node2index( QgsNetworkLoggerNode *node ) const; - QModelIndex indexOfParentLayerTreeNode( QgsNetworkLoggerNode *parentNode ) const; + QModelIndex node2index( QgsDevToolsModelNode *node ) const; + QModelIndex indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const; QgsNetworkAccessManager *mNam = nullptr; bool mIsLogging = false; diff --git a/src/app/devtools/networklogger/qgsnetworkloggernode.cpp b/src/app/devtools/networklogger/qgsnetworkloggernode.cpp index 7842100cbb7..cd2f96a6117 100644 --- a/src/app/devtools/networklogger/qgsnetworkloggernode.cpp +++ b/src/app/devtools/networklogger/qgsnetworkloggernode.cpp @@ -26,89 +26,12 @@ #include #include -// -// QgsNetworkLoggerNode -// - -QgsNetworkLoggerNode::QgsNetworkLoggerNode() = default; -QgsNetworkLoggerNode::~QgsNetworkLoggerNode() = default; - - -// -// QgsNetworkLoggerGroup -// - -QgsNetworkLoggerGroup::QgsNetworkLoggerGroup( const QString &title ) - : mGroupTitle( title ) -{ -} - -void QgsNetworkLoggerGroup::addChild( std::unique_ptr child ) -{ - if ( !child ) - return; - - Q_ASSERT( !child->mParent ); - child->mParent = this; - - mChildren.emplace_back( std::move( child ) ); -} - -int QgsNetworkLoggerGroup::indexOf( QgsNetworkLoggerNode *child ) const -{ - Q_ASSERT( child->mParent == this ); - auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr &p ) - { - return p.get() == child; - } ); - if ( it != mChildren.end() ) - return std::distance( mChildren.begin(), it ); - return -1; -} - -QgsNetworkLoggerNode *QgsNetworkLoggerGroup::childAt( int index ) -{ - Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() ); - return mChildren[ index ].get(); -} - -void QgsNetworkLoggerGroup::clear() -{ - mChildren.clear(); -} - -QVariant QgsNetworkLoggerGroup::data( int role ) const -{ - switch ( role ) - { - case Qt::DisplayRole: - return mGroupTitle; - - default: - break; - } - return QVariant(); -} - -QVariant QgsNetworkLoggerGroup::toVariant() const -{ - QVariantMap res; - for ( const std::unique_ptr< QgsNetworkLoggerNode > &child : mChildren ) - { - if ( const QgsNetworkLoggerValueNode *valueNode = dynamic_cast< const QgsNetworkLoggerValueNode *>( child.get() ) ) - { - res.insert( valueNode->key(), valueNode->value() ); - } - } - return res; -} - // // QgsNetworkLoggerRootNode // QgsNetworkLoggerRootNode::QgsNetworkLoggerRootNode() - : QgsNetworkLoggerGroup( QString() ) + : QgsDevToolsModelGroup( QString() ) { } @@ -126,76 +49,18 @@ void QgsNetworkLoggerRootNode::removeRow( int row ) QVariant QgsNetworkLoggerRootNode::toVariant() const { QVariantList res; - for ( const std::unique_ptr< QgsNetworkLoggerNode > &child : mChildren ) + for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren ) res << child->toVariant(); return res; } -// -// QgsNetworkLoggerValueNode -// -QgsNetworkLoggerValueNode::QgsNetworkLoggerValueNode( const QString &key, const QString &value, const QColor &color ) - : mKey( key ) - , mValue( value ) - , mColor( color ) -{ - -} - -QVariant QgsNetworkLoggerValueNode::data( int role ) const -{ - switch ( role ) - { - case Qt::DisplayRole: - case Qt::ToolTipRole: - { - return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue ); - } - - case Qt::ForegroundRole: - { - if ( mColor.isValid() ) - return QBrush( mColor ); - break; - } - default: - break; - } - return QVariant(); -} - -QList QgsNetworkLoggerValueNode::actions( QObject *parent ) -{ - QList< QAction * > res; - - QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent ); - QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ] - { - QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) ); - } ); - - res << copyAction; - - return res; -} - -// -// QgsNetworkLoggerGroup -// - -void QgsNetworkLoggerGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color ) -{ - addChild( std::make_unique< QgsNetworkLoggerValueNode >( key, value, color ) ); -} - - // // QgsNetworkLoggerRequestGroup // QgsNetworkLoggerRequestGroup::QgsNetworkLoggerRequestGroup( const QgsNetworkRequestParameters &request ) - : QgsNetworkLoggerGroup( QString() ) + : QgsDevToolsModelGroup( QString() ) , mUrl( request.request().url() ) , mRequestId( request.requestId() ) , mOperation( request.operation() ) @@ -478,7 +343,7 @@ QString QgsNetworkLoggerRequestGroup::cacheControlToString( QNetworkRequest::Cac // QgsNetworkLoggerRequestDetailsGroup::QgsNetworkLoggerRequestDetailsGroup( const QgsNetworkRequestParameters &request ) - : QgsNetworkLoggerGroup( QObject::tr( "Request" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Request" ) ) { addKeyValueNode( QObject::tr( "Operation" ), QgsNetworkLoggerRequestGroup::operationToString( request.operation() ) ); addKeyValueNode( QObject::tr( "Thread" ), request.originatingThreadId() ); @@ -521,7 +386,7 @@ QgsNetworkLoggerRequestDetailsGroup::QgsNetworkLoggerRequestDetailsGroup( const QVariant QgsNetworkLoggerRequestDetailsGroup::toVariant() const { - QVariantMap res = QgsNetworkLoggerGroup::toVariant().toMap(); + QVariantMap res = QgsDevToolsModelGroup::toVariant().toMap(); if ( mQueryGroup ) res.insert( QObject::tr( "Query" ), mQueryGroup->toVariant() ); if ( mRequestHeaders ) @@ -537,7 +402,7 @@ QVariant QgsNetworkLoggerRequestDetailsGroup::toVariant() const // QgsNetworkLoggerRequestQueryGroup::QgsNetworkLoggerRequestQueryGroup( const QUrl &url ) - : QgsNetworkLoggerGroup( QObject::tr( "Query" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Query" ) ) { QUrlQuery query( url ); const QList > queryItems = query.queryItems(); @@ -553,7 +418,7 @@ QgsNetworkLoggerRequestQueryGroup::QgsNetworkLoggerRequestQueryGroup( const QUrl // QgsNetworkLoggerRequestHeadersGroup // QgsNetworkLoggerRequestHeadersGroup::QgsNetworkLoggerRequestHeadersGroup( const QgsNetworkRequestParameters &request ) - : QgsNetworkLoggerGroup( QObject::tr( "Headers" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Headers" ) ) { const QList headers = request.request().rawHeaderList(); for ( const QByteArray &header : headers ) @@ -567,7 +432,7 @@ QgsNetworkLoggerRequestHeadersGroup::QgsNetworkLoggerRequestHeadersGroup( const // QgsNetworkLoggerPostContentGroup::QgsNetworkLoggerPostContentGroup( const QgsNetworkRequestParameters ¶meters ) - : QgsNetworkLoggerGroup( QObject::tr( "Content" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Content" ) ) { addKeyValueNode( QObject::tr( "Data" ), parameters.content() ); } @@ -578,7 +443,7 @@ QgsNetworkLoggerPostContentGroup::QgsNetworkLoggerPostContentGroup( const QgsNet // QgsNetworkLoggerReplyGroup::QgsNetworkLoggerReplyGroup( const QgsNetworkReplyContent &reply ) - : QgsNetworkLoggerGroup( QObject::tr( "Reply" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Reply" ) ) { addKeyValueNode( QObject::tr( "Status" ), reply.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toString() ); if ( reply.error() != QNetworkReply::NoError ) @@ -595,7 +460,7 @@ QgsNetworkLoggerReplyGroup::QgsNetworkLoggerReplyGroup( const QgsNetworkReplyCon QVariant QgsNetworkLoggerReplyGroup::toVariant() const { - QVariantMap res = QgsNetworkLoggerGroup::toVariant().toMap(); + QVariantMap res = QgsDevToolsModelGroup::toVariant().toMap(); if ( mReplyHeaders ) { res.insert( QObject::tr( "Headers" ), mReplyHeaders->toVariant() ); @@ -608,7 +473,7 @@ QVariant QgsNetworkLoggerReplyGroup::toVariant() const // QgsNetworkLoggerReplyHeadersGroup // QgsNetworkLoggerReplyHeadersGroup::QgsNetworkLoggerReplyHeadersGroup( const QgsNetworkReplyContent &reply ) - : QgsNetworkLoggerGroup( QObject::tr( "Headers" ) ) + : QgsDevToolsModelGroup( QObject::tr( "Headers" ) ) { const QList headers = reply.rawHeaderList(); for ( const QByteArray &header : headers ) @@ -621,7 +486,7 @@ QgsNetworkLoggerReplyHeadersGroup::QgsNetworkLoggerReplyHeadersGroup( const QgsN // QgsNetworkLoggerSslErrorGroup // QgsNetworkLoggerSslErrorGroup::QgsNetworkLoggerSslErrorGroup( const QList &errors ) - : QgsNetworkLoggerGroup( QObject::tr( "SSL errors" ) ) + : QgsDevToolsModelGroup( QObject::tr( "SSL errors" ) ) { for ( const QSslError &error : errors ) { @@ -634,15 +499,5 @@ QVariant QgsNetworkLoggerSslErrorGroup::data( int role ) const if ( role == Qt::ForegroundRole ) return QBrush( QColor( 180, 65, 210 ) ); - return QgsNetworkLoggerGroup::data( role ); -} - -QList QgsNetworkLoggerNode::actions( QObject * ) -{ - return QList< QAction * >(); -} - -QVariant QgsNetworkLoggerNode::toVariant() const -{ - return QVariant(); + return QgsDevToolsModelGroup::data( role ); } diff --git a/src/app/devtools/networklogger/qgsnetworkloggernode.h b/src/app/devtools/networklogger/qgsnetworkloggernode.h index fea03444908..9301db62342 100644 --- a/src/app/devtools/networklogger/qgsnetworkloggernode.h +++ b/src/app/devtools/networklogger/qgsnetworkloggernode.h @@ -16,6 +16,7 @@ #define QGSNETWORKLOGGERNODE_H #include "qgsnetworkaccessmanager.h" +#include "devtools/qgsdevtoolsmodelnode.h" #include #include #include @@ -24,160 +25,6 @@ #include class QAction; -class QgsNetworkLoggerGroup; - -/** - * \ingroup app - * \class QgsNetworkLoggerNode - * \brief Base class for nodes in the network logger model. - * - * \since QGIS 3.14 - */ -class QgsNetworkLoggerNode -{ - public: - - //! Custom node data roles - enum Roles - { - RoleStatus = Qt::UserRole + 1, //!< Request status role - RoleId, //!< Request ID role - }; - - virtual ~QgsNetworkLoggerNode(); - - /** - * Returns the node's parent node. - * - * If parent is NULLPTR, the node is a root node - */ - QgsNetworkLoggerGroup *parent() { return mParent; } - - /** - * Returns the node's data for the specified model \a role. - */ - virtual QVariant data( int role = Qt::DisplayRole ) const = 0; - - /** - * Returns the number of child nodes owned by this node. - */ - virtual int childCount() const = 0; - - /** - * Returns a list of actions relating to the node. - * - * The actions should be parented to \a parent. - */ - virtual QList< QAction * > actions( QObject *parent ); - - /** - * Converts the node's contents to a variant. - */ - virtual QVariant toVariant() const; - - protected: - - QgsNetworkLoggerNode(); - - private: - - QgsNetworkLoggerGroup *mParent = nullptr; - friend class QgsNetworkLoggerGroup; -}; - -/** - * \ingroup app - * \class QgsNetworkLoggerGroup - * \brief Base class for network logger model "group" nodes, which contain children of their own. - * - * \since QGIS 3.14 - */ -class QgsNetworkLoggerGroup : public QgsNetworkLoggerNode -{ - public: - - /** - * Adds a \a child node to this node. - */ - void addChild( std::unique_ptr< QgsNetworkLoggerNode > child ); - - /** - * Returns the index of the specified \a child node. - * - * \warning \a child must be a valid child of this node. - */ - int indexOf( QgsNetworkLoggerNode *child ) const; - - /** - * Returns the child at the specified \a index. - */ - QgsNetworkLoggerNode *childAt( int index ); - - /** - * Clears the group, removing all its children. - */ - void clear(); - - int childCount() const override final { return mChildren.size(); } - QVariant data( int role = Qt::DisplayRole ) const override; - QVariant toVariant() const override; - - protected: - - /** - * Constructor for a QgsNetworkLoggerGroup, with the specified \a title. - */ - QgsNetworkLoggerGroup( const QString &title ); - - /** - * Adds a simple \a key: \a value node to the group. - */ - void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); - - private: - - std::deque< std::unique_ptr< QgsNetworkLoggerNode > > mChildren; - QString mGroupTitle; - friend class QgsNetworkLoggerRootNode; - -}; - -/** - * \ingroup app - * \class QgsNetworkLoggerValueNode - * \brief A "key: value" style node for the network logger model. - * - * \since QGIS 3.14 - */ -class QgsNetworkLoggerValueNode : public QgsNetworkLoggerNode -{ - public: - - /** - * Constructor for QgsNetworkLoggerValueNode, with the specified \a key (usually translated) and \a value. - */ - QgsNetworkLoggerValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); - - /** - * Returns the node's key. - */ - QString key() const { return mKey; } - - /** - * Returns the node's value. - */ - QString value() const { return mValue; } - - QVariant data( int role = Qt::DisplayRole ) const override final; - int childCount() const override final { return 0; } - QList< QAction * > actions( QObject *parent ) override final; - - private: - - QString mKey; - QString mValue; - QColor mColor; -}; /** * \ingroup app @@ -186,7 +33,7 @@ class QgsNetworkLoggerValueNode : public QgsNetworkLoggerNode * * \since QGIS 3.14 */ -class QgsNetworkLoggerRootNode final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerRootNode final : public QgsDevToolsModelGroup { public: @@ -231,7 +78,7 @@ class QgsNetworkLoggerSslErrorGroup; * * \since QGIS 3.14 */ -class QgsNetworkLoggerRequestGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerRequestGroup final : public QgsDevToolsModelGroup { public: @@ -357,7 +204,7 @@ class QgsNetworkLoggerPostContentGroup; * * \since QGIS 3.14 */ -class QgsNetworkLoggerRequestDetailsGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerRequestDetailsGroup final : public QgsDevToolsModelGroup { public: @@ -389,7 +236,7 @@ class QgsNetworkLoggerRequestDetailsGroup final : public QgsNetworkLoggerGroup * * \since QGIS 3.14 */ -class QgsNetworkLoggerRequestHeadersGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerRequestHeadersGroup final : public QgsDevToolsModelGroup { public: @@ -415,7 +262,7 @@ class QgsNetworkLoggerRequestHeadersGroup final : public QgsNetworkLoggerGroup * * \since QGIS 3.14 */ -class QgsNetworkLoggerRequestQueryGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerRequestQueryGroup final : public QgsDevToolsModelGroup { public: @@ -439,7 +286,7 @@ class QgsNetworkLoggerRequestQueryGroup final : public QgsNetworkLoggerGroup * * \since QGIS 3.14 */ -class QgsNetworkLoggerPostContentGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerPostContentGroup final : public QgsDevToolsModelGroup { public: @@ -468,7 +315,7 @@ class QgsNetworkLoggerReplyHeadersGroup; * * \since QGIS 3.14 */ -class QgsNetworkLoggerReplyGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerReplyGroup final : public QgsDevToolsModelGroup { public: @@ -499,7 +346,7 @@ class QgsNetworkLoggerReplyGroup final : public QgsNetworkLoggerGroup * * \since QGIS 3.14 */ -class QgsNetworkLoggerReplyHeadersGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerReplyHeadersGroup final : public QgsDevToolsModelGroup { public: @@ -525,7 +372,7 @@ class QgsNetworkLoggerReplyHeadersGroup final : public QgsNetworkLoggerGroup * * \since QGIS 3.14 */ -class QgsNetworkLoggerSslErrorGroup final : public QgsNetworkLoggerGroup +class QgsNetworkLoggerSslErrorGroup final : public QgsDevToolsModelGroup { public: diff --git a/src/app/devtools/qgsdevtoolsmodelnode.cpp b/src/app/devtools/qgsdevtoolsmodelnode.cpp new file mode 100644 index 00000000000..8ccd34d8fa4 --- /dev/null +++ b/src/app/devtools/qgsdevtoolsmodelnode.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + qgsdevtoolsmodelnode.cpp + ------------------------- + begin : March 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsdevtoolsmodelnode.h" +#include "qgis.h" +#include "qgsjsonutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// QgsDevToolsModelNode +// + +QgsDevToolsModelNode::QgsDevToolsModelNode() = default; +QgsDevToolsModelNode::~QgsDevToolsModelNode() = default; + +QVariant QgsDevToolsModelNode::toVariant() const +{ + return QVariant(); +} + +QList QgsDevToolsModelNode::actions( QObject * ) +{ + return QList< QAction * >(); +} + + +// +// QgsDevToolsModelGroup +// + +QgsDevToolsModelGroup::QgsDevToolsModelGroup( const QString &title ) + : mGroupTitle( title ) +{ +} + +void QgsDevToolsModelGroup::addChild( std::unique_ptr child ) +{ + if ( !child ) + return; + + Q_ASSERT( !child->mParent ); + child->mParent = this; + + mChildren.emplace_back( std::move( child ) ); +} + +int QgsDevToolsModelGroup::indexOf( QgsDevToolsModelNode *child ) const +{ + Q_ASSERT( child->mParent == this ); + auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr &p ) + { + return p.get() == child; + } ); + if ( it != mChildren.end() ) + return std::distance( mChildren.begin(), it ); + return -1; +} + +QgsDevToolsModelNode *QgsDevToolsModelGroup::childAt( int index ) +{ + Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() ); + return mChildren[ index ].get(); +} + +void QgsDevToolsModelGroup::clear() +{ + mChildren.clear(); +} + +QVariant QgsDevToolsModelGroup::data( int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + return mGroupTitle; + + default: + break; + } + return QVariant(); +} + +QVariant QgsDevToolsModelGroup::toVariant() const +{ + QVariantMap res; + for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren ) + { + if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) ) + { + res.insert( valueNode->key(), valueNode->value() ); + } + } + return res; +} + + +// +// QgsDevToolsModelValueNode +// +QgsDevToolsModelValueNode::QgsDevToolsModelValueNode( const QString &key, const QString &value, const QColor &color ) + : mKey( key ) + , mValue( value ) + , mColor( color ) +{ + +} + +QVariant QgsDevToolsModelValueNode::data( int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + case Qt::ToolTipRole: + { + return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue ); + } + + case Qt::ForegroundRole: + { + if ( mColor.isValid() ) + return QBrush( mColor ); + break; + } + default: + break; + } + return QVariant(); +} + +QList QgsDevToolsModelValueNode::actions( QObject *parent ) +{ + QList< QAction * > res; + + QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent ); + QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ] + { + QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) ); + } ); + + res << copyAction; + + return res; +} + +// +// QgsDevToolsModelGroup +// + +void QgsDevToolsModelGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color ) +{ + addChild( std::make_unique< QgsDevToolsModelValueNode >( key, value, color ) ); +} + diff --git a/src/app/devtools/qgsdevtoolsmodelnode.h b/src/app/devtools/qgsdevtoolsmodelnode.h new file mode 100644 index 00000000000..4b2c99ea944 --- /dev/null +++ b/src/app/devtools/qgsdevtoolsmodelnode.h @@ -0,0 +1,176 @@ +/*************************************************************************** + qgsdevtoolsmodelnode.h + ------------------------- + begin : March 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSDEVTOOLSMODELNODE_H +#define QGSDEVTOOLSMODELNODE_H + +#include +#include +#include +#include +#include +#include + +class QAction; +class QgsDevToolsModelGroup; + +/** + * \ingroup app + * \class QgsDevToolsModelNode + * \brief Base class for nodes in a dev tools model. + */ +class QgsDevToolsModelNode +{ + public: + + //! Custom node data roles + enum Roles + { + RoleStatus = Qt::UserRole + 1, //!< Request status role + RoleId, //!< Request ID role + }; + + virtual ~QgsDevToolsModelNode(); + + /** + * Returns the node's parent node. + * + * If parent is NULLPTR, the node is a root node + */ + QgsDevToolsModelGroup *parent() { return mParent; } + + /** + * Returns the node's data for the specified model \a role. + */ + virtual QVariant data( int role = Qt::DisplayRole ) const = 0; + + /** + * Returns the number of child nodes owned by this node. + */ + virtual int childCount() const = 0; + + /** + * Returns a list of actions relating to the node. + * + * The actions should be parented to \a parent. + */ + virtual QList< QAction * > actions( QObject *parent ); + + /** + * Converts the node's contents to a variant. + */ + virtual QVariant toVariant() const; + + protected: + + QgsDevToolsModelNode(); + + private: + + QgsDevToolsModelGroup *mParent = nullptr; + friend class QgsDevToolsModelGroup; +}; + +/** + * \ingroup app + * \class QgsDevToolsModelGroup + * \brief Base class for dev tools model "group" nodes, which contain children of their own. + */ +class QgsDevToolsModelGroup : public QgsDevToolsModelNode +{ + public: + + /** + * Adds a \a child node to this node. + */ + void addChild( std::unique_ptr< QgsDevToolsModelNode > child ); + + /** + * Returns the index of the specified \a child node. + * + * \warning \a child must be a valid child of this node. + */ + int indexOf( QgsDevToolsModelNode *child ) const; + + /** + * Returns the child at the specified \a index. + */ + QgsDevToolsModelNode *childAt( int index ); + + /** + * Clears the group, removing all its children. + */ + void clear(); + + int childCount() const override final { return mChildren.size(); } + QVariant data( int role = Qt::DisplayRole ) const override; + QVariant toVariant() const override; + + protected: + + /** + * Constructor for a QgsDevToolsModelGroup, with the specified \a title. + */ + QgsDevToolsModelGroup( const QString &title ); + + /** + * Adds a simple \a key: \a value node to the group. + */ + void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); + + protected: + std::deque< std::unique_ptr< QgsDevToolsModelNode > > mChildren; + + private: + + QString mGroupTitle; + +}; + +/** + * \ingroup app + * \class QgsDevToolsModelValueNode + * \brief A "key: value" style node for a dev tools model. + */ +class QgsDevToolsModelValueNode : public QgsDevToolsModelNode +{ + public: + + /** + * Constructor for QgsDevToolsModelValueNode, with the specified \a key (usually translated) and \a value. + */ + QgsDevToolsModelValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); + + /** + * Returns the node's key. + */ + QString key() const { return mKey; } + + /** + * Returns the node's value. + */ + QString value() const { return mValue; } + + QVariant data( int role = Qt::DisplayRole ) const override final; + int childCount() const override final { return 0; } + QList< QAction * > actions( QObject *parent ) override final; + + private: + + QString mKey; + QString mValue; + QColor mColor; +}; + +#endif // QGSDEVTOOLSMODELNODE_H diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index 0b412391178..7aac7f95f4e 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -16,6 +16,7 @@ #include "qgsappquerylogger.h" #include "qgsqueryloggernode.h" #include "qgsapplication.h" +#include "devtools/qgsdevtoolsmodelnode.h" #include "qgssettings.h" #include "qgis.h" #include @@ -29,34 +30,16 @@ QgsAppQueryLogger::QgsAppQueryLogger( QObject *parent ) // logger must be created on the main thread Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() ); - if ( QgsSettings().value( QStringLiteral( "logQueries" ), false, QgsSettings::App ).toBool() ) - enableLogging( true ); -} - -bool QgsAppQueryLogger::isLogging() const -{ - return mIsLogging; + connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged ); + connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryFinished, this, &QgsAppQueryLogger::queryFinished ); } QgsAppQueryLogger::~QgsAppQueryLogger() = default; -void QgsAppQueryLogger::enableLogging( bool enabled ) -{ - if ( enabled ) - { - connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged, Qt::UniqueConnection ); - } - else - { - disconnect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged ); - } - mIsLogging = enabled; -} - void QgsAppQueryLogger::clear() { beginResetModel(); - mRequestGroups.clear(); + mQueryGroups.clear(); mRootNode->clear(); endResetModel(); } @@ -67,30 +50,48 @@ void QgsAppQueryLogger::queryLogged( const QgsDatabaseQueryLogEntry &query ) beginInsertRows( QModelIndex(), childCount, childCount ); - std::unique_ptr< QgsDatabaseQueryLoggerGroup > group = std::make_unique< QgsDatabaseQueryLoggerGroup >( query.query ); -// mRequestGroups.insert( parameters.requestId(), group.get() ); + std::unique_ptr< QgsDatabaseQueryLoggerQueryGroup > group = std::make_unique< QgsDatabaseQueryLoggerQueryGroup >( query ); + mQueryGroups.insert( query.queryId, group.get() ); mRootNode->addChild( std::move( group ) ); endInsertRows(); } -QgsDatabaseQueryLoggerNode *QgsAppQueryLogger::index2node( const QModelIndex &index ) const +void QgsAppQueryLogger::queryFinished( const QgsDatabaseQueryLogEntry &query ) +{ + QgsDatabaseQueryLoggerQueryGroup *queryGroup = mQueryGroups.value( query.queryId ); + if ( !queryGroup ) + return; + + // find the row: the position of the request in the rootNode + const QModelIndex requestIndex = node2index( queryGroup ); + if ( !requestIndex.isValid() ) + return; + + beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() ); + queryGroup->setFinished( query ); + endInsertRows(); + + emit dataChanged( requestIndex, requestIndex ); +} + +QgsDevToolsModelNode *QgsAppQueryLogger::index2node( const QModelIndex &index ) const { if ( !index.isValid() ) return mRootNode.get(); - return reinterpret_cast( index.internalPointer() ); + return reinterpret_cast( index.internalPointer() ); } QList QgsAppQueryLogger::actions( const QModelIndex &index, QObject *parent ) { - QgsDatabaseQueryLoggerNode *node = index2node( index ); + QgsDevToolsModelNode *node = index2node( index ); if ( !node ) return QList< QAction * >(); return node->actions( parent ); } -QModelIndex QgsAppQueryLogger::node2index( QgsDatabaseQueryLoggerNode *node ) const +QModelIndex QgsAppQueryLogger::node2index( QgsDevToolsModelNode *node ) const { if ( !node || !node->parent() ) return QModelIndex(); // this is the only root item -> invalid index @@ -102,11 +103,11 @@ QModelIndex QgsAppQueryLogger::node2index( QgsDatabaseQueryLoggerNode *node ) co return index( row, 0, parentIndex ); } -QModelIndex QgsAppQueryLogger::indexOfParentLayerTreeNode( QgsDatabaseQueryLoggerNode *parentNode ) const +QModelIndex QgsAppQueryLogger::indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const { Q_ASSERT( parentNode ); - QgsDatabaseQueryLoggerGroup *grandParentNode = parentNode->parent(); + QgsDevToolsModelGroup *grandParentNode = parentNode->parent(); if ( !grandParentNode ) return QModelIndex(); // root node -> invalid index @@ -123,8 +124,8 @@ void QgsAppQueryLogger::removeRequestRows( const QList &rows ) for ( int row : std::as_const( res ) ) { - int popId = data( index( row, 0, QModelIndex() ), QgsDatabaseQueryLoggerNode::RoleId ).toInt(); - mRequestGroups.remove( popId ); + int popId = data( index( row, 0, QModelIndex() ), QgsDevToolsModelNode::RoleId ).toInt(); + mQueryGroups.remove( popId ); beginRemoveRows( QModelIndex(), row, row ); mRootNode->removeRow( row ); @@ -139,7 +140,7 @@ QgsDatabaseQueryLoggerRootNode *QgsAppQueryLogger::rootGroup() int QgsAppQueryLogger::rowCount( const QModelIndex &parent ) const { - QgsDatabaseQueryLoggerNode *n = index2node( parent ); + QgsDevToolsModelNode *n = index2node( parent ); if ( !n ) return 0; @@ -158,7 +159,7 @@ QModelIndex QgsAppQueryLogger::index( int row, int column, const QModelIndex &pa row < 0 || row >= rowCount( parent ) ) return QModelIndex(); - QgsDatabaseQueryLoggerGroup *n = dynamic_cast< QgsDatabaseQueryLoggerGroup * >( index2node( parent ) ); + QgsDevToolsModelGroup *n = dynamic_cast< QgsDevToolsModelGroup * >( index2node( parent ) ); if ( !n ) return QModelIndex(); // have no children @@ -170,7 +171,7 @@ QModelIndex QgsAppQueryLogger::parent( const QModelIndex &child ) const if ( !child.isValid() ) return QModelIndex(); - if ( QgsDatabaseQueryLoggerNode *n = index2node( child ) ) + if ( QgsDevToolsModelNode *n = index2node( child ) ) { return indexOfParentLayerTreeNode( n->parent() ); // must not be null } @@ -186,7 +187,7 @@ QVariant QgsAppQueryLogger::data( const QModelIndex &index, int role ) const if ( !index.isValid() || index.column() > 1 ) return QVariant(); - QgsDatabaseQueryLoggerNode *node = index2node( index ); + QgsDevToolsModelNode *node = index2node( index ); if ( !node ) return QVariant(); @@ -232,7 +233,7 @@ void QgsDatabaseQueryLoggerProxyModel::setFilterString( const QString &string ) bool QgsDatabaseQueryLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const { - QgsDatabaseQueryLoggerNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); + QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); #if 0 if ( QgsDatabaseQueryLoggerRequestGroup *request = dynamic_cast< QgsDatabaseQueryLoggerRequestGroup * >( node ) ) { diff --git a/src/app/devtools/querylogger/qgsappquerylogger.h b/src/app/devtools/querylogger/qgsappquerylogger.h index ede6e92a692..4917ec2b85a 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.h +++ b/src/app/devtools/querylogger/qgsappquerylogger.h @@ -21,9 +21,10 @@ #include "qgsdbquerylog.h" #include -class QgsDatabaseQueryLoggerNode; -class QgsDatabaseQueryLoggerRequestGroup; +class QgsDevToolsModelNode; +class QgsDevToolsModelGroup; class QgsDatabaseQueryLoggerRootNode; +class QgsDatabaseQueryLoggerQueryGroup; class QAction; /** @@ -46,11 +47,6 @@ class QgsAppQueryLogger : public QAbstractItemModel QgsAppQueryLogger( QObject *parent ); ~QgsAppQueryLogger() override; - /** - * Returns TRUE if the logger is currently logging activity. - */ - bool isLogging() const; - // Implementation of virtual functions from QAbstractItemModel int rowCount( const QModelIndex &parent = QModelIndex() ) const override; @@ -64,7 +60,7 @@ class QgsAppQueryLogger : public QAbstractItemModel /** * Returns node for given index. Returns root node for invalid index. */ - QgsDatabaseQueryLoggerNode *index2node( const QModelIndex &index ) const; + QgsDevToolsModelNode *index2node( const QModelIndex &index ) const; /** * Returns a list of actions corresponding to the item at the specified \a index. @@ -87,11 +83,6 @@ class QgsAppQueryLogger : public QAbstractItemModel public slots: - /** - * Enables or disables logging, depending on the value of \a enabled. - */ - void enableLogging( bool enabled ); - /** * Clears all logged entries. */ @@ -99,18 +90,17 @@ class QgsAppQueryLogger : public QAbstractItemModel private slots: void queryLogged( const QgsDatabaseQueryLogEntry &query ); + void queryFinished( const QgsDatabaseQueryLogEntry &query ); private: //! Returns index for a given node - QModelIndex node2index( QgsDatabaseQueryLoggerNode *node ) const; - QModelIndex indexOfParentLayerTreeNode( QgsDatabaseQueryLoggerNode *parentNode ) const; - - bool mIsLogging = false; + QModelIndex node2index( QgsDevToolsModelNode *node ) const; + QModelIndex indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const; std::unique_ptr< QgsDatabaseQueryLoggerRootNode > mRootNode; - QHash< int, QgsDatabaseQueryLoggerRequestGroup * > mRequestGroups; + QHash< int, QgsDatabaseQueryLoggerQueryGroup * > mQueryGroups; }; diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index ed25255682c..61a404e9341 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -26,89 +26,13 @@ #include #include -// -// QgsDatabaseQueryLoggerNode -// - -QgsDatabaseQueryLoggerNode::QgsDatabaseQueryLoggerNode() = default; -QgsDatabaseQueryLoggerNode::~QgsDatabaseQueryLoggerNode() = default; - - -// -// QgsDatabaseQueryLoggerGroup -// - -QgsDatabaseQueryLoggerGroup::QgsDatabaseQueryLoggerGroup( const QString &title ) - : mGroupTitle( title ) -{ -} - -void QgsDatabaseQueryLoggerGroup::addChild( std::unique_ptr child ) -{ - if ( !child ) - return; - - Q_ASSERT( !child->mParent ); - child->mParent = this; - - mChildren.emplace_back( std::move( child ) ); -} - -int QgsDatabaseQueryLoggerGroup::indexOf( QgsDatabaseQueryLoggerNode *child ) const -{ - Q_ASSERT( child->mParent == this ); - auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr &p ) - { - return p.get() == child; - } ); - if ( it != mChildren.end() ) - return std::distance( mChildren.begin(), it ); - return -1; -} - -QgsDatabaseQueryLoggerNode *QgsDatabaseQueryLoggerGroup::childAt( int index ) -{ - Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() ); - return mChildren[ index ].get(); -} - -void QgsDatabaseQueryLoggerGroup::clear() -{ - mChildren.clear(); -} - -QVariant QgsDatabaseQueryLoggerGroup::data( int role ) const -{ - switch ( role ) - { - case Qt::DisplayRole: - return mGroupTitle; - - default: - break; - } - return QVariant(); -} - -QVariant QgsDatabaseQueryLoggerGroup::toVariant() const -{ - QVariantMap res; - for ( const std::unique_ptr< QgsDatabaseQueryLoggerNode > &child : mChildren ) - { - if ( const QgsDatabaseQueryLoggerValueNode *valueNode = dynamic_cast< const QgsDatabaseQueryLoggerValueNode *>( child.get() ) ) - { - res.insert( valueNode->key(), valueNode->value() ); - } - } - return res; -} // // QgsDatabaseQueryLoggerRootNode // QgsDatabaseQueryLoggerRootNode::QgsDatabaseQueryLoggerRootNode() - : QgsDatabaseQueryLoggerGroup( QString() ) + : QgsDevToolsModelGroup( QString() ) { } @@ -126,76 +50,214 @@ void QgsDatabaseQueryLoggerRootNode::removeRow( int row ) QVariant QgsDatabaseQueryLoggerRootNode::toVariant() const { QVariantList res; - for ( const std::unique_ptr< QgsDatabaseQueryLoggerNode > &child : mChildren ) + for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren ) res << child->toVariant(); return res; } // -// QgsDatabaseQueryLoggerValueNode +// QgsDatabaseQueryLoggerQueryGroup // -QgsDatabaseQueryLoggerValueNode::QgsDatabaseQueryLoggerValueNode( const QString &key, const QString &value, const QColor &color ) - : mKey( key ) - , mValue( value ) - , mColor( color ) + +QgsDatabaseQueryLoggerQueryGroup::QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ) + : QgsDevToolsModelGroup( QString() ) + , mSql( query.query ) + , mQueryId( query.queryId ) { +#if 0 + std::unique_ptr< QgsNetworkLoggerRequestDetailsGroup > detailsGroup = std::make_unique< QgsNetworkLoggerRequestDetailsGroup >( request ); + mDetailsGroup = detailsGroup.get(); + addChild( std::move( detailsGroup ) ); +#endif + + addKeyValueNode( QObject::tr( "Provider" ), query.provider ); + addKeyValueNode( QObject::tr( "URI" ), query.uri ); + addKeyValueNode( QObject::tr( "Started at" ), QDateTime::fromMSecsSinceEpoch( query.startedTime ).toString( Qt::ISODateWithMs ) ); +#if 0 + addKeyValueNode( QObject::tr( "Thread" ), query.originatingThreadId() ); +#endif + addKeyValueNode( QObject::tr( "Initiator" ), query.initiatorClass.isEmpty() ? QObject::tr( "unknown" ) : query.initiatorClass ); + if ( !query.origin.isEmpty() ) + addKeyValueNode( QObject::tr( "Location" ), query.origin ); } -QVariant QgsDatabaseQueryLoggerValueNode::data( int role ) const +QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const { switch ( role ) { case Qt::DisplayRole: + return QStringLiteral( "%1 %2" ).arg( QString::number( mQueryId ), + mSql ); + case Qt::ToolTipRole: { - return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue ); +#if 0 + QString bytes = QObject::tr( "unknown" ); + if ( mBytesTotal != 0 ) + { + if ( mBytesReceived > 0 && mBytesReceived < mBytesTotal ) + bytes = QStringLiteral( "%1/%2" ).arg( QString::number( mBytesReceived ), QString::number( mBytesTotal ) ); + else if ( mBytesReceived > 0 && mBytesReceived == mBytesTotal ) + bytes = QString::number( mBytesTotal ); + } + // ?? adding
instead of \n after (very long) url seems to break url up + // COMPLETE, Status: 200 - text/xml; charset=utf-8 - 2334 bytes - 657 milliseconds + return QStringLiteral( "%1
%2 - Status: %3 - %4 - %5 bytes - %6 msec - %7 replies" ) + .arg( mUrl.url(), + statusToString( mStatus ), + QString::number( mHttpStatus ), + mContentType, + bytes, + mStatus == Status::Pending ? QString::number( mTimer.elapsed() / 1000 ) : QString::number( mTotalTime ), + QString::number( mReplies ) ); +#endif } + case RoleStatus: + return static_cast< int >( mStatus ); + + case RoleId: + return mQueryId; + case Qt::ForegroundRole: { - if ( mColor.isValid() ) - return QBrush( mColor ); + if ( mHasSslErrors ) + return QBrush( QColor( 180, 65, 210 ) ); + switch ( mStatus ) + { + case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: + case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: + return QBrush( QColor( 0, 0, 0, 100 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Error: + return QBrush( QColor( 235, 10, 10 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: + return QBrush( QColor( 235, 10, 10 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: + break; + } break; } + + case Qt::FontRole: + { + if ( mStatus == Status::Canceled ) + { + QFont f; + f.setStrikeOut( true ); + return f; + } + break; + } + default: break; } return QVariant(); } -QList QgsDatabaseQueryLoggerValueNode::actions( QObject *parent ) +QList QgsDatabaseQueryLoggerQueryGroup::actions( QObject *parent ) { QList< QAction * > res; - QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent ); - QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ] + QAction *copyUrlAction = new QAction( QObject::tr( "Copy SQL" ), parent ); + QObject::connect( copyUrlAction, &QAction::triggered, copyUrlAction, [ = ] { - QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) ); + QApplication::clipboard()->setText( mSql ); } ); + res << copyUrlAction; - res << copyAction; + QAction *copyJsonAction = new QAction( QObject::tr( "Copy as JSON" ), parent ); + QObject::connect( copyJsonAction, &QAction::triggered, copyJsonAction, [ = ] + { + const QVariant value = toVariant(); + const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( value ).dump( 2 ) ); + QApplication::clipboard()->setText( json ); + + } ); + res << copyJsonAction; return res; } -// -// QgsDatabaseQueryLoggerGroup -// - -void QgsDatabaseQueryLoggerGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color ) +QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const { - addChild( std::make_unique< QgsDatabaseQueryLoggerValueNode >( key, value, color ) ); + QVariantMap res; + res.insert( QStringLiteral( "SQL" ), mSql ); + res.insert( QStringLiteral( "Total time (ms)" ), mTotalTime ); +#if 0 + if ( mDetailsGroup ) + { + const QVariantMap detailsMap = mDetailsGroup->toVariant().toMap(); + for ( auto it = detailsMap.constBegin(); it != detailsMap.constEnd(); ++it ) + res.insert( it.key(), it.value() ); + } + if ( mReplyGroup ) + { + res.insert( QObject::tr( "Reply" ), mReplyGroup->toVariant() ); + } + if ( mSslErrorsGroup ) + { + res.insert( QObject::tr( "SSL Errors" ), mSslErrorsGroup->toVariant() ); + } +#endif + return res; +} + +void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEntry &query ) +{ +#if 0 + switch ( reply.error() ) + { + case QNetworkReply::OperationCanceledError: + mStatus = Status::Canceled; + break; + + case QNetworkReply::NoError: +#endif + mStatus = Status::Complete; +#if 0 + break; + + default: + mStatus = Status::Error; + break; + } + +#endif +#if 0 + mTotalTime = mTimer.elapsed(); + mHttpStatus = reply.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); + mContentType = reply.rawHeader( "Content - Type" ); + + std::unique_ptr< QgsNetworkLoggerReplyGroup > replyGroup = std::make_unique< QgsNetworkLoggerReplyGroup >( reply ) ; + mReplyGroup = replyGroup.get(); + addChild( std::move( replyGroup ) ); +#endif +} + +void QgsDatabaseQueryLoggerQueryGroup::setTimedOut() +{ + mStatus = Status::TimeOut; +} + +QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLoggerQueryGroup::Status status ) +{ + switch ( status ) + { + case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: + return QObject::tr( "Pending" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: + return QObject::tr( "Complete" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Error: + return QObject::tr( "Error" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: + return QObject::tr( "Timeout" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: + return QObject::tr( "Canceled" ); + } + return QString(); } -QList QgsDatabaseQueryLoggerNode::actions( QObject * ) -{ - return QList< QAction * >(); -} - -QVariant QgsDatabaseQueryLoggerNode::toVariant() const -{ - return QVariant(); -} diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index 494fd4c2b1f..224ddbbd98a 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -12,8 +12,8 @@ * (at your option) any later version. * * * ***************************************************************************/ -#ifndef QgsDatabaseQueryLoggerNODE_H -#define QgsDatabaseQueryLoggerNODE_H +#ifndef QGSDBQUERYLOGGERNODE_H +#define QGSDBQUERYLOGGERNODE_H #include #include @@ -21,164 +21,17 @@ #include #include #include +#include "devtools/qgsdevtoolsmodelnode.h" +#include "qgsdbquerylog.h" class QAction; -class QgsDatabaseQueryLoggerGroup; - -/** - * \ingroup app - * \class QgsDatabaseQueryLoggerNode - * \brief Base class for nodes in the query logger model. - */ -class QgsDatabaseQueryLoggerNode -{ - public: - - //! Custom node data roles - enum Roles - { - RoleStatus = Qt::UserRole + 1, //!< Request status role - RoleId, //!< Request ID role - }; - - virtual ~QgsDatabaseQueryLoggerNode(); - - /** - * Returns the node's parent node. - * - * If parent is NULLPTR, the node is a root node - */ - QgsDatabaseQueryLoggerGroup *parent() { return mParent; } - - /** - * Returns the node's data for the specified model \a role. - */ - virtual QVariant data( int role = Qt::DisplayRole ) const = 0; - - /** - * Returns the number of child nodes owned by this node. - */ - virtual int childCount() const = 0; - - /** - * Returns a list of actions relating to the node. - * - * The actions should be parented to \a parent. - */ - virtual QList< QAction * > actions( QObject *parent ); - - /** - * Converts the node's contents to a variant. - */ - virtual QVariant toVariant() const; - - protected: - - QgsDatabaseQueryLoggerNode(); - - private: - - QgsDatabaseQueryLoggerGroup *mParent = nullptr; - friend class QgsDatabaseQueryLoggerGroup; -}; - -/** - * \ingroup app - * \class QgsDatabaseQueryLoggerGroup - * \brief Base class for query logger model "group" nodes, which contain children of their own. - */ -class QgsDatabaseQueryLoggerGroup : public QgsDatabaseQueryLoggerNode -{ - public: - - /** - * Adds a \a child node to this node. - */ - void addChild( std::unique_ptr< QgsDatabaseQueryLoggerNode > child ); - - /** - * Returns the index of the specified \a child node. - * - * \warning \a child must be a valid child of this node. - */ - int indexOf( QgsDatabaseQueryLoggerNode *child ) const; - - /** - * Returns the child at the specified \a index. - */ - QgsDatabaseQueryLoggerNode *childAt( int index ); - - /** - * Clears the group, removing all its children. - */ - void clear(); - - int childCount() const override final { return mChildren.size(); } - QVariant data( int role = Qt::DisplayRole ) const override; - QVariant toVariant() const override; - -// protected: - - /** - * Constructor for a QgsDatabaseQueryLoggerGroup, with the specified \a title. - */ - QgsDatabaseQueryLoggerGroup( const QString &title ); - - /** - * Adds a simple \a key: \a value node to the group. - */ - void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); - - private: - - std::deque< std::unique_ptr< QgsDatabaseQueryLoggerNode > > mChildren; - QString mGroupTitle; - friend class QgsDatabaseQueryLoggerRootNode; - friend class QgsAppQueryLogger; - -}; - -/** - * \ingroup app - * \class QgsDatabaseQueryLoggerValueNode - * \brief A "key: value" style node for the query logger model. - */ -class QgsDatabaseQueryLoggerValueNode : public QgsDatabaseQueryLoggerNode -{ - public: - - /** - * Constructor for QgsDatabaseQueryLoggerValueNode, with the specified \a key (usually translated) and \a value. - */ - QgsDatabaseQueryLoggerValueNode( const QString &key, const QString &value, const QColor &color = QColor() ); - - /** - * Returns the node's key. - */ - QString key() const { return mKey; } - - /** - * Returns the node's value. - */ - QString value() const { return mValue; } - - QVariant data( int role = Qt::DisplayRole ) const override final; - int childCount() const override final { return 0; } - QList< QAction * > actions( QObject *parent ) override final; - - private: - - QString mKey; - QString mValue; - QColor mColor; -}; /** * \ingroup app * \class QgsDatabaseQueryLoggerRootNode * \brief Root node for the query logger model. */ -class QgsDatabaseQueryLoggerRootNode final : public QgsDatabaseQueryLoggerGroup +class QgsDatabaseQueryLoggerRootNode final : public QgsDevToolsModelGroup { public: @@ -194,4 +47,93 @@ class QgsDatabaseQueryLoggerRootNode final : public QgsDatabaseQueryLoggerGroup }; -#endif // QgsDatabaseQueryLoggerNODE_H +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerQueryGroup + * \brief Parent group for all database queries, showing the query id, SQL, URI, + * and containing child groups with detailed query and result information. + * + * Visually, a QgsDatabaseQueryLoggerQueryGroup is structured by: + * + * |__ QgsDatabaseQueryLoggerQueryGroup (showing sql, uri,...) + * |__ QgsNetworkLoggerRequestDetailsGroup (holding Request details) + * |__ QgsNetworkLoggerValueNode (key-value pairs with info) + * ... + * |__ QgsNetworkLoggerRequestQueryGroup (holding query info) + * |__ ... + * |__ QgsNetworkLoggerRequestHeadersGroup ('Headers') + * |__ ... + * |__ QgsNetworkLoggerPostContentGroup (showing Data in case of POST) + * |__ ... + * |__ QgsNetworkLoggerReplyGroup (holding Reply details) + * |__ QgsNetworkLoggerReplyHeadersGroup (Reply 'Headers') + * |__ ... + * |__ QgsNetworkLoggerSslErrorGroup (holding SSL error details, if encountered) + * |__ ... + */ +class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup +{ + public: + + //! Query status + enum class Status + { + Pending, //!< Query underway + Complete, //!< Query was successfully completed + Error, //!< Query encountered an error + TimeOut, //!< Query timed out + Canceled, //!< Query was manually canceled + }; + + /** + * Constructor for QgsDatabaseQueryLoggerQueryGroup, populated from the + * specified \a query details. + */ + QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ); + QVariant data( int role = Qt::DisplayRole ) const override; + QList< QAction * > actions( QObject *parent ) override final; + QVariant toVariant() const override; + + /** + * Returns the query's status. + */ + Status status() const { return mStatus; } + + /** + * Called when the \a query is finished. + * + * Will automatically create children encapsulating the completed details. + */ + void setFinished( const QgsDatabaseQueryLogEntry &query ); + + /** + * Flags the query as having timed out. + */ + void setTimedOut(); + + /** + * Converts a request \a status to a translated string value. + */ + static QString statusToString( Status status ); + + private: + + QString mSql; + int mQueryId = 0; + QElapsedTimer mTimer; + qint64 mTotalTime = 0; + int mHttpStatus = -1; + QString mContentType; + int mReplies = 0; + QByteArray mData; + Status mStatus = Status::Pending; + bool mHasSslErrors = false; + QList< QPair< QString, QString > > mHeaders; +#if 0 + QgsNetworkLoggerRequestDetailsGroup *mDetailsGroup = nullptr; + QgsNetworkLoggerReplyGroup *mReplyGroup = nullptr; + QgsNetworkLoggerSslErrorGroup *mSslErrorsGroup = nullptr; +#endif +}; + +#endif // QGSDBQUERYLOGGERNODE_H diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp index 37c8b8580b6..ede668c0be6 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp @@ -157,24 +157,24 @@ QgsDatabaseQueryLoggerPanelWidget::QgsDatabaseQueryLoggerPanelWidget( QgsAppQuer mFilterLineEdit->setShowClearButton( true ); mFilterLineEdit->setShowSearchIcon( true ); - mFilterLineEdit->setPlaceholderText( tr( "Filter requests" ) ); + mFilterLineEdit->setPlaceholderText( tr( "Filter queries" ) ); - mActionRecord->setChecked( mLogger->isLogging() ); + mActionRecord->setChecked( QgsApplication::databaseQueryLog()->enabled() ); connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, mTreeView, &QgsDatabaseQueryLoggerTreeView::setFilterString ); connect( mActionClear, &QAction::triggered, mLogger, &QgsAppQueryLogger::clear ); connect( mActionRecord, &QAction::toggled, this, [ = ]( bool enabled ) { - QgsSettings().setValue( QStringLiteral( "logNetworkRequests" ), enabled, QgsSettings::App ); - mLogger->enableLogging( enabled ); + QgsSettings().setValue( QStringLiteral( "logDatabaseQueries" ), enabled, QgsSettings::App ); + QgsApplication::databaseQueryLog()->setEnabled( enabled ); } ); connect( mActionSaveLog, &QAction::triggered, this, [ = ]() { - if ( QMessageBox::warning( this, tr( "Save Network Log" ), - tr( "Security warning: network logs may contain sensitive data including usernames or passwords. Treat this log as confidential and be careful who you share it with. Continue?" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No ) + if ( QMessageBox::warning( this, tr( "Save Database Query Log" ), + tr( "Security warning: query logs may contain sensitive data including usernames or passwords. Treat this log as confidential and be careful who you share it with. Continue?" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No ) return; - const QString saveFilePath = QFileDialog::getSaveFileName( this, tr( "Save Network Log" ), QDir::homePath(), tr( "Log files" ) + " (*.json)" ); + const QString saveFilePath = QFileDialog::getSaveFileName( this, tr( "Save Query Log" ), QDir::homePath(), tr( "Log files" ) + " (*.json)" ); if ( saveFilePath.isEmpty() ) { return; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 130e7e491c1..d3c5e3602ce 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -991,10 +991,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers mNetworkLogger = new QgsNetworkLogger( QgsNetworkAccessManager::instance(), this ); endProfile(); - startProfile( tr( "Create database query logger" ) ); - mQueryLogger = new QgsAppQueryLogger( this ); - endProfile(); - // load GUI: actions, menus, toolbars startProfile( tr( "Setting up UI" ) ); setupUi( this ); @@ -1040,6 +1036,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers QgsSettings settings; + startProfile( tr( "Create database query logger" ) ); + mQueryLogger = new QgsAppQueryLogger( this ); + QgsApplication::databaseQueryLog()->setEnabled( settings.value( QStringLiteral( "logDatabaseQueries" ), false, QgsSettings::App ).toBool() ); + endProfile(); startProfile( tr( "Building style sheet" ) ); // set up stylesheet builder and apply saved or default style options diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index ca2714e216b..7731a2cb0bb 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** qgsdbquerylog.h ------------ Date : October 2021 @@ -44,6 +44,12 @@ class CORE_EXPORT QgsDatabaseQueryLogEntry */ int queryId = 0; + //! Database URI + QString uri; + + //! Provider key + QString provider; + //! The logged database query (e.g. the SQL query) QString query; diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index c4e724d9996..73eee7ccb8c 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -413,8 +413,8 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s if ( mPostgresqlVersion >= 90000 ) { - PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) ); - PQexecNR( QStringLiteral( "SET extra_float_digits=3" ) ); + LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "SET application_name='QGIS'" ) ); + LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "SET extra_float_digits=3" ) ); } PQsetNoticeProcessor( mConn, noticeProcessor, nullptr ); @@ -499,7 +499,7 @@ void QgsPostgresConn::addColumnInfo( QgsPostgresLayerProperty &layerProperty, co .arg( quotedIdentifier( schemaName ), quotedIdentifier( viewName ) ); QgsDebugMsgLevel( "getting column info: " + sql, 2 ); - QgsPostgresResult colRes( PQexec( sql ) ); + QgsPostgresResult colRes( LoggedPQexec( "QgsPostgresConn", sql ) ); layerProperty.pkCols.clear(); layerProperty.nSpCols = 0; @@ -657,7 +657,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsDebugMsgLevel( "getting table info from layer registries: " + query, 2 ); - result = PQexec( query, true ); + result = LoggedPQexec( "QgsPostgresConn", query ); // NOTE: we intentionally continue if the query fails // (for example because PostGIS is not installed) for ( int idx = 0; idx < result.PQntuples(); idx++ ) @@ -811,14 +811,14 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsDebugMsgLevel( "getting spatial table info from pg_catalog: " + sql, 2 ); - result = PQexec( sql ); + result = LoggedPQexec( "QgsPostresConn", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined. The error message from the database was:\n%1\n" ) .arg( result.PQresultErrorMessage() ), tr( "PostGIS" ) ); - PQexecNR( QStringLiteral( "COMMIT" ) ); + LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "COMMIT" ) ); return false; } @@ -923,7 +923,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsDebugMsgLevel( "getting non-spatial table info: " + sql, 2 ); - result = PQexec( sql ); + result = LoggedPQexec( "QgsPostresConn", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -1019,10 +1019,10 @@ bool QgsPostgresConn::getSchemas( QList &schemas ) QString sql = QStringLiteral( "SELECT nspname, pg_get_userbyid(nspowner), pg_catalog.obj_description(oid) FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema' ORDER BY nspname" ); - result = PQexec( sql, true ); + result = LoggedPQexec( "QgsPostresConn", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { - PQexecNR( QStringLiteral( "COMMIT" ) ); + LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "COMMIT" ) ); return false; } @@ -1122,7 +1122,7 @@ QString QgsPostgresConn::postgisVersion() const // apparently PostGIS 1.5.2 doesn't report capabilities in postgis_version() anymore if ( mPostgisVersionMajor > 1 || ( mPostgisVersionMajor == 1 && mPostgisVersionMinor >= 5 ) ) { - result = PQexec( QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) ); + result = LoggedPQexec( "QgsPostresConn", QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) ); mGeosAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ); mProjAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 1 ); QgsDebugMsgLevel( QStringLiteral( "geos:%1 proj:%2" ) @@ -1147,19 +1147,18 @@ QString QgsPostgresConn::postgisVersion() const mTopologyAvailable = false; if ( mPostgisVersionMajor > 1 ) { - QgsPostgresResult result( - PQexec( - QStringLiteral( - "SELECT has_schema_privilege(n.oid, 'usage')" - " AND has_table_privilege(t.oid, 'select')" - " AND has_table_privilege(l.oid, 'select')" - " FROM pg_namespace n, pg_class t, pg_class l" - " WHERE n.nspname = 'topology'" - " AND t.relnamespace = n.oid" - " AND l.relnamespace = n.oid" - " AND t.relname = 'topology'" - " AND l.relname = 'layer'" - ) ) ); + const QString query = QStringLiteral( + "SELECT has_schema_privilege(n.oid, 'usage')" + " AND has_table_privilege(t.oid, 'select')" + " AND has_table_privilege(l.oid, 'select')" + " FROM pg_namespace n, pg_class t, pg_class l" + " WHERE n.nspname = 'topology'" + " AND t.relnamespace = n.oid" + " AND l.relnamespace = n.oid" + " AND t.relname = 'topology'" + " AND l.relname = 'layer'" + ); + QgsPostgresResult result( LoggedPQexec( "QgsPostresConn", query ) ); if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) { mTopologyAvailable = true; @@ -1328,13 +1327,20 @@ QString QgsPostgresConn::quotedJsonValue( const QVariant &value ) return quotedString( QString::fromStdString( j.dump() ) ); } -PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool retry ) const +PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool retry, const QString &originatorClass, const QString &queryOrigin ) const { QMutexLocker locker( &mLock ); QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); + QgsDatabaseQueryLogEntry logEntry( query ); + logEntry.provider = QStringLiteral( "postgres" ); + logEntry.uri = mConnInfo; + logEntry.initiatorClass = originatorClass; + logEntry.origin = queryOrigin; + QgsDatabaseQueryLog::log( logEntry ); PGresult *res = ::PQexec( mConn, query.toUtf8() ); + QgsDatabaseQueryLog::finished( logEntry ); // libpq may return a non null ptr with conn status not OK so we need to check for it to allow a retry below if ( res && PQstatus() == CONNECTION_OK ) @@ -1386,7 +1392,10 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret { QgsMessageLog::logMessage( tr( "resetting bad connection." ), tr( "PostGIS" ) ); ::PQreset( mConn ); + QgsDatabaseQueryLogEntry logEntry( query ); + QgsDatabaseQueryLog::log( logEntry ); res = PQexec( query, logError, false ); + QgsDatabaseQueryLog::finished( logEntry ); if ( PQstatus() == CONNECTION_OK ) { if ( res ) @@ -1443,8 +1452,8 @@ bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql preStr = QStringLiteral( "BEGIN;" ); } QgsDebugMsgLevel( QStringLiteral( "Binary cursor %1 for %2" ).arg( cursorName, sql ), 3 ); - return PQexecNR( QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). - arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ) ); + return LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ). + arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ) ); } bool QgsPostgresConn::closeCursor( const QString &cursorName ) @@ -1458,7 +1467,7 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName ) postStr = QStringLiteral( ";COMMIT" ); } - if ( !PQexecNR( QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) + if ( !LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) return false; return true; @@ -1470,11 +1479,11 @@ QString QgsPostgresConn::uniqueCursorName() return QStringLiteral( "qgis_%1" ).arg( ++mNextCursorId ); } -bool QgsPostgresConn::PQexecNR( const QString &query ) +bool QgsPostgresConn::PQexecNR( const QString &query, const QString &originatorClass, const QString &queryOrigin ) { QMutexLocker locker( &mLock ); // to protect access to mOpenCursors - QgsPostgresResult res( PQexec( query, false ) ); + QgsPostgresResult res( PQexec( query, false, true, originatorClass, queryOrigin ) ); ExecStatusType errorStatus = res.PQresultStatus(); if ( errorStatus == PGRES_COMMAND_OK ) @@ -1496,7 +1505,7 @@ bool QgsPostgresConn::PQexecNR( const QString &query ) if ( PQstatus() == CONNECTION_OK ) { - PQexecNR( QStringLiteral( "ROLLBACK" ) ); + LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK" ) ); } return false; @@ -1576,11 +1585,11 @@ bool QgsPostgresConn::begin() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); } else { - return PQexecNR( QStringLiteral( "BEGIN" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "BEGIN" ) ); } } @@ -1589,11 +1598,11 @@ bool QgsPostgresConn::commit() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - return PQexecNR( QStringLiteral( "COMMIT" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "COMMIT" ) ); } } @@ -1602,12 +1611,12 @@ bool QgsPostgresConn::rollback() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return PQexecNR( QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) - && PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) + && LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - return PQexecNR( QStringLiteral( "ROLLBACK" ) ); + return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK" ) ); } } @@ -1893,7 +1902,7 @@ void QgsPostgresConn::deduceEndian() .arg( queryCounter ) .arg( errorCounter ), 2 ); - QgsPostgresResult res( PQexec( QStringLiteral( "select regclass('pg_class')::oid" ) ) ); + QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", QStringLiteral( "select regclass('pg_class')::oid" ) ) ); QString oidValue = res.PQgetvalue( 0, 0 ); QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 ); @@ -1903,7 +1912,7 @@ void QgsPostgresConn::deduceEndian() QgsDebugMsgLevel( QStringLiteral( "Fetching a record and attempting to get check endian-ness" ), 2 ); - res = PQexec( QStringLiteral( "fetch forward 1 from oidcursor" ) ); + res = LoggedPQexec( "QgsPostresConn", QStringLiteral( "fetch forward 1 from oidcursor" ) ); mSwapEndian = true; if ( res.PQntuples() > 0 ) @@ -2085,7 +2094,7 @@ void QgsPostgresConn::retrieveLayerTypes( QVector &l QgsDebugMsgLevel( "Layer types,srids and dims query: " + query, 3 ); - QgsPostgresResult res( PQexec( query ) ); + QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", query ) ); if ( res.PQresultStatus() != PGRES_TUPLES_OK ) { // TODO: print some error here ? @@ -2679,7 +2688,7 @@ QString QgsPostgresConn::currentDatabase() const QMutexLocker locker( &mLock ); QString database; QString sql = "SELECT current_database()"; - QgsPostgresResult res( PQexec( sql ) ); + QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", sql ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { diff --git a/src/providers/postgres/qgspostgresconn.h b/src/providers/postgres/qgspostgresconn.h index 50b25511d84..9cb53a4d1b4 100644 --- a/src/providers/postgres/qgspostgresconn.h +++ b/src/providers/postgres/qgspostgresconn.h @@ -200,6 +200,11 @@ class QgsPoolPostgresConn class QgsPostgresConn *get() const { return mPgConn; } }; +#include "qgsconfig.h" +constexpr int sPostgresConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); +#define LoggedPQexecNR(_class, query) PQexecNR( query, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) +#define LoggedPQexec(_class, query) PQexec( query, true, true, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) + class QgsPostgresConn : public QObject { Q_OBJECT @@ -247,7 +252,7 @@ class QgsPostgresConn : public QObject int pgVersion() const { return mPostgresqlVersion; } //! run a query and free result buffer - bool PQexecNR( const QString &query ); + bool PQexecNR( const QString &query, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); //! cursor handling bool openCursor( const QString &cursorName, const QString &declare ); @@ -264,7 +269,7 @@ class QgsPostgresConn : public QObject // // run a query and check for errors, thread-safe - PGresult *PQexec( const QString &query, bool logError = true, bool retry = true ) const; + PGresult *PQexec( const QString &query, bool logError = true, bool retry = true, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ) const; int PQCancel(); void PQfinish(); QString PQerrorMessage() const; diff --git a/src/providers/postgres/qgspostgresdataitemguiprovider.cpp b/src/providers/postgres/qgspostgresdataitemguiprovider.cpp index bfd9c6c6f66..f10b5767362 100644 --- a/src/providers/postgres/qgspostgresdataitemguiprovider.cpp +++ b/src/providers/postgres/qgspostgresdataitemguiprovider.cpp @@ -252,7 +252,7 @@ void QgsPostgresDataItemGuiProvider::createSchema( QgsDataItem *item, QgsDataIte //create the schema const QString sql = QStringLiteral( "CREATE SCHEMA %1" ).arg( QgsPostgresConn::quotedIdentifier( schemaName ) ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { notify( tr( "New Schema" ), tr( "Unable to create schema '%1'\n%2" ).arg( schemaName, @@ -284,7 +284,7 @@ void QgsPostgresDataItemGuiProvider::deleteSchema( QgsPGSchemaItem *schemaItem, } const QString sql = QStringLiteral( "SELECT table_name FROM information_schema.tables WHERE table_schema='%1'" ).arg( schemaItem->name() ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { notify( tr( "Delete Schema" ), tr( "Unable to delete schema." ), context, Qgis::MessageLevel::Warning ); @@ -360,7 +360,7 @@ void QgsPostgresDataItemGuiProvider::renameSchema( QgsPGSchemaItem *schemaItem, const QString sql = QStringLiteral( "ALTER SCHEMA %1 RENAME TO %2" ) .arg( schemaName, QgsPostgresConn::quotedIdentifier( dlg.name() ) ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { notify( tr( "Rename Schema" ), tr( "Unable to rename schema '%1'\n%2" ).arg( schemaItem->name(), @@ -418,7 +418,7 @@ void QgsPostgresDataItemGuiProvider::renameLayer( QgsPGLayerItem *layerItem, Qgs sql = QStringLiteral( "ALTER TABLE %1 RENAME TO %2" ).arg( oldName, newName ); } - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { notify( tr( "Rename %1" ).arg( typeName ), tr( "Unable to rename '%1' %2\n%3" ).arg( lowerTypeName, layerItem->name(), @@ -463,7 +463,7 @@ void QgsPostgresDataItemGuiProvider::truncateTable( QgsPGLayerItem *layerItem, Q const QString sql = QStringLiteral( "TRUNCATE TABLE %1" ).arg( tableRef ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { notify( tr( "Truncate Table" ), tr( "Unable to truncate '%1'\n%2" ).arg( tableName, @@ -503,7 +503,7 @@ void QgsPostgresDataItemGuiProvider::refreshMaterializedView( QgsPGLayerItem *la const QString sql = QStringLiteral( "REFRESH MATERIALIZED VIEW CONCURRENTLY %1" ).arg( tableRef ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { notify( tr( "Refresh View" ), tr( "Unable to refresh the view '%1'\n%2" ).arg( tableRef, diff --git a/src/providers/postgres/qgspostgresdataitems.cpp b/src/providers/postgres/qgspostgresdataitems.cpp index 24b07822eea..8b8c4cdb74e 100644 --- a/src/providers/postgres/qgspostgresdataitems.cpp +++ b/src/providers/postgres/qgspostgresdataitems.cpp @@ -58,12 +58,12 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) // handle deletion of views QString sqlViewCheck = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ) .arg( QgsPostgresConn::quotedValue( schemaTableName ) ); - QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) ); + QgsPostgresResult resViewCheck( conn->LoggedPQexec( "QgsPostgresUtils", sqlViewCheck ) ); QString type = resViewCheck.PQgetvalue( 0, 0 ); if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) ) { QString sql = QStringLiteral( "DROP %1VIEW %2" ).arg( type == QLatin1String( "m" ) ? QStringLiteral( "MATERIALIZED " ) : QString(), schemaTableName ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { errCause = QObject::tr( "Unable to delete view %1: \n%2" ) @@ -85,7 +85,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) "AND f_table_schema=%1 AND f_table_name=%2" ) .arg( QgsPostgresConn::quotedValue( schemaName ), QgsPostgresConn::quotedValue( tableName ) ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { errCause = QObject::tr( "Unable to delete layer %1: \n%2" ) @@ -113,7 +113,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) QgsPostgresConn::quotedValue( tableName ) ); } - result = conn->PQexec( sql ); + result = conn->LoggedPQexec( "QgsPostgresUtils", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { errCause = QObject::tr( "Unable to delete layer %1: \n%2" ) @@ -147,7 +147,7 @@ bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceU QString sql = QStringLiteral( "DROP SCHEMA %1 %2" ) .arg( schemaName, cascade ? QStringLiteral( "CASCADE" ) : QString() ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { errCause = QObject::tr( "Unable to delete schema %1: \n%2" ) diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index ee00f330c0e..0126ed9e1dd 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -423,7 +423,7 @@ bool QgsPostgresFeatureIterator::rewind() // move cursor to first record - mConn->PQexecNR( QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) ); + mConn->LoggedPQexecNR( "QgsPostgresFeatureIterator", QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) ); mFeatureQueue.clear(); mFetched = 0; mLastFetch = false; diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index b604432eaac..7bf53b820de 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -2369,7 +2369,7 @@ void QgsPostgresProvider::dropOrphanedTopoGeoms() QgsDebugMsgLevel( "TopoGeom orphans cleanup query: " + sql, 2 ); - connectionRW()->PQexecNR( sql ); + connectionRW()->LoggedPQexecNR( "QgsPostgresProvider", sql ); } QString QgsPostgresProvider::geomParam( int offset ) const @@ -2748,7 +2748,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags ) } } - conn->PQexecNR( QStringLiteral( "DEALLOCATE addfeatures" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE addfeatures" ) ); returnvalue &= conn->commit(); if ( mTransaction ) @@ -2760,7 +2760,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags ) { pushError( tr( "PostGIS error while adding features: %1" ).arg( e.errorMessage() ) ); conn->rollback(); - conn->PQexecNR( QStringLiteral( "DEALLOCATE addfeatures" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE addfeatures" ) ); returnvalue = false; } @@ -3434,11 +3434,11 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m } // for each feature - conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeatures" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeatures" ) ); if ( mSpatialColType == SctTopoGeometry ) { - connectionRO()->PQexecNR( QStringLiteral( "DEALLOCATE getid" ) ); - conn->PQexecNR( QStringLiteral( "DEALLOCATE replacetopogeom" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE getid" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE replacetopogeom" ) ); } returnvalue &= conn->commit(); @@ -3449,11 +3449,11 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m { pushError( tr( "PostGIS error while changing geometry values: %1" ).arg( e.errorMessage() ) ); conn->rollback(); - conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeatures" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeatures" ) ); if ( mSpatialColType == SctTopoGeometry ) { - connectionRO()->PQexecNR( QStringLiteral( "DEALLOCATE getid" ) ); - conn->PQexecNR( QStringLiteral( "DEALLOCATE replacetopogeom" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE getid" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE replacetopogeom" ) ); } returnvalue = false; } @@ -3609,7 +3609,7 @@ bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_ma throw PGException( result ); } - conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeature" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeature" ) ); } // update feature id map if key was changed @@ -3886,7 +3886,7 @@ QgsRectangle QgsPostgresProvider::extent() const result = connectionRO()->PQexec( sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) - connectionRO()->PQexecNR( QStringLiteral( "ROLLBACK" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "ROLLBACK" ) ); else if ( result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) ext = result.PQgetvalue( 0, 0 ); } @@ -4086,7 +4086,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } if ( detectedType.isEmpty() ) @@ -4111,7 +4111,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } } @@ -4142,7 +4142,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } } @@ -4166,7 +4166,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } } @@ -4197,7 +4197,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } } } @@ -4231,7 +4231,7 @@ bool QgsPostgresProvider::getGeometryDetails() } else { - connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); + connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); detectedType = mRequestedGeomType == QgsWkbTypes::Unknown ? QString() : QgsPostgresConn::postgisWkbTypeName( mRequestedGeomType ); } } @@ -4587,7 +4587,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u try { - conn->PQexecNR( QStringLiteral( "BEGIN" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "BEGIN" ) ); // We want a valid schema name ... if ( schemaName.isEmpty() ) @@ -4688,7 +4688,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u geometryColumn.clear(); } - conn->PQexecNR( QStringLiteral( "COMMIT" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) ); } catch ( PGException &e ) { @@ -4697,7 +4697,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u .arg( schemaTableName, e.errorMessage() ); - conn->PQexecNR( QStringLiteral( "ROLLBACK" ) ); + conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "ROLLBACK" ) ); conn->unref(); return Qgis::VectorExportResult::ErrorCreatingLayer; } @@ -4879,7 +4879,7 @@ QString QgsPostgresProvider::getNextString( const QString &txt, int &i, const QS } i += match.captured( 1 ).length() + 2; jumpSpace( txt, i ); - if ( !QStringView{txt}.mid( i ).startsWith( sep ) && i < txt.length() ) + if ( !QStringView{txt} .mid( i ).startsWith( sep ) && i < txt.length() ) { QgsMessageLog::logMessage( tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), tr( "PostGIS" ) ); return QString(); @@ -4892,9 +4892,9 @@ QString QgsPostgresProvider::getNextString( const QString &txt, int &i, const QS int start = i; for ( ; i < txt.length(); i++ ) { - if ( QStringView{txt}.mid( i ).startsWith( sep ) ) + if ( QStringView{txt} .mid( i ).startsWith( sep ) ) { - QStringView v( QStringView{txt}.mid( start, i - start ) ); + QStringView v( QStringView{txt} .mid( start, i - start ) ); i += sep.length(); return v.trimmed().toString(); } diff --git a/src/providers/postgres/qgspostgresproviderconnection.cpp b/src/providers/postgres/qgspostgresproviderconnection.cpp index 42f8a00530e..325305c680e 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.cpp +++ b/src/providers/postgres/qgspostgresproviderconnection.cpp @@ -257,7 +257,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection } std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - std::unique_ptr res = std::make_unique( conn->PQexec( sql ) ); + std::unique_ptr res = std::make_unique( conn->LoggedPQexec( "QgsPostgresProviderConnection", sql ) ); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); results.setQueryExecutionTime( std::chrono::duration_cast( end - begin ).count() ); diff --git a/src/providers/postgres/qgspostgrestransaction.cpp b/src/providers/postgres/qgspostgrestransaction.cpp index 55caa27bf2b..615606a51f0 100644 --- a/src/providers/postgres/qgspostgrestransaction.cpp +++ b/src/providers/postgres/qgspostgrestransaction.cpp @@ -72,7 +72,7 @@ bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg, } QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); - QgsPostgresResult r( mConn->PQexec( sql, true ) ); + QgsPostgresResult r( mConn->LoggedPQexec( "QgsPostgresTransaction", sql ) ); if ( r.PQresultStatus() == PGRES_BAD_RESPONSE || r.PQresultStatus() == PGRES_FATAL_ERROR ) { From f388ef7a340f4b55cf2f760f42621d36d438726f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 6 Apr 2022 15:11:28 +0200 Subject: [PATCH 05/24] More on query logging --- images/images.qrc | 1 + .../themes/default/propertyicons/database.svg | 1 + .../core/auto_generated/qgsdbquerylog.sip.in | 7 +- .../querylogger/qgsqueryloggernode.cpp | 6 +- .../qgsqueryloggerwidgetfactory.cpp | 2 +- src/core/qgsdbquerylog.h | 45 ++++++- src/providers/postgres/qgspostgresconn.h | 1 + .../postgres/qgspostgresfeatureiterator.cpp | 4 + .../postgres/qgspostgresprovider.cpp | 123 +++++++++--------- 9 files changed, 124 insertions(+), 66 deletions(-) create mode 100644 images/themes/default/propertyicons/database.svg diff --git a/images/images.qrc b/images/images.qrc index 00176d5929f..09c7829f45a 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -591,6 +591,7 @@ themes/default/propertyicons/attributes.svg themes/default/propertyicons/CRS.svg themes/default/propertyicons/datadefined.svg + themes/default/propertyicons/database.svg themes/default/propertyicons/diagram.svg themes/default/propertyicons/digitizing.svg themes/default/propertyicons/display.svg diff --git a/images/themes/default/propertyicons/database.svg b/images/themes/default/propertyicons/database.svg new file mode 100644 index 00000000000..3018057f41c --- /dev/null +++ b/images/themes/default/propertyicons/database.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in index ff26589fd0c..384bf06579b 100644 --- a/python/core/auto_generated/qgsdbquerylog.sip.in +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -28,6 +28,10 @@ Constructor for QgsDatabaseQueryLogEntry. int queryId; + QString uri; + + QString provider; + QString query; quint64 startedTime; @@ -92,7 +96,7 @@ This method can be safely called from any thread. static void finished( const QgsDatabaseQueryLogEntry &query ); %Docstring -Records that the database ``query`` as finished. +Records that the database ``query`` has finished. This method can be safely called from any thread. %End @@ -107,6 +111,7 @@ This method can be safely called from any thread. }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index 61a404e9341..fd4941597d4 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -93,6 +93,9 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const case Qt::ToolTipRole: { + // Show no more than 255 characters + return mSql.length() > 255 ? mSql.mid( 0, 255 ).append( QStringLiteral( "…" ) ) : mSql; + #if 0 QString bytes = QObject::tr( "unknown" ); if ( mBytesTotal != 0 ) @@ -154,7 +157,7 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const default: break; } - return QVariant(); + return QVariant( ); } QList QgsDatabaseQueryLoggerQueryGroup::actions( QObject *parent ) @@ -235,6 +238,7 @@ void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEnt mReplyGroup = replyGroup.get(); addChild( std::move( replyGroup ) ); #endif + addKeyValueNode( QObject::tr( "Total time" ), QLocale().toString( ( query.finishedTime - query.startedTime ) / 1000.0 ).append( QStringLiteral( " s" ) ) ); } void QgsDatabaseQueryLoggerQueryGroup::setTimedOut() diff --git a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp index 134f3059697..7ff82ec41f3 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp @@ -18,7 +18,7 @@ #include "qgsapplication.h" QgsDatabaseQueryLoggerWidgetFactory::QgsDatabaseQueryLoggerWidgetFactory( QgsAppQueryLogger *logger ) - : QgsDevToolWidgetFactory( QObject::tr( "Query Logger" ), QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/network_and_proxy.svg" ) ) ) + : QgsDevToolWidgetFactory( QObject::tr( "Query Logger" ), QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/database.svg" ) ) ) , mLogger( logger ) { } diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index 7731a2cb0bb..5140639d56f 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** qgsdbquerylog.h ------------ Date : October 2021 @@ -90,6 +90,7 @@ Q_DECLARE_METATYPE( QgsDatabaseQueryLogEntry ); #include "qgsconfig.h" constexpr int sQueryLoggerFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); #define QgsSetQueryLogClass(entry, _class) entry.initiatorClass = _class; entry.origin = QString(QString( __FILE__ ).mid( sQueryLoggerFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")"); +#define QGS_QUERY_LOG_ORIGIN QString(QString( __FILE__ ).mid( sQueryLoggerFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") #endif /** @@ -151,7 +152,7 @@ class CORE_EXPORT QgsDatabaseQueryLog: public QObject static void log( const QgsDatabaseQueryLogEntry &query ); /** - * Records that the database \a query as finished. + * Records that the database \a query has finished. * * This method can be safely called from any thread. */ @@ -195,4 +196,44 @@ class CORE_EXPORT QgsDatabaseQueryLog: public QObject }; +#ifndef SIP_RUN +///@cond private + +/** + * The QgsDatabaseQueryLogWrapper class is a RIIA wrapper for the query logger. + */ +class QgsDatabaseQueryLogWrapper +{ + + public: + + QgsDatabaseQueryLogWrapper( const QString &query, const QString &uri, const QString &provider, const QString &initiatorClass, const QString &origin ) + : mEntry( query ) + { + mEntry.uri = uri; + mEntry.origin = origin; + mEntry.initiatorClass = initiatorClass; + mEntry.provider = provider; + QgsDatabaseQueryLog::log( mEntry ); + } + + ~QgsDatabaseQueryLogWrapper( ) + { + QgsDatabaseQueryLog::finished( mEntry ); + } + + QgsDatabaseQueryLogEntry &entry() + { + return mEntry; + } + + private: + + QgsDatabaseQueryLogEntry mEntry; + +}; + +///@endcond +#endif + #endif // QGSDBQUERYLOG_H diff --git a/src/providers/postgres/qgspostgresconn.h b/src/providers/postgres/qgspostgresconn.h index 9cb53a4d1b4..57ce301c33f 100644 --- a/src/providers/postgres/qgspostgresconn.h +++ b/src/providers/postgres/qgspostgresconn.h @@ -204,6 +204,7 @@ class QgsPoolPostgresConn constexpr int sPostgresConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); #define LoggedPQexecNR(_class, query) PQexecNR( query, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) #define LoggedPQexec(_class, query) PQexec( query, true, true, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) +#define LoggedPQexecNoLogError(_class, query ) PQexec( query, false, true, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) class QgsPostgresConn : public QObject { diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index 0126ed9e1dd..53273e5d2c4 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -19,6 +19,7 @@ #include "qgspostgresprovider.h" #include "qgspostgrestransaction.h" #include "qgslogger.h" +#include "qgsdbquerylog.h" #include "qgsmessagelog.h" #include "qgssettings.h" #include "qgsexception.h" @@ -292,6 +293,9 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature ) QgsDebugMsgLevel( QStringLiteral( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 ); lock(); + + QgsDatabaseQueryLogWrapper logWrapper { fetch, mSource->mConnInfo, QStringLiteral( "postgres" ), QStringLiteral( "QgsPostgresFeatureIterator" ), QGS_QUERY_LOG_ORIGIN }; + if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously { QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 7bf53b820de..fdf28ab71d3 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -41,6 +41,7 @@ #include "qgssettings.h" #include "qgsstringutils.h" #include "qgsjsonutils.h" +#include "qgsdbquerylog.h" #include "qgspostgresprovider.h" #include "qgsprovidermetadata.h" @@ -925,7 +926,7 @@ bool QgsPostgresProvider::loadFields() // Get the table description sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=regclass(%1)::oid AND objsubid=0" ).arg( quotedValue( mQuery ) ); - QgsPostgresResult tresult( connectionRO()->PQexec( sql ) ); + QgsPostgresResult tresult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( tresult.PQntuples() > 0 ) { mDataComment = tresult.PQgetvalue( 0, 0 ); @@ -937,7 +938,7 @@ bool QgsPostgresProvider::loadFields() // field name, type, length, and precision (if numeric) sql = QStringLiteral( "SELECT * FROM %1 LIMIT 0" ).arg( mQuery ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); QMap > fmtFieldTypeMap, descrMap, defValMap, identityMap, generatedMap; QMap > attTypeIdMap; @@ -988,7 +989,7 @@ bool QgsPostgresProvider::loadFields() connectionRO()->pgVersion() >= 120000 ? QStringLiteral( ", attgenerated" ) : QString(), tableoidsFilter ); - QgsPostgresResult fmtFieldTypeResult( connectionRO()->PQexec( sql ) ); + QgsPostgresResult fmtFieldTypeResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i ) { Oid attrelid = fmtFieldTypeResult.PQgetvalue( i, 0 ).toUInt(); @@ -1036,7 +1037,7 @@ bool QgsPostgresProvider::loadFields() // Collect type info sql = QStringLiteral( "SELECT oid,typname,typtype,typelem,typlen FROM pg_type %1" ).arg( attroidsFilter ); - QgsPostgresResult typeResult( connectionRO()->PQexec( sql ) ); + QgsPostgresResult typeResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); QMap typeMap; for ( int i = 0; i < typeResult.PQntuples(); ++i ) @@ -1080,7 +1081,7 @@ bool QgsPostgresProvider::loadFields() { // get correct formatted field type for domain sql = QStringLiteral( "SELECT format_type(%1, %2)" ).arg( fldtyp ).arg( fldMod ); - QgsPostgresResult fmtFieldModResult( connectionRO()->PQexec( sql ) ); + QgsPostgresResult fmtFieldModResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( fmtFieldModResult.PQntuples() > 0 ) { formattedFieldType = fmtFieldModResult.PQgetvalue( 0, 0 ); @@ -1413,7 +1414,7 @@ void QgsPostgresProvider::setEditorWidgets() "AND field_name IN ( %4 )" ) . arg( EDITOR_WIDGET_STYLES_TABLE, quotedValue( mSchemaName ), quotedValue( mTableName ), quotedFnames.join( "," ) ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); for ( int i = 0; i < result.PQntuples(); ++i ) { if ( result.PQgetisnull( i, 2 ) ) continue; // config can be null and it's OK @@ -1459,7 +1460,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities() { // Check that we can read from the table (i.e., we have select permission). QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery ); - QgsPostgresResult testAccess( connectionRO()->PQexec( sql ) ); + QgsPostgresResult testAccess( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( tr( "Unable to access the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" ) @@ -1473,7 +1474,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities() if ( connectionRO()->pgVersion() >= 90000 ) { - testAccess = connectionRO()->PQexec( QStringLiteral( "SELECT pg_is_in_recovery()" ) ); + testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", QStringLiteral( "SELECT pg_is_in_recovery()" ) ); if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK || testAccess.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) { QgsMessageLog::logMessage( tr( "PostgreSQL is still in recovery after a database crash\n(or you are connected to a (read-only) standby server).\nWrite accesses will be denied." ), tr( "PostGIS" ) ); @@ -1517,7 +1518,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities() .arg( quotedValue( mQuery ) ); } - testAccess = connectionRO()->PQexec( sql ); + testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" ) @@ -1563,7 +1564,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities() .arg( quotedValue( mTableName ), quotedValue( mSchemaName ), connectionRO()->pgVersion() < 80100 ? "pg_get_userbyid(relowner)=current_user" : "pg_has_role(relowner,'MEMBER')" ); - testAccess = connectionRO()->PQexec( sql ); + testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( testAccess.PQresultStatus() == PGRES_TUPLES_OK && testAccess.PQntuples() == 1 ) { mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes; @@ -1599,7 +1600,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities() QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery ); - testAccess = connectionRO()->PQexec( sql ); + testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) @@ -1652,13 +1653,13 @@ bool QgsPostgresProvider::determinePrimaryKey() { sql = QStringLiteral( "SELECT count(*) FROM pg_inherits WHERE inhparent=%1::regclass" ).arg( quotedValue( mQuery ) ); QgsDebugMsgLevel( QStringLiteral( "Checking whether %1 is a parent table" ).arg( sql ), 2 ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); bool isParentTable( res.PQntuples() == 0 || res.PQgetvalue( 0, 0 ).toInt() > 0 ); sql = QStringLiteral( "SELECT indexrelid FROM pg_index WHERE indrelid=%1::regclass AND (indisprimary OR indisunique) ORDER BY CASE WHEN indisprimary THEN 1 ELSE 2 END LIMIT 1" ).arg( quotedValue( mQuery ) ); QgsDebugMsgLevel( QStringLiteral( "Retrieving first primary or unique index: %1" ).arg( sql ), 2 ); - res = connectionRO()->PQexec( sql ); + res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 ); QStringList log; @@ -1686,7 +1687,7 @@ bool QgsPostgresProvider::determinePrimaryKey() { // If there is an generated id on the table, use that instead, sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attidentity IN ('a','d') AND attrelid=regclass(%1) LIMIT 1" ).arg( quotedValue( mQuery ) ); - res = connectionRO()->PQexec( sql ); + res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( res.PQntuples() == 1 ) { // Could warn the user here that performance will suffer if @@ -1703,7 +1704,7 @@ bool QgsPostgresProvider::determinePrimaryKey() // If there is an oid on the table, use that instead, sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) ); - res = connectionRO()->PQexec( sql ); + res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( res.PQntuples() == 1 ) { // Could warn the user here that performance will suffer if @@ -1717,7 +1718,7 @@ bool QgsPostgresProvider::determinePrimaryKey() { sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) ); - res = connectionRO()->PQexec( sql ); + res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( res.PQntuples() == 1 ) { mPrimaryKeyType = PktTid; @@ -1750,7 +1751,7 @@ bool QgsPostgresProvider::determinePrimaryKey() sql = QStringLiteral( "SELECT attname,attnotnull FROM pg_index,pg_attribute WHERE indexrelid=%1 AND indrelid=attrelid AND pg_attribute.attnum=any(pg_index.indkey)" ).arg( indrelid ); QgsDebugMsgLevel( "Retrieving key columns: " + sql, 2 ); - res = connectionRO()->PQexec( sql ); + res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 ); bool mightBeNull = false; @@ -1934,7 +1935,7 @@ bool QgsPostgresProvider::uniqueData( const QString "edColNames ) mQuery, filterWhereClause() ); - QgsPostgresResult unique( connectionRO()->PQexec( sql ) ); + QgsPostgresResult unique( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( unique.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -1962,7 +1963,7 @@ QVariant QgsPostgresProvider::minimumValue( int index ) const sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); - QgsPostgresResult rmin( connectionRO()->PQexec( sql ) ); + QgsPostgresResult rmin( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); return convertValue( fld.type(), fld.subType(), rmin.PQgetvalue( 0, 0 ), fld.typeName() ); } catch ( PGFieldNotFound ) @@ -1998,7 +1999,7 @@ QSet QgsPostgresProvider::uniqueValues( int index, int limit ) const sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { for ( int i = 0; i < res.PQntuples(); i++ ) @@ -2040,7 +2041,7 @@ QStringList QgsPostgresProvider::uniqueStringsMatching( int index, const QString sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { for ( int i = 0; i < res.PQntuples(); i++ ) @@ -2230,7 +2231,7 @@ QVariant QgsPostgresProvider::maximumValue( int index ) const sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); - QgsPostgresResult rmax( connectionRO()->PQexec( sql ) ); + QgsPostgresResult rmax( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); return convertValue( fld.type(), fld.subType(), rmax.PQgetvalue( 0, 0 ), fld.typeName() ); } @@ -2336,7 +2337,7 @@ bool QgsPostgresProvider::getTopoLayerInfo() .arg( quotedValue( mSchemaName ), quotedValue( mTableName ), quotedValue( mGeometryColumn ) ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { throw PGException( result ); // we should probably not do this @@ -2808,7 +2809,7 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds &ids ) QgsDebugMsgLevel( "delete sql: " + sql, 2 ); //send DELETE statement and do error handling - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); @@ -2872,7 +2873,7 @@ bool QgsPostgresProvider::truncate() QgsDebugMsgLevel( "truncate sql: " + sql, 2 ); //send truncate statement and do error handling - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); @@ -2947,7 +2948,7 @@ bool QgsPostgresProvider::addAttributes( const QList &attributes ) } //send sql statement and do error handling - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); @@ -2959,7 +2960,7 @@ bool QgsPostgresProvider::addAttributes( const QList &attributes ) .arg( mQuery, quotedIdentifier( iter->name() ), quotedValue( iter->comment() ) ); - result = conn->PQexec( sql ); + result = conn->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); } @@ -3014,7 +3015,7 @@ bool QgsPostgresProvider::deleteAttributes( const QgsAttributeIds &ids ) quotedIdentifier( column ) ); //send sql statement and do error handling - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); @@ -3081,7 +3082,7 @@ bool QgsPostgresProvider::renameAttributes( const QgsFieldNameMap &renamedAttrib { conn->begin(); //send sql statement and do error handling - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); returnvalue = conn->commit(); @@ -3208,7 +3209,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap & // or if the user only changed GENERATED fields in the form/attribute table. if ( numChangedFields > 0 ) { - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); } @@ -3575,7 +3576,7 @@ bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_ma { sql += QStringLiteral( " WHERE %1" ).arg( whereClause( fid ) ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); } @@ -3694,7 +3695,7 @@ bool QgsPostgresProvider::setSubsetString( const QString &theSQL, bool updateFea sql += QLatin1String( " LIMIT 0" ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( res.PQresultStatus() != PGRES_TUPLES_OK ) { pushError( res.PQresultErrorMessage() ); @@ -3759,7 +3760,7 @@ long long QgsPostgresProvider::featureCount() const // parse explain output to estimate feature count // we don't use pg_class reltuples because it returns 0 for view sql = QStringLiteral( "EXPLAIN (FORMAT JSON) SELECT 1 FROM %1%2" ).arg( mQuery, filterWhereClause() ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); const QString json = result.PQgetvalue( 0, 0 ); const QVariantList explain = QgsJsonUtils::parseJson( json ).toList(); @@ -3774,14 +3775,14 @@ long long QgsPostgresProvider::featureCount() const else { sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); num = result.PQgetvalue( 0, 0 ).toLongLong(); } } else { sql = QStringLiteral( "SELECT count(*) FROM %1%2" ).arg( mQuery, filterWhereClause() ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); QgsDebugMsgLevel( "number of features as text: " + result.PQgetvalue( 0, 0 ), 2 ); @@ -3798,7 +3799,7 @@ long long QgsPostgresProvider::featureCount() const bool QgsPostgresProvider::empty() const { QString sql = QStringLiteral( "SELECT EXISTS (SELECT * FROM %1%2 LIMIT 1)" ).arg( mQuery, filterWhereClause() ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( res.PQresultStatus() != PGRES_TUPLES_OK ) { pushError( res.PQresultErrorMessage() ); @@ -3830,13 +3831,13 @@ QgsRectangle QgsPostgresProvider::extent() const .arg( quotedValue( mSchemaName ), quotedValue( mTableName ), quotedValue( mGeometryColumn ) ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 ) { if ( result.PQgetvalue( 0, 0 ).toInt() > 0 ) { sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 && result.PQgetvalue( 0, 0 ).toLong() > 0 ) @@ -3847,7 +3848,7 @@ QgsRectangle QgsPostgresProvider::extent() const quotedValue( mSchemaName ), quotedValue( mTableName ), quotedValue( mGeometryColumn ) ); - result = mConnectionRO->PQexec( sql ); + result = mConnectionRO->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) { ext = result.PQgetvalue( 0, 0 ); @@ -3884,7 +3885,7 @@ QgsRectangle QgsPostgresProvider::extent() const mQuery, filterWhereClause() ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "ROLLBACK" ) ); else if ( result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) @@ -3960,7 +3961,7 @@ bool QgsPostgresProvider::getGeometryDetails() } QgsDebugMsgLevel( QStringLiteral( "Getting the spatial column type: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( PGRES_TUPLES_OK == result.PQresultStatus() ) { geomColType = result.PQgetvalue( 0, 0 ); @@ -3996,17 +3997,17 @@ bool QgsPostgresProvider::getGeometryDetails() QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 ); - QgsPostgresResult result( connectionRO()->PQexec( sql ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( PGRES_TUPLES_OK == result.PQresultStatus() ) { Oid tableoid = result.PQftable( 0 ); int column = result.PQftablecol( 0 ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( tableoid > 0 && PGRES_TUPLES_OK == result.PQresultStatus() ) { sql = QStringLiteral( "SELECT pg_namespace.nspname,pg_class.relname FROM pg_class,pg_namespace WHERE pg_class.relnamespace=pg_namespace.oid AND pg_class.oid=%1" ).arg( tableoid ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() ) { @@ -4014,7 +4015,7 @@ bool QgsPostgresProvider::getGeometryDetails() tableName = result.PQgetvalue( 0, 1 ); sql = QStringLiteral( "SELECT a.attname, t.typname FROM pg_attribute a, pg_type t WHERE a.attrelid=%1 AND a.attnum=%2 AND a.atttypid = t.oid" ).arg( tableoid ).arg( column ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() ) { geomCol = result.PQgetvalue( 0, 0 ); @@ -4061,7 +4062,7 @@ bool QgsPostgresProvider::getGeometryDetails() quotedValue( schemaName ) ); QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Geometry column query returned %1 rows" ).arg( result.PQntuples() ), 2 ); if ( result.PQntuples() == 1 ) @@ -4098,7 +4099,7 @@ bool QgsPostgresProvider::getGeometryDetails() quotedValue( schemaName ) ); QgsDebugMsgLevel( QStringLiteral( "Getting geography column: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql, false ); + result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Geography column query returned %1" ).arg( result.PQntuples() ), 2 ); if ( result.PQntuples() == 1 ) @@ -4131,7 +4132,7 @@ bool QgsPostgresProvider::getGeometryDetails() quotedValue( schemaName ) ); QgsDebugMsgLevel( QStringLiteral( "Getting TopoGeometry column: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql, false ); + result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "TopoGeometry column query returned %1" ).arg( result.PQntuples() ), 2 ); if ( result.PQntuples() == 1 ) @@ -4155,7 +4156,7 @@ bool QgsPostgresProvider::getGeometryDetails() quotedValue( schemaName ) ); QgsDebugMsgLevel( QStringLiteral( "Getting pointcloud column: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql, false ); + result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Pointcloud column query returned %1" ).arg( result.PQntuples() ), 2 ); if ( result.PQntuples() == 1 ) @@ -4181,7 +4182,7 @@ bool QgsPostgresProvider::getGeometryDetails() quotedValue( geomCol ), quotedValue( schemaName ) ); QgsDebugMsgLevel( QStringLiteral( "Getting column datatype: %1" ).arg( sql ), 2 ); - result = connectionRO()->PQexec( sql, false ); + result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql ); QgsDebugMsgLevel( QStringLiteral( "Column datatype query returned %1" ).arg( result.PQntuples() ), 2 ); if ( result.PQntuples() == 1 ) { @@ -4204,12 +4205,12 @@ bool QgsPostgresProvider::getGeometryDetails() else { sql = QStringLiteral( "SELECT %1 FROM %2 LIMIT 0" ).arg( quotedIdentifier( mGeometryColumn ), mQuery ); - result = connectionRO()->PQexec( sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( PGRES_TUPLES_OK == result.PQresultStatus() ) { sql = QStringLiteral( "SELECT (SELECT t.typname FROM pg_type t WHERE oid = %1), upper(postgis_typmod_type(%2)), postgis_typmod_srid(%2)" ) .arg( QString::number( result.PQftype( 0 ) ), QString::number( result.PQfmod( 0 ) ) ); - result = connectionRO()->PQexec( sql, false ); + result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql ); if ( result.PQntuples() == 1 ) { geomColType = result.PQgetvalue( 0, 0 ); @@ -4593,7 +4594,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u if ( schemaName.isEmpty() ) { QString sql = QString( "SELECT current_schema" ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); schemaName = result.PQgetvalue( 0, 0 ); @@ -4610,7 +4611,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u .arg( quotedValue( tableName ), quotedValue( schemaName ) ); - QgsPostgresResult result( conn->PQexec( sql ) ); + QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); @@ -4626,7 +4627,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u .arg( quotedValue( schemaName ), quotedValue( tableName ) ); - result = conn->PQexec( sql ); + result = conn->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); } @@ -4658,7 +4659,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u } sql += QStringLiteral( ", PRIMARY KEY (%1) )" ) .arg( pk ); - result = conn->PQexec( sql ); + result = conn->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); @@ -4679,7 +4680,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u .arg( quotedValue( geometryType ) ) .arg( dim ); - result = conn->PQexec( sql ); + result = conn->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); } @@ -5130,7 +5131,7 @@ QList QgsPostgresProvider::discoverRelations( const QgsVectorLayer " fk.conname ;" ); - QgsPostgresResult sqlResult( connectionRO()->PQexec( sql ) ); + QgsPostgresResult sqlResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); if ( sqlResult.PQresultStatus() != PGRES_TUPLES_OK ) { QgsLogger::warning( "Error getting the foreign keys of " + mTableName ); @@ -5224,7 +5225,7 @@ QgsPostgresProvider::Relkind QgsPostgresProvider::relkind() const else { QString sql = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); - QgsPostgresResult res( connectionRO()->PQexec( sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); QString type = res.PQgetvalue( 0, 0 ); mKind = Relkind::Unknown; @@ -5481,7 +5482,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & .arg( wkbTypeString ) .arg( QgsPostgresConn::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); - QgsPostgresResult res( conn->PQexec( checkQuery ) ); + QgsPostgresResult res( conn->LoggedPQexec( "QgsPostgresProvider", checkQuery ) ); if ( res.PQntuples() > 0 ) { sql = QString( "UPDATE layer_styles" @@ -5529,7 +5530,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql ); } - res = conn->PQexec( sql ); + res = conn->LoggedPQexec( "QgsPostgresProvider", sql ); bool saved = res.PQresultStatus() == PGRES_COMMAND_OK; if ( !saved ) From 8043bbdb55f03b32417baef59c4ced3deee5b504 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 6 Apr 2022 18:10:22 +0200 Subject: [PATCH 06/24] Row count --- .../core/auto_generated/qgsdbquerylog.sip.in | 2 ++ .../querylogger/qgsappquerylogger.cpp | 2 +- .../querylogger/qgsqueryloggernode.cpp | 29 +++++++------------ .../devtools/querylogger/qgsqueryloggernode.h | 10 ------- src/core/qgsdbquerylog.h | 9 ++++-- .../postgres/qgspostgresfeatureiterator.cpp | 8 +++++ 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in index 384bf06579b..9dad8e9cdb8 100644 --- a/python/core/auto_generated/qgsdbquerylog.sip.in +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -42,6 +42,8 @@ Constructor for QgsDatabaseQueryLogEntry. QString origin; + long long fetchedRows; + }; diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index 7aac7f95f4e..faaf8f317d5 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -67,7 +67,7 @@ void QgsAppQueryLogger::queryFinished( const QgsDatabaseQueryLogEntry &query ) if ( !requestIndex.isValid() ) return; - beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() ); + beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() + ( query.fetchedRows != -1 ? 1 : 0 ) ); queryGroup->setFinished( query ); endInsertRows(); diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index fd4941597d4..8087a77756f 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -126,8 +126,6 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const case Qt::ForegroundRole: { - if ( mHasSslErrors ) - return QBrush( QColor( 180, 65, 210 ) ); switch ( mStatus ) { case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: @@ -188,23 +186,14 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const { QVariantMap res; res.insert( QStringLiteral( "SQL" ), mSql ); - res.insert( QStringLiteral( "Total time (ms)" ), mTotalTime ); -#if 0 - if ( mDetailsGroup ) + + for ( const auto &child : std::as_const( mChildren ) ) { - const QVariantMap detailsMap = mDetailsGroup->toVariant().toMap(); - for ( auto it = detailsMap.constBegin(); it != detailsMap.constEnd(); ++it ) - res.insert( it.key(), it.value() ); + if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) ) + { + res.insert( valueNode->key(), valueNode->value() ); + } } - if ( mReplyGroup ) - { - res.insert( QObject::tr( "Reply" ), mReplyGroup->toVariant() ); - } - if ( mSslErrorsGroup ) - { - res.insert( QObject::tr( "SSL Errors" ), mSslErrorsGroup->toVariant() ); - } -#endif return res; } @@ -238,7 +227,11 @@ void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEnt mReplyGroup = replyGroup.get(); addChild( std::move( replyGroup ) ); #endif - addKeyValueNode( QObject::tr( "Total time" ), QLocale().toString( ( query.finishedTime - query.startedTime ) / 1000.0 ).append( QStringLiteral( " s" ) ) ); + addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); + if ( query.fetchedRows != -1 ) + { + addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) ); + } } void QgsDatabaseQueryLoggerQueryGroup::setTimedOut() diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index 224ddbbd98a..aaf322212f7 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -122,18 +122,8 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup int mQueryId = 0; QElapsedTimer mTimer; qint64 mTotalTime = 0; - int mHttpStatus = -1; - QString mContentType; - int mReplies = 0; QByteArray mData; Status mStatus = Status::Pending; - bool mHasSslErrors = false; - QList< QPair< QString, QString > > mHeaders; -#if 0 - QgsNetworkLoggerRequestDetailsGroup *mDetailsGroup = nullptr; - QgsNetworkLoggerReplyGroup *mReplyGroup = nullptr; - QgsNetworkLoggerSslErrorGroup *mSslErrorsGroup = nullptr; -#endif }; #endif // QGSDBQUERYLOGGERNODE_H diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index 5140639d56f..34a258c957d 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -79,6 +79,11 @@ class CORE_EXPORT QgsDatabaseQueryLogEntry */ QString origin; + /** + * Number of fetched rows + */ + long long fetchedRows = -1; + private: static QAtomicInt sQueryId; @@ -222,9 +227,9 @@ class QgsDatabaseQueryLogWrapper QgsDatabaseQueryLog::finished( mEntry ); } - QgsDatabaseQueryLogEntry &entry() + void setFetchedRows( long long fetchedRows ) { - return mEntry; + mEntry.fetchedRows = fetchedRows; } private: diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index 53273e5d2c4..b45bea230f5 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -302,6 +302,7 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature ) } QgsPostgresResult queryResult; + long long fetchedRows { 0 }; for ( ;; ) { queryResult = mConn->PQgetResult(); @@ -317,6 +318,8 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature ) int rows = queryResult.PQntuples(); if ( rows == 0 ) continue; + else + fetchedRows += rows; mLastFetch = rows < mFeatureQueueSize; @@ -328,6 +331,11 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature ) } unlock(); + if ( fetchedRows > 0 ) + { + logWrapper.setFetchedRows( fetchedRows ); + } + #if 0 //disabled dynamic queue size if ( timer.elapsed() > 500 && mFeatureQueueSize > 1 ) { From 7b03204e6f6802557e25c86718d1dfbcfac47ed8 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 7 Apr 2022 17:41:09 +0200 Subject: [PATCH 07/24] Search filter on proxy model --- .../querylogger/qgsappquerylogger.cpp | 23 ++++++++++++++----- .../devtools/querylogger/qgsqueryloggernode.h | 18 +++------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index faaf8f317d5..e535924f63d 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -233,13 +233,24 @@ void QgsDatabaseQueryLoggerProxyModel::setFilterString( const QString &string ) bool QgsDatabaseQueryLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const { - QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); -#if 0 - if ( QgsDatabaseQueryLoggerRequestGroup *request = dynamic_cast< QgsDatabaseQueryLoggerRequestGroup * >( node ) ) + if ( ! mFilterString.isEmpty() ) { - return mFilterString.isEmpty() || request->url().url().contains( mFilterString, Qt::CaseInsensitive ); + QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); + if ( QgsDatabaseQueryLoggerQueryGroup *request = dynamic_cast< QgsDatabaseQueryLoggerQueryGroup * >( node ) ) + { + if ( request->data().toString().contains( mFilterString, Qt::CaseInsensitive ) ) + { + return true; + } + for ( int i = 0; i < request->childCount(); i++ ) + { + if ( QgsDevToolsModelValueNode *valueNode = static_cast( request->childAt( i ) ); valueNode->value().contains( mFilterString, Qt::CaseInsensitive ) ) + { + return true; + } + } + return false; + } } -#endif - return true; } diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index aaf322212f7..aad23167667 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -50,26 +50,14 @@ class QgsDatabaseQueryLoggerRootNode final : public QgsDevToolsModelGroup /** * \ingroup app * \class QgsDatabaseQueryLoggerQueryGroup - * \brief Parent group for all database queries, showing the query id, SQL, URI, + * \brief Parent group for all database queries, showing the query id, SQL * and containing child groups with detailed query and result information. * * Visually, a QgsDatabaseQueryLoggerQueryGroup is structured by: * * |__ QgsDatabaseQueryLoggerQueryGroup (showing sql, uri,...) - * |__ QgsNetworkLoggerRequestDetailsGroup (holding Request details) - * |__ QgsNetworkLoggerValueNode (key-value pairs with info) - * ... - * |__ QgsNetworkLoggerRequestQueryGroup (holding query info) - * |__ ... - * |__ QgsNetworkLoggerRequestHeadersGroup ('Headers') - * |__ ... - * |__ QgsNetworkLoggerPostContentGroup (showing Data in case of POST) - * |__ ... - * |__ QgsNetworkLoggerReplyGroup (holding Reply details) - * |__ QgsNetworkLoggerReplyHeadersGroup (Reply 'Headers') - * |__ ... - * |__ QgsNetworkLoggerSslErrorGroup (holding SSL error details, if encountered) - * |__ ... + * |__ QgsDevToolsModelValueNode(key-value pairs with info) + * ... */ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup { From 566c3cdb99f655dab8c0f862e365eb4ea86a48d2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 7 Apr 2022 18:14:34 +0200 Subject: [PATCH 08/24] Handle errors --- .../core/auto_generated/qgsdbquerylog.sip.in | 2 + .../querylogger/qgsappquerylogger.cpp | 1 + .../querylogger/qgsqueryloggernode.cpp | 43 ++++++------------- .../devtools/querylogger/qgsqueryloggernode.h | 16 +++---- src/core/qgsdbquerylog.h | 10 +++++ src/providers/postgres/qgspostgresconn.cpp | 29 +++++++------ .../postgres/qgspostgresfeatureiterator.cpp | 4 +- 7 files changed, 51 insertions(+), 54 deletions(-) diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in index 9dad8e9cdb8..b8501f675fe 100644 --- a/python/core/auto_generated/qgsdbquerylog.sip.in +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -44,6 +44,8 @@ Constructor for QgsDatabaseQueryLogEntry. long long fetchedRows; + QString error; + }; diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index e535924f63d..c34f01c1f4a 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -67,6 +67,7 @@ void QgsAppQueryLogger::queryFinished( const QgsDatabaseQueryLogEntry &query ) if ( !requestIndex.isValid() ) return; + // Calculate the number of children: if error or not fetched rows 1 row is added else 2 rows are added beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() + ( query.fetchedRows != -1 ? 1 : 0 ) ); queryGroup->setFinished( query ); endInsertRows(); diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index 8087a77756f..1efc877e84e 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -199,44 +199,25 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEntry &query ) { -#if 0 - switch ( reply.error() ) + if ( query.error.isEmpty() ) { - case QNetworkReply::OperationCanceledError: - mStatus = Status::Canceled; - break; - - case QNetworkReply::NoError: -#endif - mStatus = Status::Complete; -#if 0 - break; - - default: - mStatus = Status::Error; - break; + mStatus = Status::Complete; + addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); + if ( query.fetchedRows != -1 ) + { + addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) ); + } } - -#endif -#if 0 - mTotalTime = mTimer.elapsed(); - mHttpStatus = reply.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); - mContentType = reply.rawHeader( "Content - Type" ); - - std::unique_ptr< QgsNetworkLoggerReplyGroup > replyGroup = std::make_unique< QgsNetworkLoggerReplyGroup >( reply ) ; - mReplyGroup = replyGroup.get(); - addChild( std::move( replyGroup ) ); -#endif - addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); - if ( query.fetchedRows != -1 ) + else { - addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) ); + mStatus = Status::Error; + addKeyValueNode( QObject::tr( "Error" ), query.error ); } } -void QgsDatabaseQueryLoggerQueryGroup::setTimedOut() +void QgsDatabaseQueryLoggerQueryGroup::setStatus( QgsDatabaseQueryLoggerQueryGroup::Status status ) { - mStatus = Status::TimeOut; + mStatus = status; } QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLoggerQueryGroup::Status status ) diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index aad23167667..5dac95659f8 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -82,11 +82,6 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup QList< QAction * > actions( QObject *parent ) override final; QVariant toVariant() const override; - /** - * Returns the query's status. - */ - Status status() const { return mStatus; } - /** * Called when the \a query is finished. * @@ -95,9 +90,14 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup void setFinished( const QgsDatabaseQueryLogEntry &query ); /** - * Flags the query as having timed out. + * Returns the query's status. */ - void setTimedOut(); + Status status() const { return mStatus; } + + /** + * Set the query \a status + */ + void setStatus( Status status ); /** * Converts a request \a status to a translated string value. @@ -108,8 +108,6 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup QString mSql; int mQueryId = 0; - QElapsedTimer mTimer; - qint64 mTotalTime = 0; QByteArray mData; Status mStatus = Status::Pending; }; diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index 34a258c957d..e80e3f0846a 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -84,6 +84,11 @@ class CORE_EXPORT QgsDatabaseQueryLogEntry */ long long fetchedRows = -1; + /** + * Error reported by the provider, normally blank + */ + QString error; + private: static QAtomicInt sQueryId; @@ -232,6 +237,11 @@ class QgsDatabaseQueryLogWrapper mEntry.fetchedRows = fetchedRows; } + void setError( const QString &error ) + { + mEntry.error = error; + } + private: QgsDatabaseQueryLogEntry mEntry; diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index 73eee7ccb8c..66a856e6357 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -1333,14 +1333,9 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); - QgsDatabaseQueryLogEntry logEntry( query ); - logEntry.provider = QStringLiteral( "postgres" ); - logEntry.uri = mConnInfo; - logEntry.initiatorClass = originatorClass; - logEntry.origin = queryOrigin; - QgsDatabaseQueryLog::log( logEntry ); + QgsDatabaseQueryLogWrapper logWrapper( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin ); + PGresult *res = ::PQexec( mConn, query.toUtf8() ); - QgsDatabaseQueryLog::finished( logEntry ); // libpq may return a non null ptr with conn status not OK so we need to check for it to allow a retry below if ( res && PQstatus() == CONNECTION_OK ) @@ -1348,11 +1343,13 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret int errorStatus = PQresultStatus( res ); if ( errorStatus != PGRES_COMMAND_OK && errorStatus != PGRES_TUPLES_OK ) { + const QString error { tr( "Erroneous query: %1 returned %2 [%3]" ) + .arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) }; + logWrapper.setError( error ); + if ( logError ) { - QgsMessageLog::logMessage( tr( "Erroneous query: %1 returned %2 [%3]" ) - .arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ), - tr( "PostGIS" ) ); + QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); } else { @@ -1364,10 +1361,12 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret } if ( PQstatus() != CONNECTION_OK ) { + const QString error { tr( "Connection error: %1 returned %2 [%3]" ) + .arg( query ).arg( PQstatus() ).arg( PQerrorMessage() ) }; + logWrapper.setError( error ); if ( logError ) { - QgsMessageLog::logMessage( tr( "Connection error: %1 returned %2 [%3]" ) - .arg( query ).arg( PQstatus() ).arg( PQerrorMessage() ), + QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); } else @@ -1378,9 +1377,11 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret } else { + const QString error { tr( "Query failed: %1\nError: no result buffer" ).arg( query ) }; + logWrapper.setError( error ); if ( logError ) { - QgsMessageLog::logMessage( tr( "Query failed: %1\nError: no result buffer" ).arg( query ), tr( "PostGIS" ) ); + QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); } else { @@ -1400,11 +1401,13 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret { if ( res ) { + logWrapper.setError( QString( ) ); QgsMessageLog::logMessage( tr( "retry after reset succeeded." ), tr( "PostGIS" ) ); return res; } else { + QgsMessageLog::logMessage( tr( "retry after reset failed again." ), tr( "PostGIS" ) ); return nullptr; } diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index b45bea230f5..0ba177dc04b 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -298,7 +298,9 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature ) if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously { - QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); + const QString error { QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ) }; + QgsMessageLog::logMessage( error, QObject::tr( "PostGIS" ) ); + logWrapper.setError( error ); } QgsPostgresResult queryResult; From 3ab638389d501d2ea870c0a8f64495caf2292583 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 8 Apr 2022 17:59:31 +0200 Subject: [PATCH 09/24] More query logging --- src/providers/postgres/qgspostgresconn.cpp | 100 +++++++++--------- .../postgres/qgspostgresprovider.cpp | 77 +++++++------- 2 files changed, 90 insertions(+), 87 deletions(-) diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index 66a856e6357..092eeee549f 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -811,7 +811,9 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsDebugMsgLevel( "getting spatial table info from pg_catalog: " + sql, 2 ); - result = LoggedPQexec( "QgsPostresConn", sql ); + + + result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -923,7 +925,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP QgsDebugMsgLevel( "getting non-spatial table info: " + sql, 2 ); - result = LoggedPQexec( "QgsPostresConn", sql ); + result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { @@ -1019,7 +1021,7 @@ bool QgsPostgresConn::getSchemas( QList &schemas ) QString sql = QStringLiteral( "SELECT nspname, pg_get_userbyid(nspowner), pg_catalog.obj_description(oid) FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema' ORDER BY nspname" ); - result = LoggedPQexec( "QgsPostresConn", sql ); + result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "COMMIT" ) ); @@ -1085,7 +1087,7 @@ QString QgsPostgresConn::postgisVersion() const mPostgresqlVersion = PQserverVersion( mConn ); - QgsPostgresResult result( PQexec( QStringLiteral( "SELECT postgis_version()" ), false ) ); + QgsPostgresResult result( LoggedPQexecNoLogError( QStringLiteral( "QgsPostgresConn" ), QStringLiteral( "SELECT postgis_version()" ) ) ); if ( result.PQntuples() != 1 ) { QgsMessageLog::logMessage( tr( "No PostGIS support in the database." ), tr( "PostGIS" ) ); @@ -1122,7 +1124,7 @@ QString QgsPostgresConn::postgisVersion() const // apparently PostGIS 1.5.2 doesn't report capabilities in postgis_version() anymore if ( mPostgisVersionMajor > 1 || ( mPostgisVersionMajor == 1 && mPostgisVersionMinor >= 5 ) ) { - result = LoggedPQexec( "QgsPostresConn", QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) ); + result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) ); mGeosAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ); mProjAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 1 ); QgsDebugMsgLevel( QStringLiteral( "geos:%1 proj:%2" ) @@ -1158,7 +1160,7 @@ QString QgsPostgresConn::postgisVersion() const " AND t.relname = 'topology'" " AND l.relname = 'layer'" ); - QgsPostgresResult result( LoggedPQexec( "QgsPostresConn", query ) ); + QgsPostgresResult result( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), query ) ); if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) { mTopologyAvailable = true; @@ -1179,17 +1181,17 @@ QString QgsPostgresConn::postgisVersion() const if ( mPostgresqlVersion >= 90000 ) { QgsDebugMsgLevel( QStringLiteral( "Checking for pointcloud support" ), 2 ); - result = PQexec( QStringLiteral( - "SELECT has_table_privilege(c.oid, 'select')" - " AND has_table_privilege(f.oid, 'select')" - " FROM pg_class c, pg_class f, pg_namespace n, pg_extension e" - " WHERE c.relnamespace = n.oid" - " AND c.relname = 'pointcloud_columns'" - " AND f.relnamespace = n.oid" - " AND f.relname = 'pointcloud_formats'" - " AND n.oid = e.extnamespace" - " AND e.extname = 'pointcloud'" - ), false ); + result = LoggedPQexecNoLogError( QStringLiteral( "QgsPostresConn" ), QStringLiteral( + "SELECT has_table_privilege(c.oid, 'select')" + " AND has_table_privilege(f.oid, 'select')" + " FROM pg_class c, pg_class f, pg_namespace n, pg_extension e" + " WHERE c.relnamespace = n.oid" + " AND c.relname = 'pointcloud_columns'" + " AND f.relnamespace = n.oid" + " AND f.relname = 'pointcloud_formats'" + " AND n.oid = e.extnamespace" + " AND e.extname = 'pointcloud'" + ) ); if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) { mPointcloudAvailable = true; @@ -1200,14 +1202,14 @@ QString QgsPostgresConn::postgisVersion() const QgsDebugMsgLevel( QStringLiteral( "Checking for raster support" ), 2 ); if ( mPostgisVersionMajor >= 2 ) { - result = PQexec( QStringLiteral( - "SELECT has_table_privilege(c.oid, 'select')" - " FROM pg_class c, pg_namespace n, pg_type t" - " WHERE c.relnamespace = n.oid" - " AND n.oid = t.typnamespace" - " AND c.relname = 'raster_columns'" - " AND t.typname = 'raster'" - ), false ); + result = LoggedPQexecNoLogError( QStringLiteral( "QgsPostresConn" ), QStringLiteral( + "SELECT has_table_privilege(c.oid, 'select')" + " FROM pg_class c, pg_namespace n, pg_type t" + " WHERE c.relnamespace = n.oid" + " AND n.oid = t.typnamespace" + " AND c.relname = 'raster_columns'" + " AND t.typname = 'raster'" + ) ); if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) { mRasterAvailable = true; @@ -1333,7 +1335,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); - QgsDatabaseQueryLogWrapper logWrapper( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin ); + std::unique_ptr logWrapper = std::make_unique( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin ); PGresult *res = ::PQexec( mConn, query.toUtf8() ); @@ -1345,7 +1347,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret { const QString error { tr( "Erroneous query: %1 returned %2 [%3]" ) .arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) }; - logWrapper.setError( error ); + logWrapper->setError( error ); if ( logError ) { @@ -1363,7 +1365,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret { const QString error { tr( "Connection error: %1 returned %2 [%3]" ) .arg( query ).arg( PQstatus() ).arg( PQerrorMessage() ) }; - logWrapper.setError( error ); + logWrapper->setError( error ); if ( logError ) { QgsMessageLog::logMessage( error, @@ -1378,7 +1380,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret else { const QString error { tr( "Query failed: %1\nError: no result buffer" ).arg( query ) }; - logWrapper.setError( error ); + logWrapper->setError( error ); if ( logError ) { QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); @@ -1393,28 +1395,28 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret { QgsMessageLog::logMessage( tr( "resetting bad connection." ), tr( "PostGIS" ) ); ::PQreset( mConn ); - QgsDatabaseQueryLogEntry logEntry( query ); - QgsDatabaseQueryLog::log( logEntry ); + logWrapper.reset( new QgsDatabaseQueryLogWrapper( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin ) ); res = PQexec( query, logError, false ); - QgsDatabaseQueryLog::finished( logEntry ); if ( PQstatus() == CONNECTION_OK ) { if ( res ) { - logWrapper.setError( QString( ) ); QgsMessageLog::logMessage( tr( "retry after reset succeeded." ), tr( "PostGIS" ) ); return res; } else { - - QgsMessageLog::logMessage( tr( "retry after reset failed again." ), tr( "PostGIS" ) ); + const QString error { tr( "retry after reset failed again." ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); return nullptr; } } else { - QgsMessageLog::logMessage( tr( "connection still bad after reset." ), tr( "PostGIS" ) ); + const QString error { tr( "connection still bad after reset." ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "PostGIS" ) ); } } else @@ -1470,7 +1472,7 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName ) postStr = QStringLiteral( ";COMMIT" ); } - if ( !LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) + if ( !LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) return false; return true; @@ -1508,7 +1510,7 @@ bool QgsPostgresConn::PQexecNR( const QString &query, const QString &originatorC if ( PQstatus() == CONNECTION_OK ) { - LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK" ) ); + LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK" ) ); } return false; @@ -1588,11 +1590,11 @@ bool QgsPostgresConn::begin() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); } else { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "BEGIN" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "BEGIN" ) ); } } @@ -1601,11 +1603,11 @@ bool QgsPostgresConn::commit() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "COMMIT" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "COMMIT" ) ); } } @@ -1614,12 +1616,12 @@ bool QgsPostgresConn::rollback() QMutexLocker locker( &mLock ); if ( mTransaction ) { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) - && LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) + && LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); } else { - return LoggedPQexecNR( "QgsPostresConn", QStringLiteral( "ROLLBACK" ) ); + return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK" ) ); } } @@ -1905,7 +1907,7 @@ void QgsPostgresConn::deduceEndian() .arg( queryCounter ) .arg( errorCounter ), 2 ); - QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", QStringLiteral( "select regclass('pg_class')::oid" ) ) ); + QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "select regclass('pg_class')::oid" ) ) ); QString oidValue = res.PQgetvalue( 0, 0 ); QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 ); @@ -1915,7 +1917,7 @@ void QgsPostgresConn::deduceEndian() QgsDebugMsgLevel( QStringLiteral( "Fetching a record and attempting to get check endian-ness" ), 2 ); - res = LoggedPQexec( "QgsPostresConn", QStringLiteral( "fetch forward 1 from oidcursor" ) ); + res = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "fetch forward 1 from oidcursor" ) ); mSwapEndian = true; if ( res.PQntuples() > 0 ) @@ -2097,7 +2099,7 @@ void QgsPostgresConn::retrieveLayerTypes( QVector &l QgsDebugMsgLevel( "Layer types,srids and dims query: " + query, 3 ); - QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", query ) ); + QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), query ) ); if ( res.PQresultStatus() != PGRES_TUPLES_OK ) { // TODO: print some error here ? @@ -2691,7 +2693,7 @@ QString QgsPostgresConn::currentDatabase() const QMutexLocker locker( &mLock ); QString database; QString sql = "SELECT current_database()"; - QgsPostgresResult res( LoggedPQexec( "QgsPostresConn", sql ) ); + QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index fdf28ab71d3..f8cb811fe51 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -66,13 +66,13 @@ inline qint32 FID2PKINT( qint64 x ) static bool tableExists( QgsPostgresConn &conn, const QString &name ) { - QgsPostgresResult res( conn.PQexec( "SELECT EXISTS ( SELECT oid FROM pg_catalog.pg_class WHERE relname=" + QgsPostgresConn::quotedValue( name ) + ")" ) ); + QgsPostgresResult res( conn.LoggedPQexec( QStringLiteral( "tableExists" ), "SELECT EXISTS ( SELECT oid FROM pg_catalog.pg_class WHERE relname=" + QgsPostgresConn::quotedValue( name ) + ")" ) ); return res.PQgetvalue( 0, 0 ).startsWith( 't' ); } static bool columnExists( QgsPostgresConn &conn, const QString &table, const QString &column ) { - QgsPostgresResult res( conn.PQexec( "SELECT COUNT(*) FROM information_schema.columns WHERE table_name=" + QgsPostgresConn::quotedValue( table ) + " and column_name=" + QgsPostgresConn::quotedValue( column ) ) ); + QgsPostgresResult res( conn.LoggedPQexec( QStringLiteral( "columnExists" ), "SELECT COUNT(*) FROM information_schema.columns WHERE table_name=" + QgsPostgresConn::quotedValue( table ) + " and column_name=" + QgsPostgresConn::quotedValue( column ) ) ); return res.PQgetvalue( 0, 0 ).toInt() > 0; } @@ -435,7 +435,7 @@ QgsCoordinateReferenceSystem QgsPostgresProvider::sridToCrs( int srid, QgsPostgr { if ( conn ) { - QgsPostgresResult result( conn->PQexec( QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) ); + QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) ); if ( result.PQresultStatus() == PGRES_TUPLES_OK ) { if ( result.PQntuples() > 0 ) @@ -2041,7 +2041,7 @@ QStringList QgsPostgresProvider::uniqueStringsMatching( int index, const QString sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); - QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), sql ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { for ( int i = 0; i < res.PQntuples(); i++ ) @@ -2082,7 +2082,7 @@ void QgsPostgresProvider::enumValues( int index, QStringList &enumList ) const //is type an enum? const QString typeSql = QStringLiteral( "SELECT typtype FROM pg_type WHERE typname=%1" ).arg( quotedValue( typeName ) ); - QgsPostgresResult typeRes( connectionRO()->PQexec( typeSql ) ); + QgsPostgresResult typeRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), typeSql ) ); if ( typeRes.PQresultStatus() != PGRES_TUPLES_OK || typeRes.PQntuples() < 1 ) { mShared->setFieldSupportsEnumValues( index, false ); @@ -2115,7 +2115,7 @@ bool QgsPostgresProvider::parseEnumRange( QStringList &enumValues, const QString QString enumRangeSql = QStringLiteral( "SELECT enumlabel FROM pg_catalog.pg_enum WHERE enumtypid=(SELECT atttypid::regclass FROM pg_attribute WHERE attrelid=%1::regclass AND attname=%2)" ) .arg( quotedValue( mQuery ), quotedValue( attributeName ) ); - QgsPostgresResult enumRangeRes( connectionRO()->PQexec( enumRangeSql ) ); + QgsPostgresResult enumRangeRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), enumRangeSql ) ); if ( enumRangeRes.PQresultStatus() != PGRES_TUPLES_OK ) return false; @@ -2133,7 +2133,7 @@ bool QgsPostgresProvider::parseDomainCheckConstraint( QStringList &enumValues, c //is it a domain type with a check constraint? QString domainSql = QStringLiteral( "SELECT domain_name, domain_schema FROM information_schema.columns WHERE table_name=%1 AND column_name=%2" ).arg( quotedValue( mTableName ), quotedValue( attributeName ) ); - QgsPostgresResult domainResult( connectionRO()->PQexec( domainSql ) ); + QgsPostgresResult domainResult( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), domainSql ) ); if ( domainResult.PQresultStatus() == PGRES_TUPLES_OK && domainResult.PQntuples() > 0 && !domainResult.PQgetvalue( 0, 0 ).isNull() ) { QString domainCheckDefinitionSql; @@ -2169,7 +2169,7 @@ bool QgsPostgresProvider::parseDomainCheckConstraint( QStringList &enumValues, c } - QgsPostgresResult domainCheckRes( connectionRO()->PQexec( domainCheckDefinitionSql ) ); + QgsPostgresResult domainCheckRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), domainCheckDefinitionSql ) ); if ( domainCheckRes.PQresultStatus() == PGRES_TUPLES_OK && domainCheckRes.PQntuples() > 0 ) { QString checkDefinition = domainCheckRes.PQgetvalue( 0, 0 ); @@ -2279,7 +2279,7 @@ QVariant QgsPostgresProvider::defaultValue( int fieldId ) const { QgsField fld = field( fieldId ); - QgsPostgresResult res( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defVal ) ) ); + QgsPostgresResult res( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT %1" ).arg( defVal ) ) ); if ( res.result() ) { @@ -2316,7 +2316,7 @@ QString QgsPostgresProvider::paramValue( const QString &fieldValue, const QStrin if ( fieldValue == defaultValue && !defaultValue.isNull() ) { - QgsPostgresResult result( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defaultValue ) ) ); + QgsPostgresResult result( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT %1" ).arg( defaultValue ) ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) throw PGException( result ); @@ -3409,7 +3409,7 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m .arg( quotedIdentifier( mTopoLayerInfo.topologyName ) ) .arg( mTopoLayerInfo.layerId ) .arg( old_tg_id ); - result = conn->PQexec( replace ); + result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), replace ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" ) @@ -3424,7 +3424,7 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m .arg( mTopoLayerInfo.layerId ) .arg( new_tg_id ); QgsDebugMsgLevel( "relation swap: " + replace, 2 ); - result = conn->PQexec( replace ); + result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), replace ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" ) @@ -4844,7 +4844,7 @@ QString QgsPostgresProvider::description() const QgsPostgresResult result; /* TODO: expose a cached QgsPostgresConn::version() ? */ - result = lConnectionRO->PQexec( QStringLiteral( "SELECT version()" ) ); + result = lConnectionRO->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT version()" ) ); if ( result.PQresultStatus() == PGRES_TUPLES_OK ) { pgVersion = result.PQgetvalue( 0, 0 ); @@ -5357,7 +5357,7 @@ bool QgsPostgresProviderMetadata::styleExists( const QString &uri, const QString .arg( wkbTypeString ) .arg( QgsPostgresConn::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) ); - QgsPostgresResult res( conn->PQexec( checkQuery ) ); + QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), checkQuery ) ); if ( res.PQresultStatus() == PGRES_TUPLES_OK ) { return res.PQntuples() > 0; @@ -5390,22 +5390,23 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & if ( !tableExists( *conn, QStringLiteral( "layer_styles" ) ) ) { - QgsPostgresResult res( conn->PQexec( "CREATE TABLE layer_styles(" - "id SERIAL PRIMARY KEY" - ",f_table_catalog varchar" - ",f_table_schema varchar" - ",f_table_name varchar" - ",f_geometry_column varchar" - ",styleName text" - ",styleQML xml" - ",styleSLD xml" - ",useAsDefault boolean" - ",description text" - ",owner varchar(63) DEFAULT CURRENT_USER" - ",ui xml" - ",update_time timestamp DEFAULT CURRENT_TIMESTAMP" - ",type varchar" - ")" ) ); + QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), + "CREATE TABLE layer_styles(" + "id SERIAL PRIMARY KEY" + ",f_table_catalog varchar" + ",f_table_schema varchar" + ",f_table_name varchar" + ",f_geometry_column varchar" + ",styleName text" + ",styleQML xml" + ",styleSLD xml" + ",useAsDefault boolean" + ",description text" + ",owner varchar(63) DEFAULT CURRENT_USER" + ",ui xml" + ",update_time timestamp DEFAULT CURRENT_TIMESTAMP" + ",type varchar" + ")" ) ); if ( res.PQresultStatus() != PGRES_COMMAND_OK ) { errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() ); @@ -5417,7 +5418,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & { if ( !columnExists( *conn, QStringLiteral( "layer_styles" ), QStringLiteral( "type" ) ) ) { - QgsPostgresResult res( conn->PQexec( "ALTER TABLE layer_styles ADD COLUMN type varchar NULL" ) ); + QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), "ALTER TABLE layer_styles ADD COLUMN type varchar NULL" ) ); if ( res.PQresultStatus() != PGRES_COMMAND_OK ) { errCause = QObject::tr( "Unable to add column type to layer_styles table. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() ); @@ -5482,7 +5483,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & .arg( wkbTypeString ) .arg( QgsPostgresConn::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); - QgsPostgresResult res( conn->LoggedPQexec( "QgsPostgresProvider", checkQuery ) ); + QgsPostgresResult res( conn->LoggedPQexec( "QgsPostgresProviderMetadata", checkQuery ) ); if ( res.PQntuples() > 0 ) { sql = QString( "UPDATE layer_styles" @@ -5530,7 +5531,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString & sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql ); } - res = conn->LoggedPQexec( "QgsPostgresProvider", sql ); + res = conn->LoggedPQexec( "QgsPostgresProviderMetadata", sql ); bool saved = res.PQresultStatus() == PGRES_COMMAND_OK; if ( !saved ) @@ -5611,7 +5612,7 @@ QString QgsPostgresProviderMetadata::loadStyle( const QString &uri, QString &err .arg( wkbTypeString ); } - QgsPostgresResult result( conn->PQexec( selectQmlQuery ) ); + QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectQmlQuery ) ); QString style = result.PQntuples() == 1 ? result.PQgetvalue( 0, 0 ) : QString(); conn->unref(); @@ -5661,7 +5662,7 @@ int QgsPostgresProviderMetadata::listStyles( const QString &uri, QStringList &id QString( "f_geometry_column=%1" ).arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) ) ) .arg( wkbTypeString ); - QgsPostgresResult result( conn->PQexec( selectRelatedQuery ) ); + QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectRelatedQuery ) ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectRelatedQuery ) ); @@ -5688,7 +5689,7 @@ int QgsPostgresProviderMetadata::listStyles( const QString &uri, QStringList &id .arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) ) .arg( wkbTypeString ); - result = conn->PQexec( selectOthersQuery ); + result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectOthersQuery ); if ( result.PQresultStatus() != PGRES_TUPLES_OK ) { QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectOthersQuery ) ); @@ -5724,7 +5725,7 @@ bool QgsPostgresProviderMetadata::deleteStyleById( const QString &uri, const QSt { QString deleteStyleQuery = QStringLiteral( "DELETE FROM layer_styles WHERE id=%1" ).arg( QgsPostgresConn::quotedValue( styleId ) ); - QgsPostgresResult result( conn->PQexec( deleteStyleQuery ) ); + QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), deleteStyleQuery ) ); if ( result.PQresultStatus() != PGRES_COMMAND_OK ) { QgsDebugMsg( @@ -5756,7 +5757,7 @@ QString QgsPostgresProviderMetadata::getStyleById( const QString &uri, const QSt QString style; QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsPostgresConn::quotedValue( styleId ) ); - QgsPostgresResult result( conn->PQexec( selectQmlQuery ) ); + QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectQmlQuery ) ); if ( result.PQresultStatus() == PGRES_TUPLES_OK ) { if ( result.PQntuples() == 1 ) From 18f70a570674f3af2ec3132eedd180478e76fbbe Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 11 Apr 2022 14:01:15 +0200 Subject: [PATCH 10/24] Oracle sql logging --- src/providers/oracle/qgsoracleconn.cpp | 168 +++++++++++---- src/providers/oracle/qgsoracleconn.h | 11 + .../oracle/qgsoraclefeatureiterator.cpp | 20 +- src/providers/oracle/qgsoracleprovider.cpp | 198 +++++++++++------- src/providers/oracle/qgsoracletransaction.cpp | 6 + 5 files changed, 282 insertions(+), 121 deletions(-) diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index 3448d7d2f5d..9e5ff6256f0 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -71,7 +71,8 @@ QgsOracleConn::QgsOracleConn( QgsDataSourceUri uri, bool transaction ) { QgsDebugMsgLevel( QStringLiteral( "New Oracle connection for " ) + uri.connectionInfo( false ), 2 ); - uri = QgsDataSourceUri( uri.connectionInfo( true ) ); + mConnInfo = uri.connectionInfo( true ); + uri = QgsDataSourceUri( mConnInfo ); QString database = databaseName( uri.database(), uri.host(), uri.port() ); QgsDebugMsgLevel( QStringLiteral( "New Oracle database " ) + database, 2 ); @@ -257,18 +258,28 @@ QStringList QgsOracleConn::pkCandidates( const QString &ownerName, const QString QStringList cols; QSqlQuery qry( mDatabase ); + + QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner='%1' AND table_name='%2' ORDER BY column_id" ).arg( ownerName, viewName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner=? AND table_name=? ORDER BY column_id" ), QVariantList() << ownerName << viewName ) ) { - QgsMessageLog::logMessage( tr( "SQL: %1 [owner: %2 table_name: %3]\nerror: %4\n" ).arg( qry.lastQuery(), qry.lastError().text(), ownerName, viewName ), tr( "Oracle" ) ); + const QString error { tr( "SQL: %1 [owner: %2 table_name: %3]\nerror: %4\n" ).arg( qry.lastQuery(), qry.lastError().text(), ownerName, viewName ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return cols; } + long long fetchedRows { 0 }; + while ( qry.next() ) { cols << qry.value( 0 ).toString(); + fetchedRows++; } + logWrapper.setFetchedRows( fetchedRows ); + qry.finish(); return cols; @@ -308,12 +319,19 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly, } QSqlQuery qry( mDatabase ); + + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, sql, QVariantList() ) ) { - QgsMessageLog::logMessage( tr( "Querying available tables failed.\nSQL: %1\nerror: %2\n" ).arg( qry.lastQuery(), qry.lastError().text() ), tr( "Oracle" ) ); + const QString error { tr( "Querying available tables failed.\nSQL: %1\nerror: %2\n" ).arg( qry.lastQuery(), qry.lastError().text() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } + long long fetchedRows { 0 }; + while ( qry.next() ) { QgsOracleLayerProperty layerProperty; @@ -326,8 +344,12 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly, layerProperty.pkCols.clear(); mLayersSupported << layerProperty; + + fetchedRows++; } + logWrapper.setFetchedRows( fetchedRows ); + if ( mLayersSupported.size() == 0 ) { QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined." ), tr( "Oracle" ) ); @@ -417,19 +439,22 @@ bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMes QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); QSqlQuery qry( mDatabase ); + if ( !exec( qry, query, QVariantList() ) ) { QString error = qry.lastError().text(); if ( logError ) { - QgsMessageLog::logMessage( tr( "Connection error: %1 returned %2" ) - .arg( query, error ), + const QString errorMsg { tr( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsMessageLog::logMessage( errorMsg, tr( "Oracle" ) ); } else { - QgsDebugMsg( QStringLiteral( "Connection error: %1 returned %2" ) - .arg( query, error ) ); + const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsDebugMsg( errorMsg ); } if ( errorMessage ) *errorMessage = error; @@ -438,12 +463,29 @@ bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMes return true; } +bool QgsOracleConn::execLogged( const QString &sql, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) +{ + + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; + QString error; + const bool res { exec( sql, logError, &error ) }; + if ( ! res ) + { + logWrapper.setError( error ); + if ( errorMessage ) + { + *errorMessage = error; + } + } + return res; +} + bool QgsOracleConn::begin( QSqlDatabase &db ) { QMutexLocker locker( &mLock ); if ( mTransaction ) { - return exec( QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); + return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); } else { @@ -456,7 +498,7 @@ bool QgsOracleConn::commit( QSqlDatabase &db ) QMutexLocker locker( &mLock ); if ( mTransaction ) { - return exec( QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); + return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); } else { @@ -469,7 +511,7 @@ bool QgsOracleConn::rollback( QSqlDatabase &db ) QMutexLocker locker( &mLock ); if ( mTransaction ) { - return exec( QStringLiteral( "ROLLBACK TO SAVEPOINT sp%1" ).arg( mSavePointId ) ); + return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "ROLLBACK TO SAVEPOINT sp%1" ).arg( mSavePointId ) ); } else { @@ -581,14 +623,18 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b sql += QLatin1String( " FROM %2 t WHERE NOT t.%1 IS NULL%3" ); + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, sql .arg( quotedIdentifier( layerProperty.geometryColName ), table, where.isEmpty() ? QString() : QStringLiteral( " AND (%1)" ).arg( where ) ), QVariantList() ) ) { - QgsMessageLog::logMessage( tr( "SQL: %1\nerror: %2\n" ) - .arg( qry.lastQuery(), - qry.lastError().text() ), + const QString error { tr( "SQL: %1\nerror: %2\n" ) + .arg( qry.lastQuery(), + qry.lastError().text() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return; } @@ -597,8 +643,10 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b layerProperty.srids.clear(); QSet srids; + long long fetchedRows { 0 }; while ( qry.next() ) { + fetchedRows++; if ( detectedType == QgsWkbTypes::Unknown ) { QgsWkbTypes::Type type = wkbTypeFromDatabase( qry.value( 0 ).toInt() ); @@ -623,6 +671,8 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b srids << srid; } + logWrapper.setFetchedRows( fetchedRows ); + qry.finish(); if ( !onlyExistingTypes ) @@ -926,7 +976,9 @@ bool QgsOracleConn::hasSpatial() if ( mHasSpatial == -1 ) { QSqlQuery qry( mDatabase ); - mHasSpatial = exec( qry, QStringLiteral( "SELECT 1 FROM v$option WHERE parameter='Spatial' AND value='TRUE'" ), QVariantList() ) && qry.next(); + const QString sql { QStringLiteral( "SELECT 1 FROM v$option WHERE parameter='Spatial' AND value='TRUE'" ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + mHasSpatial = exec( qry, sql, QVariantList() ) && qry.next(); } return mHasSpatial; @@ -935,16 +987,19 @@ bool QgsOracleConn::hasSpatial() int QgsOracleConn::version() { QSqlQuery qry( mDatabase ); - QString sql = QStringLiteral( "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION" ); + const QString sql = QStringLiteral( "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION" ); + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; if ( exec( qry, sql, QVariantList() ) && qry.next() ) { return qry.value( 0 ).toString().split( '.' ).at( 0 ).toInt(); } else { - QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), tr( "Oracle" ) ); + const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return -1; } } @@ -956,7 +1011,9 @@ QString QgsOracleConn::currentUser() if ( mCurrentUser.isNull() ) { QSqlQuery qry( mDatabase ); - if ( exec( qry, QStringLiteral( "SELECT user FROM dual" ), QVariantList() ) && qry.next() ) + const QString sql { QStringLiteral( "SELECT user FROM dual" ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + if ( exec( qry, sql, QVariantList() ) && qry.next() ) { mCurrentUser = qry.value( 0 ).toString(); } @@ -993,10 +1050,15 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr QString name; QSqlQuery qry( mDatabase ); - if ( exec( qry, QString( "SELECT i.index_name,i.domidx_opstatus" - " FROM all_indexes i" - " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?" - " WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ), + + QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT i.index_name,i.domidx_opstatus" + " FROM all_indexes i" + " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=%1" + " WHERE i.table_owner=%2 AND i.table_name=%3 AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ).arg( geometryColumn, ownerName, tableName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + if ( exec( qry, QStringLiteral( "SELECT i.index_name,i.domidx_opstatus" + " FROM all_indexes i" + " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?" + " WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ), QVariantList() << geometryColumn << ownerName << tableName ) ) { if ( qry.next() ) @@ -1025,11 +1087,13 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr } else { - QgsMessageLog::logMessage( tr( "Probing for spatial index on column %1.%2.%3 failed [%4]" ) - .arg( ownerName ) - .arg( tableName ) - .arg( geometryColumn ) - .arg( qry.lastError().text() ), + const QString error { tr( "Probing for spatial index on column %1.%2.%3 failed [%4]" ) + .arg( ownerName ) + .arg( tableName ) + .arg( geometryColumn ) + .arg( qry.lastError().text() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); isValid = false; @@ -1043,21 +1107,27 @@ QString QgsOracleConn::createSpatialIndex( const QString &ownerName, const QStri QSqlQuery qry( mDatabase ); int n = 0; - if ( exec( qry, QString( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ), QVariantList() ) && + const QString sql { QStringLiteral( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ) }; + std::unique_ptr logWrapper = std::make_unique( sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN ); + if ( exec( qry, sql, QVariantList() ) && qry.next() ) { n = qry.value( 0 ).toInt() + 1; } - if ( !exec( qry, QString( "CREATE INDEX QGIS_IDX_%1 ON %2.%3(%4) INDEXTYPE IS MDSYS.SPATIAL_INDEX PARALLEL" ) - .arg( n, 10, 10, QChar( '0' ) ) - .arg( quotedIdentifier( ownerName ) ) - .arg( quotedIdentifier( tableName ) ) - .arg( quotedIdentifier( geometryColumn ) ), QVariantList() ) ) + const QString sql2 { QStringLiteral( "CREATE INDEX QGIS_IDX_%1 ON %2.%3(%4) INDEXTYPE IS MDSYS.SPATIAL_INDEX PARALLEL" ) + .arg( n, 10, 10, QChar( '0' ) ) + .arg( quotedIdentifier( ownerName ) ) + .arg( quotedIdentifier( tableName ) ) + .arg( quotedIdentifier( geometryColumn ) ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql2, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !exec( qry, sql2, QVariantList() ) ) { - QgsMessageLog::logMessage( tr( "Creation spatial index failed.\nSQL: %1\nError: %2" ) - .arg( qry.lastQuery() ) - .arg( qry.lastError().text() ), + const QString error { tr( "Creation spatial index failed.\nSQL: %1\nError: %2" ) + .arg( qry.lastQuery() ) + .arg( qry.lastError().text() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return QString(); } @@ -1071,23 +1141,33 @@ QStringList QgsOracleConn::getPrimaryKeys( const QString &ownerName, const QStri QStringList result; - if ( !exec( qry, QString( "SELECT column_name" - " FROM all_cons_columns a" - " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" - " WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ), + QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT column_name" + " FROM all_cons_columns a" + " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" + " WHERE b.constraint_type='P' AND b.owner=%1 AND b.table_name=%2" ).arg( ownerName, tableName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, QStringLiteral( "SELECT column_name" + " FROM all_cons_columns a" + " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" + " WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ), QVariantList() << ownerName << tableName ) ) { - QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), tr( "Oracle" ) ); + const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return result; } + long long fetchedRows { 0 }; while ( qry.next() ) { + fetchedRows++; QString name = qry.value( 0 ).toString(); result << name; } + logWrapper.setFetchedRows( fetchedRows ); return result; } diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h index 1db743d557d..3544a025db6 100644 --- a/src/providers/oracle/qgsoracleconn.h +++ b/src/providers/oracle/qgsoracleconn.h @@ -31,6 +31,7 @@ #include "qgslogger.h" #include "qgsdatasourceuri.h" #include "qgsvectordataprovider.h" +#include "qgsdbquerylog.h" #include #include @@ -112,6 +113,12 @@ struct QgsOracleLayerProperty #endif }; + +#include "qgsconfig.h" +constexpr int sOracleConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); +#define LoggedExec(_class, query) execLogged( query, true, nullptr, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) + + /** * Wraps acquireConnection() and releaseConnection() from a QgsOracleConnPool. * This can be used to ensure a connection is correctly released when scope ends @@ -157,6 +164,7 @@ class QgsOracleConn : public QObject static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ); bool exec( const QString &query, bool logError = true, QString *errorMessage = nullptr ); + bool execLogged( const QString &sql, bool logError = true, QString *errorMessage = nullptr, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); bool begin( QSqlDatabase &db ); bool commit( QSqlDatabase &db ); @@ -280,6 +288,9 @@ class QgsOracleConn : public QObject static QMap sConnections; static int snConnections; static QMap sBrokenConnections; + + // Connection URI string representation for query logger + QString mConnInfo; }; #endif diff --git a/src/providers/oracle/qgsoraclefeatureiterator.cpp b/src/providers/oracle/qgsoraclefeatureiterator.cpp index 69a169c8c3e..f7d75feb7ab 100644 --- a/src/providers/oracle/qgsoraclefeatureiterator.cpp +++ b/src/providers/oracle/qgsoraclefeatureiterator.cpp @@ -294,11 +294,14 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature &feature ) if ( mRewind ) { mRewind = false; + QgsDatabaseQueryLogWrapper logWrapper { mSql, mConnection->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN }; if ( !execQuery( mSql, mArgs, 1 ) ) { - QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) - .arg( mQry.lastQuery(), - mQry.lastError().text() ), + const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) + .arg( mQry.lastQuery(), + mQry.lastError().text() ) }; + logWrapper.setError( error ); + QgsMessageLog::logMessage( error, QObject::tr( "Oracle" ) ); return false; } @@ -531,13 +534,18 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar QgsDebugMsgLevel( QStringLiteral( "Fetch features: %1" ).arg( query ), 2 ); mSql = query; mArgs = args; + + QgsDatabaseQueryLogWrapper logWrapper { query, mConnection->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN }; if ( !execQuery( query, args, 1 ) ) { + + const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) + .arg( mQry.lastQuery(), + mQry.lastError().text() ) }; + logWrapper.setError( error ); if ( showLog ) { - QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) - .arg( mQry.lastQuery(), - mQry.lastError().text() ), + QgsMessageLog::logMessage( error, QObject::tr( "Oracle" ) ); } return false; diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index e2f7d546111..3fbe694d4a4 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -29,6 +29,7 @@ #include "qgscoordinatereferencesystem.h" #include "qgsvectorlayerexporter.h" #include "qgslogger.h" +#include "qgsdbquerylog.h" #include "qgsoracleprovider.h" #include "qgsoracletablemodel.h" @@ -561,16 +562,20 @@ bool QgsOracleProvider::loadFields() QMap defvalues; QMap alwaysGenerated; + std::unique_ptr logWrapper; + if ( !mIsQuery ) { QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 ); - if ( exec( qry, QString( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), + logWrapper.reset( new QgsDatabaseQueryLogWrapper{ QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=%1 AND table_name=%2" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); + + if ( exec( qry, QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName ) ) { if ( qry.next() ) mDataComment = qry.value( 0 ).toString(); - else if ( exec( qry, QString( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), + else if ( exec( qry, QStringLiteral( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), QVariantList() << mOwnerName << mTableName ) ) { if ( qry.next() ) @@ -579,28 +584,38 @@ bool QgsOracleProvider::loadFields() } else { - QgsMessageLog::logMessage( tr( "Loading comment for table %1.%2 failed [%3]" ) - .arg( mOwnerName ) - .arg( mTableName ) - .arg( qry.lastError().text() ), + const QString error { tr( "Loading comment for table %1.%2 failed [%3]" ) + .arg( mOwnerName ) + .arg( mTableName ) + .arg( qry.lastError().text() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } qry.finish(); + logWrapper.reset( new QgsDatabaseQueryLogWrapper{ QStringLiteral( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=%1 AND t.table_name=%2" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); + if ( exec( qry, QString( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ), QVariantList() << mOwnerName << mTableName ) ) { + long long fetchedRows { 0 }; while ( qry.next() ) { + fetchedRows++; if ( qry.value( 0 ).toString() == mGeometryColumn ) continue; comments.insert( qry.value( 0 ).toString(), qry.value( 1 ).toString() ); + } + logWrapper->setFetchedRows( fetchedRows ); } else { - QgsMessageLog::logMessage( tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ), tr( "Oracle" ) ); + const QString error { tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } qry.finish(); @@ -638,10 +653,15 @@ bool QgsOracleProvider::loadFields() sql += " ORDER BY t.column_id"; + logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); + + if ( exec( qry, sql, args ) ) { + long long fetchedRows { 0 }; while ( qry.next() ) { + fetchedRows++; QString name = qry.value( 0 ).toString(); QString type = qry.value( 1 ).toString(); int prec = qry.value( 2 ).toInt(); @@ -678,13 +698,16 @@ bool QgsOracleProvider::loadFields() defvalues.insert( name, defValue ); alwaysGenerated.insert( name, alwaysGen ); } + logWrapper->setFetchedRows( fetchedRows ); } else { - QgsMessageLog::logMessage( tr( "Loading field types for table %1.%2 failed [%3]" ) - .arg( mOwnerName ) - .arg( mTableName ) - .arg( qry.lastError().text() ), + const QString error { tr( "Loading field types for table %1.%2 failed [%3]" ) + .arg( mOwnerName ) + .arg( mTableName ) + .arg( qry.lastError().text() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } @@ -698,14 +721,21 @@ bool QgsOracleProvider::loadFields() { if ( !mHasSpatialIndex ) { - mHasSpatialIndex = qry.exec( QString( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" ) - .arg( mQuery ) - .arg( quotedIdentifier( mGeometryColumn ) ) - .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) ); + const QString sql{ QStringLiteral( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" ) + .arg( mQuery ) + .arg( quotedIdentifier( mGeometryColumn ) ) + .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); + mHasSpatialIndex = qry.exec( sql ); + if ( qry.lastError().isValid() ) + { + logWrapper->setError( qry.lastError().text() ); + } } if ( !mHasSpatialIndex ) { + const QString error { }; QgsMessageLog::logMessage( tr( "No spatial index on column %1.%2.%3 found - expect poor performance." ) .arg( mOwnerName ) .arg( mTableName ) @@ -716,13 +746,19 @@ bool QgsOracleProvider::loadFields() qry.finish(); - if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) ) + const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); + + if ( !exec( qry, sql, QVariantList() ) ) { - QgsMessageLog::logMessage( tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ), tr( "Oracle" ) ); + const QString error { tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } QSqlRecord record = qry.record(); + logWrapper->setFetchedRows( record.count() ); for ( int i = 0; i < record.count(); i++ ) { @@ -763,6 +799,9 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() mEnabledCapabilities |= QgsVectorDataProvider::CircularGeometries; QgsOracleConn *conn = connectionRO(); + + std::unique_ptr logWrapper; + QSqlQuery qry( *conn ); if ( !mIsQuery ) { @@ -778,58 +817,71 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() | QgsVectorDataProvider::RenameAttributes ; } - else if ( exec( qry, QString( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ), - QVariantList() << mOwnerName << mTableName ) ) - { - // check grants - while ( qry.next() ) - { - QString priv = qry.value( 0 ).toString(); - - if ( priv == "DELETE" ) - { - mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures; - } - else if ( priv == "UPDATE" ) - { - mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues; - } - else if ( priv == "INSERT" ) - { - mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures; - } - else if ( priv == "ALTER TABLE" ) - { - mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes; - } - } - - if ( !mGeometryColumn.isNull() ) - { - if ( exec( qry, QString( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ), - QVariantList() << mOwnerName << mTableName << mGeometryColumn ) ) - { - if ( qry.next() ) - mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries; - } - else - { - QgsMessageLog::logMessage( tr( "Unable to determine geometry column access privileges for column %1.%2.\nThe error message from the database was:\n%3.\nSQL: %4" ) - .arg( mQuery ) - .arg( mGeometryColumn ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), - tr( "Oracle" ) ); - } - } - } else { - QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the table %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) - .arg( mQuery ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), - tr( "Oracle" ) ); + logWrapper.reset( new QgsDatabaseQueryLogWrapper( QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=%1 AND table_name=%2 AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( exec( qry, QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ), + QVariantList() << mOwnerName << mTableName ) ) + { + // check grants + long long fetchedRows { 0 }; + while ( qry.next() ) + { + fetchedRows++; + QString priv = qry.value( 0 ).toString(); + + if ( priv == "DELETE" ) + { + mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures; + } + else if ( priv == "UPDATE" ) + { + mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues; + } + else if ( priv == "INSERT" ) + { + mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures; + } + else if ( priv == "ALTER TABLE" ) + { + mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes; + } + } + + logWrapper->setFetchedRows( fetchedRows ); + + if ( !mGeometryColumn.isNull() ) + { + logWrapper.reset( new QgsDatabaseQueryLogWrapper( QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=%1? AND table_name=%2 AND column_name=%3 AND privilege='UPDATE'" ).arg( mOwnerName, mTableName, mGeometryColumn ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( exec( qry, QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ), + QVariantList() << mOwnerName << mTableName << mGeometryColumn ) ) + { + if ( qry.next() ) + mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries; + } + else + { + const QString error { tr( "Unable to determine geometry column access privileges for column %1.%2.\nThe error message from the database was:\n%3.\nSQL: %4" ) + .arg( mQuery ) + .arg( mGeometryColumn ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, + tr( "Oracle" ) ); + } + } + } + else + { + const QString error { tr( "Unable to determine table access privileges for the table %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) + .arg( mQuery ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, + tr( "Oracle" ) ); + } } } else @@ -841,11 +893,15 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() return false; } - if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) ) + const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !exec( qry, sql, QVariantList() ) ) { - QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), tr( "Oracle" ) ); + const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } } diff --git a/src/providers/oracle/qgsoracletransaction.cpp b/src/providers/oracle/qgsoracletransaction.cpp index 0a6f666d762..c218c96b530 100644 --- a/src/providers/oracle/qgsoracletransaction.cpp +++ b/src/providers/oracle/qgsoracletransaction.cpp @@ -73,7 +73,13 @@ bool QgsOracleTransaction::executeSql( const QString &sql, QString &errorMsg, bo } QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); + + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnString, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; const bool res = mConn->exec( sql, true, &errorMsg ); + if ( ! errorMsg.isEmpty() ) + { + logWrapper.setError( errorMsg ); + } if ( !res ) { if ( isDirty ) From fd67e4f245e87a737c54c71a6635752d2057ea6f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 11 Apr 2022 19:22:01 +0200 Subject: [PATCH 11/24] Some more work on oracle and mssql --- src/core/qgsdbquerylog.h | 5 + .../mssql/qgsmssqlfeatureiterator.cpp | 48 +- src/providers/mssql/qgsmssqlfeatureiterator.h | 5 + src/providers/mssql/qgsmssqlprovider.cpp | 320 ++++++++++--- src/providers/oracle/qgsoracleconn.cpp | 87 +++- src/providers/oracle/qgsoracleconn.h | 2 + src/providers/oracle/qgsoracleprovider.cpp | 427 ++++++++++++------ src/providers/oracle/qgsoracleprovider.h | 2 + src/providers/oracle/qgsoracletablemodel.cpp | 1 + 9 files changed, 688 insertions(+), 209 deletions(-) diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index e80e3f0846a..755260a8a9e 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -237,6 +237,11 @@ class QgsDatabaseQueryLogWrapper mEntry.fetchedRows = fetchedRows; } + void setQuery( const QString &query ) + { + mEntry.query = query; + } + void setError( const QString &error ) { mEntry.error = error; diff --git a/src/providers/mssql/qgsmssqlfeatureiterator.cpp b/src/providers/mssql/qgsmssqlfeatureiterator.cpp index aef66f15a7b..626afb43785 100644 --- a/src/providers/mssql/qgsmssqlfeatureiterator.cpp +++ b/src/providers/mssql/qgsmssqlfeatureiterator.cpp @@ -20,6 +20,7 @@ #include "qgsmssqlprovider.h" #include "qgsmssqltransaction.h" #include "qgslogger.h" +#include "qgsdbquerylog.h" #include "qgssettings.h" #include "qgsexception.h" #include "qgsmssqldatabase.h" @@ -525,7 +526,7 @@ bool QgsMssqlFeatureIterator::fetchFeature( QgsFeature &feature ) case PktFidMap: { QVariantList primaryKeyVals; - for ( int idx : mSource->mPrimaryKeyAttrs ) + for ( int idx : std::as_const( mSource->mPrimaryKeyAttrs ) ) { QgsField fld = mSource->mFields.at( idx ); @@ -607,29 +608,50 @@ bool QgsMssqlFeatureIterator::rewind() mQuery->clear(); mQuery->setForwardOnly( true ); - bool result = mQuery->exec( mOrderByClause.isEmpty() ? mStatement : mStatement + mOrderByClause ); - if ( !result && !mFallbackStatement.isEmpty() ) + QString sql { mOrderByClause.isEmpty() ? mStatement : mStatement + mOrderByClause }; + std::unique_ptr logWrapper = std::make_unique( sql, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ); + + bool result = mQuery->exec( sql ); + if ( !result ) { - //try with fallback statement - result = mQuery->exec( mOrderByClause.isEmpty() ? mFallbackStatement : mFallbackStatement + mOrderByClause ); - if ( result ) + logWrapper->setError( mQuery->lastError().text() ); + if ( !mFallbackStatement.isEmpty() ) { - mExpressionCompiled = false; - mCompileStatus = NoCompilation; + //try with fallback statement + sql = mOrderByClause.isEmpty() ? mFallbackStatement : mFallbackStatement + mOrderByClause; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) ); + result = mQuery->exec( sql ); + if ( result ) + { + mExpressionCompiled = false; + mCompileStatus = NoCompilation; + } + else + { + logWrapper->setError( mQuery->lastError().text() ); + } } } if ( !result && !mOrderByClause.isEmpty() ) { //try without order by clause + logWrapper.reset( new QgsDatabaseQueryLogWrapper( mStatement, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) ); result = mQuery->exec( mStatement ); if ( result ) + { mOrderByCompiled = false; + } + else + { + logWrapper->setError( mQuery->lastError().text() ); + } } if ( !result && !mFallbackStatement.isEmpty() && !mOrderByClause.isEmpty() ) { //try with fallback statement and without order by clause + logWrapper.reset( new QgsDatabaseQueryLogWrapper( mFallbackStatement, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) ); result = mQuery->exec( mFallbackStatement ); if ( result ) { @@ -637,6 +659,10 @@ bool QgsMssqlFeatureIterator::rewind() mOrderByCompiled = false; mCompileStatus = NoCompilation; } + else + { + logWrapper->setError( mQuery->lastError().text() ); + } } if ( !result ) @@ -689,9 +715,15 @@ QgsMssqlFeatureSource::QgsMssqlFeatureSource( const QgsMssqlProvider *p ) , mDisableInvalidGeometryHandling( p->mDisableInvalidGeometryHandling ) , mCrs( p->crs() ) , mTransactionConn( p->transaction() ? static_cast( p->transaction() )->conn() : std::shared_ptr() ) + , mConnInfo( p->uri().uri( ) ) {} QgsFeatureIterator QgsMssqlFeatureSource::getFeatures( const QgsFeatureRequest &request ) { return QgsFeatureIterator( new QgsMssqlFeatureIterator( this, false, request ) ); } + +const QString &QgsMssqlFeatureSource::connInfo() const +{ + return mConnInfo; +} diff --git a/src/providers/mssql/qgsmssqlfeatureiterator.h b/src/providers/mssql/qgsmssqlfeatureiterator.h index f16cbfc4ec8..92d54152af4 100644 --- a/src/providers/mssql/qgsmssqlfeatureiterator.h +++ b/src/providers/mssql/qgsmssqlfeatureiterator.h @@ -38,6 +38,8 @@ class QgsMssqlFeatureSource final: public QgsAbstractFeatureSource QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override; + const QString &connInfo() const; + private: QgsFields mFields; QgsMssqlPrimaryKeyType mPrimaryKeyType; @@ -76,6 +78,9 @@ class QgsMssqlFeatureSource final: public QgsAbstractFeatureSource // Return True if this feature source has spatial attributes. bool isSpatial() { return !mGeometryColName.isEmpty() || !mGeometryColType.isEmpty(); } + // Uri information for query logger + QString mConnInfo; + friend class QgsMssqlFeatureIterator; friend class QgsMssqlExpressionCompiler; }; diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp index 5a3b88528f8..a1d57ef1f75 100644 --- a/src/providers/mssql/qgsmssqlprovider.cpp +++ b/src/providers/mssql/qgsmssqlprovider.cpp @@ -20,6 +20,7 @@ #include "qgsmssqldatabase.h" #include "qgsmssqlproviderconnection.h" #include "qgsfeedback.h" +#include "qgsdbquerylog.h" #include #include @@ -361,44 +362,64 @@ void QgsMssqlProvider::loadFields() QSqlQuery query = createQuery(); query.setForwardOnly( true ); + const QString sql { QStringLiteral( "SELECT name FROM sys.columns WHERE is_computed = 1 AND object_id = OBJECT_ID('[%1].[%2]')" ).arg( mSchemaName, mTableName ) }; + std::unique_ptr logWrapper = std::make_unique( sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); + // Get computed columns which need to be ignored on insert or update. - if ( !query.exec( QStringLiteral( "SELECT name FROM sys.columns WHERE is_computed = 1 AND object_id = OBJECT_ID('[%1].[%2]')" ).arg( mSchemaName, mTableName ) ) ) + if ( !query.exec( sql ) ) { + logWrapper->setError( query.lastError().text() ); pushError( query.lastError().text() ); return; } + long long fetchedRows { 0 }; while ( query.next() ) { + fetchedRows++; mComputedColumns.append( query.value( 0 ).toString() ); } + logWrapper->setFetchedRows( fetchedRows ); // Field has unique constraint QSet setColumnUnique; { - if ( !query.exec( QStringLiteral( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC" - " INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC ON TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME" - " WHERE TC.CONSTRAINT_SCHEMA = '%1' AND TC.TABLE_NAME = '%2' AND TC.CONSTRAINT_TYPE = 'unique'" ) - .arg( mSchemaName, mTableName ) ) ) + const QString sql2 { QStringLiteral( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC" + " INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC ON TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME" + " WHERE TC.CONSTRAINT_SCHEMA = '%1' AND TC.TABLE_NAME = '%2' AND TC.CONSTRAINT_TYPE = 'unique'" ) + .arg( mSchemaName, mTableName ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql2, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( sql2 ) ) { + logWrapper->setError( query.lastError().text() ); pushError( query.lastError().text() ); return; } + fetchedRows = 0; while ( query.next() ) + { + fetchedRows++; setColumnUnique.insert( query.value( QStringLiteral( "COLUMN_NAME" ) ).toString() ); + } + logWrapper->setFetchedRows( fetchedRows ); } - if ( !query.exec( QStringLiteral( "exec sp_columns @table_name = N%1, @table_owner = %2" ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) ) ) + const QString sql3 { QStringLiteral( "exec sp_columns @table_name = N%1, @table_owner = %2" ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql3, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( sql3 ) ) { pushError( query.lastError().text() ); + logWrapper->setError( query.lastError().text() ); return; } + fetchedRows = 0; int i = 0; QStringList pkCandidates; while ( query.next() ) { + fetchedRows++; const QString colName = query.value( QStringLiteral( "COLUMN_NAME" ) ).toString(); const QString sqlTypeName = query.value( QStringLiteral( "TYPE_NAME" ) ).toString(); bool columnIsIdentity = false; @@ -497,6 +518,7 @@ void QgsMssqlProvider::loadFields() ++i; } + logWrapper->setFetchedRows( fetchedRows ); } // get primary key @@ -504,8 +526,11 @@ void QgsMssqlProvider::loadFields() { query.clear(); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "exec sp_pkeys @table_name = N%1, @table_owner = %2 " ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) ) ) + const QString sql4 { QStringLiteral( "exec sp_pkeys @table_name = N%1, @table_owner = %2 " ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql4, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( sql4 ) ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -542,9 +567,12 @@ void QgsMssqlProvider::loadFields() { query.clear(); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "select count(distinct [%1]), count([%1]) from [%2].[%3]" ) - .arg( pk, mSchemaName, mTableName ) ) ) + const QString sql5 { QStringLiteral( "select count(distinct [%1]), count([%1]) from [%2].[%3]" ) + .arg( pk, mSchemaName, mTableName ) }; + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql5, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( sql5 ) ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -636,10 +664,13 @@ QVariant QgsMssqlProvider::defaultValue( int fieldId ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( sql ) ) { const QString errorMessage( tr( "Could not execute query: %1" ).arg( query.lastError().text() ) ); QgsDebugMsg( errorMessage ); + logWrapper.setError( errorMessage ); pushError( errorMessage ); return QVariant(); } @@ -704,8 +735,11 @@ QVariant QgsMssqlProvider::minimumValue( int index ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( sql ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -745,8 +779,11 @@ QVariant QgsMssqlProvider::maximumValue( int index ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( sql ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -794,16 +831,21 @@ QSet QgsMssqlProvider::uniqueValues( int index, int limit ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( sql ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } if ( query.isActive() ) { + long long fetchedRows { 0 }; // read all features while ( query.next() ) { + fetchedRows++; QVariant v = query.value( 0 ); if ( fld.type() == QVariant::Time ) v = convertTimeValue( v ); @@ -811,6 +853,7 @@ QSet QgsMssqlProvider::uniqueValues( int index, int limit ) const v = convertValue( fld.type(), v.toString() ); uniqueValues.insert( v ); } + logWrapper.setFetchedRows( fetchedRows ); } return uniqueValues; } @@ -848,8 +891,11 @@ QStringList QgsMssqlProvider::uniqueStringsMatching( int index, const QString &s QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( sql ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -862,6 +908,7 @@ QStringList QgsMssqlProvider::uniqueStringsMatching( int index, const QString &s if ( feedback && feedback->isCanceled() ) break; } + logWrapper.setFetchedRows( results.length() ); } return results; } @@ -896,6 +943,8 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const statement = QString( sql ).arg( mSchemaName, mTableName ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( query.exec( statement ) ) { if ( query.next() && ( !query.value( 0 ).isNull() || @@ -913,6 +962,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const } else { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -984,6 +1034,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const const QString statementSample = statement + ( mSqlWhereClause.isEmpty() ? " WHERE " : " AND " ) + sampleFilter; + QgsDatabaseQueryLogWrapper logWrapperNested { statementSample, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( query.exec( statementSample ) && query.next() && !query.value( 0 ).isNull() && query.value( 4 ).toInt() >= minSampleCount ) { @@ -997,6 +1048,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const if ( !query.exec( statement ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -1014,9 +1066,11 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const return; } + long long fetchedRows { 0 }; // We have to read all the geometry if readAllGeography is true. while ( query.next() ) { + fetchedRows++; QByteArray ar = query.value( 0 ).toByteArray(); std::unique_ptr geom = mParser.parseSqlGeometry( reinterpret_cast< unsigned char * >( ar.data() ), ar.size() ); if ( geom ) @@ -1036,6 +1090,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const mSRId = mParser.GetSRSId(); } } + logWrapper.setFetchedRows( fetchedRows ); } // Return the extent of the layer @@ -1074,12 +1129,14 @@ long long QgsMssqlProvider::featureCount() const " JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)" " WHERE SCHEMA_NAME(t.schema_id) = %1 AND OBJECT_NAME(t.OBJECT_ID) = %2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ); + QgsDatabaseQueryLogWrapper logWrapper{ statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( query.exec( statement ) && query.next() ) { return query.value( 0 ).toLongLong(); } else { + logWrapper.setError( query.lastError().text() ); // We couldn't get the rows from the sys tables. Can that ever happen? // Should just do a select count(*) here. return -1; @@ -1351,10 +1408,13 @@ bool QgsMssqlProvider::addFeatures( QgsFeatureList &flist, Flags flags ) } } + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( !query.exec() ) { const QString msg = query.lastError().text(); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); + logWrapper.setError( msg ); + logWrapper.setQuery( query.lastQuery() ); if ( !mSkipFailures ) { pushError( msg ); @@ -1432,8 +1492,10 @@ bool QgsMssqlProvider::addAttributes( const QList &attributes ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( !query.exec( statement ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } @@ -1464,14 +1526,17 @@ bool QgsMssqlProvider::deleteAttributes( const QgsAttributeIds &attributes ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); - + std::unique_ptr logWrapper = std::make_unique( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); if ( !query.exec( statement ) ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } query.finish(); + logWrapper.release(); + loadFields(); if ( mTransaction ) @@ -1540,9 +1605,12 @@ bool QgsMssqlProvider::changeAttributeValues( const QgsChangedAttributesMap &att // set attribute filter statement += QStringLiteral( " WHERE " ) + whereClauseFid( fid ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + // use prepared statement to prevent from sql injection if ( !query.prepare( statement ) ) { + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } @@ -1610,10 +1678,14 @@ bool QgsMssqlProvider::changeAttributeValues( const QgsChangedAttributesMap &att if ( !query.exec() ) { + logWrapper.setQuery( query.lastQuery() ); + logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } + logWrapper.setQuery( query.lastQuery() ); + if ( pkChanged && mPrimaryKeyType == PktFidMap ) { const QVariant v = mShared->removeFid( fid ); @@ -1677,8 +1749,11 @@ bool QgsMssqlProvider::changeGeometryValues( const QgsGeometryMap &geometry_map // set attribute filter statement += QStringLiteral( " WHERE " ) + whereClauseFid( fid ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.prepare( statement ) ) { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1700,9 +1775,12 @@ bool QgsMssqlProvider::changeGeometryValues( const QgsGeometryMap &geometry_map if ( !query.exec() ) { + logWrapper.setQuery( query.lastQuery() ); + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } + logWrapper.setQuery( query.lastQuery() ); } if ( mTransaction ) @@ -1732,6 +1810,7 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) query.setForwardOnly( true ); const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE [%3] IN (%4)" ).arg( mSchemaName, mTableName, mAttributeFields.at( mPrimaryKeyAttrs[0] ).name(), featureIds ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( query.exec( statement ) ) { if ( query.numRowsAffected() == ids.size() ) @@ -1744,7 +1823,10 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) pushError( tr( "Only %1 of %2 features deleted" ).arg( query.numRowsAffected() ).arg( ids.size() ) ); } else + { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); + } } else if ( mPrimaryKeyType == PktFidMap ) { @@ -1754,8 +1836,10 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) for ( QgsFeatureIds::const_iterator it = ids.begin(); it != ids.end(); ++it ) { const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE %3" ).arg( mSchemaName, mTableName, whereClauseFid( *it ) ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( query.exec( statement ) ) { + logWrapper.setFetchedRows( query.numRowsAffected() ); if ( query.numRowsAffected() == 1 ) { mShared->removeFid( *it ); @@ -1764,6 +1848,7 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) } else { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); break; } @@ -1830,8 +1915,11 @@ bool QgsMssqlProvider::createSpatialIndex() statement += QLatin1String( " USING GEOGRAPHY_GRID" ); } + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( statement ) ) { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1854,8 +1942,11 @@ bool QgsMssqlProvider::createAttributeIndex( int field ) statement = QStringLiteral( "CREATE NONCLUSTERED INDEX [qgs_%1_idx] ON [%2].[%3] ( [%4] )" ).arg( mGeometryColName, mSchemaName, mTableName, mAttributeFields.at( field ).name() ); + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !query.exec( statement ) ) { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1870,7 +1961,11 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const // try to load crs from the database tables as a fallback QSqlQuery query = createQuery(); query.setForwardOnly( true ); - bool execOk = query.exec( QStringLiteral( "SELECT srtext FROM spatial_ref_sys WHERE srid=%1" ).arg( mSRId ) ); + const QString statement { QStringLiteral( "SELECT srtext FROM spatial_ref_sys WHERE srid=%1" ).arg( mSRId ) }; + + std::unique_ptr logWrapper = std::make_unique( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); + + bool execOk = query.exec( statement ); if ( execOk && query.isActive() ) { if ( query.next() ) @@ -1882,10 +1977,15 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const query.finish(); } + else + { + logWrapper->setError( query.lastError().text() ); + } query.clear(); query.setForwardOnly( true ); // Look in the system reference table for the data if we can't find it yet + logWrapper.reset( new QgsDatabaseQueryLogWrapper( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); execOk = query.exec( QStringLiteral( "SELECT well_known_text FROM sys.spatial_reference_systems WHERE spatial_reference_id=%1" ).arg( mSRId ) ); if ( execOk && query.isActive() && query.next() ) { @@ -1893,6 +1993,10 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const if ( mCrs.isValid() ) return mCrs; } + else + { + logWrapper->setError( query.lastError().text() ); + } } return mCrs; } @@ -1949,8 +2053,10 @@ bool QgsMssqlProvider::setSubsetString( const QString &theSQL, bool ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( !query.exec( sql ) ) { + logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); mSqlWhereClause = prevWhere; return false; @@ -2202,8 +2308,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, "WHERE object_id = OBJECT_ID(N'[dbo].[spatial_ref_sys]') AND type in (N'U')) " "CREATE TABLE spatial_ref_sys (srid integer not null " "PRIMARY KEY, auth_name varchar(256), auth_srid integer, srtext varchar(2048), proj4text varchar(2048))" ); + + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); + if ( !q.exec( sql ) ) { + logWrapper->setError( q.lastError().text() ); if ( errorMessage ) *errorMessage = q.lastError().text(); return Qgis::VectorExportResult::ErrorCreatingLayer; @@ -2228,8 +2338,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, auth_srid, quotedValue( srs.toWkt() ), quotedValue( srs.toProj() ) ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !q.exec( sql ) ) { + logWrapper->setError( q.lastError().text() ); if ( errorMessage ) *errorMessage = q.lastError().text(); return Qgis::VectorExportResult::ErrorCreatingLayer; @@ -2246,8 +2360,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, // remove the old table with the same name sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) BEGIN DROP TABLE [%1].[%2] DELETE FROM geometry_columns where f_table_schema='%1' and f_table_name='%2' END;" ) .arg( schemaName, tableName ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !q.exec( sql ) ) { + logWrapper->setError( q.lastError().text() ); if ( errorMessage ) *errorMessage = q.lastError().text(); return Qgis::VectorExportResult::ErrorCreatingLayer; @@ -2258,8 +2376,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, // test for existing sql = QStringLiteral( "SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')" ) .arg( schemaName, tableName ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !q.exec( sql ) ) { + logWrapper->setError( q.lastError().text() ); if ( errorMessage ) *errorMessage = q.lastError().text(); return Qgis::VectorExportResult::ErrorCreatingLayer; @@ -2276,11 +2398,11 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, if ( !geometryColumn.isEmpty() ) { - sql = QString( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" - "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL, [%4] [geometry] NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" - "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" - "INSERT INTO [geometry_columns] ([f_table_catalog], [f_table_schema],[f_table_name], " - "[f_geometry_column],[coord_dimension],[srid],[geometry_type]) VALUES ('%5', '%1', '%2', '%4', %6, %7, '%8')" ) + sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" + "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL, [%4] [geometry] NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" + "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" + "INSERT INTO [geometry_columns] ([f_table_catalog], [f_table_schema],[f_table_name], " + "[f_geometry_column],[coord_dimension],[srid],[geometry_type]) VALUES ('%5', '%1', '%2', '%4', %6, %7, '%8')" ) .arg( schemaName, tableName, primaryKey, @@ -2293,17 +2415,20 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri, else { //geometryless table - sql = QString( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" - "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" - "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" - ) + sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" + "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" + "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" + ) .arg( schemaName, tableName, primaryKey ); } + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !q.exec( sql ) ) { + logWrapper->setError( q.lastError().text() ); if ( errorMessage ) *errorMessage = q.lastError().text(); return Qgis::VectorExportResult::ErrorCreatingLayer; @@ -2462,9 +2587,14 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s QSqlQuery query = QSqlQuery( db->db() ); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'layer_styles'" ) ) ) + const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'layer_styles'" ) }; + + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); + + if ( !query.exec( sql ) ) { errorCause = QObject::tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); + logWrapper->setError( errorCause ); return false; } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2489,9 +2619,12 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) ); + logWrapper.reset( new QgsDatabaseQueryLogWrapper( checkQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( checkQuery ) ) { errorCause = QObject::tr( "Checking for style failed: %1" ).arg( query.lastError().text() ); + logWrapper->setError( errorCause ); return false; } @@ -2527,7 +2660,11 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, QSqlQuery query = QSqlQuery( db->db() ); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) + + QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); + + if ( !query.exec( sql ) ) { QgsDebugMsg( query.lastError().text() ); return false; @@ -2535,24 +2672,30 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) { QgsDebugMsgLevel( QStringLiteral( "Need to create styles table" ), 2 ); - const bool execOk = query.exec( QString( "CREATE TABLE [dbo].[layer_styles](" - "[id] int IDENTITY(1,1) PRIMARY KEY," - "[f_table_catalog] [varchar](1024) NULL," - "[f_table_schema] [varchar](1024) NULL," - "[f_table_name] [varchar](1024) NULL," - "[f_geometry_column] [varchar](1024) NULL," - "[styleName] [varchar](1024) NULL," - "[styleQML] [text] NULL," - "[styleSLD] [text] NULL," - "[useAsDefault] [int] NULL," - "[description] [text] NULL," - "[owner] [varchar](1024) NULL," - "[ui] [text] NULL," - "[update_time] [datetime] NULL" - ") ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" ) ); + + sql = QStringLiteral( "CREATE TABLE [dbo].[layer_styles](" + "[id] int IDENTITY(1,1) PRIMARY KEY," + "[f_table_catalog] [varchar](1024) NULL," + "[f_table_schema] [varchar](1024) NULL," + "[f_table_name] [varchar](1024) NULL," + "[f_geometry_column] [varchar](1024) NULL," + "[styleName] [varchar](1024) NULL," + "[styleQML] [text] NULL," + "[styleSLD] [text] NULL," + "[useAsDefault] [int] NULL," + "[description] [text] NULL," + "[owner] [varchar](1024) NULL," + "[ui] [text] NULL," + "[update_time] [datetime] NULL" + ") ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + const bool execOk = query.exec( sql ); if ( !execOk ) { - errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" ); + const QString error { QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" ) }; + logWrapper->setError( error ); + errCause = error; return false; } query.finish(); @@ -2573,39 +2716,41 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, // replaced by the QString.arg function. To ensure that the final SQL string is not corrupt these // two values are both replaced in the final .arg call of the string construction. - QString sql = QString( "INSERT INTO layer_styles" - "(f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11" - ") VALUES (" - "%1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12" - ")" ) - .arg( QgsMssqlProvider::quotedValue( dsUri.database() ) ) - .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) - .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) - .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) - .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ) - .arg( QgsMssqlProvider::quotedValue( qmlStyle ) ) - .arg( QgsMssqlProvider::quotedValue( sldStyle ) ) - .arg( useAsDefault ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) - .arg( QgsMssqlProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) ) - .arg( QgsMssqlProvider::quotedValue( dsUri.username() ) ) - .arg( uiFileColumn ) - .arg( uiFileValue ); + sql = QStringLiteral( "INSERT INTO layer_styles" + "(f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11" + ") VALUES (" + "%1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12" + ")" ) + .arg( QgsMssqlProvider::quotedValue( dsUri.database() ) ) + .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) + .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) + .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) + .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ) + .arg( QgsMssqlProvider::quotedValue( qmlStyle ) ) + .arg( QgsMssqlProvider::quotedValue( sldStyle ) ) + .arg( useAsDefault ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) + .arg( QgsMssqlProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) ) + .arg( QgsMssqlProvider::quotedValue( dsUri.username() ) ) + .arg( uiFileColumn ) + .arg( uiFileValue ); - const QString checkQuery = QString( "SELECT styleName" - " FROM layer_styles" - " WHERE f_table_catalog=%1" - " AND f_table_schema=%2" - " AND f_table_name=%3" - " AND f_geometry_column=%4" - " AND styleName=%5" ) + const QString checkQuery = QStringLiteral( "SELECT styleName" + " FROM layer_styles" + " WHERE f_table_catalog=%1" + " AND f_table_schema=%2" + " AND f_table_name=%3" + " AND f_geometry_column=%4" + " AND styleName=%5" ) .arg( QgsMssqlProvider::quotedValue( dsUri.database() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); + logWrapper.reset( new QgsDatabaseQueryLogWrapper( checkQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); if ( !query.exec( checkQuery ) ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "Check Query failed" ) ); return false; @@ -2652,11 +2797,15 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, QgsDebugMsgLevel( QStringLiteral( "Inserting styles" ), 2 ); QgsDebugMsgLevel( sql, 2 ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + const bool execOk = query.exec( sql ); if ( !execOk ) { errCause = QObject::tr( "Unable to save layer style. It's not possible to insert a new record into the style table. Maybe this is due to table permissions. Please contact your database administrator." ); + logWrapper->setError( query.lastError().text() ); } return execOk; } @@ -2677,10 +2826,16 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau } QSqlQuery query = QSqlQuery( db->db() ); + query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) + + const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); + + if ( !query.exec( sql ) ) { errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); + logWrapper->setError( errCause ); return QString(); } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2706,10 +2861,13 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); + logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + if ( !query.exec( selectQmlQuery ) ) { QgsDebugMsgLevel( QStringLiteral( "Load of style failed" ), 2 ); const QString msg = query.lastError().text(); + logWrapper->setError( msg ); errCause = msg; QgsDebugMsg( msg ); return QString(); @@ -2742,10 +2900,14 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, QSqlQuery query = QSqlQuery( db->db() ); query.setForwardOnly( true ); + QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); + // check if layer_styles table already exist - if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) + if ( !query.exec( sql ) ) { const QString msg = query.lastError().text(); + logWrapper->setError( msg ); errCause = msg; QgsDebugMsg( msg ); return -1; @@ -2767,9 +2929,13 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectRelatedQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + bool queryOk = query.exec( selectRelatedQuery ); if ( !queryOk ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); return -1; } @@ -2791,9 +2957,13 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); QgsDebugMsgLevel( selectOthersQuery, 2 ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectOthersQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); + queryOk = query.exec( selectOthersQuery ); if ( !queryOk ) { + logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); return -1; } @@ -2828,9 +2998,12 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin QSqlQuery query = QSqlQuery( db->db() ); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) + const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; + std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); + if ( !query.exec( sql ) ) { errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); + logWrapper->setError( errCause ); return QString(); } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2846,11 +3019,14 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin QString style; const QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsMssqlProvider::quotedValue( styleId ) ); + + logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectQmlQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); const bool queryOk = query.exec( selectQmlQuery ); if ( !queryOk ) { QgsDebugMsg( query.lastError().text() ); errCause = query.lastError().text(); + logWrapper->setError( errCause ); return QString(); } if ( !query.next() ) @@ -3156,6 +3332,9 @@ bool QgsMssqlProvider::getExtentFromGeometryColumns( QgsRectangle &extent ) cons "AND NOT (qgis_xmin IS NULL OR qgis_xmax IS NULL OR qgis_ymin IS NULL OR qgis_ymax IS NULL)" ); const QString statement = sql.arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ); + + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( query.exec( statement ) && query.isActive() ) { query.next(); @@ -3168,6 +3347,10 @@ bool QgsMssqlProvider::getExtentFromGeometryColumns( QgsRectangle &extent ) cons return true; } + else + { + logWrapper.setError( query.lastError().text() ); + } } return false; @@ -3182,6 +3365,9 @@ bool QgsMssqlProvider::getPrimaryKeyFromGeometryColumns( QStringList &primaryKey const QString sql = QStringLiteral( "SELECT qgis_pkey FROM geometry_columns WHERE f_table_name = '%1' AND NOT qgis_pkey IS NULL" ); const QString statement = sql.arg( mTableName ); + + QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( query.exec( statement ) && query.isActive() ) { query.next(); @@ -3192,6 +3378,10 @@ bool QgsMssqlProvider::getPrimaryKeyFromGeometryColumns( QStringList &primaryKey return true; } } + else + { + logWrapper.setError( query.lastError().text() ); + } return false; } diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index 9e5ff6256f0..8273897a968 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -253,6 +253,48 @@ bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList return res; } +bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass, const QString &queryOrigin ) +{ + QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); + + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; + + bool res = qry.prepare( sql ); + if ( res ) + { + for ( const auto ¶m : params ) + { + QgsDebugMsgLevel( QStringLiteral( " ARG: %1 [%2]" ).arg( param.toString(), param.typeName() ), 4 ); + qry.addBindValue( param ); + } + + res = qry.exec(); + } + + logWrapper.setQuery( qry.lastQuery() ); + + if ( !res ) + { + logWrapper.setError( qry.lastError().text() ); + QgsDebugMsg( QStringLiteral( "SQL: %1\nERROR: %2" ) + .arg( qry.lastQuery(), + qry.lastError().text() ) ); + } + else + { + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); + } + } + + return res; +} + QStringList QgsOracleConn::pkCandidates( const QString &ownerName, const QString &viewName ) { QStringList cols; @@ -463,21 +505,54 @@ bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMes return true; } -bool QgsOracleConn::execLogged( const QString &sql, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) +bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) { - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; - QString error; - const bool res { exec( sql, logError, &error ) }; + QMutexLocker locker( &mLock ); + QgsDatabaseQueryLogWrapper logWrapper { query, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; + + QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); + + QSqlQuery qry( mDatabase ); + + const bool res { !exec( qry, query, QVariantList() ) }; + + logWrapper.setQuery( qry.lastQuery() ); + if ( ! res ) { + const QString error = qry.lastError().text(); logWrapper.setError( error ); - if ( errorMessage ) + if ( logError ) { + const QString errorMsg { tr( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsMessageLog::logMessage( errorMsg, + tr( "Oracle" ) ); + } + else + { + const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsDebugMsg( errorMsg ); + } + if ( errorMessage ) *errorMessage = error; + return false; + } + else + { + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); } } - return res; + + return true; } bool QgsOracleConn::begin( QSqlDatabase &db ) diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h index 3544a025db6..27ec38371c6 100644 --- a/src/providers/oracle/qgsoracleconn.h +++ b/src/providers/oracle/qgsoracleconn.h @@ -117,6 +117,7 @@ struct QgsOracleLayerProperty #include "qgsconfig.h" constexpr int sOracleConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); #define LoggedExec(_class, query) execLogged( query, true, nullptr, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) +#define LoggedExecPrivate(_class, query, sql, params ) execLogged( query, sql, params, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) /** @@ -266,6 +267,7 @@ class QgsOracleConn : public QObject ~QgsOracleConn() override; bool exec( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms ); + bool execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); //! reference count int mRef; diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 3fbe694d4a4..df3e3bd6c1e 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -50,6 +50,10 @@ #include "ocispatial/wkbptr.h" + +#define LoggedExecStatic(query, sql, args, uri, _class) execLoggedStatic( query, sql, args, uri, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) + + const QString ORACLE_KEY = "oracle"; const QString ORACLE_DESCRIPTION = "Oracle data provider"; @@ -307,6 +311,23 @@ bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &a return res; } +bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass, const QString &queryOrigin ) +{ + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; + const bool res { exec( qry, sql, args ) }; + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setError( qry.lastError().text() ); + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); + } + return res; +} + void QgsOracleProvider::setTransaction( QgsTransaction *transaction ) { // static_cast since layers cannot be added to a transaction of a non-matching provider @@ -562,21 +583,17 @@ bool QgsOracleProvider::loadFields() QMap defvalues; QMap alwaysGenerated; - std::unique_ptr logWrapper; - if ( !mIsQuery ) { QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper{ QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=%1 AND table_name=%2" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); - - if ( exec( qry, QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), - QVariantList() << mOwnerName << mTableName ) ) + if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), + QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { if ( qry.next() ) mDataComment = qry.value( 0 ).toString(); - else if ( exec( qry, QStringLiteral( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), - QVariantList() << mOwnerName << mTableName ) ) + else if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), + QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { if ( qry.next() ) mDataComment = qry.value( 0 ).toString(); @@ -588,33 +605,26 @@ bool QgsOracleProvider::loadFields() .arg( mOwnerName ) .arg( mTableName ) .arg( qry.lastError().text() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } qry.finish(); - logWrapper.reset( new QgsDatabaseQueryLogWrapper{ QStringLiteral( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=%1 AND t.table_name=%2" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); - - if ( exec( qry, QString( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ), - QVariantList() << mOwnerName << mTableName ) ) + if ( LoggedExecStatic( qry, QStringLiteral( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ), + QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { - long long fetchedRows { 0 }; while ( qry.next() ) { - fetchedRows++; if ( qry.value( 0 ).toString() == mGeometryColumn ) continue; comments.insert( qry.value( 0 ).toString(), qry.value( 1 ).toString() ); } - logWrapper->setFetchedRows( fetchedRows ); } else { const QString error { tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } @@ -653,10 +663,7 @@ bool QgsOracleProvider::loadFields() sql += " ORDER BY t.column_id"; - logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); - - - if ( exec( qry, sql, args ) ) + if ( LoggedExecStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { long long fetchedRows { 0 }; while ( qry.next() ) @@ -698,7 +705,7 @@ bool QgsOracleProvider::loadFields() defvalues.insert( name, defValue ); alwaysGenerated.insert( name, alwaysGen ); } - logWrapper->setFetchedRows( fetchedRows ); + } else { @@ -706,7 +713,7 @@ bool QgsOracleProvider::loadFields() .arg( mOwnerName ) .arg( mTableName ) .arg( qry.lastError().text() ) }; - logWrapper->setError( error ); + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } @@ -725,17 +732,12 @@ bool QgsOracleProvider::loadFields() .arg( mQuery ) .arg( quotedIdentifier( mGeometryColumn ) ) .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); - mHasSpatialIndex = qry.exec( sql ); - if ( qry.lastError().isValid() ) - { - logWrapper->setError( qry.lastError().text() ); - } + + mHasSpatialIndex = LoggedExecStatic( qry, sql, {}, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ); } if ( !mHasSpatialIndex ) { - const QString error { }; QgsMessageLog::logMessage( tr( "No spatial index on column %1.%2.%3 found - expect poor performance." ) .arg( mOwnerName ) .arg( mTableName ) @@ -747,18 +749,15 @@ bool QgsOracleProvider::loadFields() qry.finish(); const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper{ sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN } ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, {}, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { const QString error { tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } QSqlRecord record = qry.record(); - logWrapper->setFetchedRows( record.count() ); for ( int i = 0; i < record.count(); i++ ) { @@ -800,8 +799,6 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() QgsOracleConn *conn = connectionRO(); - std::unique_ptr logWrapper; - QSqlQuery qry( *conn ); if ( !mIsQuery ) { @@ -819,9 +816,8 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() } else { - logWrapper.reset( new QgsDatabaseQueryLogWrapper( QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=%1 AND table_name=%2 AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ).arg( mOwnerName, mTableName ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( exec( qry, QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ), - QVariantList() << mOwnerName << mTableName ) ) + if ( LoggedExecStatic( qry, QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ), + QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { // check grants long long fetchedRows { 0 }; @@ -848,13 +844,11 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() } } - logWrapper->setFetchedRows( fetchedRows ); - if ( !mGeometryColumn.isNull() ) { - logWrapper.reset( new QgsDatabaseQueryLogWrapper( QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=%1? AND table_name=%2 AND column_name=%3 AND privilege='UPDATE'" ).arg( mOwnerName, mTableName, mGeometryColumn ), mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( exec( qry, QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ), - QVariantList() << mOwnerName << mTableName << mGeometryColumn ) ) + + if ( LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ), + QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { if ( qry.next() ) mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries; @@ -866,7 +860,6 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() .arg( mGeometryColumn ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } @@ -878,7 +871,6 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() .arg( mQuery ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); } @@ -894,13 +886,11 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() } const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } @@ -953,21 +943,25 @@ bool QgsOracleProvider::determinePrimaryKey() { mPrimaryKeyType = ( mPrimaryKeyAttrs.size() == 1 && isInt ) ? PktInt : PktFidMap; } - else if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName ) ) - { - QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), tr( "Oracle" ) ); - } - else if ( qry.next() ) - { - // is table - QgsMessageLog::logMessage( tr( "No primary key found, using ROWID." ), tr( "Oracle" ) ); - mPrimaryKeyType = PktRowId; - } else { - determinePrimaryKeyFromUriKeyColumn(); + if ( !LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + { + const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ) }; + QgsMessageLog::logMessage( error, tr( "Oracle" ) ); + } + else if ( qry.next() ) + { + // is table + QgsMessageLog::logMessage( tr( "No primary key found, using ROWID." ), tr( "Oracle" ) ); + mPrimaryKeyType = PktRowId; + } + else + { + determinePrimaryKeyFromUriKeyColumn(); + } } } else @@ -1053,7 +1047,7 @@ bool QgsOracleProvider::uniqueData( QString query, QString colName ) QString sql = QString( "SELECT (SELECT count(distinct %1) FROM %2)-(SELECT count(%1) FROM %2) FROM dual" ) .arg( colName.startsWith( QLatin1String( "qgis_generated_uid_" ) ) ? colName : quotedIdentifier( colName ), mQuery ); - if ( !exec( qry, sql, QVariantList() ) || !qry.next() ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) || !qry.next() ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1084,7 +1078,7 @@ QVariant QgsOracleProvider::minimumValue( int index ) const QSqlQuery qry( *conn ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1134,7 +1128,7 @@ QSet QgsOracleProvider::uniqueValues( int index, int limit ) const QSqlQuery qry( *conn ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1176,7 +1170,7 @@ QVariant QgsOracleProvider::maximumValue( int index ) const QSqlQuery qry( *conn ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1257,7 +1251,10 @@ QVariant QgsOracleProvider::evaluateDefaultExpression( const QString &value, con QgsOracleConn *conn = connectionRO(); QSqlQuery qry( *conn ); - if ( !exec( qry, QString( "SELECT %1 FROM dual" ).arg( value ), QVariantList() ) || !qry.next() ) + + const QString sql { QStringLiteral( "SELECT %1 FROM dual" ).arg( value ) }; + + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) || !qry.next() ) { throw OracleException( tr( "Evaluation of default value failed" ), qry ); } @@ -1373,7 +1370,8 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag insert += values + ")"; QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( insert ), 4 ); - if ( !ins.prepare( insert ) ) + + if ( !ins.prepare( insert + "$$fsdfsd" ) ) { throw OracleException( tr( "Could not prepare insert statement" ), ins ); } @@ -1405,8 +1403,16 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag ins.addBindValue( value ); } + QgsDatabaseQueryLogWrapper logWrapper { insert, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !ins.exec() ) + { + logWrapper.setQuery( ins.lastQuery() ); + logWrapper.setError( ins.lastError().text() ); throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins ); + } + + logWrapper.setQuery( ins.lastQuery() ); if ( !( flags & QgsFeatureSink::FastInsert ) ) { @@ -1420,7 +1426,15 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag if ( ins.lastInsertId().isValid() ) { getfid.addBindValue( QVariant( ins.lastInsertId() ) ); - if ( !getfid.exec() || !getfid.next() ) + + QgsDatabaseQueryLogWrapper logWrapper { insert, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + const bool result { getfid.exec() }; + if ( ! result ) + { + logWrapper.setError( getfid.lastError().text() ); + } + if ( !result || !getfid.next() ) throw OracleException( tr( "Could not retrieve feature id %1" ).arg( features->id() ), getfid ); int col = 0; @@ -1518,12 +1532,14 @@ bool QgsOracleProvider::deleteFeatures( const QgsFeatureIds &id ) for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it ) { QVariantList args; - QString sql = QString( "DELETE FROM %1 WHERE %2" ) + QString sql = QStringLiteral( "DELETE FROM %1 WHERE %2" ) .arg( mQuery, whereClause( *it, args ) ); QgsDebugMsgLevel( "delete sql: " + sql, 2 ); - if ( !exec( qry, sql, args ) ) + if ( !LoggedExecStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + { throw OracleException( tr( "Deletion of feature %1 failed" ).arg( *it ), qry ); + } mShared->removeFid( *it ); } @@ -1593,15 +1609,20 @@ bool QgsOracleProvider::addAttributes( const QList &attributes ) .arg( mQuery, quotedIdentifier( iter->name() ), type ); QgsDebugMsgLevel( sql, 2 ); - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + { throw OracleException( tr( "Adding attribute %1 failed" ).arg( iter->name() ), qry ); + } if ( !iter->comment().isEmpty() ) { sql = QString( "COMMENT ON COLUMN %1.%2 IS ?" ) .arg( mQuery, quotedIdentifier( iter->name() ) ); - if ( !exec( qry, sql, QVariantList() << iter->comment() ) ) + + if ( !LoggedExecStatic( qry, sql, QVariantList() << iter->comment(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + { throw OracleException( tr( "Setting comment on %1 failed" ).arg( iter->name() ), qry ); + } } qry.finish(); @@ -1665,8 +1686,10 @@ bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids ) .arg( mQuery, quotedIdentifier( fld.name() ) ); //send sql statement and do error handling - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + { throw OracleException( tr( "Dropping column %1 failed" ).arg( fld.name() ), qry ); + } //delete the attribute from mAttributeFields mAttributeFields.remove( id ); @@ -1741,11 +1764,12 @@ bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttribut for ( renameIt = renamedAttributes.constBegin(); renameIt != renamedAttributes.constEnd(); ++renameIt ) { QString src( mAttributeFields.at( renameIt.key() ).name() ); + const QString sql { QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3" ) + .arg( mQuery, + quotedIdentifier( src ), + quotedIdentifier( renameIt.value() ) ) }; - if ( !exec( qry, QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3" ) - .arg( mQuery, - quotedIdentifier( src ), - quotedIdentifier( renameIt.value() ) ), QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) { throw OracleException( tr( "Renaming column %1 to %2 failed" ) .arg( quotedIdentifier( src ), @@ -1883,10 +1907,17 @@ bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &at for ( const auto &arg : std::as_const( args ) ) qry.addBindValue( arg ); + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !qry.exec() ) + { + logWrapper.setError( qry.lastError().text() ); + logWrapper.setQuery( qry.lastQuery() ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); + } qry.finish(); + logWrapper.setQuery( qry.lastQuery() ); // update feature id map if key was changed if ( pkChanged && mPrimaryKeyType == PktFidMap ) @@ -2360,8 +2391,15 @@ bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map appendGeomParam( iter.value(), qry ); appendPkParams( iter.key(), qry ); + QgsDatabaseQueryLogWrapper logWrapper { update, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !qry.exec() ) + { + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); + } + logWrapper.setQuery( qry.lastQuery() ); } qry.finish(); @@ -2415,8 +2453,12 @@ bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatu sql += "1=0"; QSqlQuery qry( *conn ); + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); pushError( qry.lastError().text() ); mSqlWhereClause = prevWhere; qry.finish(); @@ -2479,10 +2521,12 @@ long long QgsOracleProvider::featureCount() const } QSqlQuery qry( *conn ); - if ( exec( qry, sql, args ) && qry.next() ) + + if ( execLoggedStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) && qry.next() ) { mFeaturesCounted = qry.value( 0 ).toLongLong(); } + qry.finish(); QgsDebugMsgLevel( "number of features: " + QString::number( mFeaturesCounted ), 2 ); @@ -2507,7 +2551,12 @@ QgsRectangle QgsOracleProvider::extent() const if ( mUseEstimatedMetadata ) { // TODO: make SDO_DIMNAME values configurable (#16252) - if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ), + + const QString sql { QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( exec( qry, sql, QVariantList() << mOwnerName << mTableName << mGeometryColumn ) && qry.next() ) { @@ -2523,13 +2572,26 @@ QgsRectangle QgsOracleProvider::extent() const return mLayerExtent; } } + else + { + logWrapper.setError( qry.lastError().text() ); + } + + logWrapper.setQuery( qry.lastQuery() ); } if ( mHasSpatialIndex && mUseEstimatedMetadata ) { + const QString sql { QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; ok = exec( qry, - QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ), + sql, QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn ); + logWrapper.setQuery( qry.lastQuery() ); + if ( ! ok ) + { + logWrapper.setError( qry.lastError().text() ); + } } } @@ -2540,7 +2602,13 @@ QgsRectangle QgsOracleProvider::extent() const if ( !mSqlWhereClause.isEmpty() ) sql += QString( " WHERE %1" ).arg( mSqlWhereClause ); + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + ok = exec( qry, sql, QVariantList() ); + if ( ! ok ) + { + logWrapper.setError( qry.lastError().text() ); + } } if ( ok && qry.next() ) @@ -2589,8 +2657,13 @@ bool QgsOracleProvider::getGeometryDetails() QSqlQuery qry( *conn ); if ( mIsQuery ) { - if ( !exec( qry, QString( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ), QVariantList() ) ) + const QString sql { QStringLiteral( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not execute query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ), tr( "Oracle" ) ); @@ -2614,32 +2687,47 @@ bool QgsOracleProvider::getGeometryDetails() if ( !ownerName.isEmpty() ) { - if ( exec( qry, QString( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ), - QVariantList() << ownerName << tableName << geomCol ) ) + { - if ( qry.next() ) + const QString sql {QStringLiteral( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( exec( qry, sql, + QVariantList() << ownerName << tableName << geomCol ) ) { - detectedSrid = qry.value( 0 ).toInt(); + if ( qry.next() ) + { + detectedSrid = qry.value( 0 ).toInt(); + } + else + { + QgsMessageLog::logMessage( tr( "Could not retrieve SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) + .arg( mQuery ) + .arg( qry.lastError().text() ) + .arg( qry.lastQuery() ), tr( "Oracle" ) ); + } } else { - QgsMessageLog::logMessage( tr( "Could not retrieve SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) + logWrapper.setError( qry.lastError().text() ); + QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) .arg( mQuery ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ), tr( "Oracle" ) ); } - } - else - { - QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) - .arg( mQuery ) - .arg( qry.lastError().text() ) - .arg( qry.lastQuery() ), tr( "Oracle" ) ); + + logWrapper.setQuery( qry.lastQuery() ); } - if ( exec( qry, QString( mUseEstimatedMetadata - ? "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<100) WHERE rownum<=2" - : "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ).arg( quotedIdentifier( geomCol ) ).arg( mQuery ), QVariantList() ) ) + QString sql { mUseEstimatedMetadata ? + QStringLiteral( "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<100) WHERE rownum<=2" ) : + QStringLiteral( "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ) }; + + sql = sql.arg( quotedIdentifier( geomCol ), mQuery ); + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( exec( qry, sql, QVariantList() ) ) { if ( qry.next() ) { @@ -2659,6 +2747,7 @@ bool QgsOracleProvider::getGeometryDetails() } else { + logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not determine geometry type of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) .arg( mQuery ) .arg( qry.lastError().text() ) @@ -2761,32 +2850,51 @@ bool QgsOracleProvider::createSpatialIndex() // TODO: make precision configurable // TODO: make SDO_DIMNAME values configurable (#16252) QgsRectangle r( extent() ); - if ( !exec( qry, QString( "UPDATE mdsys.user_sdo_geom_metadata SET diminfo=mdsys.sdo_dim_array(" - "mdsys.sdo_dim_element('X', ?, ?, 0.001)," - "mdsys.sdo_dim_element('Y', ?, ?, 0.001)" - ") WHERE table_name=? AND column_name=?" ), - QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn ) - ) + + const QString sql { QStringLiteral( "UPDATE mdsys.user_sdo_geom_metadata SET diminfo=mdsys.sdo_dim_array(" + "mdsys.sdo_dim_element('X', ?, ?, 0.001)," + "mdsys.sdo_dim_element('Y', ?, ?, 0.001)" + ") WHERE table_name=? AND column_name=?" ) }; + { - QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" ) - .arg( mTableName ) - .arg( mGeometryColumn ) - .arg( qry.lastQuery() ) - .arg( qry.lastError().text() ), - tr( "Oracle" ) ); - return false; + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, + QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn ) + ) + { + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setError( qry.lastError().text() ); + QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" ) + .arg( mTableName ) + .arg( mGeometryColumn ) + .arg( qry.lastQuery() ) + .arg( qry.lastError().text() ), + tr( "Oracle" ) ); + return false; + } + + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setFetchedRows( qry.numRowsAffected() ); } if ( qry.numRowsAffected() == 0 ) { - if ( !exec( qry, QString( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,mdsys.sdo_dim_array(" - "mdsys.sdo_dim_element('X', ?, ?, 0.001)," - "mdsys.sdo_dim_element('Y', ?, ?, 0.001)" - "))" ), + + const QString sql { QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,mdsys.sdo_dim_array(" + "mdsys.sdo_dim_element('X', ?, ?, 0.001)," + "mdsys.sdo_dim_element('Y', ?, ?, 0.001)" + "))" ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid ) << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() ) ) { + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" ) .arg( quotedValue( mTableName ) ) .arg( quotedValue( mGeometryColumn ) ) @@ -2795,6 +2903,7 @@ bool QgsOracleProvider::createSpatialIndex() tr( "Oracle" ) ); return false; } + logWrapper.setQuery( qry.lastQuery() ); } } else @@ -2812,8 +2921,14 @@ bool QgsOracleProvider::createSpatialIndex() } else { - if ( !exec( qry, QString( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ), QVariantList() ) ) + + const QString sql { QStringLiteral( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Rebuild of spatial index failed.\nSQL: %1\nError: %2" ) .arg( qry.lastQuery() ) .arg( qry.lastError().text() ), @@ -2984,11 +3099,21 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( throw OracleException( tr( "Could not start transaction" ), db ); } - if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), - QVariantList() << ownerName << tableName - ) ) { - throw OracleException( tr( "Could not determine table existence." ), qry ); + const QString sql { QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, + QVariantList() << ownerName << tableName + ) ) + { + logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setError( qry.lastError().text() ); + throw OracleException( tr( "Could not determine table existence." ), qry ); + } + + logWrapper.setQuery( qry.lastQuery() ); } bool exists = qry.next(); @@ -2998,8 +3123,14 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( if ( overwrite ) { // delete the table if exists, then re-create it - if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) ) + + const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Table %1 could not be dropped." ).arg( ownerTableName ), qry ); } } @@ -3009,7 +3140,7 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( } } - QString sql = QString( "CREATE TABLE %1(" ).arg( ownerTableName ); + QString sql = QStringLiteral( "CREATE TABLE %1(" ).arg( ownerTableName ); QString delim; if ( hasPrimaryKey ) @@ -3027,8 +3158,11 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( sql += ")"; + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Table creation failed." ), qry ); } @@ -3055,8 +3189,14 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( if ( created ) { - if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) ) + + const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() ) ) { + logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Drop created table %1 failed.\nSQL: %2\nError: %3" ) .arg( qry.lastQuery() ) .arg( qry.lastError().text() ), tr( "Oracle" ) ); @@ -3227,9 +3367,20 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & QgsDebugMsgLevel( QStringLiteral( "%1:%2 not found in mdsys.cs_srs - trying WKT" ).arg( parts[0] ).arg( parts[1] ), 2 ); QString wkt = srs.toWkt(); - if ( !exec( qry, QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ), QVariantList() << wkt ) ) + + const QString sql { QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ) }; + { - throw OracleException( tr( "Could not lookup WKT." ), qry ); + QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() << wkt ) ) + { + logWrapper.setError( qry.lastError().text() ); + logWrapper.setQuery( qry.lastQuery() ); + throw OracleException( tr( "Could not lookup WKT." ), qry ); + } + + logWrapper.setQuery( qry.lastQuery() ); } if ( qry.next() ) @@ -3238,20 +3389,34 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & } else { - if ( !exec( qry, QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ), QVariantList() ) || !qry.next() ) + { - throw OracleException( tr( "Could not determine new srid." ), qry ); + const QString sql { QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() ) || !qry.next() ) + { + logWrapper.setError( qry.lastError().text() ); + throw OracleException( tr( "Could not determine new srid." ), qry ); + } } srid = qry.value( 0 ).toInt(); - if ( !exec( qry, QStringLiteral( "INSERT" - " INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)" - " VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ), + const QString sql { QStringLiteral( "INSERT" + " INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)" + " VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ) }; + + QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + + if ( !exec( qry, sql, QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt ) ) { + logWrapper.setError( qry.lastError().text() ); + logWrapper.setQuery( qry.lastQuery() ); throw OracleException( tr( "CRS not found and could not be created." ), qry ); } + logWrapper.setQuery( qry.lastQuery() ); } } @@ -3259,7 +3424,9 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & throw OracleException( tr( "Cannot insert geometry metadata for table '%1' and geometry column '%2'. Both needs to be uppercase" ).arg( tableName, geometryColumn ), qry ); - if ( !exec( qry, QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,%1)" ).arg( diminfo ), + const QString sql { QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,%1)" ).arg( diminfo ) }; + QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; + if ( !exec( qry, sql, QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid ) ) { throw OracleException( tr( "Could not insert metadata." ), qry ); diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h index c79bb978087..98a1abe1bec 100644 --- a/src/providers/oracle/qgsoracleprovider.h +++ b/src/providers/oracle/qgsoracleprovider.h @@ -50,6 +50,7 @@ enum QgsOraclePrimaryKeyType PktFidMap }; + /** * \class QgsOracleProvider * \brief Data provider for oracle layers. @@ -174,6 +175,7 @@ class QgsOracleProvider final: public QgsVectorDataProvider QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override; static bool exec( QSqlQuery &qry, QString sql, const QVariantList &args ); + static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; } void setTransaction( QgsTransaction *transaction ) override; diff --git a/src/providers/oracle/qgsoracletablemodel.cpp b/src/providers/oracle/qgsoracletablemodel.cpp index 3c5be08ef56..c8df22dca62 100644 --- a/src/providers/oracle/qgsoracletablemodel.cpp +++ b/src/providers/oracle/qgsoracletablemodel.cpp @@ -61,6 +61,7 @@ bool QgsOracleTableModel::searchableColumn( int column ) const case DbtmSelectAtId: return false; } + return false; } void QgsOracleTableModel::addTableEntry( const QgsOracleLayerProperty &layerProperty ) From 7078d15b28966f10e3dcc9c632ee8df58af17639 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 12 Apr 2022 17:30:56 +0200 Subject: [PATCH 12/24] More macros for oracle and mmsql --- .../querylogger/qgsappquerylogger.cpp | 5 + .../querylogger/qgsqueryloggernode.cpp | 10 + .../devtools/querylogger/qgsqueryloggernode.h | 10 + src/providers/mssql/qgsmssqlprovider.cpp | 269 +++++++----------- src/providers/mssql/qgsmssqlprovider.h | 6 + src/providers/oracle/qgsoracleconn.cpp | 86 ++---- .../oracle/qgsoraclefeatureiterator.cpp | 6 +- src/providers/oracle/qgsoracleprovider.cpp | 28 +- src/providers/oracle/qgsoracleprovider.h | 2 + 9 files changed, 179 insertions(+), 243 deletions(-) diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index c34f01c1f4a..b1db05db0cd 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -67,6 +67,11 @@ void QgsAppQueryLogger::queryFinished( const QgsDatabaseQueryLogEntry &query ) if ( !requestIndex.isValid() ) return; + if ( query.query != queryGroup->sql() ) + { + queryGroup->setSql( query.query ); + } + // Calculate the number of children: if error or not fetched rows 1 row is added else 2 rows are added beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() + ( query.fetchedRows != -1 ? 1 : 0 ) ); queryGroup->setFinished( query ); diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index 1efc877e84e..81e2832de41 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -238,4 +238,14 @@ QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLogger return QString(); } +void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &newSql ) +{ + mSql = newSql; +} + +const QString &QgsDatabaseQueryLoggerQueryGroup::sql() const +{ + return mSql; +} + diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index 5dac95659f8..dbe9910e4f4 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -104,6 +104,16 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup */ static QString statusToString( Status status ); + /** + * Sets the SQL to \a sql. + */ + void setSql( const QString &newSql ); + + /** + * Returns the group SQL. + */ + const QString &sql() const; + private: QString mSql; diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp index a1d57ef1f75..83cd2bbab83 100644 --- a/src/providers/mssql/qgsmssqlprovider.cpp +++ b/src/providers/mssql/qgsmssqlprovider.cpp @@ -54,6 +54,12 @@ #include "qgsmssqltransaction.h" +#include "qgsconfig.h" +constexpr int sMssqlConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR ); +#define LoggedExec(query, sql ) execLogged( query, sql, QString(QString( __FILE__ ).mid( sMssqlConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) +#define LoggedExecMetadata(query, sql, uri ) execLogged( query, sql, uri, QString(QString( __FILE__ ).mid( sMssqlConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) + + const QString QgsMssqlProvider::MSSQL_PROVIDER_KEY = QStringLiteral( "mssql" ); const QString QgsMssqlProvider::MSSQL_PROVIDER_DESCRIPTION = QStringLiteral( "MSSQL spatial data provider" ); int QgsMssqlProvider::sConnectionId = 0; @@ -315,7 +321,7 @@ void QgsMssqlProvider::loadMetadata() QSqlQuery query = createQuery(); query.setForwardOnly( true ); - if ( !query.exec( QStringLiteral( "SELECT f_geometry_column, srid, geometry_type, coord_dimension FROM geometry_columns WHERE f_table_schema=%1 AND f_table_name=%2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ) ) ) + if ( !LoggedExec( query, QStringLiteral( "SELECT f_geometry_column, srid, geometry_type, coord_dimension FROM geometry_columns WHERE f_table_schema=%1 AND f_table_name=%2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ) ) ) { QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -334,6 +340,29 @@ void QgsMssqlProvider::loadMetadata() } } +bool QgsMssqlProvider::execLogged( QSqlQuery &qry, const QString &sql, const QString &queryOrigin ) const +{ + QgsDatabaseQueryLogWrapper logWrapper{ sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), queryOrigin }; + const bool res { qry.exec( sql ) }; + if ( ! res ) + { + logWrapper.setError( qry.lastError().text() ); + } + else + { + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); + } + } + logWrapper.setQuery( qry.lastQuery() ); + return res; +} + void QgsMssqlProvider::setLastError( const QString &error ) { appendError( error ); @@ -363,23 +392,18 @@ void QgsMssqlProvider::loadFields() query.setForwardOnly( true ); const QString sql { QStringLiteral( "SELECT name FROM sys.columns WHERE is_computed = 1 AND object_id = OBJECT_ID('[%1].[%2]')" ).arg( mSchemaName, mTableName ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); // Get computed columns which need to be ignored on insert or update. - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper->setError( query.lastError().text() ); pushError( query.lastError().text() ); return; } - long long fetchedRows { 0 }; while ( query.next() ) { - fetchedRows++; mComputedColumns.append( query.value( 0 ).toString() ); } - logWrapper->setFetchedRows( fetchedRows ); // Field has unique constraint QSet setColumnUnique; @@ -388,38 +412,29 @@ void QgsMssqlProvider::loadFields() " INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC ON TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME" " WHERE TC.CONSTRAINT_SCHEMA = '%1' AND TC.TABLE_NAME = '%2' AND TC.CONSTRAINT_TYPE = 'unique'" ) .arg( mSchemaName, mTableName ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql2, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !query.exec( sql2 ) ) + if ( !LoggedExec( query, sql2 ) ) { - logWrapper->setError( query.lastError().text() ); pushError( query.lastError().text() ); return; } - fetchedRows = 0; while ( query.next() ) { - fetchedRows++; setColumnUnique.insert( query.value( QStringLiteral( "COLUMN_NAME" ) ).toString() ); } - logWrapper->setFetchedRows( fetchedRows ); } const QString sql3 { QStringLiteral( "exec sp_columns @table_name = N%1, @table_owner = %2" ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql3, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !query.exec( sql3 ) ) + if ( !LoggedExec( query, sql3 ) ) { pushError( query.lastError().text() ); - logWrapper->setError( query.lastError().text() ); return; } - fetchedRows = 0; int i = 0; QStringList pkCandidates; while ( query.next() ) { - fetchedRows++; const QString colName = query.value( QStringLiteral( "COLUMN_NAME" ) ).toString(); const QString sqlTypeName = query.value( QStringLiteral( "TYPE_NAME" ) ).toString(); bool columnIsIdentity = false; @@ -518,7 +533,6 @@ void QgsMssqlProvider::loadFields() ++i; } - logWrapper->setFetchedRows( fetchedRows ); } // get primary key @@ -527,10 +541,8 @@ void QgsMssqlProvider::loadFields() query.clear(); query.setForwardOnly( true ); const QString sql4 { QStringLiteral( "exec sp_pkeys @table_name = N%1, @table_owner = %2 " ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql4, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !query.exec( sql4 ) ) + if ( !LoggedExec( query, sql4 ) ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -569,10 +581,8 @@ void QgsMssqlProvider::loadFields() query.setForwardOnly( true ); const QString sql5 { QStringLiteral( "select count(distinct [%1]), count([%1]) from [%2].[%3]" ) .arg( pk, mSchemaName, mTableName ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql5, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !query.exec( sql5 ) ) + if ( !LoggedExec( query, sql5 ) ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -664,13 +674,10 @@ QVariant QgsMssqlProvider::defaultValue( int fieldId ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { const QString errorMessage( tr( "Could not execute query: %1" ).arg( query.lastError().text() ) ); QgsDebugMsg( errorMessage ); - logWrapper.setError( errorMessage ); pushError( errorMessage ); return QVariant(); } @@ -735,11 +742,8 @@ QVariant QgsMssqlProvider::minimumValue( int index ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -779,11 +783,8 @@ QVariant QgsMssqlProvider::maximumValue( int index ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -831,21 +832,16 @@ QSet QgsMssqlProvider::uniqueValues( int index, int limit ) const QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } if ( query.isActive() ) { - long long fetchedRows { 0 }; // read all features while ( query.next() ) { - fetchedRows++; QVariant v = query.value( 0 ); if ( fld.type() == QVariant::Time ) v = convertTimeValue( v ); @@ -853,7 +849,6 @@ QSet QgsMssqlProvider::uniqueValues( int index, int limit ) const v = convertValue( fld.type(), v.toString() ); uniqueValues.insert( v ); } - logWrapper.setFetchedRows( fetchedRows ); } return uniqueValues; } @@ -891,11 +886,8 @@ QStringList QgsMssqlProvider::uniqueStringsMatching( int index, const QString &s QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -908,7 +900,6 @@ QStringList QgsMssqlProvider::uniqueStringsMatching( int index, const QString &s if ( feedback && feedback->isCanceled() ) break; } - logWrapper.setFetchedRows( results.length() ); } return results; } @@ -943,9 +934,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const statement = QString( sql ).arg( mSchemaName, mTableName ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( query.exec( statement ) ) + if ( LoggedExec( query, statement ) ) { if ( query.next() && ( !query.value( 0 ).isNull() || !query.value( 1 ).isNull() || @@ -962,7 +951,6 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const } else { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -1034,8 +1022,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const const QString statementSample = statement + ( mSqlWhereClause.isEmpty() ? " WHERE " : " AND " ) + sampleFilter; - QgsDatabaseQueryLogWrapper logWrapperNested { statementSample, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( query.exec( statementSample ) && query.next() && + if ( LoggedExec( query, statementSample ) && query.next() && !query.value( 0 ).isNull() && query.value( 4 ).toInt() >= minSampleCount ) { mExtent.setXMinimum( query.value( 0 ).toDouble() ); @@ -1046,9 +1033,8 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const } } - if ( !query.exec( statement ) ) + if ( !LoggedExec( query, statement ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); } @@ -1066,11 +1052,9 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const return; } - long long fetchedRows { 0 }; // We have to read all the geometry if readAllGeography is true. while ( query.next() ) { - fetchedRows++; QByteArray ar = query.value( 0 ).toByteArray(); std::unique_ptr geom = mParser.parseSqlGeometry( reinterpret_cast< unsigned char * >( ar.data() ), ar.size() ); if ( geom ) @@ -1090,7 +1074,6 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const mSRId = mParser.GetSRSId(); } } - logWrapper.setFetchedRows( fetchedRows ); } // Return the extent of the layer @@ -1129,14 +1112,12 @@ long long QgsMssqlProvider::featureCount() const " JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)" " WHERE SCHEMA_NAME(t.schema_id) = %1 AND OBJECT_NAME(t.OBJECT_ID) = %2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ); - QgsDatabaseQueryLogWrapper logWrapper{ statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( query.exec( statement ) && query.next() ) + if ( LoggedExec( query, statement ) && query.next() ) { return query.value( 0 ).toLongLong(); } else { - logWrapper.setError( query.lastError().text() ); // We couldn't get the rows from the sys tables. Can that ever happen? // Should just do a select count(*) here. return -1; @@ -1408,13 +1389,10 @@ bool QgsMssqlProvider::addFeatures( QgsFeatureList &flist, Flags flags ) } } - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; if ( !query.exec() ) { const QString msg = query.lastError().text(); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); - logWrapper.setError( msg ); - logWrapper.setQuery( query.lastQuery() ); if ( !mSkipFailures ) { pushError( msg ); @@ -1492,10 +1470,8 @@ bool QgsMssqlProvider::addAttributes( const QList &attributes ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !query.exec( statement ) ) + if ( !LoggedExec( query, statement ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } @@ -1526,16 +1502,14 @@ bool QgsMssqlProvider::deleteAttributes( const QgsAttributeIds &attributes ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); - std::unique_ptr logWrapper = std::make_unique( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); - if ( !query.exec( statement ) ) + + if ( !LoggedExec( query, statement ) ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } query.finish(); - logWrapper.release(); loadFields(); @@ -1605,12 +1579,9 @@ bool QgsMssqlProvider::changeAttributeValues( const QgsChangedAttributesMap &att // set attribute filter statement += QStringLiteral( " WHERE " ) + whereClauseFid( fid ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - // use prepared statement to prevent from sql injection if ( !query.prepare( statement ) ) { - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } @@ -1678,14 +1649,10 @@ bool QgsMssqlProvider::changeAttributeValues( const QgsChangedAttributesMap &att if ( !query.exec() ) { - logWrapper.setQuery( query.lastQuery() ); - logWrapper.setError( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); return false; } - logWrapper.setQuery( query.lastQuery() ); - if ( pkChanged && mPrimaryKeyType == PktFidMap ) { const QVariant v = mShared->removeFid( fid ); @@ -1749,11 +1716,8 @@ bool QgsMssqlProvider::changeGeometryValues( const QgsGeometryMap &geometry_map // set attribute filter statement += QStringLiteral( " WHERE " ) + whereClauseFid( fid ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !query.prepare( statement ) ) { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1775,12 +1739,9 @@ bool QgsMssqlProvider::changeGeometryValues( const QgsGeometryMap &geometry_map if ( !query.exec() ) { - logWrapper.setQuery( query.lastQuery() ); - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } - logWrapper.setQuery( query.lastQuery() ); } if ( mTransaction ) @@ -1810,8 +1771,8 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) query.setForwardOnly( true ); const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE [%3] IN (%4)" ).arg( mSchemaName, mTableName, mAttributeFields.at( mPrimaryKeyAttrs[0] ).name(), featureIds ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( query.exec( statement ) ) + + if ( LoggedExec( query, statement ) ) { if ( query.numRowsAffected() == ids.size() ) { @@ -1824,7 +1785,6 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) } else { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); } } @@ -1836,10 +1796,9 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) for ( QgsFeatureIds::const_iterator it = ids.begin(); it != ids.end(); ++it ) { const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE %3" ).arg( mSchemaName, mTableName, whereClauseFid( *it ) ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( query.exec( statement ) ) + + if ( LoggedExec( query, statement ) ) { - logWrapper.setFetchedRows( query.numRowsAffected() ); if ( query.numRowsAffected() == 1 ) { mShared->removeFid( *it ); @@ -1848,7 +1807,6 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids ) } else { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); break; } @@ -1915,11 +1873,8 @@ bool QgsMssqlProvider::createSpatialIndex() statement += QLatin1String( " USING GEOGRAPHY_GRID" ); } - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( statement ) ) + if ( !LoggedExec( query, statement ) ) { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1942,11 +1897,8 @@ bool QgsMssqlProvider::createAttributeIndex( int field ) statement = QStringLiteral( "CREATE NONCLUSTERED INDEX [qgs_%1_idx] ON [%2].[%3] ( [%4] )" ).arg( mGeometryColName, mSchemaName, mTableName, mAttributeFields.at( field ).name() ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !query.exec( statement ) ) + if ( !LoggedExec( query, statement ) ) { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); return false; } @@ -1963,9 +1915,7 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const query.setForwardOnly( true ); const QString statement { QStringLiteral( "SELECT srtext FROM spatial_ref_sys WHERE srid=%1" ).arg( mSRId ) }; - std::unique_ptr logWrapper = std::make_unique( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); - - bool execOk = query.exec( statement ); + bool execOk = LoggedExec( query, statement ); if ( execOk && query.isActive() ) { if ( query.next() ) @@ -1977,26 +1927,17 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const query.finish(); } - else - { - logWrapper->setError( query.lastError().text() ); - } query.clear(); query.setForwardOnly( true ); // Look in the system reference table for the data if we can't find it yet - logWrapper.reset( new QgsDatabaseQueryLogWrapper( statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - execOk = query.exec( QStringLiteral( "SELECT well_known_text FROM sys.spatial_reference_systems WHERE spatial_reference_id=%1" ).arg( mSRId ) ); + execOk = LoggedExec( query, QStringLiteral( "SELECT well_known_text FROM sys.spatial_reference_systems WHERE spatial_reference_id=%1" ).arg( mSRId ) ); if ( execOk && query.isActive() && query.next() ) { mCrs = QgsCoordinateReferenceSystem::fromWkt( query.value( 0 ).toString() ); if ( mCrs.isValid() ) return mCrs; } - else - { - logWrapper->setError( query.lastError().text() ); - } } return mCrs; } @@ -2053,10 +1994,8 @@ bool QgsMssqlProvider::setSubsetString( const QString &theSQL, bool ) QSqlQuery query = createQuery(); query.setForwardOnly( true ); - QgsDatabaseQueryLogWrapper logWrapper { sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !query.exec( sql ) ) + if ( !LoggedExec( query, sql ) ) { - logWrapper.setError( query.lastError().text() ); pushError( query.lastError().text() ); mSqlWhereClause = prevWhere; return false; @@ -2589,12 +2528,9 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s query.setForwardOnly( true ); const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'layer_styles'" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); - - if ( !query.exec( sql ) ) + if ( !LoggedExecMetadata( query, sql, uri ) ) { errorCause = QObject::tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); - logWrapper->setError( errorCause ); return false; } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2619,12 +2555,9 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( checkQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - - if ( !query.exec( checkQuery ) ) + if ( !LoggedExecMetadata( query, checkQuery, uri ) ) { errorCause = QObject::tr( "Checking for style failed: %1" ).arg( query.lastError().text() ); - logWrapper->setError( errorCause ); return false; } @@ -2662,9 +2595,8 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, query.setForwardOnly( true ); QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); - if ( !query.exec( sql ) ) + if ( !LoggedExecMetadata( query, sql, uri ) ) { QgsDebugMsg( query.lastError().text() ); return false; @@ -2689,12 +2621,10 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, "[update_time] [datetime] NULL" ") ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - const bool execOk = query.exec( sql ); + const bool execOk = LoggedExecMetadata( query, sql, uri ); if ( !execOk ) { const QString error { QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" ) }; - logWrapper->setError( error ); errCause = error; return false; } @@ -2747,10 +2677,8 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( checkQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !query.exec( checkQuery ) ) + if ( !LoggedExecMetadata( query, checkQuery, uri ) ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( QStringLiteral( "Check Query failed" ) ); return false; @@ -2798,14 +2726,11 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri, QgsDebugMsgLevel( QStringLiteral( "Inserting styles" ), 2 ); QgsDebugMsgLevel( sql, 2 ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - - const bool execOk = query.exec( sql ); + const bool execOk = LoggedExecMetadata( query, sql, uri ); if ( !execOk ) { errCause = QObject::tr( "Unable to save layer style. It's not possible to insert a new record into the style table. Maybe this is due to table permissions. Please contact your database administrator." ); - logWrapper->setError( query.lastError().text() ); } return execOk; } @@ -2830,12 +2755,10 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau query.setForwardOnly( true ); const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); - if ( !query.exec( sql ) ) + if ( !LoggedExecMetadata( query, sql, uri ) ) { errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); - logWrapper->setError( errCause ); return QString(); } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2861,13 +2784,10 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - - if ( !query.exec( selectQmlQuery ) ) + if ( !LoggedExecMetadata( query, selectQmlQuery, uri ) ) { QgsDebugMsgLevel( QStringLiteral( "Load of style failed" ), 2 ); const QString msg = query.lastError().text(); - logWrapper->setError( msg ); errCause = msg; QgsDebugMsg( msg ); return QString(); @@ -2901,13 +2821,11 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, query.setForwardOnly( true ); QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ); // check if layer_styles table already exist - if ( !query.exec( sql ) ) + if ( !LoggedExecMetadata( query, sql, uri ) ) { const QString msg = query.lastError().text(); - logWrapper->setError( msg ); errCause = msg; QgsDebugMsg( msg ); return -1; @@ -2930,12 +2848,10 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectRelatedQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - bool queryOk = query.exec( selectRelatedQuery ); + bool queryOk = LoggedExecMetadata( query, selectRelatedQuery, uri ); if ( !queryOk ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); return -1; } @@ -2958,12 +2874,9 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri, .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); QgsDebugMsgLevel( selectOthersQuery, 2 ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectOthersQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), QGS_QUERY_LOG_ORIGIN ) ); - - queryOk = query.exec( selectOthersQuery ); + queryOk = LoggedExecMetadata( query, selectOthersQuery, uri ); if ( !queryOk ) { - logWrapper->setError( query.lastError().text() ); QgsDebugMsg( query.lastError().text() ); return -1; } @@ -2999,11 +2912,10 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin query.setForwardOnly( true ); const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ); - if ( !query.exec( sql ) ) + + if ( !LoggedExecMetadata( query, sql, uri ) ) { errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); - logWrapper->setError( errCause ); return QString(); } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -3020,13 +2932,11 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin QString style; const QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsMssqlProvider::quotedValue( styleId ) ); - logWrapper.reset( new QgsDatabaseQueryLogWrapper( selectQmlQuery, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) ); - const bool queryOk = query.exec( selectQmlQuery ); + const bool queryOk = LoggedExecMetadata( query, selectQmlQuery, uri ); if ( !queryOk ) { QgsDebugMsg( query.lastError().text() ); errCause = query.lastError().text(); - logWrapper->setError( errCause ); return QString(); } if ( !query.next() ) @@ -3171,6 +3081,29 @@ QString QgsMssqlProviderMetadata::encodeUri( const QVariantMap &parts ) const return dsUri.uri(); } +bool QgsMssqlProviderMetadata::execLogged( QSqlQuery &qry, const QString &sql, const QString &uri, const QString &queryOrigin ) const +{ + QgsDatabaseQueryLogWrapper logWrapper{ sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), queryOrigin }; + const bool res { qry.exec( sql ) }; + if ( ! res ) + { + logWrapper.setError( qry.lastError().text() ); + } + else + { + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); + } + } + logWrapper.setQuery( qry.lastQuery() ); + return res; +} + QGISEXTERN QgsProviderMetadata *providerMetadataFactory() { return new QgsMssqlProviderMetadata(); @@ -3333,9 +3266,7 @@ bool QgsMssqlProvider::getExtentFromGeometryColumns( QgsRectangle &extent ) cons const QString statement = sql.arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( query.exec( statement ) && query.isActive() ) + if ( LoggedExec( query, statement ) && query.isActive() ) { query.next(); if ( query.isValid() ) @@ -3347,10 +3278,6 @@ bool QgsMssqlProvider::getExtentFromGeometryColumns( QgsRectangle &extent ) cons return true; } - else - { - logWrapper.setError( query.lastError().text() ); - } } return false; @@ -3366,9 +3293,7 @@ bool QgsMssqlProvider::getPrimaryKeyFromGeometryColumns( QStringList &primaryKey const QString sql = QStringLiteral( "SELECT qgis_pkey FROM geometry_columns WHERE f_table_name = '%1' AND NOT qgis_pkey IS NULL" ); const QString statement = sql.arg( mTableName ); - QgsDatabaseQueryLogWrapper logWrapper { statement, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( query.exec( statement ) && query.isActive() ) + if ( LoggedExec( query, statement ) && query.isActive() ) { query.next(); if ( query.isValid() ) @@ -3378,10 +3303,6 @@ bool QgsMssqlProvider::getPrimaryKeyFromGeometryColumns( QStringList &primaryKey return true; } } - else - { - logWrapper.setError( query.lastError().text() ); - } return false; } diff --git a/src/providers/mssql/qgsmssqlprovider.h b/src/providers/mssql/qgsmssqlprovider.h index 867b8a719ec..552430d2943 100644 --- a/src/providers/mssql/qgsmssqlprovider.h +++ b/src/providers/mssql/qgsmssqlprovider.h @@ -176,6 +176,8 @@ class QgsMssqlProvider final: public QgsVectorDataProvider private: + bool execLogged( QSqlQuery &qry, const QString &sql, const QString &queryOrigin = QString() ) const; + //! Fields QgsFields mAttributeFields; QMap mDefaultValues; @@ -328,6 +330,10 @@ class QgsMssqlProviderMetadata final: public QgsProviderMetadata QVariantMap decodeUri( const QString &uri ) const override; QString encodeUri( const QVariantMap &parts ) const override; + private: + + bool execLogged( QSqlQuery &qry, const QString &sql, const QString &uri, const QString &queryOrigin = QString() ) const; + }; #endif // QGSMSSQLPROVIDER_H diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index 8273897a968..af4246f6ab4 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -301,27 +301,19 @@ QStringList QgsOracleConn::pkCandidates( const QString &ownerName, const QString QSqlQuery qry( mDatabase ); - QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner='%1' AND table_name='%2' ORDER BY column_id" ).arg( ownerName, viewName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner=? AND table_name=? ORDER BY column_id" ), - QVariantList() << ownerName << viewName ) ) + if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner=? AND table_name=? ORDER BY column_id" ), + QVariantList() << ownerName << viewName ) ) { const QString error { tr( "SQL: %1 [owner: %2 table_name: %3]\nerror: %4\n" ).arg( qry.lastQuery(), qry.lastError().text(), ownerName, viewName ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return cols; } - long long fetchedRows { 0 }; - while ( qry.next() ) { cols << qry.value( 0 ).toString(); - fetchedRows++; } - logWrapper.setFetchedRows( fetchedRows ); - qry.finish(); return cols; @@ -362,18 +354,13 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly, QSqlQuery qry( mDatabase ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) ) { const QString error { tr( "Querying available tables failed.\nSQL: %1\nerror: %2\n" ).arg( qry.lastQuery(), qry.lastError().text() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return false; } - long long fetchedRows { 0 }; - while ( qry.next() ) { QgsOracleLayerProperty layerProperty; @@ -387,10 +374,8 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly, mLayersSupported << layerProperty; - fetchedRows++; } - logWrapper.setFetchedRows( fetchedRows ); if ( mLayersSupported.size() == 0 ) { @@ -698,17 +683,14 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b sql += QLatin1String( " FROM %2 t WHERE NOT t.%1 IS NULL%3" ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql - .arg( quotedIdentifier( layerProperty.geometryColName ), - table, - where.isEmpty() ? QString() : QStringLiteral( " AND (%1)" ).arg( where ) ), QVariantList() ) ) + if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql + .arg( quotedIdentifier( layerProperty.geometryColName ), + table, + where.isEmpty() ? QString() : QStringLiteral( " AND (%1)" ).arg( where ) ), QVariantList() ) ) { const QString error { tr( "SQL: %1\nerror: %2\n" ) .arg( qry.lastQuery(), qry.lastError().text() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return; @@ -746,8 +728,6 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b srids << srid; } - logWrapper.setFetchedRows( fetchedRows ); - qry.finish(); if ( !onlyExistingTypes ) @@ -1052,8 +1032,7 @@ bool QgsOracleConn::hasSpatial() { QSqlQuery qry( mDatabase ); const QString sql { QStringLiteral( "SELECT 1 FROM v$option WHERE parameter='Spatial' AND value='TRUE'" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - mHasSpatial = exec( qry, sql, QVariantList() ) && qry.next(); + mHasSpatial = LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next(); } return mHasSpatial; @@ -1063,8 +1042,7 @@ int QgsOracleConn::version() { QSqlQuery qry( mDatabase ); const QString sql = QStringLiteral( "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION" ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - if ( exec( qry, sql, QVariantList() ) && qry.next() ) + if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next() ) { return qry.value( 0 ).toString().split( '.' ).at( 0 ).toInt(); } @@ -1073,7 +1051,6 @@ int QgsOracleConn::version() const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return -1; } @@ -1087,8 +1064,7 @@ QString QgsOracleConn::currentUser() { QSqlQuery qry( mDatabase ); const QString sql { QStringLiteral( "SELECT user FROM dual" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - if ( exec( qry, sql, QVariantList() ) && qry.next() ) + if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next() ) { mCurrentUser = qry.value( 0 ).toString(); } @@ -1126,15 +1102,11 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr QSqlQuery qry( mDatabase ); - QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT i.index_name,i.domidx_opstatus" - " FROM all_indexes i" - " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=%1" - " WHERE i.table_owner=%2 AND i.table_name=%3 AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ).arg( geometryColumn, ownerName, tableName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - if ( exec( qry, QStringLiteral( "SELECT i.index_name,i.domidx_opstatus" - " FROM all_indexes i" - " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?" - " WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ), - QVariantList() << geometryColumn << ownerName << tableName ) ) + if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT i.index_name,i.domidx_opstatus" + " FROM all_indexes i" + " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?" + " WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ), + QVariantList() << geometryColumn << ownerName << tableName ) ) { if ( qry.next() ) { @@ -1167,7 +1139,6 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr .arg( tableName ) .arg( geometryColumn ) .arg( qry.lastError().text() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); @@ -1183,8 +1154,8 @@ QString QgsOracleConn::createSpatialIndex( const QString &ownerName, const QStri int n = 0; const QString sql { QStringLiteral( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ) }; - std::unique_ptr logWrapper = std::make_unique( sql, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN ); - if ( exec( qry, sql, QVariantList() ) && + + if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next() ) { n = qry.value( 0 ).toInt() + 1; @@ -1195,13 +1166,11 @@ QString QgsOracleConn::createSpatialIndex( const QString &ownerName, const QStri .arg( quotedIdentifier( ownerName ) ) .arg( quotedIdentifier( tableName ) ) .arg( quotedIdentifier( geometryColumn ) ) }; - logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql2, mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN ) ); - if ( !exec( qry, sql2, QVariantList() ) ) + if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql2, QVariantList() ) ) { const QString error { tr( "Creation spatial index failed.\nSQL: %1\nError: %2" ) .arg( qry.lastQuery() ) .arg( qry.lastError().text() ) }; - logWrapper->setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return QString(); @@ -1216,33 +1185,24 @@ QStringList QgsOracleConn::getPrimaryKeys( const QString &ownerName, const QStri QStringList result; - QgsDatabaseQueryLogWrapper logWrapper { QStringLiteral( "SELECT column_name" - " FROM all_cons_columns a" - " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" - " WHERE b.constraint_type='P' AND b.owner=%1 AND b.table_name=%2" ).arg( ownerName, tableName ), mConnInfo, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, QStringLiteral( "SELECT column_name" - " FROM all_cons_columns a" - " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" - " WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ), - QVariantList() << ownerName << tableName ) ) + if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT column_name" + " FROM all_cons_columns a" + " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" + " WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ), + QVariantList() << ownerName << tableName ) ) { const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, tr( "Oracle" ) ); return result; } - long long fetchedRows { 0 }; while ( qry.next() ) { - fetchedRows++; QString name = qry.value( 0 ).toString(); result << name; } - logWrapper.setFetchedRows( fetchedRows ); return result; } diff --git a/src/providers/oracle/qgsoraclefeatureiterator.cpp b/src/providers/oracle/qgsoraclefeatureiterator.cpp index f7d75feb7ab..d2a184e46d0 100644 --- a/src/providers/oracle/qgsoraclefeatureiterator.cpp +++ b/src/providers/oracle/qgsoraclefeatureiterator.cpp @@ -294,13 +294,11 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature &feature ) if ( mRewind ) { mRewind = false; - QgsDatabaseQueryLogWrapper logWrapper { mSql, mConnection->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN }; if ( !execQuery( mSql, mArgs, 1 ) ) { const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) .arg( mQry.lastQuery(), mQry.lastError().text() ) }; - logWrapper.setError( error ); QgsMessageLog::logMessage( error, QObject::tr( "Oracle" ) ); return false; @@ -535,14 +533,12 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar mSql = query; mArgs = args; - QgsDatabaseQueryLogWrapper logWrapper { query, mConnection->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN }; if ( !execQuery( query, args, 1 ) ) { const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) .arg( mQry.lastQuery(), mQry.lastError().text() ) }; - logWrapper.setError( error ); if ( showLog ) { QgsMessageLog::logMessage( error, @@ -562,7 +558,7 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar bool QgsOracleFeatureIterator::execQuery( const QString &query, const QVariantList &args, int retryCount ) { lock(); - if ( !QgsOracleProvider::exec( mQry, query, args ) ) + if ( !QgsOracleProvider::execLoggedStatic( mQry, query, args, mSource->mUri.uri(), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) ) { unlock(); if ( retryCount != 0 ) diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index df3e3bd6c1e..e62c6bef3cf 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -45,6 +45,7 @@ #endif #include +#include #include #include @@ -315,8 +316,10 @@ bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, co { QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; const bool res { exec( qry, sql, args ) }; - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); + // ORACLE does not support size so this will always be -1 + // we leave it here in case this changes in the future if ( qry.isSelect() ) { logWrapper.setFetchedRows( qry.size() ); @@ -328,6 +331,29 @@ bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, co return res; } +QString QgsOracleProvider::getLastExecutedQuery( const QSqlQuery &query ) +{ + QString str = query.lastQuery(); + QMapIterator it( query.boundValues() ); + while ( it.hasNext() ) + { + it.next(); + const QVariant &var { it.value().toString() }; + QSqlField field( QLatin1String( "" ), var.type() ); + if ( var.isNull() ) + { + field.clear(); + } + else + { + field.setValue( var ); + } + const QString formatV = query.driver()->formatValue( field ); + str.replace( it.key(), formatV ); + } + return str; +} + void QgsOracleProvider::setTransaction( QgsTransaction *transaction ) { // static_cast since layers cannot be added to a transaction of a non-matching provider diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h index 98a1abe1bec..ccebb4602e0 100644 --- a/src/providers/oracle/qgsoracleprovider.h +++ b/src/providers/oracle/qgsoracleprovider.h @@ -177,6 +177,8 @@ class QgsOracleProvider final: public QgsVectorDataProvider static bool exec( QSqlQuery &qry, QString sql, const QVariantList &args ); static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); + static QString getLastExecutedQuery( const QSqlQuery &query ); + bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; } void setTransaction( QgsTransaction *transaction ) override; QgsTransaction *transaction() const override; From 3e66e29d84893ef531a009d3c706b28b9d464585 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 13 Apr 2022 09:52:33 +0200 Subject: [PATCH 13/24] Fix oracle --- src/providers/oracle/qgsoracleprovider.cpp | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index e62c6bef3cf..4e909416ca4 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -1397,7 +1397,7 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( insert ), 4 ); - if ( !ins.prepare( insert + "$$fsdfsd" ) ) + if ( !ins.prepare( insert ) ) { throw OracleException( tr( "Could not prepare insert statement" ), ins ); } @@ -1433,12 +1433,12 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag if ( !ins.exec() ) { - logWrapper.setQuery( ins.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( ins ) ); logWrapper.setError( ins.lastError().text() ); throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins ); } - logWrapper.setQuery( ins.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( ins ) ); if ( !( flags & QgsFeatureSink::FastInsert ) ) { @@ -1938,12 +1938,12 @@ bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &at if ( !qry.exec() ) { logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); } qry.finish(); - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); // update feature id map if key was changed if ( pkChanged && mPrimaryKeyType == PktFidMap ) @@ -2421,11 +2421,11 @@ bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map if ( !qry.exec() ) { - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } qry.finish(); @@ -2603,7 +2603,7 @@ QgsRectangle QgsOracleProvider::extent() const logWrapper.setError( qry.lastError().text() ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } if ( mHasSpatialIndex && mUseEstimatedMetadata ) @@ -2613,7 +2613,7 @@ QgsRectangle QgsOracleProvider::extent() const ok = exec( qry, sql, QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn ); - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); if ( ! ok ) { logWrapper.setError( qry.lastError().text() ); @@ -2742,7 +2742,7 @@ bool QgsOracleProvider::getGeometryDetails() .arg( qry.lastQuery() ), tr( "Oracle" ) ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } QString sql { mUseEstimatedMetadata ? @@ -2889,7 +2889,7 @@ bool QgsOracleProvider::createSpatialIndex() QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn ) ) { - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" ) .arg( mTableName ) @@ -2900,7 +2900,7 @@ bool QgsOracleProvider::createSpatialIndex() return false; } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setFetchedRows( qry.numRowsAffected() ); } @@ -2919,7 +2919,7 @@ bool QgsOracleProvider::createSpatialIndex() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() ) ) { - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" ) .arg( quotedValue( mTableName ) ) @@ -2929,7 +2929,7 @@ bool QgsOracleProvider::createSpatialIndex() tr( "Oracle" ) ); return false; } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } } else @@ -3134,12 +3134,12 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( QVariantList() << ownerName << tableName ) ) { - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Could not determine table existence." ), qry ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } bool exists = qry.next(); @@ -3402,11 +3402,11 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & if ( !exec( qry, sql, QVariantList() << wkt ) ) { logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); throw OracleException( tr( "Could not lookup WKT." ), qry ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } if ( qry.next() ) @@ -3439,10 +3439,10 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt ) ) { logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); throw OracleException( tr( "CRS not found and could not be created." ), qry ); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); } } From 98d5c263ff4681f3d5d82f03b27771bed6be938e Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 13 Apr 2022 10:05:34 +0200 Subject: [PATCH 14/24] QString() --- src/providers/oracle/qgsoracleprovider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 4e909416ca4..95994154937 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -339,7 +339,7 @@ QString QgsOracleProvider::getLastExecutedQuery( const QSqlQuery &query ) { it.next(); const QVariant &var { it.value().toString() }; - QSqlField field( QLatin1String( "" ), var.type() ); + QSqlField field( QString( ), var.type() ); if ( var.isNull() ) { field.clear(); From 485cd0a15d969510a8a3d838e2427f7bdd45aba6 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 13 Apr 2022 11:04:38 +0200 Subject: [PATCH 15/24] Log execSql from connections API --- python/core/auto_generated/qgsdbquerylog.sip.in | 2 ++ src/app/devtools/querylogger/qgsqueryloggernode.cpp | 2 +- .../ogr/qgsgeopackageproviderconnection.cpp | 10 +++++++++- src/core/qgsdbquerylog.h | 13 ++++++++++++- .../oracle/qgsoracleproviderconnection.cpp | 7 ++++++- src/providers/postgres/qgspostgresconn.cpp | 1 + .../spatialite/qgsspatialiteproviderconnection.cpp | 7 +++++++ 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/python/core/auto_generated/qgsdbquerylog.sip.in b/python/core/auto_generated/qgsdbquerylog.sip.in index b8501f675fe..a8ce8473683 100644 --- a/python/core/auto_generated/qgsdbquerylog.sip.in +++ b/python/core/auto_generated/qgsdbquerylog.sip.in @@ -46,6 +46,8 @@ Constructor for QgsDatabaseQueryLogEntry. QString error; + bool canceled; + }; diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index 81e2832de41..9dbea975635 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -201,7 +201,7 @@ void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEnt { if ( query.error.isEmpty() ) { - mStatus = Status::Complete; + mStatus = query.canceled ? Status::Canceled : Status::Complete; addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); if ( query.fetchedRows != -1 ) { diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp index c90ae7d5d32..051ee11cf21 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp @@ -28,6 +28,7 @@ #include "qgsfeedback.h" #include "qgsogrutils.h" #include "qgsfielddomain.h" +#include "qgsdbquerylog.h" #include #include @@ -351,8 +352,11 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities() QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const { + QgsDatabaseQueryLogWrapper logWrapper( sql, uri(), providerKey(), QStringLiteral( "QgsGeoPackageProviderConnection" ), QGS_QUERY_LOG_ORIGIN ); + if ( feedback && feedback->isCanceled() ) { + logWrapper.setCanceled(); return QgsAbstractDatabaseProviderConnection::QueryResult(); } @@ -363,6 +367,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti if ( feedback && feedback->isCanceled() ) { + logWrapper.setCanceled(); return QgsAbstractDatabaseProviderConnection::QueryResult(); } @@ -379,9 +384,9 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti results.setQueryExecutionTime( std::chrono::duration_cast( end - begin ).count() ); gdal::ogr_feature_unique_ptr fet; + if ( fet.reset( OGR_L_GetNextFeature( ogrLayer ) ), fet ) { - // pk column name QString pkColumnName; @@ -453,11 +458,13 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti if ( ! errCause.isEmpty() ) { + logWrapper.setError( errCause ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) ); } OGR_L_ResetReading( ogrLayer ); iterator->nextRow(); + return results; } @@ -475,6 +482,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti if ( !errCause.isEmpty() ) { + logWrapper.setError( errCause ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) ); } diff --git a/src/core/qgsdbquerylog.h b/src/core/qgsdbquerylog.h index 755260a8a9e..e4eb6b0cb52 100644 --- a/src/core/qgsdbquerylog.h +++ b/src/core/qgsdbquerylog.h @@ -80,7 +80,8 @@ class CORE_EXPORT QgsDatabaseQueryLogEntry QString origin; /** - * Number of fetched rows + * Number of fetched/affected rows. + * \warning Not all providers support this information. */ long long fetchedRows = -1; @@ -89,6 +90,11 @@ class CORE_EXPORT QgsDatabaseQueryLogEntry */ QString error; + /** + * Canceled flag for user canceled queries. + */ + bool canceled = false; + private: static QAtomicInt sQueryId; @@ -247,6 +253,11 @@ class QgsDatabaseQueryLogWrapper mEntry.error = error; } + void setCanceled( ) + { + mEntry.canceled = true; + } + private: QgsDatabaseQueryLogEntry mEntry; diff --git a/src/providers/oracle/qgsoracleproviderconnection.cpp b/src/providers/oracle/qgsoracleproviderconnection.cpp index d9d4297940b..d8513699f99 100644 --- a/src/providers/oracle/qgsoracleproviderconnection.cpp +++ b/src/providers/oracle/qgsoracleproviderconnection.cpp @@ -1365,15 +1365,20 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsOracleProviderConnection:: QSqlQuery qry( *pconn.get() ); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + + QgsDatabaseQueryLogWrapper logWrapper { sql, uri(), providerKey(), QStringLiteral( "QgsAbstractDatabaseProviderConnection" ), QGS_QUERY_LOG_ORIGIN }; + if ( !qry.exec( sql ) ) { + logWrapper.setError( qry.lastError().text() ); throw QgsProviderConnectionException( QObject::tr( "SQL error: %1 returned %2" ) .arg( qry.lastQuery(), qry.lastError().text() ) ); } if ( feedback && feedback->isCanceled() ) - return QgsAbstractDatabaseProviderConnection::QueryResult(); + logWrapper.setCanceled(); + return QgsAbstractDatabaseProviderConnection::QueryResult(); if ( qry.isActive() ) { diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index 092eeee549f..3be38308650 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -1359,6 +1359,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret .arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) ); } } + logWrapper->setFetchedRows( PQntuples( res ) ); return res; } if ( PQstatus() != CONNECTION_OK ) diff --git a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp index 6ac8c5937eb..179010b98fc 100644 --- a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp +++ b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp @@ -23,6 +23,7 @@ #include "qgsapplication.h" #include "qgsvectorlayer.h" #include "qgsfeedback.h" +#include "qgsdbquerylog.h" #include #include @@ -466,8 +467,11 @@ void QgsSpatiaLiteProviderConnection::setDefaultCapabilities() QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QString &sql, QgsFeedback *feedback ) const { + QgsDatabaseQueryLogWrapper logWrapper( sql, uri(), providerKey(), QStringLiteral( "QgsSpatiaLiteProviderConnection" ), QGS_QUERY_LOG_ORIGIN ); + if ( feedback && feedback->isCanceled() ) { + logWrapper.setCanceled(); return QgsAbstractDatabaseProviderConnection::QueryResult(); } @@ -478,6 +482,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti if ( feedback && feedback->isCanceled() ) { + logWrapper.setCanceled(); return QgsAbstractDatabaseProviderConnection::QueryResult(); } @@ -540,6 +545,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti if ( ! errCause.isEmpty() ) { + logWrapper.setError( errCause ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) ); } @@ -562,6 +568,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti if ( !errCause.isEmpty() ) { + logWrapper.setError( errCause ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) ); } From c4bbd385e6016b7dc67f506149e079e56e74615d Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 20 Apr 2022 12:04:07 +0200 Subject: [PATCH 16/24] spatialite SQL logging --- .../qgsspatialitefeatureiterator.cpp | 5 ++ .../spatialite/qgsspatialitefeatureiterator.h | 6 ++ .../spatialite/qgsspatialiteprovider.cpp | 66 ++++++++++--------- .../spatialite/qgsspatialiteprovider.h | 10 +-- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp index adc4f8304d8..2c277b770d0 100644 --- a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp +++ b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp @@ -235,6 +235,10 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature sqliteStatement = nullptr; close(); } + else + { + mQueryLogWrapper = std::make_unique( mLastSql, mSource->mSqlitePath, QStringLiteral( "spatialite" ), QStringLiteral( "QgsSpatiaLiteFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ); + } } } @@ -395,6 +399,7 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString &whereClause, QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), QObject::tr( "SpatiaLite" ) ); return false; } + mLastSql = sql; } catch ( QgsSpatiaLiteProvider::SLFieldNotFound ) { diff --git a/src/providers/spatialite/qgsspatialitefeatureiterator.h b/src/providers/spatialite/qgsspatialitefeatureiterator.h index a06d93cc86a..ac49e0206a5 100644 --- a/src/providers/spatialite/qgsspatialitefeatureiterator.h +++ b/src/providers/spatialite/qgsspatialitefeatureiterator.h @@ -18,6 +18,7 @@ #include "qgsfeatureiterator.h" #include "qgsfields.h" #include "qgscoordinatetransform.h" +#include "qgsdbquerylog.h" extern "C" { @@ -113,6 +114,11 @@ class QgsSpatiaLiteFeatureIterator final: public QgsAbstractFeatureIteratorFromS QgsCoordinateTransform mTransform; QgsGeometry mDistanceWithinGeom; std::unique_ptr< QgsGeometryEngine > mDistanceWithinEngine; + + // Last prepared sql statement for logging purposes + QString mLastSql; + + std::unique_ptr mQueryLogWrapper; }; #endif // QGSSPATIALITEFEATUREITERATOR_H diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 665ec264358..da7f8573d1e 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -33,6 +33,7 @@ email : a.furieri@lqt.it #include "qgsspatialiteconnection.h" #include "qgsspatialitetransaction.h" #include "qgsspatialiteproviderconnection.h" +#include "qgsdbquerylog.h" #include "qgsjsonutils.h" #include "qgsvectorlayer.h" @@ -225,7 +226,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString try { - int ret = sqlite3_exec( sqliteHandle, "BEGIN", nullptr, nullptr, &errMsg ); + int ret = exec_sql( sqliteHandle, "BEGIN", uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); @@ -237,14 +238,14 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString sql = QStringLiteral( "DROP TABLE IF EXISTS %1" ) .arg( QgsSqliteUtils::quotedIdentifier( tableName ) ); - ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); sql = QStringLiteral( "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%1)" ) .arg( QgsSqliteUtils::quotedString( tableName ) ); - ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); } @@ -255,7 +256,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString primaryKeyType, primaryKeyType == QLatin1String( "INTEGER" ) ? QStringLiteral( " AUTOINCREMENT" ) : QString() ); - ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); @@ -333,7 +334,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString .arg( QgsSqliteUtils::quotedString( geometryType ) ) .arg( dim ); - ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); } @@ -342,7 +343,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString geometryColumn = QString(); } - ret = sqlite3_exec( sqliteHandle, "COMMIT", nullptr, nullptr, &errMsg ); + ret = exec_sql( sqliteHandle, "COMMIT", uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) throw SLException( errMsg ); @@ -363,7 +364,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString if ( toCommit ) { // ROLLBACK after some previous error - sqlite3_exec( sqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr ); + exec_sql( sqliteHandle, "ROLLBACK", uri, nullptr, QGS_QUERY_LOG_ORIGIN ); } QgsSqliteHandle::closeDb( handle ); @@ -486,7 +487,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri, const Provider for ( const auto &pragma : pragmaList ) { char *errMsg = nullptr; - int ret = exec_sql( QStringLiteral( "PRAGMA %1" ).arg( pragma ), errMsg ); + int ret = exec_sql( mSqliteHandle, QStringLiteral( "PRAGMA %1" ).arg( pragma ), uri, errMsg ); if ( ret != SQLITE_OK ) { QgsDebugMsg( QStringLiteral( "PRAGMA " ) + pragma + QString( " failed : %1" ).arg( errMsg ? errMsg : "" ) ); @@ -1143,14 +1144,15 @@ void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, if ( ! savepointId.isEmpty() ) { // ROLLBACK after some previous error - ( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO \"%1\"" ).arg( savepointId ) ); + ( void )exec_sql( sqliteHandle(), QStringLiteral( "ROLLBACK TRANSACTION TO \"%1\"" ).arg( savepointId ), uri().uri(), nullptr, QGS_QUERY_LOG_ORIGIN ); } } -int QgsSpatiaLiteProvider::exec_sql( const QString &sql, char *errMsg ) +int QgsSpatiaLiteProvider::exec_sql( sqlite3 *handle, const QString &sql, const QString &uri, char *errMsg, const QString &origin ) { + QgsDatabaseQueryLogWrapper logWrapper( sql, uri, QStringLiteral( "spatialite" ), QStringLiteral( "QgsSpatiaLiteProvider" ), origin ); // Use transaction's handle (if any) - return sqlite3_exec( sqliteHandle(), sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + return sqlite3_exec( handle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); } sqlite3 *QgsSpatiaLiteProvider::sqliteHandle() const @@ -1391,7 +1393,7 @@ bool QgsSpatiaLiteProvider::hasRowid() // table without rowid column QString sql = QStringLiteral( "SELECT rowid FROM %1 WHERE 0" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ); char *errMsg = nullptr; - return exec_sql( sql, errMsg ) == SQLITE_OK; + return exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ) == SQLITE_OK; } @@ -4154,7 +4156,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret == SQLITE_OK ) { toCommit = true; @@ -4354,7 +4356,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) { - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); } } // BEGIN statement @@ -4370,7 +4372,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) if ( toCommit ) { // ROLLBACK after some previous error - ( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO SAVEPOINT \"%1\"" ).arg( savepointId ) ); + ( void )exec_sql( sqliteHandle(), QStringLiteral( "ROLLBACK TRANSACTION TO SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), nullptr, QGS_QUERY_LOG_ORIGIN ); } } else @@ -4402,7 +4404,7 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field ) const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4415,14 +4417,14 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field ) .arg( createIndexName( mTableName, fieldName ), mTableName, QgsSqliteUtils::quotedIdentifier( fieldName ) ); - ret = exec_sql( sql, errMsg ); + ret = exec_sql( sqliteHandle(), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); return false; } - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4457,7 +4459,7 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id ) const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4506,7 +4508,7 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id ) sqlite3_finalize( stmt ); - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4526,7 +4528,7 @@ bool QgsSpatiaLiteProvider::truncate() const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4534,14 +4536,14 @@ bool QgsSpatiaLiteProvider::truncate() } sql = QStringLiteral( "DELETE FROM %1" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ); - ret = exec_sql( sql, errMsg ); + ret = exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); return false; } - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4564,7 +4566,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList &attributes ) const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4577,7 +4579,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList &attributes ) .arg( mTableName, iter->name(), iter->typeName() ); - ret = exec_sql( sql, errMsg ); + ret = exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4585,7 +4587,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList &attributes ) } } - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4614,7 +4616,7 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4781,7 +4783,7 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap } } - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -4802,7 +4804,7 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; - int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, QString() ); @@ -4860,7 +4862,7 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry sqlite3_finalize( stmt ); - ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); + ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ); if ( ret != SQLITE_OK ) { handleError( sql, errMsg, savepointId ); @@ -6177,7 +6179,7 @@ bool QgsSpatiaLiteProviderMetadata::saveStyle( const QString &uri, const QString ",ui text" ",update_time timestamp DEFAULT CURRENT_TIMESTAMP" ")" ); - ret = sqlite3_exec( sqliteHandle, createQuery.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = QgsSpatiaLiteProvider::exec_sql( sqliteHandle, createQuery.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( SQLITE_OK != ret ) { QgsSqliteHandle::closeDb( handle ); @@ -6269,7 +6271,7 @@ bool QgsSpatiaLiteProviderMetadata::saveStyle( const QString &uri, const QString sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql ); } - ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); + ret = QgsSpatiaLiteProvider::exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN ); if ( SQLITE_OK != ret ) { QgsSqliteHandle::closeDb( handle ); diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h index 31916a46fc6..42dda06c28a 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.h +++ b/src/providers/spatialite/qgsspatialiteprovider.h @@ -193,6 +193,11 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider */ QgsSqliteHandle *mHandle = nullptr; + /** + * Sqlite exec sql wrapper for SQL logging + */ + static int exec_sql( sqlite3 *handle, const QString &sql, const QString &uri, char *errMsg = nullptr, const QString &origin = QString() ); + private: //! Loads fields from input file to member mAttributeFields @@ -397,11 +402,6 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider */ void handleError( const QString &sql, char *errorMessage, const QString &savepointId ); - /** - * Sqlite exec sql wrapper for SQL logging - */ - int exec_sql( const QString &sql, char *errMsg = nullptr ); - /** * Returns the sqlite handle to be used, if we are inside a transaction it will be the transaction's handle */ From 7e62607c749e6c1ae7d8df7c1e396baea50e45c7 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 21 Apr 2022 09:58:09 +0200 Subject: [PATCH 17/24] Fix oracle query --- src/providers/oracle/qgsoracleproviderconnection.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/providers/oracle/qgsoracleproviderconnection.cpp b/src/providers/oracle/qgsoracleproviderconnection.cpp index d8513699f99..b8f7ec5851c 100644 --- a/src/providers/oracle/qgsoracleproviderconnection.cpp +++ b/src/providers/oracle/qgsoracleproviderconnection.cpp @@ -1377,8 +1377,10 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsOracleProviderConnection:: } if ( feedback && feedback->isCanceled() ) + { logWrapper.setCanceled(); - return QgsAbstractDatabaseProviderConnection::QueryResult(); + return QgsAbstractDatabaseProviderConnection::QueryResult(); + } if ( qry.isActive() ) { From 019bbdcdb8b72bdf4344128150ca99937890429a Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 21 Apr 2022 13:28:22 +0200 Subject: [PATCH 18/24] Fix docs --- src/app/devtools/querylogger/qgsqueryloggernode.cpp | 4 ++-- src/app/devtools/querylogger/qgsqueryloggernode.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp index 9dbea975635..e4e0c0b8d1f 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggernode.cpp @@ -238,9 +238,9 @@ QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLogger return QString(); } -void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &newSql ) +void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &sql ) { - mSql = newSql; + mSql = sql; } const QString &QgsDatabaseQueryLoggerQueryGroup::sql() const diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h index dbe9910e4f4..58a85b95d1b 100644 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ b/src/app/devtools/querylogger/qgsqueryloggernode.h @@ -107,7 +107,7 @@ class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup /** * Sets the SQL to \a sql. */ - void setSql( const QString &newSql ); + void setSql( const QString &sql ); /** * Returns the group SQL. From 6fa10e2631d0cd7379b9ab07d2c199ae2555fe06 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 26 Apr 2022 12:52:11 +0200 Subject: [PATCH 19/24] Address PR comments --- src/app/CMakeLists.txt | 2 +- .../querylogger/qgsappquerylogger.cpp | 2 +- .../querylogger/qgsqueryloggernode.cpp | 251 ------------------ .../devtools/querylogger/qgsqueryloggernode.h | 125 --------- .../querylogger/qgsqueryloggerpanelwidget.cpp | 2 +- src/providers/oracle/qgsoracledataitems.cpp | 14 +- src/providers/oracle/qgsoracleprovider.cpp | 201 ++++---------- src/providers/oracle/qgsoracleprovider.h | 1 - 8 files changed, 68 insertions(+), 530 deletions(-) delete mode 100644 src/app/devtools/querylogger/qgsqueryloggernode.cpp delete mode 100644 src/app/devtools/querylogger/qgsqueryloggernode.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index cccf8de1cc8..b3d4784632c 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -171,7 +171,7 @@ set(QGIS_APP_SRCS devtools/profiler/qgsprofilerpanelwidget.cpp devtools/profiler/qgsprofilerwidgetfactory.cpp devtools/querylogger/qgsappquerylogger.cpp - devtools/querylogger/qgsqueryloggernode.cpp + devtools/querylogger/qgsdatabasequeryloggernode.cpp devtools/querylogger/qgsqueryloggerpanelwidget.cpp devtools/querylogger/qgsqueryloggerwidgetfactory.cpp diff --git a/src/app/devtools/querylogger/qgsappquerylogger.cpp b/src/app/devtools/querylogger/qgsappquerylogger.cpp index b1db05db0cd..80f60be0774 100644 --- a/src/app/devtools/querylogger/qgsappquerylogger.cpp +++ b/src/app/devtools/querylogger/qgsappquerylogger.cpp @@ -14,7 +14,7 @@ ***************************************************************************/ #include "qgsappquerylogger.h" -#include "qgsqueryloggernode.h" +#include "qgsdatabasequeryloggernode.h" #include "qgsapplication.h" #include "devtools/qgsdevtoolsmodelnode.h" #include "qgssettings.h" diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.cpp b/src/app/devtools/querylogger/qgsqueryloggernode.cpp deleted file mode 100644 index e4e0c0b8d1f..00000000000 --- a/src/app/devtools/querylogger/qgsqueryloggernode.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/*************************************************************************** - QgsDatabaseQueryLoggernode.cpp - ------------------------- - begin : October 2021 - copyright : (C) 2021 by Nyall Dawson - email : nyall dot dawson at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsqueryloggernode.h" -#include "qgis.h" -#include "qgsjsonutils.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// -// QgsDatabaseQueryLoggerRootNode -// - -QgsDatabaseQueryLoggerRootNode::QgsDatabaseQueryLoggerRootNode() - : QgsDevToolsModelGroup( QString() ) -{ - -} - -QVariant QgsDatabaseQueryLoggerRootNode::data( int ) const -{ - return QVariant(); -} - -void QgsDatabaseQueryLoggerRootNode::removeRow( int row ) -{ - mChildren.erase( mChildren.begin() + row ); -} - -QVariant QgsDatabaseQueryLoggerRootNode::toVariant() const -{ - QVariantList res; - for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren ) - res << child->toVariant(); - return res; -} - - -// -// QgsDatabaseQueryLoggerQueryGroup -// - -QgsDatabaseQueryLoggerQueryGroup::QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ) - : QgsDevToolsModelGroup( QString() ) - , mSql( query.query ) - , mQueryId( query.queryId ) -{ -#if 0 - std::unique_ptr< QgsNetworkLoggerRequestDetailsGroup > detailsGroup = std::make_unique< QgsNetworkLoggerRequestDetailsGroup >( request ); - mDetailsGroup = detailsGroup.get(); - addChild( std::move( detailsGroup ) ); -#endif - - addKeyValueNode( QObject::tr( "Provider" ), query.provider ); - addKeyValueNode( QObject::tr( "URI" ), query.uri ); - addKeyValueNode( QObject::tr( "Started at" ), QDateTime::fromMSecsSinceEpoch( query.startedTime ).toString( Qt::ISODateWithMs ) ); -#if 0 - addKeyValueNode( QObject::tr( "Thread" ), query.originatingThreadId() ); -#endif - addKeyValueNode( QObject::tr( "Initiator" ), query.initiatorClass.isEmpty() ? QObject::tr( "unknown" ) : query.initiatorClass ); - if ( !query.origin.isEmpty() ) - addKeyValueNode( QObject::tr( "Location" ), query.origin ); - -} - -QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const -{ - switch ( role ) - { - case Qt::DisplayRole: - return QStringLiteral( "%1 %2" ).arg( QString::number( mQueryId ), - mSql ); - - case Qt::ToolTipRole: - { - // Show no more than 255 characters - return mSql.length() > 255 ? mSql.mid( 0, 255 ).append( QStringLiteral( "…" ) ) : mSql; - -#if 0 - QString bytes = QObject::tr( "unknown" ); - if ( mBytesTotal != 0 ) - { - if ( mBytesReceived > 0 && mBytesReceived < mBytesTotal ) - bytes = QStringLiteral( "%1/%2" ).arg( QString::number( mBytesReceived ), QString::number( mBytesTotal ) ); - else if ( mBytesReceived > 0 && mBytesReceived == mBytesTotal ) - bytes = QString::number( mBytesTotal ); - } - // ?? adding
instead of \n after (very long) url seems to break url up - // COMPLETE, Status: 200 - text/xml; charset=utf-8 - 2334 bytes - 657 milliseconds - return QStringLiteral( "%1
%2 - Status: %3 - %4 - %5 bytes - %6 msec - %7 replies" ) - .arg( mUrl.url(), - statusToString( mStatus ), - QString::number( mHttpStatus ), - mContentType, - bytes, - mStatus == Status::Pending ? QString::number( mTimer.elapsed() / 1000 ) : QString::number( mTotalTime ), - QString::number( mReplies ) ); -#endif - } - - case RoleStatus: - return static_cast< int >( mStatus ); - - case RoleId: - return mQueryId; - - case Qt::ForegroundRole: - { - switch ( mStatus ) - { - case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: - case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: - return QBrush( QColor( 0, 0, 0, 100 ) ); - case QgsDatabaseQueryLoggerQueryGroup::Status::Error: - return QBrush( QColor( 235, 10, 10 ) ); - case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: - return QBrush( QColor( 235, 10, 10 ) ); - case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: - break; - } - break; - } - - case Qt::FontRole: - { - if ( mStatus == Status::Canceled ) - { - QFont f; - f.setStrikeOut( true ); - return f; - } - break; - } - - default: - break; - } - return QVariant( ); -} - -QList QgsDatabaseQueryLoggerQueryGroup::actions( QObject *parent ) -{ - QList< QAction * > res; - - QAction *copyUrlAction = new QAction( QObject::tr( "Copy SQL" ), parent ); - QObject::connect( copyUrlAction, &QAction::triggered, copyUrlAction, [ = ] - { - QApplication::clipboard()->setText( mSql ); - } ); - res << copyUrlAction; - - QAction *copyJsonAction = new QAction( QObject::tr( "Copy as JSON" ), parent ); - QObject::connect( copyJsonAction, &QAction::triggered, copyJsonAction, [ = ] - { - const QVariant value = toVariant(); - const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( value ).dump( 2 ) ); - QApplication::clipboard()->setText( json ); - - } ); - res << copyJsonAction; - - return res; -} - -QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const -{ - QVariantMap res; - res.insert( QStringLiteral( "SQL" ), mSql ); - - for ( const auto &child : std::as_const( mChildren ) ) - { - if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) ) - { - res.insert( valueNode->key(), valueNode->value() ); - } - } - return res; -} - -void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEntry &query ) -{ - if ( query.error.isEmpty() ) - { - mStatus = query.canceled ? Status::Canceled : Status::Complete; - addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); - if ( query.fetchedRows != -1 ) - { - addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) ); - } - } - else - { - mStatus = Status::Error; - addKeyValueNode( QObject::tr( "Error" ), query.error ); - } -} - -void QgsDatabaseQueryLoggerQueryGroup::setStatus( QgsDatabaseQueryLoggerQueryGroup::Status status ) -{ - mStatus = status; -} - -QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLoggerQueryGroup::Status status ) -{ - switch ( status ) - { - case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: - return QObject::tr( "Pending" ); - case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: - return QObject::tr( "Complete" ); - case QgsDatabaseQueryLoggerQueryGroup::Status::Error: - return QObject::tr( "Error" ); - case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: - return QObject::tr( "Timeout" ); - case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: - return QObject::tr( "Canceled" ); - } - return QString(); -} - -void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &sql ) -{ - mSql = sql; -} - -const QString &QgsDatabaseQueryLoggerQueryGroup::sql() const -{ - return mSql; -} - - diff --git a/src/app/devtools/querylogger/qgsqueryloggernode.h b/src/app/devtools/querylogger/qgsqueryloggernode.h deleted file mode 100644 index 58a85b95d1b..00000000000 --- a/src/app/devtools/querylogger/qgsqueryloggernode.h +++ /dev/null @@ -1,125 +0,0 @@ -/*************************************************************************** - QgsDatabaseQueryLoggernode.h - ------------------------- - begin : October 2021 - copyright : (C) 2021 by Nyall Dawson - email : nyall dot dawson at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -#ifndef QGSDBQUERYLOGGERNODE_H -#define QGSDBQUERYLOGGERNODE_H - -#include -#include -#include -#include -#include -#include -#include "devtools/qgsdevtoolsmodelnode.h" -#include "qgsdbquerylog.h" - -class QAction; - -/** - * \ingroup app - * \class QgsDatabaseQueryLoggerRootNode - * \brief Root node for the query logger model. - */ -class QgsDatabaseQueryLoggerRootNode final : public QgsDevToolsModelGroup -{ - public: - - QgsDatabaseQueryLoggerRootNode(); - QVariant data( int role = Qt::DisplayRole ) const override final; - - /** - * Removes a \a row from the root group. - */ - void removeRow( int row ); - - QVariant toVariant() const override; -}; - - -/** - * \ingroup app - * \class QgsDatabaseQueryLoggerQueryGroup - * \brief Parent group for all database queries, showing the query id, SQL - * and containing child groups with detailed query and result information. - * - * Visually, a QgsDatabaseQueryLoggerQueryGroup is structured by: - * - * |__ QgsDatabaseQueryLoggerQueryGroup (showing sql, uri,...) - * |__ QgsDevToolsModelValueNode(key-value pairs with info) - * ... - */ -class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup -{ - public: - - //! Query status - enum class Status - { - Pending, //!< Query underway - Complete, //!< Query was successfully completed - Error, //!< Query encountered an error - TimeOut, //!< Query timed out - Canceled, //!< Query was manually canceled - }; - - /** - * Constructor for QgsDatabaseQueryLoggerQueryGroup, populated from the - * specified \a query details. - */ - QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ); - QVariant data( int role = Qt::DisplayRole ) const override; - QList< QAction * > actions( QObject *parent ) override final; - QVariant toVariant() const override; - - /** - * Called when the \a query is finished. - * - * Will automatically create children encapsulating the completed details. - */ - void setFinished( const QgsDatabaseQueryLogEntry &query ); - - /** - * Returns the query's status. - */ - Status status() const { return mStatus; } - - /** - * Set the query \a status - */ - void setStatus( Status status ); - - /** - * Converts a request \a status to a translated string value. - */ - static QString statusToString( Status status ); - - /** - * Sets the SQL to \a sql. - */ - void setSql( const QString &sql ); - - /** - * Returns the group SQL. - */ - const QString &sql() const; - - private: - - QString mSql; - int mQueryId = 0; - QByteArray mData; - Status mStatus = Status::Pending; -}; - -#endif // QGSDBQUERYLOGGERNODE_H diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp index ede668c0be6..978dfefb007 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp @@ -17,7 +17,7 @@ #include "qgsguiutils.h" #include "qgsjsonutils.h" #include "qgsqueryloggerpanelwidget.h" -#include "qgsqueryloggernode.h" +#include "qgsdatabasequeryloggernode.h" #include "qgsappquerylogger.h" #include "qgssettings.h" diff --git a/src/providers/oracle/qgsoracledataitems.cpp b/src/providers/oracle/qgsoracledataitems.cpp index ef8d944d873..2048e697e5b 100644 --- a/src/providers/oracle/qgsoracledataitems.cpp +++ b/src/providers/oracle/qgsoracledataitems.cpp @@ -60,10 +60,10 @@ bool deleteLayer( const QString &uri, QString &errCause ) QSqlQuery qry( *conn ); // check the geometry column count - if ( !QgsOracleProvider::exec( qry, QString( "SELECT count(*)" - " FROM user_tab_columns" - " WHERE table_name=? AND data_type='SDO_GEOMETRY' AND data_type_owner='MDSYS'" ), - QVariantList() << tableName ) + if ( !QgsOracleProvider::execLoggedStatic( qry, QString( "SELECT count(*)" + " FROM user_tab_columns" + " WHERE table_name=? AND data_type='SDO_GEOMETRY' AND data_type_owner='MDSYS'" ), + QVariantList() << tableName, dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN ) || !qry.next() ) { errCause = QObject::tr( "Unable to determine number of geometry columns of layer %1.%2: \n%3" ) @@ -97,7 +97,7 @@ bool deleteLayer( const QString &uri, QString &errCause ) args << tableName; } - if ( !QgsOracleProvider::exec( qry, dropTable, QVariantList() ) ) + if ( !QgsOracleProvider::execLoggedStatic( qry, dropTable, QVariantList(), dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN ) ) { errCause = QObject::tr( "Unable to delete layer %1.%2: \n%3" ) .arg( ownerName ) @@ -107,7 +107,7 @@ bool deleteLayer( const QString &uri, QString &errCause ) return false; } - if ( !QgsOracleProvider::exec( qry, cleanView, args ) ) + if ( !QgsOracleProvider::execLoggedStatic( qry, cleanView, args, dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN ) ) { errCause = QObject::tr( "Unable to clean metadata %1.%2: \n%3" ) .arg( ownerName ) @@ -544,7 +544,7 @@ QVector QgsOracleRootItem::createChildren() { QVector connections; const QStringList list = QgsOracleConn::connectionList(); - for ( QString connName : list ) + for ( const QString &connName : std::as_const( list ) ) { connections << new QgsOracleConnectionItem( this, connName, mPath + '/' + connName ); } diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 95994154937..73839590f0e 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -52,7 +52,7 @@ #include "ocispatial/wkbptr.h" -#define LoggedExecStatic(query, sql, args, uri, _class) execLoggedStatic( query, sql, args, uri, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) +#define LoggedExecStatic(query, sql, args, uri ) execLoggedStatic( query, sql, args, uri, QStringLiteral( "QgsOracleProvider" ), QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") ) const QString ORACLE_KEY = "oracle"; @@ -285,13 +285,14 @@ QgsOracleConn *QgsOracleProvider::connectionRO() const return mTransaction ? mTransaction->connection() : QgsOracleConn::connectDb( mUri, false ); } -bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &args ) +bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass, const QString &queryOrigin ) { - QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); + QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; qry.setForwardOnly( true ); bool res = qry.prepare( sql ); + if ( res ) { for ( const auto &arg : args ) @@ -309,13 +310,6 @@ bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &a .arg( qry.lastError().text() ) ); } - return res; -} - -bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass, const QString &queryOrigin ) -{ - QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; - const bool res { exec( qry, sql, args ) }; logWrapper.setQuery( getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); // ORACLE does not support size so this will always be -1 @@ -614,12 +608,12 @@ bool QgsOracleProvider::loadFields() QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 ); if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), - QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + QVariantList() << mOwnerName << mTableName, mUri.uri() ) ) { if ( qry.next() ) mDataComment = qry.value( 0 ).toString(); else if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), - QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + QVariantList() << mOwnerName << mTableName, mUri.uri() ) ) { if ( qry.next() ) mDataComment = qry.value( 0 ).toString(); @@ -638,7 +632,7 @@ bool QgsOracleProvider::loadFields() qry.finish(); if ( LoggedExecStatic( qry, QStringLiteral( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ), - QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + QVariantList() << mOwnerName << mTableName, mUri.uri() ) ) { while ( qry.next() ) { @@ -689,7 +683,7 @@ bool QgsOracleProvider::loadFields() sql += " ORDER BY t.column_id"; - if ( LoggedExecStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( LoggedExecStatic( qry, sql, args, mUri.uri() ) ) { long long fetchedRows { 0 }; while ( qry.next() ) @@ -759,7 +753,7 @@ bool QgsOracleProvider::loadFields() .arg( quotedIdentifier( mGeometryColumn ) ) .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) }; - mHasSpatialIndex = LoggedExecStatic( qry, sql, {}, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ); + mHasSpatialIndex = LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ); } if ( !mHasSpatialIndex ) @@ -776,7 +770,7 @@ bool QgsOracleProvider::loadFields() const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; - if ( !LoggedExecStatic( qry, sql, {}, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { const QString error { tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ) }; QgsMessageLog::logMessage( error, tr( "Oracle" ) ); @@ -843,7 +837,7 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() else { if ( LoggedExecStatic( qry, QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ), - QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + QVariantList() << mOwnerName << mTableName, mUri.uri() ) ) { // check grants long long fetchedRows { 0 }; @@ -874,7 +868,7 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() { if ( LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ), - QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) ) { if ( qry.next() ) mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries; @@ -912,7 +906,7 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities() } const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) }; - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) @@ -971,7 +965,7 @@ bool QgsOracleProvider::determinePrimaryKey() } else { - if ( !LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName, mUri.uri() ) ) { const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) @@ -1073,7 +1067,7 @@ bool QgsOracleProvider::uniqueData( QString query, QString colName ) QString sql = QString( "SELECT (SELECT count(distinct %1) FROM %2)-(SELECT count(%1) FROM %2) FROM dual" ) .arg( colName.startsWith( QLatin1String( "qgis_generated_uid_" ) ) ? colName : quotedIdentifier( colName ), mQuery ); - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) || !qry.next() ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) || !qry.next() ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1104,7 +1098,7 @@ QVariant QgsOracleProvider::minimumValue( int index ) const QSqlQuery qry( *conn ); - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1154,7 +1148,7 @@ QSet QgsOracleProvider::uniqueValues( int index, int limit ) const QSqlQuery qry( *conn ); - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1196,7 +1190,7 @@ QVariant QgsOracleProvider::maximumValue( int index ) const QSqlQuery qry( *conn ); - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); @@ -1280,7 +1274,7 @@ QVariant QgsOracleProvider::evaluateDefaultExpression( const QString &value, con const QString sql { QStringLiteral( "SELECT %1 FROM dual" ).arg( value ) }; - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) || !qry.next() ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) || !qry.next() ) { throw OracleException( tr( "Evaluation of default value failed" ), qry ); } @@ -1429,17 +1423,11 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag ins.addBindValue( value ); } - QgsDatabaseQueryLogWrapper logWrapper { insert, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !ins.exec() ) { - logWrapper.setQuery( getLastExecutedQuery( ins ) ); - logWrapper.setError( ins.lastError().text() ); throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins ); } - logWrapper.setQuery( getLastExecutedQuery( ins ) ); - if ( !( flags & QgsFeatureSink::FastInsert ) ) { if ( mPrimaryKeyType == PktRowId ) @@ -1453,13 +1441,8 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag { getfid.addBindValue( QVariant( ins.lastInsertId() ) ); - QgsDatabaseQueryLogWrapper logWrapper { insert, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - const bool result { getfid.exec() }; - if ( ! result ) - { - logWrapper.setError( getfid.lastError().text() ); - } + if ( !result || !getfid.next() ) throw OracleException( tr( "Could not retrieve feature id %1" ).arg( features->id() ), getfid ); @@ -1562,7 +1545,7 @@ bool QgsOracleProvider::deleteFeatures( const QgsFeatureIds &id ) .arg( mQuery, whereClause( *it, args ) ); QgsDebugMsgLevel( "delete sql: " + sql, 2 ); - if ( !LoggedExecStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, args, mUri.uri() ) ) { throw OracleException( tr( "Deletion of feature %1 failed" ).arg( *it ), qry ); } @@ -1635,7 +1618,7 @@ bool QgsOracleProvider::addAttributes( const QList &attributes ) .arg( mQuery, quotedIdentifier( iter->name() ), type ); QgsDebugMsgLevel( sql, 2 ); - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { throw OracleException( tr( "Adding attribute %1 failed" ).arg( iter->name() ), qry ); } @@ -1645,7 +1628,7 @@ bool QgsOracleProvider::addAttributes( const QList &attributes ) sql = QString( "COMMENT ON COLUMN %1.%2 IS ?" ) .arg( mQuery, quotedIdentifier( iter->name() ) ); - if ( !LoggedExecStatic( qry, sql, QVariantList() << iter->comment(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList() << iter->comment(), mUri.uri() ) ) { throw OracleException( tr( "Setting comment on %1 failed" ).arg( iter->name() ), qry ); } @@ -1712,7 +1695,7 @@ bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids ) .arg( mQuery, quotedIdentifier( fld.name() ) ); //send sql statement and do error handling - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { throw OracleException( tr( "Dropping column %1 failed" ).arg( fld.name() ), qry ); } @@ -1795,7 +1778,7 @@ bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttribut quotedIdentifier( src ), quotedIdentifier( renameIt.value() ) ) }; - if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { throw OracleException( tr( "Renaming column %1 to %2 failed" ) .arg( quotedIdentifier( src ), @@ -2480,11 +2463,9 @@ bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatu QSqlQuery qry( *conn ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { - logWrapper.setError( qry.lastError().text() ); pushError( qry.lastError().text() ); mSqlWhereClause = prevWhere; qry.finish(); @@ -2548,7 +2529,7 @@ long long QgsOracleProvider::featureCount() const QSqlQuery qry( *conn ); - if ( execLoggedStatic( qry, sql, args, mUri.uri(), QStringLiteral( "QgsOracleProvider" ) ) && qry.next() ) + if ( LoggedExecStatic( qry, sql, QVariantList( ), mUri.uri() ) && qry.next() ) { mFeaturesCounted = qry.value( 0 ).toLongLong(); } @@ -2580,17 +2561,15 @@ QgsRectangle QgsOracleProvider::extent() const const QString sql { QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( exec( qry, sql, - QVariantList() << mOwnerName << mTableName << mGeometryColumn ) && + if ( LoggedExecStatic( qry, sql, + QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) && qry.next() ) { mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() ); mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() ); - if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='Y'" ), - QVariantList() << mOwnerName << mTableName << mGeometryColumn ) && + if ( LoggedExecStatic( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='Y'" ), + QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) && qry.next() ) { mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() ); @@ -2598,26 +2577,14 @@ QgsRectangle QgsOracleProvider::extent() const return mLayerExtent; } } - else - { - logWrapper.setError( qry.lastError().text() ); - } - - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } if ( mHasSpatialIndex && mUseEstimatedMetadata ) { const QString sql { QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - ok = exec( qry, - sql, - QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn ); - logWrapper.setQuery( getLastExecutedQuery( qry ) ); - if ( ! ok ) - { - logWrapper.setError( qry.lastError().text() ); - } + ok = LoggedExecStatic( qry, + sql, + QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn, mUri.uri() ); } } @@ -2628,13 +2595,7 @@ QgsRectangle QgsOracleProvider::extent() const if ( !mSqlWhereClause.isEmpty() ) sql += QString( " WHERE %1" ).arg( mSqlWhereClause ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - ok = exec( qry, sql, QVariantList() ); - if ( ! ok ) - { - logWrapper.setError( qry.lastError().text() ); - } + ok = LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ); } if ( ok && qry.next() ) @@ -2685,11 +2646,8 @@ bool QgsOracleProvider::getGeometryDetails() { const QString sql { QStringLiteral( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not execute query.\nThe error message from the database was:\n%1.\nSQL: %2" ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ), tr( "Oracle" ) ); @@ -2716,10 +2674,9 @@ bool QgsOracleProvider::getGeometryDetails() { const QString sql {QStringLiteral( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( exec( qry, sql, - QVariantList() << ownerName << tableName << geomCol ) ) + if ( LoggedExecStatic( qry, sql, + QVariantList() << ownerName << tableName << geomCol, mUri.uri() ) ) { if ( qry.next() ) { @@ -2735,14 +2692,11 @@ bool QgsOracleProvider::getGeometryDetails() } else { - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) .arg( mQuery ) .arg( qry.lastError().text() ) .arg( qry.lastQuery() ), tr( "Oracle" ) ); } - - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } QString sql { mUseEstimatedMetadata ? @@ -2751,9 +2705,7 @@ bool QgsOracleProvider::getGeometryDetails() sql = sql.arg( quotedIdentifier( geomCol ), mQuery ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( exec( qry, sql, QVariantList() ) ) + if ( LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { if ( qry.next() ) { @@ -2773,7 +2725,6 @@ bool QgsOracleProvider::getGeometryDetails() } else { - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not determine geometry type of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) .arg( mQuery ) .arg( qry.lastError().text() ) @@ -2883,14 +2834,11 @@ bool QgsOracleProvider::createSpatialIndex() ") WHERE table_name=? AND column_name=?" ) }; { - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, - QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn ) + if ( !LoggedExecStatic( qry, sql, + QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn, mUri.uri() ) ) { - logWrapper.setQuery( getLastExecutedQuery( qry ) ); - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" ) .arg( mTableName ) .arg( mGeometryColumn ) @@ -2900,8 +2848,6 @@ bool QgsOracleProvider::createSpatialIndex() return false; } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); - logWrapper.setFetchedRows( qry.numRowsAffected() ); } if ( qry.numRowsAffected() == 0 ) @@ -2912,15 +2858,12 @@ bool QgsOracleProvider::createSpatialIndex() "mdsys.sdo_dim_element('Y', ?, ?, 0.001)" "))" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, - QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid ) - << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() ) + if ( !LoggedExecStatic( qry, sql, + QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid ) + << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum(), mUri.uri() ) ) { - logWrapper.setQuery( getLastExecutedQuery( qry ) ); - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" ) .arg( quotedValue( mTableName ) ) .arg( quotedValue( mGeometryColumn ) ) @@ -2929,7 +2872,6 @@ bool QgsOracleProvider::createSpatialIndex() tr( "Oracle" ) ); return false; } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } } else @@ -2950,11 +2892,8 @@ bool QgsOracleProvider::createSpatialIndex() const QString sql { QStringLiteral( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) ) { - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Rebuild of spatial index failed.\nSQL: %1\nError: %2" ) .arg( qry.lastQuery() ) .arg( qry.lastError().text() ), @@ -3128,18 +3067,12 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( { const QString sql { QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, - QVariantList() << ownerName << tableName - ) ) + if ( !LoggedExecStatic( qry, sql, + QVariantList() << ownerName << tableName, uri ) ) { - logWrapper.setQuery( getLastExecutedQuery( qry ) ); - logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Could not determine table existence." ), qry ); } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } bool exists = qry.next(); @@ -3152,11 +3085,9 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) ) { - logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Table %1 could not be dropped." ).arg( ownerTableName ), qry ); } } @@ -3184,11 +3115,8 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( sql += ")"; - QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) ) { - logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Table creation failed." ), qry ); } @@ -3218,11 +3146,8 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer( const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - - if ( !exec( qry, sql, QVariantList() ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) ) { - logWrapper.setError( qry.lastError().text() ); QgsMessageLog::logMessage( tr( "Drop created table %1 failed.\nSQL: %2\nError: %3" ) .arg( qry.lastQuery() ) .arg( qry.lastError().text() ), tr( "Oracle" ) ); @@ -3397,16 +3322,12 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & const QString sql { QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ) }; { - QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, QVariantList() << wkt ) ) + if ( !LoggedExecStatic( qry, sql, QVariantList() << wkt, conn->connInfo() ) ) { - logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( getLastExecutedQuery( qry ) ); throw OracleException( tr( "Could not lookup WKT." ), qry ); } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } if ( qry.next() ) @@ -3418,11 +3339,9 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & { const QString sql { QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, QVariantList() ) || !qry.next() ) + if ( !LoggedExecStatic( qry, sql, QVariantList(), conn->connInfo() ) || !qry.next() ) { - logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Could not determine new srid." ), qry ); } } @@ -3433,16 +3352,12 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & " INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)" " VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, - QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt ) ) + if ( !LoggedExecStatic( qry, sql, + QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt, conn->connInfo() ) ) { - logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( getLastExecutedQuery( qry ) ); throw OracleException( tr( "CRS not found and could not be created." ), qry ); } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); } } @@ -3451,9 +3366,9 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString & tableName, geometryColumn ), qry ); const QString sql { QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,%1)" ).arg( diminfo ) }; - QgsDatabaseQueryLogWrapper logWrapper { sql, conn->connInfo(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN }; - if ( !exec( qry, sql, - QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid ) ) + + if ( !LoggedExecStatic( qry, sql, + QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid, conn->connInfo() ) ) { throw OracleException( tr( "Could not insert metadata." ), qry ); } @@ -3466,7 +3381,7 @@ QgsCoordinateReferenceSystem QgsOracleProvider::lookupCrs( QgsOracleConn *conn, QSqlQuery qry( *conn ); // apparently some EPSG codes don't have the auth_name setup in cs_srs - if ( exec( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << srsid ) ) + if ( LoggedExecStatic( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << srsid, conn->connInfo() ) ) { if ( qry.next() ) { diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h index ccebb4602e0..50747fa0fef 100644 --- a/src/providers/oracle/qgsoracleprovider.h +++ b/src/providers/oracle/qgsoracleprovider.h @@ -174,7 +174,6 @@ class QgsOracleProvider final: public QgsVectorDataProvider QString description() const override; QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override; - static bool exec( QSqlQuery &qry, QString sql, const QVariantList &args ); static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); static QString getLastExecutedQuery( const QSqlQuery &query ); From b8801caa8617a03de531bfa33f68f0227712be01 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 26 Apr 2022 12:57:25 +0200 Subject: [PATCH 20/24] Add renamed files --- .../qgsdatabasequeryloggernode.cpp | 251 ++++++++++++++++++ .../querylogger/qgsdatabasequeryloggernode.h | 125 +++++++++ 2 files changed, 376 insertions(+) create mode 100644 src/app/devtools/querylogger/qgsdatabasequeryloggernode.cpp create mode 100644 src/app/devtools/querylogger/qgsdatabasequeryloggernode.h diff --git a/src/app/devtools/querylogger/qgsdatabasequeryloggernode.cpp b/src/app/devtools/querylogger/qgsdatabasequeryloggernode.cpp new file mode 100644 index 00000000000..4ca9880306d --- /dev/null +++ b/src/app/devtools/querylogger/qgsdatabasequeryloggernode.cpp @@ -0,0 +1,251 @@ +/*************************************************************************** + qgsdatabasequeryloggernode.cpp + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsdatabasequeryloggernode.h" +#include "qgis.h" +#include "qgsjsonutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// +// QgsDatabaseQueryLoggerRootNode +// + +QgsDatabaseQueryLoggerRootNode::QgsDatabaseQueryLoggerRootNode() + : QgsDevToolsModelGroup( QString() ) +{ + +} + +QVariant QgsDatabaseQueryLoggerRootNode::data( int ) const +{ + return QVariant(); +} + +void QgsDatabaseQueryLoggerRootNode::removeRow( int row ) +{ + mChildren.erase( mChildren.begin() + row ); +} + +QVariant QgsDatabaseQueryLoggerRootNode::toVariant() const +{ + QVariantList res; + for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren ) + res << child->toVariant(); + return res; +} + + +// +// QgsDatabaseQueryLoggerQueryGroup +// + +QgsDatabaseQueryLoggerQueryGroup::QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ) + : QgsDevToolsModelGroup( QString() ) + , mSql( query.query ) + , mQueryId( query.queryId ) +{ +#if 0 + std::unique_ptr< QgsNetworkLoggerRequestDetailsGroup > detailsGroup = std::make_unique< QgsNetworkLoggerRequestDetailsGroup >( request ); + mDetailsGroup = detailsGroup.get(); + addChild( std::move( detailsGroup ) ); +#endif + + addKeyValueNode( QObject::tr( "Provider" ), query.provider ); + addKeyValueNode( QObject::tr( "URI" ), query.uri ); + addKeyValueNode( QObject::tr( "Started at" ), QDateTime::fromMSecsSinceEpoch( query.startedTime ).toString( Qt::ISODateWithMs ) ); +#if 0 + addKeyValueNode( QObject::tr( "Thread" ), query.originatingThreadId() ); +#endif + addKeyValueNode( QObject::tr( "Initiator" ), query.initiatorClass.isEmpty() ? QObject::tr( "unknown" ) : query.initiatorClass ); + if ( !query.origin.isEmpty() ) + addKeyValueNode( QObject::tr( "Location" ), query.origin ); + +} + +QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + return QStringLiteral( "%1 %2" ).arg( QString::number( mQueryId ), + mSql ); + + case Qt::ToolTipRole: + { + // Show no more than 255 characters + return mSql.length() > 255 ? mSql.mid( 0, 255 ).append( QStringLiteral( "…" ) ) : mSql; + +#if 0 + QString bytes = QObject::tr( "unknown" ); + if ( mBytesTotal != 0 ) + { + if ( mBytesReceived > 0 && mBytesReceived < mBytesTotal ) + bytes = QStringLiteral( "%1/%2" ).arg( QString::number( mBytesReceived ), QString::number( mBytesTotal ) ); + else if ( mBytesReceived > 0 && mBytesReceived == mBytesTotal ) + bytes = QString::number( mBytesTotal ); + } + // ?? adding
instead of \n after (very long) url seems to break url up + // COMPLETE, Status: 200 - text/xml; charset=utf-8 - 2334 bytes - 657 milliseconds + return QStringLiteral( "%1
%2 - Status: %3 - %4 - %5 bytes - %6 msec - %7 replies" ) + .arg( mUrl.url(), + statusToString( mStatus ), + QString::number( mHttpStatus ), + mContentType, + bytes, + mStatus == Status::Pending ? QString::number( mTimer.elapsed() / 1000 ) : QString::number( mTotalTime ), + QString::number( mReplies ) ); +#endif + } + + case RoleStatus: + return static_cast< int >( mStatus ); + + case RoleId: + return mQueryId; + + case Qt::ForegroundRole: + { + switch ( mStatus ) + { + case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: + case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: + return QBrush( QColor( 0, 0, 0, 100 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Error: + return QBrush( QColor( 235, 10, 10 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: + return QBrush( QColor( 235, 10, 10 ) ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: + break; + } + break; + } + + case Qt::FontRole: + { + if ( mStatus == Status::Canceled ) + { + QFont f; + f.setStrikeOut( true ); + return f; + } + break; + } + + default: + break; + } + return QVariant( ); +} + +QList QgsDatabaseQueryLoggerQueryGroup::actions( QObject *parent ) +{ + QList< QAction * > res; + + QAction *copyUrlAction = new QAction( QObject::tr( "Copy SQL" ), parent ); + QObject::connect( copyUrlAction, &QAction::triggered, copyUrlAction, [ = ] + { + QApplication::clipboard()->setText( mSql ); + } ); + res << copyUrlAction; + + QAction *copyJsonAction = new QAction( QObject::tr( "Copy as JSON" ), parent ); + QObject::connect( copyJsonAction, &QAction::triggered, copyJsonAction, [ = ] + { + const QVariant value = toVariant(); + const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( value ).dump( 2 ) ); + QApplication::clipboard()->setText( json ); + + } ); + res << copyJsonAction; + + return res; +} + +QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const +{ + QVariantMap res; + res.insert( QStringLiteral( "SQL" ), mSql ); + + for ( const auto &child : std::as_const( mChildren ) ) + { + if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) ) + { + res.insert( valueNode->key(), valueNode->value() ); + } + } + return res; +} + +void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEntry &query ) +{ + if ( query.error.isEmpty() ) + { + mStatus = query.canceled ? Status::Canceled : Status::Complete; + addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) ); + if ( query.fetchedRows != -1 ) + { + addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) ); + } + } + else + { + mStatus = Status::Error; + addKeyValueNode( QObject::tr( "Error" ), query.error ); + } +} + +void QgsDatabaseQueryLoggerQueryGroup::setStatus( QgsDatabaseQueryLoggerQueryGroup::Status status ) +{ + mStatus = status; +} + +QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLoggerQueryGroup::Status status ) +{ + switch ( status ) + { + case QgsDatabaseQueryLoggerQueryGroup::Status::Pending: + return QObject::tr( "Pending" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Complete: + return QObject::tr( "Complete" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Error: + return QObject::tr( "Error" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut: + return QObject::tr( "Timeout" ); + case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled: + return QObject::tr( "Canceled" ); + } + return QString(); +} + +void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &sql ) +{ + mSql = sql; +} + +const QString &QgsDatabaseQueryLoggerQueryGroup::sql() const +{ + return mSql; +} + + diff --git a/src/app/devtools/querylogger/qgsdatabasequeryloggernode.h b/src/app/devtools/querylogger/qgsdatabasequeryloggernode.h new file mode 100644 index 00000000000..ee7f30d5e5d --- /dev/null +++ b/src/app/devtools/querylogger/qgsdatabasequeryloggernode.h @@ -0,0 +1,125 @@ +/*************************************************************************** + qgsdatabasequeryloggernode.h + ------------------------- + begin : October 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSDBQUERYLOGGERNODE_H +#define QGSDBQUERYLOGGERNODE_H + +#include +#include +#include +#include +#include +#include +#include "devtools/qgsdevtoolsmodelnode.h" +#include "qgsdbquerylog.h" + +class QAction; + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerRootNode + * \brief Root node for the query logger model. + */ +class QgsDatabaseQueryLoggerRootNode final : public QgsDevToolsModelGroup +{ + public: + + QgsDatabaseQueryLoggerRootNode(); + QVariant data( int role = Qt::DisplayRole ) const override final; + + /** + * Removes a \a row from the root group. + */ + void removeRow( int row ); + + QVariant toVariant() const override; +}; + + +/** + * \ingroup app + * \class QgsDatabaseQueryLoggerQueryGroup + * \brief Parent group for all database queries, showing the query id, SQL + * and containing child groups with detailed query and result information. + * + * Visually, a QgsDatabaseQueryLoggerQueryGroup is structured by: + * + * |__ QgsDatabaseQueryLoggerQueryGroup (showing sql, uri,...) + * |__ QgsDevToolsModelValueNode(key-value pairs with info) + * ... + */ +class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup +{ + public: + + //! Query status + enum class Status + { + Pending, //!< Query underway + Complete, //!< Query was successfully completed + Error, //!< Query encountered an error + TimeOut, //!< Query timed out + Canceled, //!< Query was manually canceled + }; + + /** + * Constructor for QgsDatabaseQueryLoggerQueryGroup, populated from the + * specified \a query details. + */ + QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query ); + QVariant data( int role = Qt::DisplayRole ) const override; + QList< QAction * > actions( QObject *parent ) override final; + QVariant toVariant() const override; + + /** + * Called when the \a query is finished. + * + * Will automatically create children encapsulating the completed details. + */ + void setFinished( const QgsDatabaseQueryLogEntry &query ); + + /** + * Returns the query's status. + */ + Status status() const { return mStatus; } + + /** + * Set the query \a status + */ + void setStatus( Status status ); + + /** + * Converts a request \a status to a translated string value. + */ + static QString statusToString( Status status ); + + /** + * Sets the SQL to \a sql. + */ + void setSql( const QString &sql ); + + /** + * Returns the group SQL. + */ + const QString &sql() const; + + private: + + QString mSql; + int mQueryId = 0; + QByteArray mData; + Status mStatus = Status::Pending; +}; + +#endif // QGSDBQUERYLOGGERNODE_H From c6c5b63e17a9471044b64ebfb3d71a611fdc9237 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 26 Apr 2022 16:59:18 +0200 Subject: [PATCH 21/24] Remove exec from conn --- src/providers/oracle/qgsoracleconn.cpp | 77 +------------------ src/providers/oracle/qgsoracleconn.h | 2 - src/providers/oracle/qgsoracletransaction.cpp | 8 +- 3 files changed, 3 insertions(+), 84 deletions(-) diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index af4246f6ab4..6254601f3cc 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -227,32 +227,6 @@ void QgsOracleConn::unref() delete this; } -bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms ) -{ - QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); - - bool res = qry.prepare( sql ); - if ( res ) - { - for ( const auto ¶m : params ) - { - QgsDebugMsgLevel( QStringLiteral( " ARG: %1 [%2]" ).arg( param.toString(), param.typeName() ), 4 ); - qry.addBindValue( param ); - } - - res = qry.exec(); - } - - if ( !res ) - { - QgsDebugMsg( QStringLiteral( "SQL: %1\nERROR: %2" ) - .arg( qry.lastQuery(), - qry.lastError().text() ) ); - } - - return res; -} - bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass, const QString &queryOrigin ) { QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); @@ -460,54 +434,16 @@ QString QgsOracleConn::quotedValue( const QVariant &value, QVariant::Type type ) return v.prepend( '\'' ).append( '\'' ); } -bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMessage ) -{ - QMutexLocker locker( &mLock ); - QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); - - QSqlQuery qry( mDatabase ); - - if ( !exec( qry, query, QVariantList() ) ) - { - QString error = qry.lastError().text(); - if ( logError ) - { - const QString errorMsg { tr( "Connection error: %1 returned %2" ) - .arg( query, error ) }; - QgsMessageLog::logMessage( errorMsg, - tr( "Oracle" ) ); - } - else - { - const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" ) - .arg( query, error ) }; - QgsDebugMsg( errorMsg ); - } - if ( errorMessage ) - *errorMessage = error; - return false; - } - return true; -} - bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) { - QMutexLocker locker( &mLock ); - QgsDatabaseQueryLogWrapper logWrapper { query, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; - - QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); - QSqlQuery qry( mDatabase ); - const bool res { !exec( qry, query, QVariantList() ) }; - - logWrapper.setQuery( qry.lastQuery() ); + const bool res { !execLogged( qry, query, QVariantList(), originatorClass, queryOrigin ) }; if ( ! res ) { const QString error = qry.lastError().text(); - logWrapper.setError( error ); if ( logError ) { const QString errorMsg { tr( "Connection error: %1 returned %2" ) @@ -525,17 +461,6 @@ bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *er *errorMessage = error; return false; } - else - { - if ( qry.isSelect() ) - { - logWrapper.setFetchedRows( qry.size() ); - } - else - { - logWrapper.setFetchedRows( qry.numRowsAffected() ); - } - } return true; } diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h index 27ec38371c6..27fde757d65 100644 --- a/src/providers/oracle/qgsoracleconn.h +++ b/src/providers/oracle/qgsoracleconn.h @@ -164,7 +164,6 @@ class QgsOracleConn : public QObject */ static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ); - bool exec( const QString &query, bool logError = true, QString *errorMessage = nullptr ); bool execLogged( const QString &sql, bool logError = true, QString *errorMessage = nullptr, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); bool begin( QSqlDatabase &db ); @@ -266,7 +265,6 @@ class QgsOracleConn : public QObject explicit QgsOracleConn( QgsDataSourceUri uri, bool transaction ); ~QgsOracleConn() override; - bool exec( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms ); bool execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); //! reference count diff --git a/src/providers/oracle/qgsoracletransaction.cpp b/src/providers/oracle/qgsoracletransaction.cpp index c218c96b530..f0de7ba7d9b 100644 --- a/src/providers/oracle/qgsoracletransaction.cpp +++ b/src/providers/oracle/qgsoracletransaction.cpp @@ -74,12 +74,8 @@ bool QgsOracleTransaction::executeSql( const QString &sql, QString &errorMsg, bo QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); - QgsDatabaseQueryLogWrapper logWrapper { sql, mConnString, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; - const bool res = mConn->exec( sql, true, &errorMsg ); - if ( ! errorMsg.isEmpty() ) - { - logWrapper.setError( errorMsg ); - } + const bool res = mConn->execLogged( sql, true, &errorMsg ); + if ( !res ) { if ( isDirty ) From 7218a18676b91aaa79da0b71fdc259c6da259df7 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 26 Apr 2022 17:05:24 +0200 Subject: [PATCH 22/24] Move getLastExecuteQuery to conn --- src/providers/oracle/qgsoracleconn.cpp | 27 ++++++++++++++++- src/providers/oracle/qgsoracleconn.h | 2 ++ src/providers/oracle/qgsoracleprovider.cpp | 34 ++++------------------ src/providers/oracle/qgsoracleprovider.h | 2 -- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index 6254601f3cc..4e9d8f442c5 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -27,6 +27,8 @@ #include "qgsvariantutils.h" #include +#include +#include QMap QgsOracleConn::sConnections; int QgsOracleConn::snConnections = 0; @@ -227,6 +229,29 @@ void QgsOracleConn::unref() delete this; } +QString QgsOracleConn::getLastExecutedQuery( const QSqlQuery &query ) +{ + QString str = query.lastQuery(); + QMapIterator it( query.boundValues() ); + while ( it.hasNext() ) + { + it.next(); + const QVariant &var { it.value().toString() }; + QSqlField field( QString( ), var.type() ); + if ( var.isNull() ) + { + field.clear(); + } + else + { + field.setValue( var ); + } + const QString formatV = query.driver()->formatValue( field ); + str.replace( it.key(), formatV ); + } + return str; +} + bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass, const QString &queryOrigin ) { QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); @@ -245,7 +270,7 @@ bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVaria res = qry.exec(); } - logWrapper.setQuery( qry.lastQuery() ); + logWrapper.setQuery( getLastExecutedQuery( qry ) ); if ( !res ) { diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h index 27fde757d65..330225d816b 100644 --- a/src/providers/oracle/qgsoracleconn.h +++ b/src/providers/oracle/qgsoracleconn.h @@ -261,6 +261,8 @@ class QgsOracleConn : public QObject operator QSqlDatabase() { return mDatabase; } + static QString getLastExecutedQuery( const QSqlQuery &query ); + private: explicit QgsOracleConn( QgsDataSourceUri uri, bool transaction ); ~QgsOracleConn() override; diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 73839590f0e..b8be26cb133 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -45,7 +45,6 @@ #endif #include -#include #include #include @@ -310,7 +309,7 @@ bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, co .arg( qry.lastError().text() ) ); } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); + logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); // ORACLE does not support size so this will always be -1 // we leave it here in case this changes in the future @@ -325,29 +324,6 @@ bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, co return res; } -QString QgsOracleProvider::getLastExecutedQuery( const QSqlQuery &query ) -{ - QString str = query.lastQuery(); - QMapIterator it( query.boundValues() ); - while ( it.hasNext() ) - { - it.next(); - const QVariant &var { it.value().toString() }; - QSqlField field( QString( ), var.type() ); - if ( var.isNull() ) - { - field.clear(); - } - else - { - field.setValue( var ); - } - const QString formatV = query.driver()->formatValue( field ); - str.replace( it.key(), formatV ); - } - return str; -} - void QgsOracleProvider::setTransaction( QgsTransaction *transaction ) { // static_cast since layers cannot be added to a transaction of a non-matching provider @@ -1921,12 +1897,12 @@ bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &at if ( !qry.exec() ) { logWrapper.setError( qry.lastError().text() ); - logWrapper.setQuery( getLastExecutedQuery( qry ) ); + logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); } qry.finish(); - logWrapper.setQuery( getLastExecutedQuery( qry ) ); + logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) ); // update feature id map if key was changed if ( pkChanged && mPrimaryKeyType == PktFidMap ) @@ -2404,11 +2380,11 @@ bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map if ( !qry.exec() ) { - logWrapper.setQuery( getLastExecutedQuery( qry ) ); + logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) ); logWrapper.setError( qry.lastError().text() ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); } - logWrapper.setQuery( getLastExecutedQuery( qry ) ); + logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) ); } qry.finish(); diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h index 50747fa0fef..230bf4bb25b 100644 --- a/src/providers/oracle/qgsoracleprovider.h +++ b/src/providers/oracle/qgsoracleprovider.h @@ -176,8 +176,6 @@ class QgsOracleProvider final: public QgsVectorDataProvider static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); - static QString getLastExecutedQuery( const QSqlQuery &query ); - bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; } void setTransaction( QgsTransaction *transaction ) override; QgsTransaction *transaction() const override; From 01a3f2c9a4e89b5b8a973e9232ec1dbd689431c1 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 26 Apr 2022 18:10:16 +0200 Subject: [PATCH 23/24] Revert "Remove exec from conn" This reverts commit c6c5b63e17a9471044b64ebfb3d71a611fdc9237. --- src/providers/oracle/qgsoracleconn.cpp | 86 +++++++++++++++++-- src/providers/oracle/qgsoracleconn.h | 2 + src/providers/oracle/qgsoracletransaction.cpp | 8 +- 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp index 4e9d8f442c5..4556b4e5cc6 100644 --- a/src/providers/oracle/qgsoracleconn.cpp +++ b/src/providers/oracle/qgsoracleconn.cpp @@ -252,6 +252,33 @@ QString QgsOracleConn::getLastExecutedQuery( const QSqlQuery &query ) return str; } +bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms ) +{ + QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); + + bool res = qry.prepare( sql ); + if ( res ) + { + for ( const auto ¶m : params ) + { + QgsDebugMsgLevel( QStringLiteral( " ARG: %1 [%2]" ).arg( param.toString(), param.typeName() ), 4 ); + qry.addBindValue( param ); + } + + res = qry.exec(); + } + + if ( !res ) + { + QgsDebugMsg( QStringLiteral( "SQL: %1\nERROR: %2" ) + .arg( qry.lastQuery(), + qry.lastError().text() ) ); + } + + return res; + +} + bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass, const QString &queryOrigin ) { QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); @@ -459,16 +486,16 @@ QString QgsOracleConn::quotedValue( const QVariant &value, QVariant::Type type ) return v.prepend( '\'' ).append( '\'' ); } -bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) +bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMessage ) { + QMutexLocker locker( &mLock ); + QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); QSqlQuery qry( mDatabase ); - const bool res { !execLogged( qry, query, QVariantList(), originatorClass, queryOrigin ) }; - - if ( ! res ) + if ( !exec( qry, query, QVariantList() ) ) { - const QString error = qry.lastError().text(); + QString error = qry.lastError().text(); if ( logError ) { const QString errorMsg { tr( "Connection error: %1 returned %2" ) @@ -486,6 +513,55 @@ bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *er *errorMessage = error; return false; } + return true; +} + +bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin ) +{ + + QMutexLocker locker( &mLock ); + QgsDatabaseQueryLogWrapper logWrapper { query, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin }; + + QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); + + QSqlQuery qry( mDatabase ); + + const bool res { !exec( qry, query, QVariantList() ) }; + + logWrapper.setQuery( qry.lastQuery() ); + + if ( ! res ) + { + const QString error = qry.lastError().text(); + logWrapper.setError( error ); + if ( logError ) + { + const QString errorMsg { tr( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsMessageLog::logMessage( errorMsg, + tr( "Oracle" ) ); + } + else + { + const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" ) + .arg( query, error ) }; + QgsDebugMsg( errorMsg ); + } + if ( errorMessage ) + *errorMessage = error; + return false; + } + else + { + if ( qry.isSelect() ) + { + logWrapper.setFetchedRows( qry.size() ); + } + else + { + logWrapper.setFetchedRows( qry.numRowsAffected() ); + } + } return true; } diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h index 330225d816b..d5b00ced3f9 100644 --- a/src/providers/oracle/qgsoracleconn.h +++ b/src/providers/oracle/qgsoracleconn.h @@ -164,6 +164,7 @@ class QgsOracleConn : public QObject */ static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ); + bool exec( const QString &query, bool logError = true, QString *errorMessage = nullptr ); bool execLogged( const QString &sql, bool logError = true, QString *errorMessage = nullptr, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); bool begin( QSqlDatabase &db ); @@ -267,6 +268,7 @@ class QgsOracleConn : public QObject explicit QgsOracleConn( QgsDataSourceUri uri, bool transaction ); ~QgsOracleConn() override; + bool exec( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms ); bool execLogged( QSqlQuery &qry, const QString &sql, const QVariantList ¶ms, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ); //! reference count diff --git a/src/providers/oracle/qgsoracletransaction.cpp b/src/providers/oracle/qgsoracletransaction.cpp index f0de7ba7d9b..c218c96b530 100644 --- a/src/providers/oracle/qgsoracletransaction.cpp +++ b/src/providers/oracle/qgsoracletransaction.cpp @@ -74,8 +74,12 @@ bool QgsOracleTransaction::executeSql( const QString &sql, QString &errorMsg, bo QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); - const bool res = mConn->execLogged( sql, true, &errorMsg ); - + QgsDatabaseQueryLogWrapper logWrapper { sql, mConnString, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN }; + const bool res = mConn->exec( sql, true, &errorMsg ); + if ( ! errorMsg.isEmpty() ) + { + logWrapper.setError( errorMsg ); + } if ( !res ) { if ( isDirty ) From 9052913daf739d9b1c3d421f7c21b2bdfbd9e235 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 27 Apr 2022 09:11:40 +0200 Subject: [PATCH 24/24] Address PR comments --- .../devtools/querylogger/qgsqueryloggerpanelwidget.cpp | 2 +- src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h | 8 ++++---- .../devtools/querylogger/qgsqueryloggerwidgetfactory.cpp | 2 +- .../devtools/querylogger/qgsqueryloggerwidgetfactory.h | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp index 978dfefb007..dceafff84dd 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - QgsDatabaseQueryLoggerpanelwidget.cpp + qgsqueryloggerpanelwidget.cpp ------------------------- begin : October 2021 copyright : (C) 2021 by Nyall Dawson diff --git a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h index 90ed89dd7ae..b73681e6576 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h +++ b/src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h @@ -1,5 +1,5 @@ /*************************************************************************** - QgsDatabaseQueryLoggerpanelwidget.h + qgsqueryloggerpanelwidget.h ------------------------- begin : October 2021 copyright : (C) 2021 by Nyall Dawson @@ -12,8 +12,8 @@ * (at your option) any later version. * * * ***************************************************************************/ -#ifndef QgsDatabaseQueryLoggerPANELWIDGET_H -#define QgsDatabaseQueryLoggerPANELWIDGET_H +#ifndef QGSQUERYLOGGERPANELWIDGET_H +#define QGSQUERYLOGGERPANELWIDGET_H #include "qgsdevtoolwidget.h" #include "ui_qgsqueryloggerpanelbase.h" @@ -81,4 +81,4 @@ class QgsDatabaseQueryLoggerPanelWidget : public QgsDevToolWidget, private Ui::Q }; -#endif // QgsDatabaseQueryLoggerPANELWIDGET_H +#endif // QGSQUERYLOGGERPANELWIDGET_H diff --git a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp index 7ff82ec41f3..c34f898849f 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp +++ b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - QgsDatabaseQueryLoggerwidgetfactory.cpp + qgsqueryloggerwidgetfactory.cpp ------------------------- begin : October 2021 copyright : (C) 2021 by Nyall Dawson diff --git a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h index 7e46d37c946..fcb22810fd3 100644 --- a/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h +++ b/src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h @@ -1,5 +1,5 @@ /*************************************************************************** - QgsDatabaseQueryLoggerwidgetfactory.h + qgsqueryloggerwidgetfactory.h ------------------------- begin : October 2021 copyright : (C) 2021 by Nyall Dawson @@ -12,8 +12,8 @@ * (at your option) any later version. * * * ***************************************************************************/ -#ifndef QgsDatabaseQueryLoggerWIDGETFACTORY_H -#define QgsDatabaseQueryLoggerWIDGETFACTORY_H +#ifndef QGSQUERYLOGGERWIDGETFACTORY_H +#define QGSQUERYLOGGERWIDGETFACTORY_H #include "qgsdevtoolwidgetfactory.h" @@ -32,4 +32,4 @@ class QgsDatabaseQueryLoggerWidgetFactory: public QgsDevToolWidgetFactory }; -#endif // QgsDatabaseQueryLoggerWIDGETFACTORY_H +#endif // QGSQUERYLOGGERWIDGETFACTORY_H