mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
Add query log
This commit is contained in:
parent
4c88bc54b0
commit
0108041c68
@ -854,6 +854,13 @@ Returns the application's bookmark manager, used for storing installation-wide b
|
|||||||
Returns the handler for recently used style items.
|
Returns the handler for recently used style items.
|
||||||
|
|
||||||
.. versionadded:: 3.22
|
.. versionadded:: 3.22
|
||||||
|
%End
|
||||||
|
|
||||||
|
static QgsDatabaseQueryLog *databaseQueryLog() /KeepReference/;
|
||||||
|
%Docstring
|
||||||
|
Returns the database query log.
|
||||||
|
|
||||||
|
.. versionadded:: 3.24
|
||||||
%End
|
%End
|
||||||
|
|
||||||
static QgsStyleModel *defaultStyleModel();
|
static QgsStyleModel *defaultStyleModel();
|
||||||
|
102
python/core/auto_generated/qgsdbquerylog.sip.in
Normal file
102
python/core/auto_generated/qgsdbquerylog.sip.in
Normal file
@ -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 *
|
||||||
|
************************************************************************/
|
@ -38,6 +38,7 @@
|
|||||||
%Include auto_generated/qgsdatasourceuri.sip
|
%Include auto_generated/qgsdatasourceuri.sip
|
||||||
%Include auto_generated/qgsdatetimestatisticalsummary.sip
|
%Include auto_generated/qgsdatetimestatisticalsummary.sip
|
||||||
%Include auto_generated/qgsdbfilterproxymodel.sip
|
%Include auto_generated/qgsdbfilterproxymodel.sip
|
||||||
|
%Include auto_generated/qgsdbquerylog.sip
|
||||||
%Include auto_generated/qgsdefaultvalue.sip
|
%Include auto_generated/qgsdefaultvalue.sip
|
||||||
%Include auto_generated/qgsdiagramrenderer.sip
|
%Include auto_generated/qgsdiagramrenderer.sip
|
||||||
%Include auto_generated/qgsdistancearea.sip
|
%Include auto_generated/qgsdistancearea.sip
|
||||||
|
@ -169,6 +169,10 @@ set(QGIS_APP_SRCS
|
|||||||
devtools/networklogger/qgsnetworkloggerwidgetfactory.cpp
|
devtools/networklogger/qgsnetworkloggerwidgetfactory.cpp
|
||||||
devtools/profiler/qgsprofilerpanelwidget.cpp
|
devtools/profiler/qgsprofilerpanelwidget.cpp
|
||||||
devtools/profiler/qgsprofilerwidgetfactory.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/qgsdwgimportdialog.cpp
|
||||||
dwg/qgsdwgimporter.cpp
|
dwg/qgsdwgimporter.cpp
|
||||||
|
244
src/app/devtools/querylogger/qgsappquerylogger.cpp
Normal file
244
src/app/devtools/querylogger/qgsappquerylogger.cpp
Normal file
@ -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 <QThread>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
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<QgsDatabaseQueryLoggerNode *>( index.internalPointer() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction *> 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<int> &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;
|
||||||
|
}
|
147
src/app/devtools/querylogger/qgsappquerylogger.h
Normal file
147
src/app/devtools/querylogger/qgsappquerylogger.h
Normal file
@ -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 <QAbstractItemModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include "qgsdbquerylog.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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
|
201
src/app/devtools/querylogger/qgsqueryloggernode.cpp
Normal file
201
src/app/devtools/querylogger/qgsqueryloggernode.cpp
Normal file
@ -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 <QUrlQuery>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsDatabaseQueryLoggerNode
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsDatabaseQueryLoggerNode::QgsDatabaseQueryLoggerNode() = default;
|
||||||
|
QgsDatabaseQueryLoggerNode::~QgsDatabaseQueryLoggerNode() = default;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsDatabaseQueryLoggerGroup
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsDatabaseQueryLoggerGroup::QgsDatabaseQueryLoggerGroup( const QString &title )
|
||||||
|
: mGroupTitle( title )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsDatabaseQueryLoggerGroup::addChild( std::unique_ptr<QgsDatabaseQueryLoggerNode> 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<QgsDatabaseQueryLoggerNode> &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<QAction *> 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<QAction *> QgsDatabaseQueryLoggerNode::actions( QObject * )
|
||||||
|
{
|
||||||
|
return QList< QAction * >();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QgsDatabaseQueryLoggerNode::toVariant() const
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
197
src/app/devtools/querylogger/qgsqueryloggernode.h
Normal file
197
src/app/devtools/querylogger/qgsqueryloggernode.h
Normal file
@ -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 <QElapsedTimer>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <memory>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
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
|
205
src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp
Normal file
205
src/app/devtools/querylogger/qgsqueryloggerpanelwidget.cpp
Normal file
@ -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 <QFileDialog>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 );
|
||||||
|
}
|
84
src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h
Normal file
84
src/app/devtools/querylogger/qgsqueryloggerpanelwidget.h
Normal file
@ -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 <QTreeView>
|
||||||
|
|
||||||
|
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
|
29
src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp
Normal file
29
src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.cpp
Normal file
@ -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 );
|
||||||
|
}
|
35
src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h
Normal file
35
src/app/devtools/querylogger/qgsqueryloggerwidgetfactory.h
Normal file
@ -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
|
@ -437,6 +437,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
|
|||||||
#include "qgsuserprofilemanager.h"
|
#include "qgsuserprofilemanager.h"
|
||||||
#include "qgsuserprofile.h"
|
#include "qgsuserprofile.h"
|
||||||
#include "qgsnetworkloggerwidgetfactory.h"
|
#include "qgsnetworkloggerwidgetfactory.h"
|
||||||
|
#include "devtools/querylogger/qgsappquerylogger.h"
|
||||||
|
#include "devtools/querylogger/qgsqueryloggerwidgetfactory.h"
|
||||||
#include "devtools/profiler/qgsprofilerwidgetfactory.h"
|
#include "devtools/profiler/qgsprofilerwidgetfactory.h"
|
||||||
#include "qgsabstractdatabaseproviderconnection.h"
|
#include "qgsabstractdatabaseproviderconnection.h"
|
||||||
#include "qgszipitem.h"
|
#include "qgszipitem.h"
|
||||||
@ -989,6 +991,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers
|
|||||||
mNetworkLogger = new QgsNetworkLogger( QgsNetworkAccessManager::instance(), this );
|
mNetworkLogger = new QgsNetworkLogger( QgsNetworkAccessManager::instance(), this );
|
||||||
endProfile();
|
endProfile();
|
||||||
|
|
||||||
|
startProfile( tr( "Create database query logger" ) );
|
||||||
|
mQueryLogger = new QgsAppQueryLogger( this );
|
||||||
|
endProfile();
|
||||||
|
|
||||||
// load GUI: actions, menus, toolbars
|
// load GUI: actions, menus, toolbars
|
||||||
startProfile( tr( "Setting up UI" ) );
|
startProfile( tr( "Setting up UI" ) );
|
||||||
setupUi( this );
|
setupUi( this );
|
||||||
@ -1829,6 +1835,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers
|
|||||||
mBearingNumericFormat.reset( QgsLocalDefaultSettings::bearingFormat() );
|
mBearingNumericFormat.reset( QgsLocalDefaultSettings::bearingFormat() );
|
||||||
|
|
||||||
mNetworkLoggerWidgetFactory.reset( std::make_unique< QgsNetworkLoggerWidgetFactory >( mNetworkLogger ) );
|
mNetworkLoggerWidgetFactory.reset( std::make_unique< QgsNetworkLoggerWidgetFactory >( mNetworkLogger ) );
|
||||||
|
mQueryLoggerWidgetFactory.reset( std::make_unique< QgsDatabaseQueryLoggerWidgetFactory >( mQueryLogger ) );
|
||||||
|
|
||||||
// update windows
|
// update windows
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
|
@ -151,6 +151,7 @@ class QgsDevToolsPanelWidget;
|
|||||||
class QgsDevToolWidgetFactory;
|
class QgsDevToolWidgetFactory;
|
||||||
class QgsNetworkLogger;
|
class QgsNetworkLogger;
|
||||||
class QgsNetworkLoggerWidgetFactory;
|
class QgsNetworkLoggerWidgetFactory;
|
||||||
|
class QgsAppQueryLogger;
|
||||||
class QgsMapToolCapture;
|
class QgsMapToolCapture;
|
||||||
class QgsElevationProfileWidget;
|
class QgsElevationProfileWidget;
|
||||||
|
|
||||||
@ -2742,6 +2743,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
QgsNetworkLogger *mNetworkLogger = nullptr;
|
QgsNetworkLogger *mNetworkLogger = nullptr;
|
||||||
QgsScopedDevToolWidgetFactory mNetworkLoggerWidgetFactory;
|
QgsScopedDevToolWidgetFactory mNetworkLoggerWidgetFactory;
|
||||||
QgsScopedDevToolWidgetFactory mStartupProfilerWidgetFactory;
|
QgsScopedDevToolWidgetFactory mStartupProfilerWidgetFactory;
|
||||||
|
QgsAppQueryLogger *mQueryLogger = nullptr;
|
||||||
|
QgsScopedDevToolWidgetFactory mQueryLoggerWidgetFactory;
|
||||||
|
|
||||||
QgsScopedOptionsWidgetFactory mCodeEditorWidgetFactory;
|
QgsScopedOptionsWidgetFactory mCodeEditorWidgetFactory;
|
||||||
QgsScopedOptionsWidgetFactory mBabelGpsDevicesWidgetFactory;
|
QgsScopedOptionsWidgetFactory mBabelGpsDevicesWidgetFactory;
|
||||||
|
@ -355,6 +355,7 @@ set(QGIS_CORE_SRCS
|
|||||||
qgsdataprovidertemporalcapabilities.cpp
|
qgsdataprovidertemporalcapabilities.cpp
|
||||||
qgsdatetimestatisticalsummary.cpp
|
qgsdatetimestatisticalsummary.cpp
|
||||||
qgsdbfilterproxymodel.cpp
|
qgsdbfilterproxymodel.cpp
|
||||||
|
qgsdbquerylog.cpp
|
||||||
qgsdefaultvalue.cpp
|
qgsdefaultvalue.cpp
|
||||||
qgsdiagramrenderer.cpp
|
qgsdiagramrenderer.cpp
|
||||||
qgsdistancearea.cpp
|
qgsdistancearea.cpp
|
||||||
@ -1004,6 +1005,7 @@ set(QGIS_CORE_HDRS
|
|||||||
qgsdatasourceuri.h
|
qgsdatasourceuri.h
|
||||||
qgsdatetimestatisticalsummary.h
|
qgsdatetimestatisticalsummary.h
|
||||||
qgsdbfilterproxymodel.h
|
qgsdbfilterproxymodel.h
|
||||||
|
qgsdbquerylog.h
|
||||||
qgsdefaultvalue.h
|
qgsdefaultvalue.h
|
||||||
qgsdiagramrenderer.h
|
qgsdiagramrenderer.h
|
||||||
qgsdistancearea.h
|
qgsdistancearea.h
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
#include "qgslocator.h"
|
#include "qgslocator.h"
|
||||||
#include "qgsreadwritelocker.h"
|
#include "qgsreadwritelocker.h"
|
||||||
#include "qgsbabelformatregistry.h"
|
#include "qgsbabelformatregistry.h"
|
||||||
|
#include "qgsdbquerylog.h"
|
||||||
|
|
||||||
#include "gps/qgsgpsconnectionregistry.h"
|
#include "gps/qgsgpsconnectionregistry.h"
|
||||||
#include "processing/qgsprocessingregistry.h"
|
#include "processing/qgsprocessingregistry.h"
|
||||||
@ -265,6 +266,7 @@ void QgsApplication::init( QString profileFolder )
|
|||||||
std::call_once( sMetaTypesRegistered, []
|
std::call_once( sMetaTypesRegistered, []
|
||||||
{
|
{
|
||||||
qRegisterMetaType<QgsGeometry::Error>( "QgsGeometry::Error" );
|
qRegisterMetaType<QgsGeometry::Error>( "QgsGeometry::Error" );
|
||||||
|
qRegisterMetaType<QgsDatabaseQueryLogEntry>( "QgsDatabaseQueryLogEntry" );
|
||||||
qRegisterMetaType<QgsProcessingFeatureSourceDefinition>( "QgsProcessingFeatureSourceDefinition" );
|
qRegisterMetaType<QgsProcessingFeatureSourceDefinition>( "QgsProcessingFeatureSourceDefinition" );
|
||||||
qRegisterMetaType<QgsProcessingOutputLayerDefinition>( "QgsProcessingOutputLayerDefinition" );
|
qRegisterMetaType<QgsProcessingOutputLayerDefinition>( "QgsProcessingOutputLayerDefinition" );
|
||||||
qRegisterMetaType<QgsUnitTypes::LayoutUnit>( "QgsUnitTypes::LayoutUnit" );
|
qRegisterMetaType<QgsUnitTypes::LayoutUnit>( "QgsUnitTypes::LayoutUnit" );
|
||||||
@ -2433,6 +2435,11 @@ QgsRecentStyleHandler *QgsApplication::recentStyleHandler()
|
|||||||
return members()->mRecentStyleHandler;
|
return members()->mRecentStyleHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsDatabaseQueryLog *QgsApplication::databaseQueryLog()
|
||||||
|
{
|
||||||
|
return members()->mQueryLogger;
|
||||||
|
}
|
||||||
|
|
||||||
QgsStyleModel *QgsApplication::defaultStyleModel()
|
QgsStyleModel *QgsApplication::defaultStyleModel()
|
||||||
{
|
{
|
||||||
return members()->mStyleModel;
|
return members()->mStyleModel;
|
||||||
@ -2512,6 +2519,11 @@ QgsApplication::ApplicationMembers::ApplicationMembers()
|
|||||||
mMessageLog = new QgsMessageLog();
|
mMessageLog = new QgsMessageLog();
|
||||||
QgsRuntimeProfiler *profiler = QgsRuntimeProfiler::threadLocalInstance();
|
QgsRuntimeProfiler *profiler = QgsRuntimeProfiler::threadLocalInstance();
|
||||||
|
|
||||||
|
{
|
||||||
|
profiler->start( tr( "Create query logger" ) );
|
||||||
|
mQueryLogger = new QgsDatabaseQueryLog();
|
||||||
|
profiler->end();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
profiler->start( tr( "Setup coordinate reference system registry" ) );
|
profiler->start( tr( "Setup coordinate reference system registry" ) );
|
||||||
mCrsRegistry = new QgsCoordinateReferenceSystemRegistry();
|
mCrsRegistry = new QgsCoordinateReferenceSystemRegistry();
|
||||||
@ -2726,6 +2738,7 @@ QgsApplication::ApplicationMembers::~ApplicationMembers()
|
|||||||
delete mConnectionRegistry;
|
delete mConnectionRegistry;
|
||||||
delete mLocalizedDataPathRegistry;
|
delete mLocalizedDataPathRegistry;
|
||||||
delete mCrsRegistry;
|
delete mCrsRegistry;
|
||||||
|
delete mQueryLogger;
|
||||||
delete mSettingsRegistryCore;
|
delete mSettingsRegistryCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ class QgsPointCloudRendererRegistry;
|
|||||||
class QgsTileDownloadManager;
|
class QgsTileDownloadManager;
|
||||||
class QgsCoordinateReferenceSystemRegistry;
|
class QgsCoordinateReferenceSystemRegistry;
|
||||||
class QgsRecentStyleHandler;
|
class QgsRecentStyleHandler;
|
||||||
|
class QgsDatabaseQueryLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup core
|
* \ingroup core
|
||||||
@ -820,6 +821,13 @@ class CORE_EXPORT QgsApplication : public QApplication
|
|||||||
*/
|
*/
|
||||||
static QgsRecentStyleHandler *recentStyleHandler() SIP_KEEPREFERENCE;
|
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()).
|
* 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;
|
QgsTileDownloadManager *mTileDownloadManager = nullptr;
|
||||||
QgsStyleModel *mStyleModel = nullptr;
|
QgsStyleModel *mStyleModel = nullptr;
|
||||||
QgsRecentStyleHandler *mRecentStyleHandler = nullptr;
|
QgsRecentStyleHandler *mRecentStyleHandler = nullptr;
|
||||||
|
QgsDatabaseQueryLog *mQueryLogger = nullptr;
|
||||||
QString mNullRepresentation;
|
QString mNullRepresentation;
|
||||||
QStringList mSvgPathCache;
|
QStringList mSvgPathCache;
|
||||||
bool mSvgPathCacheValid = false;
|
bool mSvgPathCacheValid = false;
|
||||||
|
68
src/core/qgsdbquerylog.cpp
Normal file
68
src/core/qgsdbquerylog.cpp
Normal file
@ -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 <QDateTime>
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 );
|
||||||
|
}
|
||||||
|
|
170
src/core/qgsdbquerylog.h
Normal file
170
src/core/qgsdbquerylog.h
Normal file
@ -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 <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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
|
@ -31,6 +31,7 @@
|
|||||||
#include "qgspostgresstringutils.h"
|
#include "qgspostgresstringutils.h"
|
||||||
#include "qgspostgresconnpool.h"
|
#include "qgspostgresconnpool.h"
|
||||||
#include "qgsvariantutils.h"
|
#include "qgsvariantutils.h"
|
||||||
|
#include "qgsdbquerylog.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@ -49,6 +50,14 @@
|
|||||||
|
|
||||||
const int PG_DEFAULT_TIMEOUT = 30;
|
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()
|
QgsPostgresResult::~QgsPostgresResult()
|
||||||
{
|
{
|
||||||
if ( mRes )
|
if ( mRes )
|
||||||
@ -412,8 +421,17 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s
|
|||||||
|
|
||||||
if ( mPostgresqlVersion >= 90000 )
|
if ( mPostgresqlVersion >= 90000 )
|
||||||
{
|
{
|
||||||
PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) );
|
QString query = QStringLiteral( "SET application_name='QGIS'" );
|
||||||
PQexecNR( QStringLiteral( "SET extra_float_digits=3" ) );
|
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 );
|
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" )
|
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() ),
|
.arg( result.PQresultErrorMessage() ),
|
||||||
tr( "PostGIS" ) );
|
tr( "PostGIS" ) );
|
||||||
PQexecNR( QStringLiteral( "COMMIT" ) );
|
QString query = QStringLiteral( "COMMIT" );
|
||||||
|
QgsDatabaseQueryLogEntry entry( query );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresConn" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
|
PQexecNR( query );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1021,7 +1043,11 @@ bool QgsPostgresConn::getSchemas( QList<QgsPostgresSchemaProperty> &schemas )
|
|||||||
result = PQexec( sql, true );
|
result = PQexec( sql, true );
|
||||||
if ( result.PQresultStatus() != PGRES_TUPLES_OK )
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1428,7 +1454,7 @@ int QgsPostgresConn::PQCancel()
|
|||||||
return result;
|
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
|
QMutexLocker locker( &mLock ); // to protect access to mOpenCursors
|
||||||
QString preStr;
|
QString preStr;
|
||||||
@ -1442,8 +1468,12 @@ bool QgsPostgresConn::openCursor( const QString &cursorName, const QString &sql
|
|||||||
preStr = QStringLiteral( "BEGIN;" );
|
preStr = QStringLiteral( "BEGIN;" );
|
||||||
}
|
}
|
||||||
QgsDebugMsgLevel( QStringLiteral( "Binary cursor %1 for %2" ).arg( cursorName, sql ), 3 );
|
QgsDebugMsgLevel( QStringLiteral( "Binary cursor %1 for %2" ).arg( cursorName, sql ), 3 );
|
||||||
return PQexecNR( QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ).
|
const QString query = QStringLiteral( "%1DECLARE %2 BINARY CURSOR%3 FOR %4" ).
|
||||||
arg( preStr, cursorName, !mTransaction ? QString() : QStringLiteral( " WITH HOLD" ), sql ) );
|
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 )
|
bool QgsPostgresConn::closeCursor( const QString &cursorName )
|
||||||
@ -1457,7 +1487,11 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName )
|
|||||||
postStr = QStringLiteral( ";COMMIT" );
|
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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1495,7 +1529,11 @@ bool QgsPostgresConn::PQexecNR( const QString &query )
|
|||||||
|
|
||||||
if ( PQstatus() == CONNECTION_OK )
|
if ( PQstatus() == CONNECTION_OK )
|
||||||
{
|
{
|
||||||
PQexecNR( QStringLiteral( "ROLLBACK" ) );
|
QString query = QStringLiteral( "ROLLBACK" );
|
||||||
|
QgsDatabaseQueryLogEntry entry( query );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresConn" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
|
PQexecNR( query );
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1575,11 +1613,19 @@ bool QgsPostgresConn::begin()
|
|||||||
QMutexLocker locker( &mLock );
|
QMutexLocker locker( &mLock );
|
||||||
if ( mTransaction )
|
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
|
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 );
|
QMutexLocker locker( &mLock );
|
||||||
if ( mTransaction )
|
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
|
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 );
|
QMutexLocker locker( &mLock );
|
||||||
if ( mTransaction )
|
if ( mTransaction )
|
||||||
{
|
{
|
||||||
return PQexecNR( QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) )
|
QString query = QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" );
|
||||||
&& PQexecNR( QStringLiteral( "RELEASE 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
|
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 );
|
QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 );
|
||||||
|
|
||||||
// get the same value using a binary cursor
|
// 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 );
|
QgsDebugMsgLevel( QStringLiteral( "Fetching a record and attempting to get check endian-ness" ), 2 );
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ extern "C"
|
|||||||
}
|
}
|
||||||
|
|
||||||
class QgsField;
|
class QgsField;
|
||||||
|
class QgsDatabaseQueryLogEntry;
|
||||||
|
|
||||||
//! Spatial column types
|
//! Spatial column types
|
||||||
enum QgsPostgresGeometryColumnType
|
enum QgsPostgresGeometryColumnType
|
||||||
@ -250,7 +251,7 @@ class QgsPostgresConn : public QObject
|
|||||||
bool PQexecNR( const QString &query );
|
bool PQexecNR( const QString &query );
|
||||||
|
|
||||||
//! cursor handling
|
//! 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 );
|
bool closeCursor( const QString &cursorName );
|
||||||
|
|
||||||
QString uniqueCursorName();
|
QString uniqueCursorName();
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "qgsvectorlayerexporter.h"
|
#include "qgsvectorlayerexporter.h"
|
||||||
#include "qgsprojectitem.h"
|
#include "qgsprojectitem.h"
|
||||||
#include "qgsfieldsitem.h"
|
#include "qgsfieldsitem.h"
|
||||||
|
#include "qgsdbquerylog.h"
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
@ -58,11 +59,17 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
|
|||||||
// handle deletion of views
|
// handle deletion of views
|
||||||
QString sqlViewCheck = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" )
|
QString sqlViewCheck = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" )
|
||||||
.arg( QgsPostgresConn::quotedValue( schemaTableName ) );
|
.arg( QgsPostgresConn::quotedValue( schemaTableName ) );
|
||||||
|
QgsDatabaseQueryLogEntry entry( sqlViewCheck );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresUtils" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) );
|
QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) );
|
||||||
QString type = resViewCheck.PQgetvalue( 0, 0 );
|
QString type = resViewCheck.PQgetvalue( 0, 0 );
|
||||||
if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) )
|
if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) )
|
||||||
{
|
{
|
||||||
QString sql = QStringLiteral( "DROP %1VIEW %2" ).arg( type == QLatin1String( "m" ) ? QStringLiteral( "MATERIALIZED " ) : QString(), schemaTableName );
|
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 ) );
|
QgsPostgresResult result( conn->PQexec( sql ) );
|
||||||
if ( result.PQresultStatus() != PGRES_COMMAND_OK )
|
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" )
|
"AND f_table_schema=%1 AND f_table_name=%2" )
|
||||||
.arg( QgsPostgresConn::quotedValue( schemaName ),
|
.arg( QgsPostgresConn::quotedValue( schemaName ),
|
||||||
QgsPostgresConn::quotedValue( tableName ) );
|
QgsPostgresConn::quotedValue( tableName ) );
|
||||||
|
entry = QgsDatabaseQueryLogEntry( sql );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresUtils" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
QgsPostgresResult result( conn->PQexec( sql ) );
|
QgsPostgresResult result( conn->PQexec( sql ) );
|
||||||
if ( result.PQresultStatus() != PGRES_TUPLES_OK )
|
if ( result.PQresultStatus() != PGRES_TUPLES_OK )
|
||||||
{
|
{
|
||||||
@ -113,6 +123,9 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
|
|||||||
QgsPostgresConn::quotedValue( tableName ) );
|
QgsPostgresConn::quotedValue( tableName ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry = QgsDatabaseQueryLogEntry( sql );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresUtils" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
result = conn->PQexec( sql );
|
result = conn->PQexec( sql );
|
||||||
if ( result.PQresultStatus() != PGRES_TUPLES_OK )
|
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" )
|
QString sql = QStringLiteral( "DROP SCHEMA %1 %2" )
|
||||||
.arg( schemaName, cascade ? QStringLiteral( "CASCADE" ) : QString() );
|
.arg( schemaName, cascade ? QStringLiteral( "CASCADE" ) : QString() );
|
||||||
|
|
||||||
|
QgsDatabaseQueryLogEntry entry( sql );
|
||||||
|
QgsSetQueryLogClass( entry, "QgsPostgresUtils" );
|
||||||
|
QgsDatabaseQueryLog::log( entry );
|
||||||
QgsPostgresResult result( conn->PQexec( sql ) );
|
QgsPostgresResult result( conn->PQexec( sql ) );
|
||||||
if ( result.PQresultStatus() != PGRES_COMMAND_OK )
|
if ( result.PQresultStatus() != PGRES_COMMAND_OK )
|
||||||
{
|
{
|
||||||
@ -262,7 +278,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData *data, const QString &toSc
|
|||||||
if ( srcLayer->isValid() )
|
if ( srcLayer->isValid() )
|
||||||
{
|
{
|
||||||
uri.setDataSource( QString(), u.name, srcLayer->geometryType() != QgsWkbTypes::NullGeometry ? QStringLiteral( "geom" ) : QString() );
|
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() )
|
if ( !toSchema.isNull() )
|
||||||
{
|
{
|
||||||
@ -365,7 +381,7 @@ QString QgsPGLayerItem::createUri()
|
|||||||
if ( uri.wkbType() != QgsWkbTypes::NoGeometry && mLayerProperty.srids.at( 0 ) != std::numeric_limits<int>::min() )
|
if ( uri.wkbType() != QgsWkbTypes::NoGeometry && mLayerProperty.srids.at( 0 ) != std::numeric_limits<int>::min() )
|
||||||
uri.setSrid( QString::number( mLayerProperty.srids.at( 0 ) ) );
|
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 );
|
return uri.uri( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "qgssettings.h"
|
#include "qgssettings.h"
|
||||||
#include "qgsexception.h"
|
#include "qgsexception.h"
|
||||||
#include "qgsgeometryengine.h"
|
#include "qgsgeometryengine.h"
|
||||||
|
#include "qgsdbquerylog.h"
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -422,8 +422,13 @@ bool QgsPostgresFeatureIterator::rewind()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// move cursor to first record
|
// 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();
|
mFeatureQueue.clear();
|
||||||
mFetched = 0;
|
mFetched = 0;
|
||||||
mLastFetch = false;
|
mLastFetch = false;
|
||||||
@ -763,7 +768,9 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString &whereClause, long
|
|||||||
if ( !orderBy.isEmpty() )
|
if ( !orderBy.isEmpty() )
|
||||||
query += QStringLiteral( " ORDER BY %1 " ).arg( orderBy );
|
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
|
// reloading the fields might help next time around
|
||||||
// TODO how to cleanly force reload of fields? P->loadFields();
|
// TODO how to cleanly force reload of fields? P->loadFields();
|
||||||
|
117
src/ui/qgsqueryloggerpanelbase.ui
Normal file
117
src/ui/qgsqueryloggerpanelbase.ui
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>QgsDatabaseQueryLoggerPanelBase</class>
|
||||||
|
<widget class="QgsPanelWidget" name="QgsDatabaseQueryLoggerPanelBase">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>700</width>
|
||||||
|
<height>629</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolBar" name="mToolbar">
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="floatable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<addaction name="mActionRecord"/>
|
||||||
|
<addaction name="mActionClear"/>
|
||||||
|
<addaction name="mActionSaveLog"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QgsFilterLineEdit" name="mFilterLineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>6</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
<action name="mActionClear">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../images/images.qrc">
|
||||||
|
<normaloff>:/images/themes/default/mActionDeleteSelected.svg</normaloff>:/images/themes/default/mActionDeleteSelected.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Clear Log</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="mActionRecord">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../images/images.qrc">
|
||||||
|
<normaloff>:/images/themes/default/mActionRecord.svg</normaloff>:/images/themes/default/mActionRecord.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Record Log</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="mActionSaveLog">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../images/images.qrc">
|
||||||
|
<normaloff>:/images/themes/default/mActionFileSave.svg</normaloff>:/images/themes/default/mActionFileSave.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save Log…</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>QgsPanelWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>qgspanelwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>QgsFilterLineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>qgsfilterlineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources>
|
||||||
|
<include location="../../images/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
x
Reference in New Issue
Block a user