Merge pull request #48107 from elpaso/db_logger_2

Implementation of QEP: Add SQL Logging in the debugging/development panel
This commit is contained in:
Alessandro Pasotti 2022-04-28 10:11:31 +02:00 committed by GitHub
commit 041e18a85c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 3276 additions and 923 deletions

View File

@ -591,6 +591,7 @@
<file>themes/default/propertyicons/attributes.svg</file> <file>themes/default/propertyicons/attributes.svg</file>
<file>themes/default/propertyicons/CRS.svg</file> <file>themes/default/propertyicons/CRS.svg</file>
<file>themes/default/propertyicons/datadefined.svg</file> <file>themes/default/propertyicons/datadefined.svg</file>
<file>themes/default/propertyicons/database.svg</file>
<file>themes/default/propertyicons/diagram.svg</file> <file>themes/default/propertyicons/diagram.svg</file>
<file>themes/default/propertyicons/digitizing.svg</file> <file>themes/default/propertyicons/digitizing.svg</file>
<file>themes/default/propertyicons/display.svg</file> <file>themes/default/propertyicons/display.svg</file>

View File

@ -0,0 +1 @@
<svg height="24" viewBox="0 0 6.34992 6.34992" width="24" xmlns="http://www.w3.org/2000/svg"><g transform="scale(.99975 .99997)"><path d="m.53 1.455h5.29v4.022c-1.973.87-3.596.76-5.293 0z" fill="#6d97c4"/><path d="m.53 1.244v4.18l.53.265-.002-4.446z" fill="#bacee3"/><path d="m5.822 1.323v4.1l-1.058.398v-4.497z" fill="#55769a"/><path d="m5.822 1.19h.265v3.943h-.265zm-5.557 0h.265v3.943h-.266z" fill="#415a75"/><path d="m.53 2.46v.45c.21.146.41.226.61.288.52.158 1.256.242 2.036.242s1.47-.087 1.99-.245c.24-.082.472-.15.656-.285v-.45c0 .065-.046.164-.17.25-.124.09-.313.168-.557.242-.49.147-1.16.223-1.92.223s-1.474-.075-1.96-.223c-.246-.07-.436-.15-.56-.24-.123-.088-.126-.188-.126-.25z" fill="#415a75"/><ellipse cx="3.176" cy="1.191" fill="#bacee3" rx="2.779" ry=".794" stroke="#415a75" stroke-linecap="round" stroke-linejoin="round" stroke-width=".265"/><path d="m.265 5.133c0 .17.095.328.254.44.155.115.392.21.652.29.52.16 1.224.223 2.004.223s1.474-.062 1.998-.222c.26-.078.494-.176.653-.29.156-.113.26-.27.26-.44h-.265s-.008.155-.14.245c-.125.09-.315.17-.56.244-.49.148-1.186.197-1.946.197s-1.446-.046-1.933-.195c-.242-.072-.432-.152-.556-.245-.123-.085-.156-.184-.156-.247zm.265-1.35v.45c.21.146.41.225.612.287.52.158 1.254.243 2.034.243s1.47-.087 1.99-.245c.24-.083.472-.15.656-.285v-.45c0 .065-.046.164-.17.25-.124.09-.313.168-.557.242-.49.147-1.16.223-1.92.223s-1.473-.075-1.96-.224c-.244-.07-.434-.15-.558-.24-.124-.087-.128-.187-.128-.25z" fill="#415a75"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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();

View File

@ -0,0 +1,127 @@
/************************************************************************
* 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
int queryId;
QString uri;
QString provider;
QString query;
quint64 startedTime;
quint64 finishedTime;
QString initiatorClass;
QString origin;
long long fetchedRows;
QString error;
bool canceled;
};
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
QgsDatabaseQueryLog.log('SELECT * FROM my_table')
.. versionadded:: 3.24
%End
%TypeHeaderCode
#include "qgsdbquerylog.h"
%End
public:
QgsDatabaseQueryLog( QObject *parent = 0 );
%Docstring
Creates a new query log.
QgsDatabaseQueryLog is not usually directly created, but rather accessed through
:py:func:`QgsApplication.databaseQueryLog()`.
%End
static bool enabled();
%Docstring
Returns ``True`` if logging is enabled.
.. seealso:: :py:func:`setEnabled`
%End
static void log( const QgsDatabaseQueryLogEntry &query );
%Docstring
Logs a database ``query`` as starting.
This method can be safely called from any thread.
%End
static void finished( const QgsDatabaseQueryLogEntry &query );
%Docstring
Records that the database ``query`` has finished.
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 *
************************************************************************/

View File

@ -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

View File

@ -163,12 +163,17 @@ set(QGIS_APP_SRCS
browser/qgsinbuiltdataitemproviders.cpp browser/qgsinbuiltdataitemproviders.cpp
devtools/qgsappdevtoolutils.cpp devtools/qgsappdevtoolutils.cpp
devtools/qgsdevtoolsmodelnode.cpp
devtools/networklogger/qgsnetworklogger.cpp devtools/networklogger/qgsnetworklogger.cpp
devtools/networklogger/qgsnetworkloggernode.cpp devtools/networklogger/qgsnetworkloggernode.cpp
devtools/networklogger/qgsnetworkloggerpanelwidget.cpp devtools/networklogger/qgsnetworkloggerpanelwidget.cpp
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/qgsdatabasequeryloggernode.cpp
devtools/querylogger/qgsqueryloggerpanelwidget.cpp
devtools/querylogger/qgsqueryloggerwidgetfactory.cpp
dwg/qgsdwgimportdialog.cpp dwg/qgsdwgimportdialog.cpp
dwg/qgsdwgimporter.cpp dwg/qgsdwgimporter.cpp

View File

@ -147,24 +147,24 @@ void QgsNetworkLogger::requestEncounteredSslErrors( int requestId, const QList<Q
emit dataChanged( requestIndex, requestIndex ); emit dataChanged( requestIndex, requestIndex );
} }
QgsNetworkLoggerNode *QgsNetworkLogger::index2node( const QModelIndex &index ) const QgsDevToolsModelNode *QgsNetworkLogger::index2node( const QModelIndex &index ) const
{ {
if ( !index.isValid() ) if ( !index.isValid() )
return mRootNode.get(); return mRootNode.get();
return reinterpret_cast<QgsNetworkLoggerNode *>( index.internalPointer() ); return reinterpret_cast<QgsDevToolsModelNode *>( index.internalPointer() );
} }
QList<QAction *> QgsNetworkLogger::actions( const QModelIndex &index, QObject *parent ) QList<QAction *> QgsNetworkLogger::actions( const QModelIndex &index, QObject *parent )
{ {
QgsNetworkLoggerNode *node = index2node( index ); QgsDevToolsModelNode *node = index2node( index );
if ( !node ) if ( !node )
return QList< QAction * >(); return QList< QAction * >();
return node->actions( parent ); return node->actions( parent );
} }
QModelIndex QgsNetworkLogger::node2index( QgsNetworkLoggerNode *node ) const QModelIndex QgsNetworkLogger::node2index( QgsDevToolsModelNode *node ) const
{ {
if ( !node || !node->parent() ) if ( !node || !node->parent() )
return QModelIndex(); // this is the only root item -> invalid index return QModelIndex(); // this is the only root item -> invalid index
@ -176,11 +176,11 @@ QModelIndex QgsNetworkLogger::node2index( QgsNetworkLoggerNode *node ) const
return index( row, 0, parentIndex ); return index( row, 0, parentIndex );
} }
QModelIndex QgsNetworkLogger::indexOfParentLayerTreeNode( QgsNetworkLoggerNode *parentNode ) const QModelIndex QgsNetworkLogger::indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const
{ {
Q_ASSERT( parentNode ); Q_ASSERT( parentNode );
QgsNetworkLoggerGroup *grandParentNode = parentNode->parent(); QgsDevToolsModelGroup *grandParentNode = parentNode->parent();
if ( !grandParentNode ) if ( !grandParentNode )
return QModelIndex(); // root node -> invalid index return QModelIndex(); // root node -> invalid index
@ -197,7 +197,7 @@ void QgsNetworkLogger::removeRequestRows( const QList<int> &rows )
for ( int row : std::as_const( res ) ) for ( int row : std::as_const( res ) )
{ {
int popId = data( index( row, 0, QModelIndex() ), QgsNetworkLoggerNode::RoleId ).toInt(); int popId = data( index( row, 0, QModelIndex() ), QgsDevToolsModelNode::RoleId ).toInt();
mRequestGroups.remove( popId ); mRequestGroups.remove( popId );
beginRemoveRows( QModelIndex(), row, row ); beginRemoveRows( QModelIndex(), row, row );
@ -213,7 +213,7 @@ QgsNetworkLoggerRootNode *QgsNetworkLogger::rootGroup()
int QgsNetworkLogger::rowCount( const QModelIndex &parent ) const int QgsNetworkLogger::rowCount( const QModelIndex &parent ) const
{ {
QgsNetworkLoggerNode *n = index2node( parent ); QgsDevToolsModelNode *n = index2node( parent );
if ( !n ) if ( !n )
return 0; return 0;
@ -232,7 +232,7 @@ QModelIndex QgsNetworkLogger::index( int row, int column, const QModelIndex &par
row < 0 || row >= rowCount( parent ) ) row < 0 || row >= rowCount( parent ) )
return QModelIndex(); return QModelIndex();
QgsNetworkLoggerGroup *n = dynamic_cast< QgsNetworkLoggerGroup * >( index2node( parent ) ); QgsDevToolsModelGroup *n = dynamic_cast< QgsDevToolsModelGroup * >( index2node( parent ) );
if ( !n ) if ( !n )
return QModelIndex(); // have no children return QModelIndex(); // have no children
@ -244,7 +244,7 @@ QModelIndex QgsNetworkLogger::parent( const QModelIndex &child ) const
if ( !child.isValid() ) if ( !child.isValid() )
return QModelIndex(); return QModelIndex();
if ( QgsNetworkLoggerNode *n = index2node( child ) ) if ( QgsDevToolsModelNode *n = index2node( child ) )
{ {
return indexOfParentLayerTreeNode( n->parent() ); // must not be null return indexOfParentLayerTreeNode( n->parent() ); // must not be null
} }
@ -260,7 +260,7 @@ QVariant QgsNetworkLogger::data( const QModelIndex &index, int role ) const
if ( !index.isValid() || index.column() > 1 ) if ( !index.isValid() || index.column() > 1 )
return QVariant(); return QVariant();
QgsNetworkLoggerNode *node = index2node( index ); QgsDevToolsModelNode *node = index2node( index );
if ( !node ) if ( !node )
return QVariant(); return QVariant();
@ -324,7 +324,7 @@ void QgsNetworkLoggerProxyModel::setShowCached( bool show )
bool QgsNetworkLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const bool QgsNetworkLoggerProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{ {
QgsNetworkLoggerNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) ); QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) );
if ( QgsNetworkLoggerRequestGroup *request = dynamic_cast< QgsNetworkLoggerRequestGroup * >( node ) ) if ( QgsNetworkLoggerRequestGroup *request = dynamic_cast< QgsNetworkLoggerRequestGroup * >( node ) )
{ {
if ( ( request->status() == QgsNetworkLoggerRequestGroup::Status::Complete || request->status() == QgsNetworkLoggerRequestGroup::Status::Canceled ) if ( ( request->status() == QgsNetworkLoggerRequestGroup::Status::Complete || request->status() == QgsNetworkLoggerRequestGroup::Status::Canceled )

View File

@ -20,7 +20,7 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include "qgsnetworkaccessmanager.h" #include "qgsnetworkaccessmanager.h"
class QgsNetworkLoggerNode; class QgsDevToolsModelNode;
class QgsNetworkLoggerRequestGroup; class QgsNetworkLoggerRequestGroup;
class QgsNetworkLoggerRootNode; class QgsNetworkLoggerRootNode;
class QAction; class QAction;
@ -66,7 +66,7 @@ class QgsNetworkLogger : public QAbstractItemModel
/** /**
* Returns node for given index. Returns root node for invalid index. * Returns node for given index. Returns root node for invalid index.
*/ */
QgsNetworkLoggerNode *index2node( const QModelIndex &index ) const; QgsDevToolsModelNode *index2node( const QModelIndex &index ) const;
/** /**
* Returns a list of actions corresponding to the item at the specified \a index. * Returns a list of actions corresponding to the item at the specified \a index.
@ -109,8 +109,8 @@ class QgsNetworkLogger : public QAbstractItemModel
private: private:
//! Returns index for a given node //! Returns index for a given node
QModelIndex node2index( QgsNetworkLoggerNode *node ) const; QModelIndex node2index( QgsDevToolsModelNode *node ) const;
QModelIndex indexOfParentLayerTreeNode( QgsNetworkLoggerNode *parentNode ) const; QModelIndex indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const;
QgsNetworkAccessManager *mNam = nullptr; QgsNetworkAccessManager *mNam = nullptr;
bool mIsLogging = false; bool mIsLogging = false;

View File

@ -26,89 +26,12 @@
#include <QClipboard> #include <QClipboard>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
//
// QgsNetworkLoggerNode
//
QgsNetworkLoggerNode::QgsNetworkLoggerNode() = default;
QgsNetworkLoggerNode::~QgsNetworkLoggerNode() = default;
//
// QgsNetworkLoggerGroup
//
QgsNetworkLoggerGroup::QgsNetworkLoggerGroup( const QString &title )
: mGroupTitle( title )
{
}
void QgsNetworkLoggerGroup::addChild( std::unique_ptr<QgsNetworkLoggerNode> child )
{
if ( !child )
return;
Q_ASSERT( !child->mParent );
child->mParent = this;
mChildren.emplace_back( std::move( child ) );
}
int QgsNetworkLoggerGroup::indexOf( QgsNetworkLoggerNode *child ) const
{
Q_ASSERT( child->mParent == this );
auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr<QgsNetworkLoggerNode> &p )
{
return p.get() == child;
} );
if ( it != mChildren.end() )
return std::distance( mChildren.begin(), it );
return -1;
}
QgsNetworkLoggerNode *QgsNetworkLoggerGroup::childAt( int index )
{
Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() );
return mChildren[ index ].get();
}
void QgsNetworkLoggerGroup::clear()
{
mChildren.clear();
}
QVariant QgsNetworkLoggerGroup::data( int role ) const
{
switch ( role )
{
case Qt::DisplayRole:
return mGroupTitle;
default:
break;
}
return QVariant();
}
QVariant QgsNetworkLoggerGroup::toVariant() const
{
QVariantMap res;
for ( const std::unique_ptr< QgsNetworkLoggerNode > &child : mChildren )
{
if ( const QgsNetworkLoggerValueNode *valueNode = dynamic_cast< const QgsNetworkLoggerValueNode *>( child.get() ) )
{
res.insert( valueNode->key(), valueNode->value() );
}
}
return res;
}
// //
// QgsNetworkLoggerRootNode // QgsNetworkLoggerRootNode
// //
QgsNetworkLoggerRootNode::QgsNetworkLoggerRootNode() QgsNetworkLoggerRootNode::QgsNetworkLoggerRootNode()
: QgsNetworkLoggerGroup( QString() ) : QgsDevToolsModelGroup( QString() )
{ {
} }
@ -126,76 +49,18 @@ void QgsNetworkLoggerRootNode::removeRow( int row )
QVariant QgsNetworkLoggerRootNode::toVariant() const QVariant QgsNetworkLoggerRootNode::toVariant() const
{ {
QVariantList res; QVariantList res;
for ( const std::unique_ptr< QgsNetworkLoggerNode > &child : mChildren ) for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren )
res << child->toVariant(); res << child->toVariant();
return res; return res;
} }
//
// QgsNetworkLoggerValueNode
//
QgsNetworkLoggerValueNode::QgsNetworkLoggerValueNode( const QString &key, const QString &value, const QColor &color )
: mKey( key )
, mValue( value )
, mColor( color )
{
}
QVariant QgsNetworkLoggerValueNode::data( int role ) const
{
switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
{
return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue );
}
case Qt::ForegroundRole:
{
if ( mColor.isValid() )
return QBrush( mColor );
break;
}
default:
break;
}
return QVariant();
}
QList<QAction *> QgsNetworkLoggerValueNode::actions( QObject *parent )
{
QList< QAction * > res;
QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent );
QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ]
{
QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) );
} );
res << copyAction;
return res;
}
//
// QgsNetworkLoggerGroup
//
void QgsNetworkLoggerGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color )
{
addChild( std::make_unique< QgsNetworkLoggerValueNode >( key, value, color ) );
}
// //
// QgsNetworkLoggerRequestGroup // QgsNetworkLoggerRequestGroup
// //
QgsNetworkLoggerRequestGroup::QgsNetworkLoggerRequestGroup( const QgsNetworkRequestParameters &request ) QgsNetworkLoggerRequestGroup::QgsNetworkLoggerRequestGroup( const QgsNetworkRequestParameters &request )
: QgsNetworkLoggerGroup( QString() ) : QgsDevToolsModelGroup( QString() )
, mUrl( request.request().url() ) , mUrl( request.request().url() )
, mRequestId( request.requestId() ) , mRequestId( request.requestId() )
, mOperation( request.operation() ) , mOperation( request.operation() )
@ -478,7 +343,7 @@ QString QgsNetworkLoggerRequestGroup::cacheControlToString( QNetworkRequest::Cac
// //
QgsNetworkLoggerRequestDetailsGroup::QgsNetworkLoggerRequestDetailsGroup( const QgsNetworkRequestParameters &request ) QgsNetworkLoggerRequestDetailsGroup::QgsNetworkLoggerRequestDetailsGroup( const QgsNetworkRequestParameters &request )
: QgsNetworkLoggerGroup( QObject::tr( "Request" ) ) : QgsDevToolsModelGroup( QObject::tr( "Request" ) )
{ {
addKeyValueNode( QObject::tr( "Operation" ), QgsNetworkLoggerRequestGroup::operationToString( request.operation() ) ); addKeyValueNode( QObject::tr( "Operation" ), QgsNetworkLoggerRequestGroup::operationToString( request.operation() ) );
addKeyValueNode( QObject::tr( "Thread" ), request.originatingThreadId() ); addKeyValueNode( QObject::tr( "Thread" ), request.originatingThreadId() );
@ -521,7 +386,7 @@ QgsNetworkLoggerRequestDetailsGroup::QgsNetworkLoggerRequestDetailsGroup( const
QVariant QgsNetworkLoggerRequestDetailsGroup::toVariant() const QVariant QgsNetworkLoggerRequestDetailsGroup::toVariant() const
{ {
QVariantMap res = QgsNetworkLoggerGroup::toVariant().toMap(); QVariantMap res = QgsDevToolsModelGroup::toVariant().toMap();
if ( mQueryGroup ) if ( mQueryGroup )
res.insert( QObject::tr( "Query" ), mQueryGroup->toVariant() ); res.insert( QObject::tr( "Query" ), mQueryGroup->toVariant() );
if ( mRequestHeaders ) if ( mRequestHeaders )
@ -537,7 +402,7 @@ QVariant QgsNetworkLoggerRequestDetailsGroup::toVariant() const
// //
QgsNetworkLoggerRequestQueryGroup::QgsNetworkLoggerRequestQueryGroup( const QUrl &url ) QgsNetworkLoggerRequestQueryGroup::QgsNetworkLoggerRequestQueryGroup( const QUrl &url )
: QgsNetworkLoggerGroup( QObject::tr( "Query" ) ) : QgsDevToolsModelGroup( QObject::tr( "Query" ) )
{ {
QUrlQuery query( url ); QUrlQuery query( url );
const QList<QPair<QString, QString> > queryItems = query.queryItems(); const QList<QPair<QString, QString> > queryItems = query.queryItems();
@ -553,7 +418,7 @@ QgsNetworkLoggerRequestQueryGroup::QgsNetworkLoggerRequestQueryGroup( const QUrl
// QgsNetworkLoggerRequestHeadersGroup // QgsNetworkLoggerRequestHeadersGroup
// //
QgsNetworkLoggerRequestHeadersGroup::QgsNetworkLoggerRequestHeadersGroup( const QgsNetworkRequestParameters &request ) QgsNetworkLoggerRequestHeadersGroup::QgsNetworkLoggerRequestHeadersGroup( const QgsNetworkRequestParameters &request )
: QgsNetworkLoggerGroup( QObject::tr( "Headers" ) ) : QgsDevToolsModelGroup( QObject::tr( "Headers" ) )
{ {
const QList<QByteArray> headers = request.request().rawHeaderList(); const QList<QByteArray> headers = request.request().rawHeaderList();
for ( const QByteArray &header : headers ) for ( const QByteArray &header : headers )
@ -567,7 +432,7 @@ QgsNetworkLoggerRequestHeadersGroup::QgsNetworkLoggerRequestHeadersGroup( const
// //
QgsNetworkLoggerPostContentGroup::QgsNetworkLoggerPostContentGroup( const QgsNetworkRequestParameters &parameters ) QgsNetworkLoggerPostContentGroup::QgsNetworkLoggerPostContentGroup( const QgsNetworkRequestParameters &parameters )
: QgsNetworkLoggerGroup( QObject::tr( "Content" ) ) : QgsDevToolsModelGroup( QObject::tr( "Content" ) )
{ {
addKeyValueNode( QObject::tr( "Data" ), parameters.content() ); addKeyValueNode( QObject::tr( "Data" ), parameters.content() );
} }
@ -578,7 +443,7 @@ QgsNetworkLoggerPostContentGroup::QgsNetworkLoggerPostContentGroup( const QgsNet
// //
QgsNetworkLoggerReplyGroup::QgsNetworkLoggerReplyGroup( const QgsNetworkReplyContent &reply ) QgsNetworkLoggerReplyGroup::QgsNetworkLoggerReplyGroup( const QgsNetworkReplyContent &reply )
: QgsNetworkLoggerGroup( QObject::tr( "Reply" ) ) : QgsDevToolsModelGroup( QObject::tr( "Reply" ) )
{ {
addKeyValueNode( QObject::tr( "Status" ), reply.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toString() ); addKeyValueNode( QObject::tr( "Status" ), reply.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toString() );
if ( reply.error() != QNetworkReply::NoError ) if ( reply.error() != QNetworkReply::NoError )
@ -595,7 +460,7 @@ QgsNetworkLoggerReplyGroup::QgsNetworkLoggerReplyGroup( const QgsNetworkReplyCon
QVariant QgsNetworkLoggerReplyGroup::toVariant() const QVariant QgsNetworkLoggerReplyGroup::toVariant() const
{ {
QVariantMap res = QgsNetworkLoggerGroup::toVariant().toMap(); QVariantMap res = QgsDevToolsModelGroup::toVariant().toMap();
if ( mReplyHeaders ) if ( mReplyHeaders )
{ {
res.insert( QObject::tr( "Headers" ), mReplyHeaders->toVariant() ); res.insert( QObject::tr( "Headers" ), mReplyHeaders->toVariant() );
@ -608,7 +473,7 @@ QVariant QgsNetworkLoggerReplyGroup::toVariant() const
// QgsNetworkLoggerReplyHeadersGroup // QgsNetworkLoggerReplyHeadersGroup
// //
QgsNetworkLoggerReplyHeadersGroup::QgsNetworkLoggerReplyHeadersGroup( const QgsNetworkReplyContent &reply ) QgsNetworkLoggerReplyHeadersGroup::QgsNetworkLoggerReplyHeadersGroup( const QgsNetworkReplyContent &reply )
: QgsNetworkLoggerGroup( QObject::tr( "Headers" ) ) : QgsDevToolsModelGroup( QObject::tr( "Headers" ) )
{ {
const QList<QByteArray> headers = reply.rawHeaderList(); const QList<QByteArray> headers = reply.rawHeaderList();
for ( const QByteArray &header : headers ) for ( const QByteArray &header : headers )
@ -621,7 +486,7 @@ QgsNetworkLoggerReplyHeadersGroup::QgsNetworkLoggerReplyHeadersGroup( const QgsN
// QgsNetworkLoggerSslErrorGroup // QgsNetworkLoggerSslErrorGroup
// //
QgsNetworkLoggerSslErrorGroup::QgsNetworkLoggerSslErrorGroup( const QList<QSslError> &errors ) QgsNetworkLoggerSslErrorGroup::QgsNetworkLoggerSslErrorGroup( const QList<QSslError> &errors )
: QgsNetworkLoggerGroup( QObject::tr( "SSL errors" ) ) : QgsDevToolsModelGroup( QObject::tr( "SSL errors" ) )
{ {
for ( const QSslError &error : errors ) for ( const QSslError &error : errors )
{ {
@ -634,15 +499,5 @@ QVariant QgsNetworkLoggerSslErrorGroup::data( int role ) const
if ( role == Qt::ForegroundRole ) if ( role == Qt::ForegroundRole )
return QBrush( QColor( 180, 65, 210 ) ); return QBrush( QColor( 180, 65, 210 ) );
return QgsNetworkLoggerGroup::data( role ); return QgsDevToolsModelGroup::data( role );
}
QList<QAction *> QgsNetworkLoggerNode::actions( QObject * )
{
return QList< QAction * >();
}
QVariant QgsNetworkLoggerNode::toVariant() const
{
return QVariant();
} }

View File

@ -16,6 +16,7 @@
#define QGSNETWORKLOGGERNODE_H #define QGSNETWORKLOGGERNODE_H
#include "qgsnetworkaccessmanager.h" #include "qgsnetworkaccessmanager.h"
#include "devtools/qgsdevtoolsmodelnode.h"
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QVariant> #include <QVariant>
#include <QColor> #include <QColor>
@ -24,160 +25,6 @@
#include <deque> #include <deque>
class QAction; class QAction;
class QgsNetworkLoggerGroup;
/**
* \ingroup app
* \class QgsNetworkLoggerNode
* \brief Base class for nodes in the network logger model.
*
* \since QGIS 3.14
*/
class QgsNetworkLoggerNode
{
public:
//! Custom node data roles
enum Roles
{
RoleStatus = Qt::UserRole + 1, //!< Request status role
RoleId, //!< Request ID role
};
virtual ~QgsNetworkLoggerNode();
/**
* Returns the node's parent node.
*
* If parent is NULLPTR, the node is a root node
*/
QgsNetworkLoggerGroup *parent() { return mParent; }
/**
* Returns the node's data for the specified model \a role.
*/
virtual QVariant data( int role = Qt::DisplayRole ) const = 0;
/**
* Returns the number of child nodes owned by this node.
*/
virtual int childCount() const = 0;
/**
* Returns a list of actions relating to the node.
*
* The actions should be parented to \a parent.
*/
virtual QList< QAction * > actions( QObject *parent );
/**
* Converts the node's contents to a variant.
*/
virtual QVariant toVariant() const;
protected:
QgsNetworkLoggerNode();
private:
QgsNetworkLoggerGroup *mParent = nullptr;
friend class QgsNetworkLoggerGroup;
};
/**
* \ingroup app
* \class QgsNetworkLoggerGroup
* \brief Base class for network logger model "group" nodes, which contain children of their own.
*
* \since QGIS 3.14
*/
class QgsNetworkLoggerGroup : public QgsNetworkLoggerNode
{
public:
/**
* Adds a \a child node to this node.
*/
void addChild( std::unique_ptr< QgsNetworkLoggerNode > child );
/**
* Returns the index of the specified \a child node.
*
* \warning \a child must be a valid child of this node.
*/
int indexOf( QgsNetworkLoggerNode *child ) const;
/**
* Returns the child at the specified \a index.
*/
QgsNetworkLoggerNode *childAt( int index );
/**
* Clears the group, removing all its children.
*/
void clear();
int childCount() const override final { return mChildren.size(); }
QVariant data( int role = Qt::DisplayRole ) const override;
QVariant toVariant() const override;
protected:
/**
* Constructor for a QgsNetworkLoggerGroup, with the specified \a title.
*/
QgsNetworkLoggerGroup( const QString &title );
/**
* Adds a simple \a key: \a value node to the group.
*/
void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() );
private:
std::deque< std::unique_ptr< QgsNetworkLoggerNode > > mChildren;
QString mGroupTitle;
friend class QgsNetworkLoggerRootNode;
};
/**
* \ingroup app
* \class QgsNetworkLoggerValueNode
* \brief A "key: value" style node for the network logger model.
*
* \since QGIS 3.14
*/
class QgsNetworkLoggerValueNode : public QgsNetworkLoggerNode
{
public:
/**
* Constructor for QgsNetworkLoggerValueNode, with the specified \a key (usually translated) and \a value.
*/
QgsNetworkLoggerValueNode( const QString &key, const QString &value, const QColor &color = QColor() );
/**
* Returns the node's key.
*/
QString key() const { return mKey; }
/**
* Returns the node's value.
*/
QString value() const { return mValue; }
QVariant data( int role = Qt::DisplayRole ) const override final;
int childCount() const override final { return 0; }
QList< QAction * > actions( QObject *parent ) override final;
private:
QString mKey;
QString mValue;
QColor mColor;
};
/** /**
* \ingroup app * \ingroup app
@ -186,7 +33,7 @@ class QgsNetworkLoggerValueNode : public QgsNetworkLoggerNode
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerRootNode final : public QgsNetworkLoggerGroup class QgsNetworkLoggerRootNode final : public QgsDevToolsModelGroup
{ {
public: public:
@ -231,7 +78,7 @@ class QgsNetworkLoggerSslErrorGroup;
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerRequestGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerRequestGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -357,7 +204,7 @@ class QgsNetworkLoggerPostContentGroup;
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerRequestDetailsGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerRequestDetailsGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -389,7 +236,7 @@ class QgsNetworkLoggerRequestDetailsGroup final : public QgsNetworkLoggerGroup
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerRequestHeadersGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerRequestHeadersGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -415,7 +262,7 @@ class QgsNetworkLoggerRequestHeadersGroup final : public QgsNetworkLoggerGroup
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerRequestQueryGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerRequestQueryGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -439,7 +286,7 @@ class QgsNetworkLoggerRequestQueryGroup final : public QgsNetworkLoggerGroup
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerPostContentGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerPostContentGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -468,7 +315,7 @@ class QgsNetworkLoggerReplyHeadersGroup;
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerReplyGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerReplyGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -499,7 +346,7 @@ class QgsNetworkLoggerReplyGroup final : public QgsNetworkLoggerGroup
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerReplyHeadersGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerReplyHeadersGroup final : public QgsDevToolsModelGroup
{ {
public: public:
@ -525,7 +372,7 @@ class QgsNetworkLoggerReplyHeadersGroup final : public QgsNetworkLoggerGroup
* *
* \since QGIS 3.14 * \since QGIS 3.14
*/ */
class QgsNetworkLoggerSslErrorGroup final : public QgsNetworkLoggerGroup class QgsNetworkLoggerSslErrorGroup final : public QgsDevToolsModelGroup
{ {
public: public:

View File

@ -0,0 +1,173 @@
/***************************************************************************
qgsdevtoolsmodelnode.cpp
-------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsdevtoolsmodelnode.h"
#include "qgis.h"
#include "qgsjsonutils.h"
#include <QUrlQuery>
#include <QColor>
#include <QBrush>
#include <QFont>
#include <QAction>
#include <QDesktopServices>
#include <QApplication>
#include <QClipboard>
#include <nlohmann/json.hpp>
//
// QgsDevToolsModelNode
//
QgsDevToolsModelNode::QgsDevToolsModelNode() = default;
QgsDevToolsModelNode::~QgsDevToolsModelNode() = default;
QVariant QgsDevToolsModelNode::toVariant() const
{
return QVariant();
}
QList<QAction *> QgsDevToolsModelNode::actions( QObject * )
{
return QList< QAction * >();
}
//
// QgsDevToolsModelGroup
//
QgsDevToolsModelGroup::QgsDevToolsModelGroup( const QString &title )
: mGroupTitle( title )
{
}
void QgsDevToolsModelGroup::addChild( std::unique_ptr<QgsDevToolsModelNode> child )
{
if ( !child )
return;
Q_ASSERT( !child->mParent );
child->mParent = this;
mChildren.emplace_back( std::move( child ) );
}
int QgsDevToolsModelGroup::indexOf( QgsDevToolsModelNode *child ) const
{
Q_ASSERT( child->mParent == this );
auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr<QgsDevToolsModelNode> &p )
{
return p.get() == child;
} );
if ( it != mChildren.end() )
return std::distance( mChildren.begin(), it );
return -1;
}
QgsDevToolsModelNode *QgsDevToolsModelGroup::childAt( int index )
{
Q_ASSERT( static_cast< std::size_t >( index ) < mChildren.size() );
return mChildren[ index ].get();
}
void QgsDevToolsModelGroup::clear()
{
mChildren.clear();
}
QVariant QgsDevToolsModelGroup::data( int role ) const
{
switch ( role )
{
case Qt::DisplayRole:
return mGroupTitle;
default:
break;
}
return QVariant();
}
QVariant QgsDevToolsModelGroup::toVariant() const
{
QVariantMap res;
for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren )
{
if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) )
{
res.insert( valueNode->key(), valueNode->value() );
}
}
return res;
}
//
// QgsDevToolsModelValueNode
//
QgsDevToolsModelValueNode::QgsDevToolsModelValueNode( const QString &key, const QString &value, const QColor &color )
: mKey( key )
, mValue( value )
, mColor( color )
{
}
QVariant QgsDevToolsModelValueNode::data( int role ) const
{
switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
{
return QStringLiteral( "%1: %2" ).arg( mKey.leftJustified( 30, ' ' ), mValue );
}
case Qt::ForegroundRole:
{
if ( mColor.isValid() )
return QBrush( mColor );
break;
}
default:
break;
}
return QVariant();
}
QList<QAction *> QgsDevToolsModelValueNode::actions( QObject *parent )
{
QList< QAction * > res;
QAction *copyAction = new QAction( QObject::tr( "Copy" ), parent );
QObject::connect( copyAction, &QAction::triggered, copyAction, [ = ]
{
QApplication::clipboard()->setText( QStringLiteral( "%1: %2" ).arg( mKey, mValue ) );
} );
res << copyAction;
return res;
}
//
// QgsDevToolsModelGroup
//
void QgsDevToolsModelGroup::addKeyValueNode( const QString &key, const QString &value, const QColor &color )
{
addChild( std::make_unique< QgsDevToolsModelValueNode >( key, value, color ) );
}

View File

@ -0,0 +1,176 @@
/***************************************************************************
qgsdevtoolsmodelnode.h
-------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSDEVTOOLSMODELNODE_H
#define QGSDEVTOOLSMODELNODE_H
#include <QElapsedTimer>
#include <QVariant>
#include <QColor>
#include <QUrl>
#include <memory>
#include <deque>
class QAction;
class QgsDevToolsModelGroup;
/**
* \ingroup app
* \class QgsDevToolsModelNode
* \brief Base class for nodes in a dev tools model.
*/
class QgsDevToolsModelNode
{
public:
//! Custom node data roles
enum Roles
{
RoleStatus = Qt::UserRole + 1, //!< Request status role
RoleId, //!< Request ID role
};
virtual ~QgsDevToolsModelNode();
/**
* Returns the node's parent node.
*
* If parent is NULLPTR, the node is a root node
*/
QgsDevToolsModelGroup *parent() { return mParent; }
/**
* Returns the node's data for the specified model \a role.
*/
virtual QVariant data( int role = Qt::DisplayRole ) const = 0;
/**
* Returns the number of child nodes owned by this node.
*/
virtual int childCount() const = 0;
/**
* Returns a list of actions relating to the node.
*
* The actions should be parented to \a parent.
*/
virtual QList< QAction * > actions( QObject *parent );
/**
* Converts the node's contents to a variant.
*/
virtual QVariant toVariant() const;
protected:
QgsDevToolsModelNode();
private:
QgsDevToolsModelGroup *mParent = nullptr;
friend class QgsDevToolsModelGroup;
};
/**
* \ingroup app
* \class QgsDevToolsModelGroup
* \brief Base class for dev tools model "group" nodes, which contain children of their own.
*/
class QgsDevToolsModelGroup : public QgsDevToolsModelNode
{
public:
/**
* Adds a \a child node to this node.
*/
void addChild( std::unique_ptr< QgsDevToolsModelNode > child );
/**
* Returns the index of the specified \a child node.
*
* \warning \a child must be a valid child of this node.
*/
int indexOf( QgsDevToolsModelNode *child ) const;
/**
* Returns the child at the specified \a index.
*/
QgsDevToolsModelNode *childAt( int index );
/**
* Clears the group, removing all its children.
*/
void clear();
int childCount() const override final { return mChildren.size(); }
QVariant data( int role = Qt::DisplayRole ) const override;
QVariant toVariant() const override;
protected:
/**
* Constructor for a QgsDevToolsModelGroup, with the specified \a title.
*/
QgsDevToolsModelGroup( const QString &title );
/**
* Adds a simple \a key: \a value node to the group.
*/
void addKeyValueNode( const QString &key, const QString &value, const QColor &color = QColor() );
protected:
std::deque< std::unique_ptr< QgsDevToolsModelNode > > mChildren;
private:
QString mGroupTitle;
};
/**
* \ingroup app
* \class QgsDevToolsModelValueNode
* \brief A "key: value" style node for a dev tools model.
*/
class QgsDevToolsModelValueNode : public QgsDevToolsModelNode
{
public:
/**
* Constructor for QgsDevToolsModelValueNode, with the specified \a key (usually translated) and \a value.
*/
QgsDevToolsModelValueNode( const QString &key, const QString &value, const QColor &color = QColor() );
/**
* Returns the node's key.
*/
QString key() const { return mKey; }
/**
* Returns the node's value.
*/
QString value() const { return mValue; }
QVariant data( int role = Qt::DisplayRole ) const override final;
int childCount() const override final { return 0; }
QList< QAction * > actions( QObject *parent ) override final;
private:
QString mKey;
QString mValue;
QColor mColor;
};
#endif // QGSDEVTOOLSMODELNODE_H

View File

@ -0,0 +1,262 @@
/***************************************************************************
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 "qgsdatabasequeryloggernode.h"
#include "qgsapplication.h"
#include "devtools/qgsdevtoolsmodelnode.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() );
connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryStarted, this, &QgsAppQueryLogger::queryLogged );
connect( QgsApplication::databaseQueryLog(), &QgsDatabaseQueryLog::queryFinished, this, &QgsAppQueryLogger::queryFinished );
}
QgsAppQueryLogger::~QgsAppQueryLogger() = default;
void QgsAppQueryLogger::clear()
{
beginResetModel();
mQueryGroups.clear();
mRootNode->clear();
endResetModel();
}
void QgsAppQueryLogger::queryLogged( const QgsDatabaseQueryLogEntry &query )
{
const int childCount = mRootNode->childCount();
beginInsertRows( QModelIndex(), childCount, childCount );
std::unique_ptr< QgsDatabaseQueryLoggerQueryGroup > group = std::make_unique< QgsDatabaseQueryLoggerQueryGroup >( query );
mQueryGroups.insert( query.queryId, group.get() );
mRootNode->addChild( std::move( group ) );
endInsertRows();
}
void QgsAppQueryLogger::queryFinished( const QgsDatabaseQueryLogEntry &query )
{
QgsDatabaseQueryLoggerQueryGroup *queryGroup = mQueryGroups.value( query.queryId );
if ( !queryGroup )
return;
// find the row: the position of the request in the rootNode
const QModelIndex requestIndex = node2index( queryGroup );
if ( !requestIndex.isValid() )
return;
if ( query.query != queryGroup->sql() )
{
queryGroup->setSql( query.query );
}
// Calculate the number of children: if error or not fetched rows 1 row is added else 2 rows are added
beginInsertRows( requestIndex, queryGroup->childCount(), queryGroup->childCount() + ( query.fetchedRows != -1 ? 1 : 0 ) );
queryGroup->setFinished( query );
endInsertRows();
emit dataChanged( requestIndex, requestIndex );
}
QgsDevToolsModelNode *QgsAppQueryLogger::index2node( const QModelIndex &index ) const
{
if ( !index.isValid() )
return mRootNode.get();
return reinterpret_cast<QgsDevToolsModelNode *>( index.internalPointer() );
}
QList<QAction *> QgsAppQueryLogger::actions( const QModelIndex &index, QObject *parent )
{
QgsDevToolsModelNode *node = index2node( index );
if ( !node )
return QList< QAction * >();
return node->actions( parent );
}
QModelIndex QgsAppQueryLogger::node2index( QgsDevToolsModelNode *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( QgsDevToolsModelNode *parentNode ) const
{
Q_ASSERT( parentNode );
QgsDevToolsModelGroup *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() ), QgsDevToolsModelNode::RoleId ).toInt();
mQueryGroups.remove( popId );
beginRemoveRows( QModelIndex(), row, row );
mRootNode->removeRow( row );
endRemoveRows();
}
}
QgsDatabaseQueryLoggerRootNode *QgsAppQueryLogger::rootGroup()
{
return mRootNode.get();
}
int QgsAppQueryLogger::rowCount( const QModelIndex &parent ) const
{
QgsDevToolsModelNode *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();
QgsDevToolsModelGroup *n = dynamic_cast< QgsDevToolsModelGroup * >( 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 ( QgsDevToolsModelNode *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();
QgsDevToolsModelNode *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
{
if ( ! mFilterString.isEmpty() )
{
QgsDevToolsModelNode *node = mLogger->index2node( mLogger->index( source_row, 0, source_parent ) );
if ( QgsDatabaseQueryLoggerQueryGroup *request = dynamic_cast< QgsDatabaseQueryLoggerQueryGroup * >( node ) )
{
if ( request->data().toString().contains( mFilterString, Qt::CaseInsensitive ) )
{
return true;
}
for ( int i = 0; i < request->childCount(); i++ )
{
if ( QgsDevToolsModelValueNode *valueNode = static_cast<QgsDevToolsModelValueNode *>( request->childAt( i ) ); valueNode->value().contains( mFilterString, Qt::CaseInsensitive ) )
{
return true;
}
}
return false;
}
}
return true;
}

View File

@ -0,0 +1,137 @@
/***************************************************************************
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 QgsDevToolsModelNode;
class QgsDevToolsModelGroup;
class QgsDatabaseQueryLoggerRootNode;
class QgsDatabaseQueryLoggerQueryGroup;
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;
// 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.
*/
QgsDevToolsModelNode *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:
/**
* Clears all logged entries.
*/
void clear();
private slots:
void queryLogged( const QgsDatabaseQueryLogEntry &query );
void queryFinished( const QgsDatabaseQueryLogEntry &query );
private:
//! Returns index for a given node
QModelIndex node2index( QgsDevToolsModelNode *node ) const;
QModelIndex indexOfParentLayerTreeNode( QgsDevToolsModelNode *parentNode ) const;
std::unique_ptr< QgsDatabaseQueryLoggerRootNode > mRootNode;
QHash< int, QgsDatabaseQueryLoggerQueryGroup * > mQueryGroups;
};
/**
* \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

View File

@ -0,0 +1,251 @@
/***************************************************************************
qgsdatabasequeryloggernode.cpp
-------------------------
begin : October 2021
copyright : (C) 2021 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsdatabasequeryloggernode.h"
#include "qgis.h"
#include "qgsjsonutils.h"
#include <QUrlQuery>
#include <QColor>
#include <QBrush>
#include <QFont>
#include <QAction>
#include <QDesktopServices>
#include <QApplication>
#include <QClipboard>
#include <nlohmann/json.hpp>
//
// QgsDatabaseQueryLoggerRootNode
//
QgsDatabaseQueryLoggerRootNode::QgsDatabaseQueryLoggerRootNode()
: QgsDevToolsModelGroup( QString() )
{
}
QVariant QgsDatabaseQueryLoggerRootNode::data( int ) const
{
return QVariant();
}
void QgsDatabaseQueryLoggerRootNode::removeRow( int row )
{
mChildren.erase( mChildren.begin() + row );
}
QVariant QgsDatabaseQueryLoggerRootNode::toVariant() const
{
QVariantList res;
for ( const std::unique_ptr< QgsDevToolsModelNode > &child : mChildren )
res << child->toVariant();
return res;
}
//
// QgsDatabaseQueryLoggerQueryGroup
//
QgsDatabaseQueryLoggerQueryGroup::QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query )
: QgsDevToolsModelGroup( QString() )
, mSql( query.query )
, mQueryId( query.queryId )
{
#if 0
std::unique_ptr< QgsNetworkLoggerRequestDetailsGroup > detailsGroup = std::make_unique< QgsNetworkLoggerRequestDetailsGroup >( request );
mDetailsGroup = detailsGroup.get();
addChild( std::move( detailsGroup ) );
#endif
addKeyValueNode( QObject::tr( "Provider" ), query.provider );
addKeyValueNode( QObject::tr( "URI" ), query.uri );
addKeyValueNode( QObject::tr( "Started at" ), QDateTime::fromMSecsSinceEpoch( query.startedTime ).toString( Qt::ISODateWithMs ) );
#if 0
addKeyValueNode( QObject::tr( "Thread" ), query.originatingThreadId() );
#endif
addKeyValueNode( QObject::tr( "Initiator" ), query.initiatorClass.isEmpty() ? QObject::tr( "unknown" ) : query.initiatorClass );
if ( !query.origin.isEmpty() )
addKeyValueNode( QObject::tr( "Location" ), query.origin );
}
QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const
{
switch ( role )
{
case Qt::DisplayRole:
return QStringLiteral( "%1 %2" ).arg( QString::number( mQueryId ),
mSql );
case Qt::ToolTipRole:
{
// Show no more than 255 characters
return mSql.length() > 255 ? mSql.mid( 0, 255 ).append( QStringLiteral( "" ) ) : mSql;
#if 0
QString bytes = QObject::tr( "unknown" );
if ( mBytesTotal != 0 )
{
if ( mBytesReceived > 0 && mBytesReceived < mBytesTotal )
bytes = QStringLiteral( "%1/%2" ).arg( QString::number( mBytesReceived ), QString::number( mBytesTotal ) );
else if ( mBytesReceived > 0 && mBytesReceived == mBytesTotal )
bytes = QString::number( mBytesTotal );
}
// ?? adding <br/> instead of \n after (very long) url seems to break url up
// COMPLETE, Status: 200 - text/xml; charset=utf-8 - 2334 bytes - 657 milliseconds
return QStringLiteral( "%1<br/>%2 - Status: %3 - %4 - %5 bytes - %6 msec - %7 replies" )
.arg( mUrl.url(),
statusToString( mStatus ),
QString::number( mHttpStatus ),
mContentType,
bytes,
mStatus == Status::Pending ? QString::number( mTimer.elapsed() / 1000 ) : QString::number( mTotalTime ),
QString::number( mReplies ) );
#endif
}
case RoleStatus:
return static_cast< int >( mStatus );
case RoleId:
return mQueryId;
case Qt::ForegroundRole:
{
switch ( mStatus )
{
case QgsDatabaseQueryLoggerQueryGroup::Status::Pending:
case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled:
return QBrush( QColor( 0, 0, 0, 100 ) );
case QgsDatabaseQueryLoggerQueryGroup::Status::Error:
return QBrush( QColor( 235, 10, 10 ) );
case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut:
return QBrush( QColor( 235, 10, 10 ) );
case QgsDatabaseQueryLoggerQueryGroup::Status::Complete:
break;
}
break;
}
case Qt::FontRole:
{
if ( mStatus == Status::Canceled )
{
QFont f;
f.setStrikeOut( true );
return f;
}
break;
}
default:
break;
}
return QVariant( );
}
QList<QAction *> QgsDatabaseQueryLoggerQueryGroup::actions( QObject *parent )
{
QList< QAction * > res;
QAction *copyUrlAction = new QAction( QObject::tr( "Copy SQL" ), parent );
QObject::connect( copyUrlAction, &QAction::triggered, copyUrlAction, [ = ]
{
QApplication::clipboard()->setText( mSql );
} );
res << copyUrlAction;
QAction *copyJsonAction = new QAction( QObject::tr( "Copy as JSON" ), parent );
QObject::connect( copyJsonAction, &QAction::triggered, copyJsonAction, [ = ]
{
const QVariant value = toVariant();
const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( value ).dump( 2 ) );
QApplication::clipboard()->setText( json );
} );
res << copyJsonAction;
return res;
}
QVariant QgsDatabaseQueryLoggerQueryGroup::toVariant() const
{
QVariantMap res;
res.insert( QStringLiteral( "SQL" ), mSql );
for ( const auto &child : std::as_const( mChildren ) )
{
if ( const QgsDevToolsModelValueNode *valueNode = dynamic_cast< const QgsDevToolsModelValueNode *>( child.get() ) )
{
res.insert( valueNode->key(), valueNode->value() );
}
}
return res;
}
void QgsDatabaseQueryLoggerQueryGroup::setFinished( const QgsDatabaseQueryLogEntry &query )
{
if ( query.error.isEmpty() )
{
mStatus = query.canceled ? Status::Canceled : Status::Complete;
addKeyValueNode( QObject::tr( "Total time (ms)" ), QLocale().toString( query.finishedTime - query.startedTime ) );
if ( query.fetchedRows != -1 )
{
addKeyValueNode( QObject::tr( "Row count" ), QLocale().toString( query.fetchedRows ) );
}
}
else
{
mStatus = Status::Error;
addKeyValueNode( QObject::tr( "Error" ), query.error );
}
}
void QgsDatabaseQueryLoggerQueryGroup::setStatus( QgsDatabaseQueryLoggerQueryGroup::Status status )
{
mStatus = status;
}
QString QgsDatabaseQueryLoggerQueryGroup::statusToString( QgsDatabaseQueryLoggerQueryGroup::Status status )
{
switch ( status )
{
case QgsDatabaseQueryLoggerQueryGroup::Status::Pending:
return QObject::tr( "Pending" );
case QgsDatabaseQueryLoggerQueryGroup::Status::Complete:
return QObject::tr( "Complete" );
case QgsDatabaseQueryLoggerQueryGroup::Status::Error:
return QObject::tr( "Error" );
case QgsDatabaseQueryLoggerQueryGroup::Status::TimeOut:
return QObject::tr( "Timeout" );
case QgsDatabaseQueryLoggerQueryGroup::Status::Canceled:
return QObject::tr( "Canceled" );
}
return QString();
}
void QgsDatabaseQueryLoggerQueryGroup::setSql( const QString &sql )
{
mSql = sql;
}
const QString &QgsDatabaseQueryLoggerQueryGroup::sql() const
{
return mSql;
}

View File

@ -0,0 +1,125 @@
/***************************************************************************
qgsdatabasequeryloggernode.h
-------------------------
begin : October 2021
copyright : (C) 2021 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSDBQUERYLOGGERNODE_H
#define QGSDBQUERYLOGGERNODE_H
#include <QElapsedTimer>
#include <QVariant>
#include <QColor>
#include <QUrl>
#include <memory>
#include <deque>
#include "devtools/qgsdevtoolsmodelnode.h"
#include "qgsdbquerylog.h"
class QAction;
/**
* \ingroup app
* \class QgsDatabaseQueryLoggerRootNode
* \brief Root node for the query logger model.
*/
class QgsDatabaseQueryLoggerRootNode final : public QgsDevToolsModelGroup
{
public:
QgsDatabaseQueryLoggerRootNode();
QVariant data( int role = Qt::DisplayRole ) const override final;
/**
* Removes a \a row from the root group.
*/
void removeRow( int row );
QVariant toVariant() const override;
};
/**
* \ingroup app
* \class QgsDatabaseQueryLoggerQueryGroup
* \brief Parent group for all database queries, showing the query id, SQL
* and containing child groups with detailed query and result information.
*
* Visually, a QgsDatabaseQueryLoggerQueryGroup is structured by:
*
* |__ QgsDatabaseQueryLoggerQueryGroup (showing sql, uri,...)
* |__ QgsDevToolsModelValueNode(key-value pairs with info)
* ...
*/
class QgsDatabaseQueryLoggerQueryGroup final : public QgsDevToolsModelGroup
{
public:
//! Query status
enum class Status
{
Pending, //!< Query underway
Complete, //!< Query was successfully completed
Error, //!< Query encountered an error
TimeOut, //!< Query timed out
Canceled, //!< Query was manually canceled
};
/**
* Constructor for QgsDatabaseQueryLoggerQueryGroup, populated from the
* specified \a query details.
*/
QgsDatabaseQueryLoggerQueryGroup( const QgsDatabaseQueryLogEntry &query );
QVariant data( int role = Qt::DisplayRole ) const override;
QList< QAction * > actions( QObject *parent ) override final;
QVariant toVariant() const override;
/**
* Called when the \a query is finished.
*
* Will automatically create children encapsulating the completed details.
*/
void setFinished( const QgsDatabaseQueryLogEntry &query );
/**
* Returns the query's status.
*/
Status status() const { return mStatus; }
/**
* Set the query \a status
*/
void setStatus( Status status );
/**
* Converts a request \a status to a translated string value.
*/
static QString statusToString( Status status );
/**
* Sets the SQL to \a sql.
*/
void setSql( const QString &sql );
/**
* Returns the group SQL.
*/
const QString &sql() const;
private:
QString mSql;
int mQueryId = 0;
QByteArray mData;
Status mStatus = Status::Pending;
};
#endif // QGSDBQUERYLOGGERNODE_H

View File

@ -0,0 +1,205 @@
/***************************************************************************
qgsqueryloggerpanelwidget.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 "qgsdatabasequeryloggernode.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 queries" ) );
mActionRecord->setChecked( QgsApplication::databaseQueryLog()->enabled() );
connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, mTreeView, &QgsDatabaseQueryLoggerTreeView::setFilterString );
connect( mActionClear, &QAction::triggered, mLogger, &QgsAppQueryLogger::clear );
connect( mActionRecord, &QAction::toggled, this, [ = ]( bool enabled )
{
QgsSettings().setValue( QStringLiteral( "logDatabaseQueries" ), enabled, QgsSettings::App );
QgsApplication::databaseQueryLog()->setEnabled( enabled );
} );
connect( mActionSaveLog, &QAction::triggered, this, [ = ]()
{
if ( QMessageBox::warning( this, tr( "Save Database Query Log" ),
tr( "Security warning: query logs may contain sensitive data including usernames or passwords. Treat this log as confidential and be careful who you share it with. Continue?" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
return;
const QString saveFilePath = QFileDialog::getSaveFileName( this, tr( "Save Query 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 );
}

View File

@ -0,0 +1,84 @@
/***************************************************************************
qgsqueryloggerpanelwidget.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 QGSQUERYLOGGERPANELWIDGET_H
#define QGSQUERYLOGGERPANELWIDGET_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 // QGSQUERYLOGGERPANELWIDGET_H

View File

@ -0,0 +1,29 @@
/***************************************************************************
qgsqueryloggerwidgetfactory.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/database.svg" ) ) )
, mLogger( logger )
{
}
QgsDevToolWidget *QgsDatabaseQueryLoggerWidgetFactory::createWidget( QWidget *parent ) const
{
return new QgsDatabaseQueryLoggerPanelWidget( mLogger, parent );
}

View File

@ -0,0 +1,35 @@
/***************************************************************************
qgsqueryloggerwidgetfactory.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 QGSQUERYLOGGERWIDGETFACTORY_H
#define QGSQUERYLOGGERWIDGETFACTORY_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 // QGSQUERYLOGGERWIDGETFACTORY_H

View File

@ -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"
@ -1034,6 +1036,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers
QgsSettings settings; QgsSettings settings;
startProfile( tr( "Create database query logger" ) );
mQueryLogger = new QgsAppQueryLogger( this );
QgsApplication::databaseQueryLog()->setEnabled( settings.value( QStringLiteral( "logDatabaseQueries" ), false, QgsSettings::App ).toBool() );
endProfile();
startProfile( tr( "Building style sheet" ) ); startProfile( tr( "Building style sheet" ) );
// set up stylesheet builder and apply saved or default style options // set up stylesheet builder and apply saved or default style options
@ -1831,6 +1837,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();

View File

@ -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;

View File

@ -356,6 +356,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
@ -1005,6 +1006,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

View File

@ -28,6 +28,7 @@
#include "qgsfeedback.h" #include "qgsfeedback.h"
#include "qgsogrutils.h" #include "qgsogrutils.h"
#include "qgsfielddomain.h" #include "qgsfielddomain.h"
#include "qgsdbquerylog.h"
#include <QTextCodec> #include <QTextCodec>
#include <QRegularExpression> #include <QRegularExpression>
@ -351,8 +352,11 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities()
QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const
{ {
QgsDatabaseQueryLogWrapper logWrapper( sql, uri(), providerKey(), QStringLiteral( "QgsGeoPackageProviderConnection" ), QGS_QUERY_LOG_ORIGIN );
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{ {
logWrapper.setCanceled();
return QgsAbstractDatabaseProviderConnection::QueryResult(); return QgsAbstractDatabaseProviderConnection::QueryResult();
} }
@ -363,6 +367,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{ {
logWrapper.setCanceled();
return QgsAbstractDatabaseProviderConnection::QueryResult(); return QgsAbstractDatabaseProviderConnection::QueryResult();
} }
@ -379,9 +384,9 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
results.setQueryExecutionTime( std::chrono::duration_cast<std::chrono::milliseconds>( end - begin ).count() ); results.setQueryExecutionTime( std::chrono::duration_cast<std::chrono::milliseconds>( end - begin ).count() );
gdal::ogr_feature_unique_ptr fet; gdal::ogr_feature_unique_ptr fet;
if ( fet.reset( OGR_L_GetNextFeature( ogrLayer ) ), fet ) if ( fet.reset( OGR_L_GetNextFeature( ogrLayer ) ), fet )
{ {
// pk column name // pk column name
QString pkColumnName; QString pkColumnName;
@ -453,11 +458,13 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
if ( ! errCause.isEmpty() ) if ( ! errCause.isEmpty() )
{ {
logWrapper.setError( errCause );
throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) );
} }
OGR_L_ResetReading( ogrLayer ); OGR_L_ResetReading( ogrLayer );
iterator->nextRow(); iterator->nextRow();
return results; return results;
} }
@ -475,6 +482,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti
if ( !errCause.isEmpty() ) if ( !errCause.isEmpty() )
{ {
logWrapper.setError( errCause );
throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) );
} }

View File

@ -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;
} }

View File

@ -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;

View File

@ -0,0 +1,76 @@
/***************************************************************************
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
//
bool QgsDatabaseQueryLog::sEnabled = false;
QgsDatabaseQueryLog::QgsDatabaseQueryLog( QObject *parent )
: QObject( parent )
{
}
void QgsDatabaseQueryLog::log( const QgsDatabaseQueryLogEntry &query )
{
if ( !sEnabled )
return;
QMetaObject::invokeMethod( QgsApplication::databaseQueryLog(), "queryStartedPrivate", Qt::QueuedConnection, Q_ARG( QgsDatabaseQueryLogEntry, query ) );
}
void QgsDatabaseQueryLog::finished( const QgsDatabaseQueryLogEntry &query )
{
if ( !sEnabled )
return;
// record time of completion
QgsDatabaseQueryLogEntry finishedQuery = query;
finishedQuery.finishedTime = QDateTime::currentMSecsSinceEpoch();
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 );
}

270
src/core/qgsdbquerylog.h Normal file
View File

@ -0,0 +1,270 @@
/***************************************************************************
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;
//! Database URI
QString uri;
//! Provider key
QString provider;
//! 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;
/**
* Number of fetched/affected rows.
* \warning Not all providers support this information.
*/
long long fetchedRows = -1;
/**
* Error reported by the provider, normally blank
*/
QString error;
/**
* Canceled flag for user canceled queries.
*/
bool canceled = false;
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__ + ")");
#define QGS_QUERY_LOG_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 );
/**
* Enables query logging.
*
* If disabled, no signals will be emitted by the log. By default the log is disabled,
* and clients must manually enable it.
*
* \note Not available in Python bindings
* \see enabled()
*/
static void setEnabled( bool enabled ) SIP_SKIP { sEnabled = enabled; }
/**
* Returns TRUE if logging is enabled.
*
* \see setEnabled()
*/
static bool enabled() { return sEnabled; }
/**
* Logs a database \a query as starting.
*
* This method can be safely called from any thread.
*/
static void log( const QgsDatabaseQueryLogEntry &query );
/**
* Records that the database \a query has 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;
private:
static bool sEnabled;
};
#ifndef SIP_RUN
///@cond private
/**
* The QgsDatabaseQueryLogWrapper class is a RIIA wrapper for the query logger.
*/
class QgsDatabaseQueryLogWrapper
{
public:
QgsDatabaseQueryLogWrapper( const QString &query, const QString &uri, const QString &provider, const QString &initiatorClass, const QString &origin )
: mEntry( query )
{
mEntry.uri = uri;
mEntry.origin = origin;
mEntry.initiatorClass = initiatorClass;
mEntry.provider = provider;
QgsDatabaseQueryLog::log( mEntry );
}
~QgsDatabaseQueryLogWrapper( )
{
QgsDatabaseQueryLog::finished( mEntry );
}
void setFetchedRows( long long fetchedRows )
{
mEntry.fetchedRows = fetchedRows;
}
void setQuery( const QString &query )
{
mEntry.query = query;
}
void setError( const QString &error )
{
mEntry.error = error;
}
void setCanceled( )
{
mEntry.canceled = true;
}
private:
QgsDatabaseQueryLogEntry mEntry;
};
///@endcond
#endif
#endif // QGSDBQUERYLOG_H

View File

@ -20,6 +20,7 @@
#include "qgsmssqlprovider.h" #include "qgsmssqlprovider.h"
#include "qgsmssqltransaction.h" #include "qgsmssqltransaction.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgsdbquerylog.h"
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsexception.h" #include "qgsexception.h"
#include "qgsmssqldatabase.h" #include "qgsmssqldatabase.h"
@ -525,7 +526,7 @@ bool QgsMssqlFeatureIterator::fetchFeature( QgsFeature &feature )
case PktFidMap: case PktFidMap:
{ {
QVariantList primaryKeyVals; QVariantList primaryKeyVals;
for ( int idx : mSource->mPrimaryKeyAttrs ) for ( int idx : std::as_const( mSource->mPrimaryKeyAttrs ) )
{ {
QgsField fld = mSource->mFields.at( idx ); QgsField fld = mSource->mFields.at( idx );
@ -607,29 +608,50 @@ bool QgsMssqlFeatureIterator::rewind()
mQuery->clear(); mQuery->clear();
mQuery->setForwardOnly( true ); mQuery->setForwardOnly( true );
bool result = mQuery->exec( mOrderByClause.isEmpty() ? mStatement : mStatement + mOrderByClause ); QString sql { mOrderByClause.isEmpty() ? mStatement : mStatement + mOrderByClause };
if ( !result && !mFallbackStatement.isEmpty() ) std::unique_ptr<QgsDatabaseQueryLogWrapper> logWrapper = std::make_unique<QgsDatabaseQueryLogWrapper>( sql, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN );
bool result = mQuery->exec( sql );
if ( !result )
{ {
//try with fallback statement logWrapper->setError( mQuery->lastError().text() );
result = mQuery->exec( mOrderByClause.isEmpty() ? mFallbackStatement : mFallbackStatement + mOrderByClause ); if ( !mFallbackStatement.isEmpty() )
if ( result )
{ {
mExpressionCompiled = false; //try with fallback statement
mCompileStatus = NoCompilation; sql = mOrderByClause.isEmpty() ? mFallbackStatement : mFallbackStatement + mOrderByClause;
logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) );
result = mQuery->exec( sql );
if ( result )
{
mExpressionCompiled = false;
mCompileStatus = NoCompilation;
}
else
{
logWrapper->setError( mQuery->lastError().text() );
}
} }
} }
if ( !result && !mOrderByClause.isEmpty() ) if ( !result && !mOrderByClause.isEmpty() )
{ {
//try without order by clause //try without order by clause
logWrapper.reset( new QgsDatabaseQueryLogWrapper( mStatement, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) );
result = mQuery->exec( mStatement ); result = mQuery->exec( mStatement );
if ( result ) if ( result )
{
mOrderByCompiled = false; mOrderByCompiled = false;
}
else
{
logWrapper->setError( mQuery->lastError().text() );
}
} }
if ( !result && !mFallbackStatement.isEmpty() && !mOrderByClause.isEmpty() ) if ( !result && !mFallbackStatement.isEmpty() && !mOrderByClause.isEmpty() )
{ {
//try with fallback statement and without order by clause //try with fallback statement and without order by clause
logWrapper.reset( new QgsDatabaseQueryLogWrapper( mFallbackStatement, mSource->connInfo(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) );
result = mQuery->exec( mFallbackStatement ); result = mQuery->exec( mFallbackStatement );
if ( result ) if ( result )
{ {
@ -637,6 +659,10 @@ bool QgsMssqlFeatureIterator::rewind()
mOrderByCompiled = false; mOrderByCompiled = false;
mCompileStatus = NoCompilation; mCompileStatus = NoCompilation;
} }
else
{
logWrapper->setError( mQuery->lastError().text() );
}
} }
if ( !result ) if ( !result )
@ -689,9 +715,15 @@ QgsMssqlFeatureSource::QgsMssqlFeatureSource( const QgsMssqlProvider *p )
, mDisableInvalidGeometryHandling( p->mDisableInvalidGeometryHandling ) , mDisableInvalidGeometryHandling( p->mDisableInvalidGeometryHandling )
, mCrs( p->crs() ) , mCrs( p->crs() )
, mTransactionConn( p->transaction() ? static_cast<QgsMssqlTransaction *>( p->transaction() )->conn() : std::shared_ptr<QgsMssqlDatabase>() ) , mTransactionConn( p->transaction() ? static_cast<QgsMssqlTransaction *>( p->transaction() )->conn() : std::shared_ptr<QgsMssqlDatabase>() )
, mConnInfo( p->uri().uri( ) )
{} {}
QgsFeatureIterator QgsMssqlFeatureSource::getFeatures( const QgsFeatureRequest &request ) QgsFeatureIterator QgsMssqlFeatureSource::getFeatures( const QgsFeatureRequest &request )
{ {
return QgsFeatureIterator( new QgsMssqlFeatureIterator( this, false, request ) ); return QgsFeatureIterator( new QgsMssqlFeatureIterator( this, false, request ) );
} }
const QString &QgsMssqlFeatureSource::connInfo() const
{
return mConnInfo;
}

View File

@ -38,6 +38,8 @@ class QgsMssqlFeatureSource final: public QgsAbstractFeatureSource
QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override; QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override;
const QString &connInfo() const;
private: private:
QgsFields mFields; QgsFields mFields;
QgsMssqlPrimaryKeyType mPrimaryKeyType; QgsMssqlPrimaryKeyType mPrimaryKeyType;
@ -76,6 +78,9 @@ class QgsMssqlFeatureSource final: public QgsAbstractFeatureSource
// Return True if this feature source has spatial attributes. // Return True if this feature source has spatial attributes.
bool isSpatial() { return !mGeometryColName.isEmpty() || !mGeometryColType.isEmpty(); } bool isSpatial() { return !mGeometryColName.isEmpty() || !mGeometryColType.isEmpty(); }
// Uri information for query logger
QString mConnInfo;
friend class QgsMssqlFeatureIterator; friend class QgsMssqlFeatureIterator;
friend class QgsMssqlExpressionCompiler; friend class QgsMssqlExpressionCompiler;
}; };

View File

@ -20,6 +20,7 @@
#include "qgsmssqldatabase.h" #include "qgsmssqldatabase.h"
#include "qgsmssqlproviderconnection.h" #include "qgsmssqlproviderconnection.h"
#include "qgsfeedback.h" #include "qgsfeedback.h"
#include "qgsdbquerylog.h"
#include <QtGlobal> #include <QtGlobal>
#include <QFileInfo> #include <QFileInfo>
@ -53,6 +54,12 @@
#include "qgsmssqltransaction.h" #include "qgsmssqltransaction.h"
#include "qgsconfig.h"
constexpr int sMssqlConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR );
#define LoggedExec(query, sql ) execLogged( query, sql, QString(QString( __FILE__ ).mid( sMssqlConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
#define LoggedExecMetadata(query, sql, uri ) execLogged( query, sql, uri, QString(QString( __FILE__ ).mid( sMssqlConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
const QString QgsMssqlProvider::MSSQL_PROVIDER_KEY = QStringLiteral( "mssql" ); const QString QgsMssqlProvider::MSSQL_PROVIDER_KEY = QStringLiteral( "mssql" );
const QString QgsMssqlProvider::MSSQL_PROVIDER_DESCRIPTION = QStringLiteral( "MSSQL spatial data provider" ); const QString QgsMssqlProvider::MSSQL_PROVIDER_DESCRIPTION = QStringLiteral( "MSSQL spatial data provider" );
int QgsMssqlProvider::sConnectionId = 0; int QgsMssqlProvider::sConnectionId = 0;
@ -314,7 +321,7 @@ void QgsMssqlProvider::loadMetadata()
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "SELECT f_geometry_column, srid, geometry_type, coord_dimension FROM geometry_columns WHERE f_table_schema=%1 AND f_table_name=%2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ) ) ) if ( !LoggedExec( query, QStringLiteral( "SELECT f_geometry_column, srid, geometry_type, coord_dimension FROM geometry_columns WHERE f_table_schema=%1 AND f_table_name=%2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ) ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -333,6 +340,29 @@ void QgsMssqlProvider::loadMetadata()
} }
} }
bool QgsMssqlProvider::execLogged( QSqlQuery &qry, const QString &sql, const QString &queryOrigin ) const
{
QgsDatabaseQueryLogWrapper logWrapper{ sql, uri().uri(), QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), queryOrigin };
const bool res { qry.exec( sql ) };
if ( ! res )
{
logWrapper.setError( qry.lastError().text() );
}
else
{
if ( qry.isSelect() )
{
logWrapper.setFetchedRows( qry.size() );
}
else
{
logWrapper.setFetchedRows( qry.numRowsAffected() );
}
}
logWrapper.setQuery( qry.lastQuery() );
return res;
}
void QgsMssqlProvider::setLastError( const QString &error ) void QgsMssqlProvider::setLastError( const QString &error )
{ {
appendError( error ); appendError( error );
@ -361,8 +391,10 @@ void QgsMssqlProvider::loadFields()
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
const QString sql { QStringLiteral( "SELECT name FROM sys.columns WHERE is_computed = 1 AND object_id = OBJECT_ID('[%1].[%2]')" ).arg( mSchemaName, mTableName ) };
// Get computed columns which need to be ignored on insert or update. // Get computed columns which need to be ignored on insert or update.
if ( !query.exec( QStringLiteral( "SELECT name FROM sys.columns WHERE is_computed = 1 AND object_id = OBJECT_ID('[%1].[%2]')" ).arg( mSchemaName, mTableName ) ) ) if ( !LoggedExec( query, sql ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
return; return;
@ -376,20 +408,24 @@ void QgsMssqlProvider::loadFields()
// Field has unique constraint // Field has unique constraint
QSet<QString> setColumnUnique; QSet<QString> setColumnUnique;
{ {
if ( !query.exec( QStringLiteral( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC" const QString sql2 { QStringLiteral( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC"
" INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC ON TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME" " INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC ON TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME"
" WHERE TC.CONSTRAINT_SCHEMA = '%1' AND TC.TABLE_NAME = '%2' AND TC.CONSTRAINT_TYPE = 'unique'" ) " WHERE TC.CONSTRAINT_SCHEMA = '%1' AND TC.TABLE_NAME = '%2' AND TC.CONSTRAINT_TYPE = 'unique'" )
.arg( mSchemaName, mTableName ) ) ) .arg( mSchemaName, mTableName ) };
if ( !LoggedExec( query, sql2 ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
return; return;
} }
while ( query.next() ) while ( query.next() )
{
setColumnUnique.insert( query.value( QStringLiteral( "COLUMN_NAME" ) ).toString() ); setColumnUnique.insert( query.value( QStringLiteral( "COLUMN_NAME" ) ).toString() );
}
} }
if ( !query.exec( QStringLiteral( "exec sp_columns @table_name = N%1, @table_owner = %2" ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) ) ) const QString sql3 { QStringLiteral( "exec sp_columns @table_name = N%1, @table_owner = %2" ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) };
if ( !LoggedExec( query, sql3 ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
return; return;
@ -504,7 +540,8 @@ void QgsMssqlProvider::loadFields()
{ {
query.clear(); query.clear();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "exec sp_pkeys @table_name = N%1, @table_owner = %2 " ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) ) ) const QString sql4 { QStringLiteral( "exec sp_pkeys @table_name = N%1, @table_owner = %2 " ).arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ) };
if ( !LoggedExec( query, sql4 ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -542,8 +579,9 @@ void QgsMssqlProvider::loadFields()
{ {
query.clear(); query.clear();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "select count(distinct [%1]), count([%1]) from [%2].[%3]" ) const QString sql5 { QStringLiteral( "select count(distinct [%1]), count([%1]) from [%2].[%3]" )
.arg( pk, mSchemaName, mTableName ) ) ) .arg( pk, mSchemaName, mTableName ) };
if ( !LoggedExec( query, sql5 ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -636,7 +674,7 @@ QVariant QgsMssqlProvider::defaultValue( int fieldId ) const
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
const QString errorMessage( tr( "Could not execute query: %1" ).arg( query.lastError().text() ) ); const QString errorMessage( tr( "Could not execute query: %1" ).arg( query.lastError().text() ) );
QgsDebugMsg( errorMessage ); QgsDebugMsg( errorMessage );
@ -704,7 +742,7 @@ QVariant QgsMssqlProvider::minimumValue( int index ) const
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -745,7 +783,7 @@ QVariant QgsMssqlProvider::maximumValue( int index ) const
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -794,7 +832,7 @@ QSet<QVariant> QgsMssqlProvider::uniqueValues( int index, int limit ) const
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -848,7 +886,7 @@ QStringList QgsMssqlProvider::uniqueStringsMatching( int index, const QString &s
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -896,7 +934,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const
statement = QString( sql ).arg( mSchemaName, mTableName ); statement = QString( sql ).arg( mSchemaName, mTableName );
if ( query.exec( statement ) ) if ( LoggedExec( query, statement ) )
{ {
if ( query.next() && ( !query.value( 0 ).isNull() || if ( query.next() && ( !query.value( 0 ).isNull() ||
!query.value( 1 ).isNull() || !query.value( 1 ).isNull() ||
@ -984,7 +1022,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const
const QString statementSample = statement + ( mSqlWhereClause.isEmpty() ? " WHERE " : " AND " ) + sampleFilter; const QString statementSample = statement + ( mSqlWhereClause.isEmpty() ? " WHERE " : " AND " ) + sampleFilter;
if ( query.exec( statementSample ) && query.next() && if ( LoggedExec( query, statementSample ) && query.next() &&
!query.value( 0 ).isNull() && query.value( 4 ).toInt() >= minSampleCount ) !query.value( 0 ).isNull() && query.value( 4 ).toInt() >= minSampleCount )
{ {
mExtent.setXMinimum( query.value( 0 ).toDouble() ); mExtent.setXMinimum( query.value( 0 ).toDouble() );
@ -995,7 +1033,7 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate ) const
} }
} }
if ( !query.exec( statement ) ) if ( !LoggedExec( query, statement ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
} }
@ -1074,7 +1112,7 @@ long long QgsMssqlProvider::featureCount() const
" JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)" " JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)"
" WHERE SCHEMA_NAME(t.schema_id) = %1 AND OBJECT_NAME(t.OBJECT_ID) = %2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) ); " WHERE SCHEMA_NAME(t.schema_id) = %1 AND OBJECT_NAME(t.OBJECT_ID) = %2" ).arg( quotedValue( mSchemaName ), quotedValue( mTableName ) );
if ( query.exec( statement ) && query.next() ) if ( LoggedExec( query, statement ) && query.next() )
{ {
return query.value( 0 ).toLongLong(); return query.value( 0 ).toLongLong();
} }
@ -1432,7 +1470,7 @@ bool QgsMssqlProvider::addAttributes( const QList<QgsField> &attributes )
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( statement ) ) if ( !LoggedExec( query, statement ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
return false; return false;
@ -1465,13 +1503,14 @@ bool QgsMssqlProvider::deleteAttributes( const QgsAttributeIds &attributes )
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( statement ) ) if ( !LoggedExec( query, statement ) )
{ {
QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) ); QgsDebugMsg( QStringLiteral( "SQL:%1\n Error:%2" ).arg( query.lastQuery(), query.lastError().text() ) );
return false; return false;
} }
query.finish(); query.finish();
loadFields(); loadFields();
if ( mTransaction ) if ( mTransaction )
@ -1732,7 +1771,8 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids )
query.setForwardOnly( true ); query.setForwardOnly( true );
const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE [%3] IN (%4)" ).arg( mSchemaName, mTableName, mAttributeFields.at( mPrimaryKeyAttrs[0] ).name(), featureIds ); const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE [%3] IN (%4)" ).arg( mSchemaName, mTableName, mAttributeFields.at( mPrimaryKeyAttrs[0] ).name(), featureIds );
if ( query.exec( statement ) )
if ( LoggedExec( query, statement ) )
{ {
if ( query.numRowsAffected() == ids.size() ) if ( query.numRowsAffected() == ids.size() )
{ {
@ -1744,7 +1784,9 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids )
pushError( tr( "Only %1 of %2 features deleted" ).arg( query.numRowsAffected() ).arg( ids.size() ) ); pushError( tr( "Only %1 of %2 features deleted" ).arg( query.numRowsAffected() ).arg( ids.size() ) );
} }
else else
{
pushError( query.lastError().text() ); pushError( query.lastError().text() );
}
} }
else if ( mPrimaryKeyType == PktFidMap ) else if ( mPrimaryKeyType == PktFidMap )
{ {
@ -1754,7 +1796,8 @@ bool QgsMssqlProvider::deleteFeatures( const QgsFeatureIds &ids )
for ( QgsFeatureIds::const_iterator it = ids.begin(); it != ids.end(); ++it ) for ( QgsFeatureIds::const_iterator it = ids.begin(); it != ids.end(); ++it )
{ {
const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE %3" ).arg( mSchemaName, mTableName, whereClauseFid( *it ) ); const QString statement = QStringLiteral( "DELETE FROM [%1].[%2] WHERE %3" ).arg( mSchemaName, mTableName, whereClauseFid( *it ) );
if ( query.exec( statement ) )
if ( LoggedExec( query, statement ) )
{ {
if ( query.numRowsAffected() == 1 ) if ( query.numRowsAffected() == 1 )
{ {
@ -1830,7 +1873,7 @@ bool QgsMssqlProvider::createSpatialIndex()
statement += QLatin1String( " USING GEOGRAPHY_GRID" ); statement += QLatin1String( " USING GEOGRAPHY_GRID" );
} }
if ( !query.exec( statement ) ) if ( !LoggedExec( query, statement ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
return false; return false;
@ -1854,7 +1897,7 @@ bool QgsMssqlProvider::createAttributeIndex( int field )
statement = QStringLiteral( "CREATE NONCLUSTERED INDEX [qgs_%1_idx] ON [%2].[%3] ( [%4] )" ).arg( statement = QStringLiteral( "CREATE NONCLUSTERED INDEX [qgs_%1_idx] ON [%2].[%3] ( [%4] )" ).arg(
mGeometryColName, mSchemaName, mTableName, mAttributeFields.at( field ).name() ); mGeometryColName, mSchemaName, mTableName, mAttributeFields.at( field ).name() );
if ( !query.exec( statement ) ) if ( !LoggedExec( query, statement ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
return false; return false;
@ -1870,7 +1913,9 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const
// try to load crs from the database tables as a fallback // try to load crs from the database tables as a fallback
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
bool execOk = query.exec( QStringLiteral( "SELECT srtext FROM spatial_ref_sys WHERE srid=%1" ).arg( mSRId ) ); const QString statement { QStringLiteral( "SELECT srtext FROM spatial_ref_sys WHERE srid=%1" ).arg( mSRId ) };
bool execOk = LoggedExec( query, statement );
if ( execOk && query.isActive() ) if ( execOk && query.isActive() )
{ {
if ( query.next() ) if ( query.next() )
@ -1886,7 +1931,7 @@ QgsCoordinateReferenceSystem QgsMssqlProvider::crs() const
query.setForwardOnly( true ); query.setForwardOnly( true );
// Look in the system reference table for the data if we can't find it yet // Look in the system reference table for the data if we can't find it yet
execOk = query.exec( QStringLiteral( "SELECT well_known_text FROM sys.spatial_reference_systems WHERE spatial_reference_id=%1" ).arg( mSRId ) ); execOk = LoggedExec( query, QStringLiteral( "SELECT well_known_text FROM sys.spatial_reference_systems WHERE spatial_reference_id=%1" ).arg( mSRId ) );
if ( execOk && query.isActive() && query.next() ) if ( execOk && query.isActive() && query.next() )
{ {
mCrs = QgsCoordinateReferenceSystem::fromWkt( query.value( 0 ).toString() ); mCrs = QgsCoordinateReferenceSystem::fromWkt( query.value( 0 ).toString() );
@ -1949,7 +1994,7 @@ bool QgsMssqlProvider::setSubsetString( const QString &theSQL, bool )
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( sql ) ) if ( !LoggedExec( query, sql ) )
{ {
pushError( query.lastError().text() ); pushError( query.lastError().text() );
mSqlWhereClause = prevWhere; mSqlWhereClause = prevWhere;
@ -2202,8 +2247,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
"WHERE object_id = OBJECT_ID(N'[dbo].[spatial_ref_sys]') AND type in (N'U')) " "WHERE object_id = OBJECT_ID(N'[dbo].[spatial_ref_sys]') AND type in (N'U')) "
"CREATE TABLE spatial_ref_sys (srid integer not null " "CREATE TABLE spatial_ref_sys (srid integer not null "
"PRIMARY KEY, auth_name varchar(256), auth_srid integer, srtext varchar(2048), proj4text varchar(2048))" ); "PRIMARY KEY, auth_name varchar(256), auth_srid integer, srtext varchar(2048), proj4text varchar(2048))" );
std::unique_ptr <QgsDatabaseQueryLogWrapper> logWrapper = std::make_unique<QgsDatabaseQueryLogWrapper>( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN );
if ( !q.exec( sql ) ) if ( !q.exec( sql ) )
{ {
logWrapper->setError( q.lastError().text() );
if ( errorMessage ) if ( errorMessage )
*errorMessage = q.lastError().text(); *errorMessage = q.lastError().text();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
@ -2228,8 +2277,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
auth_srid, auth_srid,
quotedValue( srs.toWkt() ), quotedValue( srs.toWkt() ),
quotedValue( srs.toProj() ) ); quotedValue( srs.toProj() ) );
logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) );
if ( !q.exec( sql ) ) if ( !q.exec( sql ) )
{ {
logWrapper->setError( q.lastError().text() );
if ( errorMessage ) if ( errorMessage )
*errorMessage = q.lastError().text(); *errorMessage = q.lastError().text();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
@ -2246,8 +2299,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
// remove the old table with the same name // remove the old table with the same name
sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) BEGIN DROP TABLE [%1].[%2] DELETE FROM geometry_columns where f_table_schema='%1' and f_table_name='%2' END;" ) sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) BEGIN DROP TABLE [%1].[%2] DELETE FROM geometry_columns where f_table_schema='%1' and f_table_name='%2' END;" )
.arg( schemaName, tableName ); .arg( schemaName, tableName );
logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) );
if ( !q.exec( sql ) ) if ( !q.exec( sql ) )
{ {
logWrapper->setError( q.lastError().text() );
if ( errorMessage ) if ( errorMessage )
*errorMessage = q.lastError().text(); *errorMessage = q.lastError().text();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
@ -2258,8 +2315,12 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
// test for existing // test for existing
sql = QStringLiteral( "SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')" ) sql = QStringLiteral( "SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')" )
.arg( schemaName, tableName ); .arg( schemaName, tableName );
logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) );
if ( !q.exec( sql ) ) if ( !q.exec( sql ) )
{ {
logWrapper->setError( q.lastError().text() );
if ( errorMessage ) if ( errorMessage )
*errorMessage = q.lastError().text(); *errorMessage = q.lastError().text();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
@ -2276,11 +2337,11 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
if ( !geometryColumn.isEmpty() ) if ( !geometryColumn.isEmpty() )
{ {
sql = QString( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n"
"CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL, [%4] [geometry] NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL, [%4] [geometry] NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n"
"DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n"
"INSERT INTO [geometry_columns] ([f_table_catalog], [f_table_schema],[f_table_name], " "INSERT INTO [geometry_columns] ([f_table_catalog], [f_table_schema],[f_table_name], "
"[f_geometry_column],[coord_dimension],[srid],[geometry_type]) VALUES ('%5', '%1', '%2', '%4', %6, %7, '%8')" ) "[f_geometry_column],[coord_dimension],[srid],[geometry_type]) VALUES ('%5', '%1', '%2', '%4', %6, %7, '%8')" )
.arg( schemaName, .arg( schemaName,
tableName, tableName,
primaryKey, primaryKey,
@ -2293,17 +2354,20 @@ Qgis::VectorExportResult QgsMssqlProvider::createEmptyLayer( const QString &uri,
else else
{ {
//geometryless table //geometryless table
sql = QString( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n" sql = QStringLiteral( "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[%1].[%2]') AND type in (N'U')) DROP TABLE [%1].[%2]\n"
"CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n" "CREATE TABLE [%1].[%2]([%3] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_%2] PRIMARY KEY CLUSTERED ( [%3] ASC ))\n"
"DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n" "DELETE FROM geometry_columns WHERE f_table_schema = '%1' AND f_table_name = '%2'\n"
) )
.arg( schemaName, .arg( schemaName,
tableName, tableName,
primaryKey ); primaryKey );
} }
logWrapper.reset( new QgsDatabaseQueryLogWrapper( sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProvider" ), QGS_QUERY_LOG_ORIGIN ) );
if ( !q.exec( sql ) ) if ( !q.exec( sql ) )
{ {
logWrapper->setError( q.lastError().text() );
if ( errorMessage ) if ( errorMessage )
*errorMessage = q.lastError().text(); *errorMessage = q.lastError().text();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
@ -2462,7 +2526,9 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s
QSqlQuery query = QSqlQuery( db->db() ); QSqlQuery query = QSqlQuery( db->db() );
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'layer_styles'" ) ) ) const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'layer_styles'" ) };
if ( !LoggedExecMetadata( query, sql, uri ) )
{ {
errorCause = QObject::tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); errorCause = QObject::tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() );
return false; return false;
@ -2489,7 +2555,7 @@ bool QgsMssqlProviderMetadata::styleExists( const QString &uri, const QString &s
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsMssqlProvider::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) ); .arg( QgsMssqlProvider::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) );
if ( !query.exec( checkQuery ) ) if ( !LoggedExecMetadata( query, checkQuery, uri ) )
{ {
errorCause = QObject::tr( "Checking for style failed: %1" ).arg( query.lastError().text() ); errorCause = QObject::tr( "Checking for style failed: %1" ).arg( query.lastError().text() );
return false; return false;
@ -2527,7 +2593,10 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri,
QSqlQuery query = QSqlQuery( db->db() ); QSqlQuery query = QSqlQuery( db->db() );
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) )
QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) };
if ( !LoggedExecMetadata( query, sql, uri ) )
{ {
QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( query.lastError().text() );
return false; return false;
@ -2535,24 +2604,28 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri,
if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 )
{ {
QgsDebugMsgLevel( QStringLiteral( "Need to create styles table" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Need to create styles table" ), 2 );
const bool execOk = query.exec( QString( "CREATE TABLE [dbo].[layer_styles]("
"[id] int IDENTITY(1,1) PRIMARY KEY," sql = QStringLiteral( "CREATE TABLE [dbo].[layer_styles]("
"[f_table_catalog] [varchar](1024) NULL," "[id] int IDENTITY(1,1) PRIMARY KEY,"
"[f_table_schema] [varchar](1024) NULL," "[f_table_catalog] [varchar](1024) NULL,"
"[f_table_name] [varchar](1024) NULL," "[f_table_schema] [varchar](1024) NULL,"
"[f_geometry_column] [varchar](1024) NULL," "[f_table_name] [varchar](1024) NULL,"
"[styleName] [varchar](1024) NULL," "[f_geometry_column] [varchar](1024) NULL,"
"[styleQML] [text] NULL," "[styleName] [varchar](1024) NULL,"
"[styleSLD] [text] NULL," "[styleQML] [text] NULL,"
"[useAsDefault] [int] NULL," "[styleSLD] [text] NULL,"
"[description] [text] NULL," "[useAsDefault] [int] NULL,"
"[owner] [varchar](1024) NULL," "[description] [text] NULL,"
"[ui] [text] NULL," "[owner] [varchar](1024) NULL,"
"[update_time] [datetime] NULL" "[ui] [text] NULL,"
") ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" ) ); "[update_time] [datetime] NULL"
") ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" );
const bool execOk = LoggedExecMetadata( query, sql, uri );
if ( !execOk ) if ( !execOk )
{ {
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" ); const QString error { QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" ) };
errCause = error;
return false; return false;
} }
query.finish(); query.finish();
@ -2573,38 +2646,38 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri,
// replaced by the QString.arg function. To ensure that the final SQL string is not corrupt these // replaced by the QString.arg function. To ensure that the final SQL string is not corrupt these
// two values are both replaced in the final .arg call of the string construction. // two values are both replaced in the final .arg call of the string construction.
QString sql = QString( "INSERT INTO layer_styles" sql = QStringLiteral( "INSERT INTO layer_styles"
"(f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11" "(f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11"
") VALUES (" ") VALUES ("
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12" "%1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12"
")" ) ")" )
.arg( QgsMssqlProvider::quotedValue( dsUri.database() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.database() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ) .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) )
.arg( QgsMssqlProvider::quotedValue( qmlStyle ) ) .arg( QgsMssqlProvider::quotedValue( qmlStyle ) )
.arg( QgsMssqlProvider::quotedValue( sldStyle ) ) .arg( QgsMssqlProvider::quotedValue( sldStyle ) )
.arg( useAsDefault ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) .arg( useAsDefault ? QStringLiteral( "1" ) : QStringLiteral( "0" ) )
.arg( QgsMssqlProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) ) .arg( QgsMssqlProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.username() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.username() ) )
.arg( uiFileColumn ) .arg( uiFileColumn )
.arg( uiFileValue ); .arg( uiFileValue );
const QString checkQuery = QString( "SELECT styleName" const QString checkQuery = QStringLiteral( "SELECT styleName"
" FROM layer_styles" " FROM layer_styles"
" WHERE f_table_catalog=%1" " WHERE f_table_catalog=%1"
" AND f_table_schema=%2" " AND f_table_schema=%2"
" AND f_table_name=%3" " AND f_table_name=%3"
" AND f_geometry_column=%4" " AND f_geometry_column=%4"
" AND styleName=%5" ) " AND styleName=%5" )
.arg( QgsMssqlProvider::quotedValue( dsUri.database() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.database() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); .arg( QgsMssqlProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) );
if ( !query.exec( checkQuery ) ) if ( !LoggedExecMetadata( query, checkQuery, uri ) )
{ {
QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( query.lastError().text() );
QgsDebugMsg( QStringLiteral( "Check Query failed" ) ); QgsDebugMsg( QStringLiteral( "Check Query failed" ) );
@ -2652,7 +2725,8 @@ bool QgsMssqlProviderMetadata::saveStyle( const QString &uri,
QgsDebugMsgLevel( QStringLiteral( "Inserting styles" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Inserting styles" ), 2 );
QgsDebugMsgLevel( sql, 2 ); QgsDebugMsgLevel( sql, 2 );
const bool execOk = query.exec( sql );
const bool execOk = LoggedExecMetadata( query, sql, uri );
if ( !execOk ) if ( !execOk )
{ {
@ -2677,8 +2751,12 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau
} }
QSqlQuery query = QSqlQuery( db->db() ); QSqlQuery query = QSqlQuery( db->db() );
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) )
const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) };
if ( !LoggedExecMetadata( query, sql, uri ) )
{ {
errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() );
return QString(); return QString();
@ -2706,7 +2784,7 @@ QString QgsMssqlProviderMetadata::loadStyle( const QString &uri, QString &errCau
.arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) );
if ( !query.exec( selectQmlQuery ) ) if ( !LoggedExecMetadata( query, selectQmlQuery, uri ) )
{ {
QgsDebugMsgLevel( QStringLiteral( "Load of style failed" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Load of style failed" ), 2 );
const QString msg = query.lastError().text(); const QString msg = query.lastError().text();
@ -2742,8 +2820,10 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri,
QSqlQuery query = QSqlQuery( db->db() ); QSqlQuery query = QSqlQuery( db->db() );
query.setForwardOnly( true ); query.setForwardOnly( true );
QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) };
// check if layer_styles table already exist // check if layer_styles table already exist
if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) if ( !LoggedExecMetadata( query, sql, uri ) )
{ {
const QString msg = query.lastError().text(); const QString msg = query.lastError().text();
errCause = msg; errCause = msg;
@ -2767,7 +2847,9 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri,
.arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.schema() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) );
bool queryOk = query.exec( selectRelatedQuery );
bool queryOk = LoggedExecMetadata( query, selectRelatedQuery, uri );
if ( !queryOk ) if ( !queryOk )
{ {
QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( query.lastError().text() );
@ -2791,7 +2873,8 @@ int QgsMssqlProviderMetadata::listStyles( const QString &uri,
.arg( QgsMssqlProvider::quotedValue( dsUri.table() ) ) .arg( QgsMssqlProvider::quotedValue( dsUri.table() ) )
.arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) ); .arg( QgsMssqlProvider::quotedValue( dsUri.geometryColumn() ) );
QgsDebugMsgLevel( selectOthersQuery, 2 ); QgsDebugMsgLevel( selectOthersQuery, 2 );
queryOk = query.exec( selectOthersQuery );
queryOk = LoggedExecMetadata( query, selectOthersQuery, uri );
if ( !queryOk ) if ( !queryOk )
{ {
QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( query.lastError().text() );
@ -2828,7 +2911,9 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin
QSqlQuery query = QSqlQuery( db->db() ); QSqlQuery query = QSqlQuery( db->db() );
query.setForwardOnly( true ); query.setForwardOnly( true );
if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) ) ) const QString sql { QStringLiteral( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= N'layer_styles'" ) };
if ( !LoggedExecMetadata( query, sql, uri ) )
{ {
errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() ); errCause = tr( "Could not check if layer_styles table exists: %1" ).arg( query.lastError().text() );
return QString(); return QString();
@ -2846,7 +2931,8 @@ QString QgsMssqlProviderMetadata::getStyleById( const QString &uri, const QStrin
QString style; QString style;
const QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsMssqlProvider::quotedValue( styleId ) ); const QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsMssqlProvider::quotedValue( styleId ) );
const bool queryOk = query.exec( selectQmlQuery );
const bool queryOk = LoggedExecMetadata( query, selectQmlQuery, uri );
if ( !queryOk ) if ( !queryOk )
{ {
QgsDebugMsg( query.lastError().text() ); QgsDebugMsg( query.lastError().text() );
@ -2995,6 +3081,29 @@ QString QgsMssqlProviderMetadata::encodeUri( const QVariantMap &parts ) const
return dsUri.uri(); return dsUri.uri();
} }
bool QgsMssqlProviderMetadata::execLogged( QSqlQuery &qry, const QString &sql, const QString &uri, const QString &queryOrigin ) const
{
QgsDatabaseQueryLogWrapper logWrapper{ sql, uri, QStringLiteral( "mssql" ), QStringLiteral( "QgsMssqlProviderMetadata" ), queryOrigin };
const bool res { qry.exec( sql ) };
if ( ! res )
{
logWrapper.setError( qry.lastError().text() );
}
else
{
if ( qry.isSelect() )
{
logWrapper.setFetchedRows( qry.size() );
}
else
{
logWrapper.setFetchedRows( qry.numRowsAffected() );
}
}
logWrapper.setQuery( qry.lastQuery() );
return res;
}
QGISEXTERN QgsProviderMetadata *providerMetadataFactory() QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
{ {
return new QgsMssqlProviderMetadata(); return new QgsMssqlProviderMetadata();
@ -3156,7 +3265,8 @@ bool QgsMssqlProvider::getExtentFromGeometryColumns( QgsRectangle &extent ) cons
"AND NOT (qgis_xmin IS NULL OR qgis_xmax IS NULL OR qgis_ymin IS NULL OR qgis_ymax IS NULL)" ); "AND NOT (qgis_xmin IS NULL OR qgis_xmax IS NULL OR qgis_ymin IS NULL OR qgis_ymax IS NULL)" );
const QString statement = sql.arg( quotedValue( mTableName ), quotedValue( mSchemaName ) ); const QString statement = sql.arg( quotedValue( mTableName ), quotedValue( mSchemaName ) );
if ( query.exec( statement ) && query.isActive() )
if ( LoggedExec( query, statement ) && query.isActive() )
{ {
query.next(); query.next();
if ( query.isValid() ) if ( query.isValid() )
@ -3182,7 +3292,8 @@ bool QgsMssqlProvider::getPrimaryKeyFromGeometryColumns( QStringList &primaryKey
const QString sql = QStringLiteral( "SELECT qgis_pkey FROM geometry_columns WHERE f_table_name = '%1' AND NOT qgis_pkey IS NULL" ); const QString sql = QStringLiteral( "SELECT qgis_pkey FROM geometry_columns WHERE f_table_name = '%1' AND NOT qgis_pkey IS NULL" );
const QString statement = sql.arg( mTableName ); const QString statement = sql.arg( mTableName );
if ( query.exec( statement ) && query.isActive() )
if ( LoggedExec( query, statement ) && query.isActive() )
{ {
query.next(); query.next();
if ( query.isValid() ) if ( query.isValid() )

View File

@ -176,6 +176,8 @@ class QgsMssqlProvider final: public QgsVectorDataProvider
private: private:
bool execLogged( QSqlQuery &qry, const QString &sql, const QString &queryOrigin = QString() ) const;
//! Fields //! Fields
QgsFields mAttributeFields; QgsFields mAttributeFields;
QMap<int, QString> mDefaultValues; QMap<int, QString> mDefaultValues;
@ -328,6 +330,10 @@ class QgsMssqlProviderMetadata final: public QgsProviderMetadata
QVariantMap decodeUri( const QString &uri ) const override; QVariantMap decodeUri( const QString &uri ) const override;
QString encodeUri( const QVariantMap &parts ) const override; QString encodeUri( const QVariantMap &parts ) const override;
private:
bool execLogged( QSqlQuery &qry, const QString &sql, const QString &uri, const QString &queryOrigin = QString() ) const;
}; };
#endif // QGSMSSQLPROVIDER_H #endif // QGSMSSQLPROVIDER_H

View File

@ -27,6 +27,8 @@
#include "qgsvariantutils.h" #include "qgsvariantutils.h"
#include <QSqlError> #include <QSqlError>
#include <QSqlField>
#include <QSqlDriver>
QMap<QString, QgsOracleConn *> QgsOracleConn::sConnections; QMap<QString, QgsOracleConn *> QgsOracleConn::sConnections;
int QgsOracleConn::snConnections = 0; int QgsOracleConn::snConnections = 0;
@ -71,7 +73,8 @@ QgsOracleConn::QgsOracleConn( QgsDataSourceUri uri, bool transaction )
{ {
QgsDebugMsgLevel( QStringLiteral( "New Oracle connection for " ) + uri.connectionInfo( false ), 2 ); QgsDebugMsgLevel( QStringLiteral( "New Oracle connection for " ) + uri.connectionInfo( false ), 2 );
uri = QgsDataSourceUri( uri.connectionInfo( true ) ); mConnInfo = uri.connectionInfo( true );
uri = QgsDataSourceUri( mConnInfo );
QString database = databaseName( uri.database(), uri.host(), uri.port() ); QString database = databaseName( uri.database(), uri.host(), uri.port() );
QgsDebugMsgLevel( QStringLiteral( "New Oracle database " ) + database, 2 ); QgsDebugMsgLevel( QStringLiteral( "New Oracle database " ) + database, 2 );
@ -226,6 +229,29 @@ void QgsOracleConn::unref()
delete this; delete this;
} }
QString QgsOracleConn::getLastExecutedQuery( const QSqlQuery &query )
{
QString str = query.lastQuery();
QMapIterator<QString, QVariant> it( query.boundValues() );
while ( it.hasNext() )
{
it.next();
const QVariant &var { it.value().toString() };
QSqlField field( QString( ), var.type() );
if ( var.isNull() )
{
field.clear();
}
else
{
field.setValue( var );
}
const QString formatV = query.driver()->formatValue( field );
str.replace( it.key(), formatV );
}
return str;
}
bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList &params ) bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList &params )
{ {
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 );
@ -249,6 +275,49 @@ bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList
qry.lastError().text() ) ); qry.lastError().text() ) );
} }
return res;
}
bool QgsOracleConn::execLogged( QSqlQuery &qry, const QString &sql, const QVariantList &params, const QString &originatorClass, const QString &queryOrigin )
{
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 );
QgsDatabaseQueryLogWrapper logWrapper { sql, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin };
bool res = qry.prepare( sql );
if ( res )
{
for ( const auto &param : params )
{
QgsDebugMsgLevel( QStringLiteral( " ARG: %1 [%2]" ).arg( param.toString(), param.typeName() ), 4 );
qry.addBindValue( param );
}
res = qry.exec();
}
logWrapper.setQuery( getLastExecutedQuery( qry ) );
if ( !res )
{
logWrapper.setError( qry.lastError().text() );
QgsDebugMsg( QStringLiteral( "SQL: %1\nERROR: %2" )
.arg( qry.lastQuery(),
qry.lastError().text() ) );
}
else
{
if ( qry.isSelect() )
{
logWrapper.setFetchedRows( qry.size() );
}
else
{
logWrapper.setFetchedRows( qry.numRowsAffected() );
}
}
return res; return res;
} }
@ -257,10 +326,12 @@ QStringList QgsOracleConn::pkCandidates( const QString &ownerName, const QString
QStringList cols; QStringList cols;
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
if ( !exec( qry, QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner=? AND table_name=? ORDER BY column_id" ),
QVariantList() << ownerName << viewName ) ) if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT column_name FROM all_tab_columns WHERE owner=? AND table_name=? ORDER BY column_id" ),
QVariantList() << ownerName << viewName ) )
{ {
QgsMessageLog::logMessage( tr( "SQL: %1 [owner: %2 table_name: %3]\nerror: %4\n" ).arg( qry.lastQuery(), qry.lastError().text(), ownerName, viewName ), tr( "Oracle" ) ); const QString error { tr( "SQL: %1 [owner: %2 table_name: %3]\nerror: %4\n" ).arg( qry.lastQuery(), qry.lastError().text(), ownerName, viewName ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return cols; return cols;
} }
@ -308,9 +379,11 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly,
} }
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
if ( !exec( qry, sql, QVariantList() ) )
if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) )
{ {
QgsMessageLog::logMessage( tr( "Querying available tables failed.\nSQL: %1\nerror: %2\n" ).arg( qry.lastQuery(), qry.lastError().text() ), tr( "Oracle" ) ); const QString error { tr( "Querying available tables failed.\nSQL: %1\nerror: %2\n" ).arg( qry.lastQuery(), qry.lastError().text() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return false; return false;
} }
@ -326,8 +399,10 @@ bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly,
layerProperty.pkCols.clear(); layerProperty.pkCols.clear();
mLayersSupported << layerProperty; mLayersSupported << layerProperty;
} }
if ( mLayersSupported.size() == 0 ) if ( mLayersSupported.size() == 0 )
{ {
QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined." ), tr( "Oracle" ) ); QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined." ), tr( "Oracle" ) );
@ -417,19 +492,22 @@ bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMes
QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 );
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
if ( !exec( qry, query, QVariantList() ) ) if ( !exec( qry, query, QVariantList() ) )
{ {
QString error = qry.lastError().text(); QString error = qry.lastError().text();
if ( logError ) if ( logError )
{ {
QgsMessageLog::logMessage( tr( "Connection error: %1 returned %2" ) const QString errorMsg { tr( "Connection error: %1 returned %2" )
.arg( query, error ), .arg( query, error ) };
QgsMessageLog::logMessage( errorMsg,
tr( "Oracle" ) ); tr( "Oracle" ) );
} }
else else
{ {
QgsDebugMsg( QStringLiteral( "Connection error: %1 returned %2" ) const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" )
.arg( query, error ) ); .arg( query, error ) };
QgsDebugMsg( errorMsg );
} }
if ( errorMessage ) if ( errorMessage )
*errorMessage = error; *errorMessage = error;
@ -438,12 +516,62 @@ bool QgsOracleConn::exec( const QString &query, bool logError, QString *errorMes
return true; return true;
} }
bool QgsOracleConn::execLogged( const QString &query, bool logError, QString *errorMessage, const QString &originatorClass, const QString &queryOrigin )
{
QMutexLocker locker( &mLock );
QgsDatabaseQueryLogWrapper logWrapper { query, mConnInfo, QStringLiteral( "oracle" ), originatorClass, queryOrigin };
QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 );
QSqlQuery qry( mDatabase );
const bool res { !exec( qry, query, QVariantList() ) };
logWrapper.setQuery( qry.lastQuery() );
if ( ! res )
{
const QString error = qry.lastError().text();
logWrapper.setError( error );
if ( logError )
{
const QString errorMsg { tr( "Connection error: %1 returned %2" )
.arg( query, error ) };
QgsMessageLog::logMessage( errorMsg,
tr( "Oracle" ) );
}
else
{
const QString errorMsg { QStringLiteral( "Connection error: %1 returned %2" )
.arg( query, error ) };
QgsDebugMsg( errorMsg );
}
if ( errorMessage )
*errorMessage = error;
return false;
}
else
{
if ( qry.isSelect() )
{
logWrapper.setFetchedRows( qry.size() );
}
else
{
logWrapper.setFetchedRows( qry.numRowsAffected() );
}
}
return true;
}
bool QgsOracleConn::begin( QSqlDatabase &db ) bool QgsOracleConn::begin( QSqlDatabase &db )
{ {
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return exec( QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) );
} }
else else
{ {
@ -456,7 +584,7 @@ bool QgsOracleConn::commit( QSqlDatabase &db )
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return exec( QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) ); return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "SAVEPOINT sp%1" ).arg( ++mSavePointId ) );
} }
else else
{ {
@ -469,7 +597,7 @@ bool QgsOracleConn::rollback( QSqlDatabase &db )
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return exec( QStringLiteral( "ROLLBACK TO SAVEPOINT sp%1" ).arg( mSavePointId ) ); return LoggedExec( QStringLiteral( "QgsOracleConn" ), QStringLiteral( "ROLLBACK TO SAVEPOINT sp%1" ).arg( mSavePointId ) );
} }
else else
{ {
@ -581,14 +709,15 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b
sql += QLatin1String( " FROM %2 t WHERE NOT t.%1 IS NULL%3" ); sql += QLatin1String( " FROM %2 t WHERE NOT t.%1 IS NULL%3" );
if ( !exec( qry, sql if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql
.arg( quotedIdentifier( layerProperty.geometryColName ), .arg( quotedIdentifier( layerProperty.geometryColName ),
table, table,
where.isEmpty() ? QString() : QStringLiteral( " AND (%1)" ).arg( where ) ), QVariantList() ) ) where.isEmpty() ? QString() : QStringLiteral( " AND (%1)" ).arg( where ) ), QVariantList() ) )
{ {
QgsMessageLog::logMessage( tr( "SQL: %1\nerror: %2\n" ) const QString error { tr( "SQL: %1\nerror: %2\n" )
.arg( qry.lastQuery(), .arg( qry.lastQuery(),
qry.lastError().text() ), qry.lastError().text() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) ); tr( "Oracle" ) );
return; return;
} }
@ -597,8 +726,10 @@ void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, b
layerProperty.srids.clear(); layerProperty.srids.clear();
QSet<int> srids; QSet<int> srids;
long long fetchedRows { 0 };
while ( qry.next() ) while ( qry.next() )
{ {
fetchedRows++;
if ( detectedType == QgsWkbTypes::Unknown ) if ( detectedType == QgsWkbTypes::Unknown )
{ {
QgsWkbTypes::Type type = wkbTypeFromDatabase( qry.value( 0 ).toInt() ); QgsWkbTypes::Type type = wkbTypeFromDatabase( qry.value( 0 ).toInt() );
@ -926,7 +1057,8 @@ bool QgsOracleConn::hasSpatial()
if ( mHasSpatial == -1 ) if ( mHasSpatial == -1 )
{ {
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
mHasSpatial = exec( qry, QStringLiteral( "SELECT 1 FROM v$option WHERE parameter='Spatial' AND value='TRUE'" ), QVariantList() ) && qry.next(); const QString sql { QStringLiteral( "SELECT 1 FROM v$option WHERE parameter='Spatial' AND value='TRUE'" ) };
mHasSpatial = LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next();
} }
return mHasSpatial; return mHasSpatial;
@ -935,16 +1067,17 @@ bool QgsOracleConn::hasSpatial()
int QgsOracleConn::version() int QgsOracleConn::version()
{ {
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
QString sql = QStringLiteral( "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION" ); const QString sql = QStringLiteral( "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION" );
if ( exec( qry, sql, QVariantList() ) && qry.next() ) if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next() )
{ {
return qry.value( 0 ).toString().split( '.' ).at( 0 ).toInt(); return qry.value( 0 ).toString().split( '.' ).at( 0 ).toInt();
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() ) .arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return -1; return -1;
} }
} }
@ -956,7 +1089,8 @@ QString QgsOracleConn::currentUser()
if ( mCurrentUser.isNull() ) if ( mCurrentUser.isNull() )
{ {
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
if ( exec( qry, QStringLiteral( "SELECT user FROM dual" ), QVariantList() ) && qry.next() ) const QString sql { QStringLiteral( "SELECT user FROM dual" ) };
if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) && qry.next() )
{ {
mCurrentUser = qry.value( 0 ).toString(); mCurrentUser = qry.value( 0 ).toString();
} }
@ -993,11 +1127,12 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr
QString name; QString name;
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
if ( exec( qry, QString( "SELECT i.index_name,i.domidx_opstatus"
" FROM all_indexes i" if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT i.index_name,i.domidx_opstatus"
" JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?" " FROM all_indexes i"
" WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ), " JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?"
QVariantList() << geometryColumn << ownerName << tableName ) ) " WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ),
QVariantList() << geometryColumn << ownerName << tableName ) )
{ {
if ( qry.next() ) if ( qry.next() )
{ {
@ -1025,11 +1160,12 @@ QString QgsOracleConn::getSpatialIndexName( const QString &ownerName, const QStr
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Probing for spatial index on column %1.%2.%3 failed [%4]" ) const QString error { tr( "Probing for spatial index on column %1.%2.%3 failed [%4]" )
.arg( ownerName ) .arg( ownerName )
.arg( tableName ) .arg( tableName )
.arg( geometryColumn ) .arg( geometryColumn )
.arg( qry.lastError().text() ), .arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) ); tr( "Oracle" ) );
isValid = false; isValid = false;
@ -1043,21 +1179,25 @@ QString QgsOracleConn::createSpatialIndex( const QString &ownerName, const QStri
QSqlQuery qry( mDatabase ); QSqlQuery qry( mDatabase );
int n = 0; int n = 0;
if ( exec( qry, QString( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ), QVariantList() ) && const QString sql { QStringLiteral( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ) };
if ( LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql, QVariantList() ) &&
qry.next() ) qry.next() )
{ {
n = qry.value( 0 ).toInt() + 1; n = qry.value( 0 ).toInt() + 1;
} }
if ( !exec( qry, QString( "CREATE INDEX QGIS_IDX_%1 ON %2.%3(%4) INDEXTYPE IS MDSYS.SPATIAL_INDEX PARALLEL" ) const QString sql2 { QStringLiteral( "CREATE INDEX QGIS_IDX_%1 ON %2.%3(%4) INDEXTYPE IS MDSYS.SPATIAL_INDEX PARALLEL" )
.arg( n, 10, 10, QChar( '0' ) ) .arg( n, 10, 10, QChar( '0' ) )
.arg( quotedIdentifier( ownerName ) ) .arg( quotedIdentifier( ownerName ) )
.arg( quotedIdentifier( tableName ) ) .arg( quotedIdentifier( tableName ) )
.arg( quotedIdentifier( geometryColumn ) ), QVariantList() ) ) .arg( quotedIdentifier( geometryColumn ) ) };
if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, sql2, QVariantList() ) )
{ {
QgsMessageLog::logMessage( tr( "Creation spatial index failed.\nSQL: %1\nError: %2" ) const QString error { tr( "Creation spatial index failed.\nSQL: %1\nError: %2" )
.arg( qry.lastQuery() ) .arg( qry.lastQuery() )
.arg( qry.lastError().text() ), .arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) ); tr( "Oracle" ) );
return QString(); return QString();
} }
@ -1071,15 +1211,16 @@ QStringList QgsOracleConn::getPrimaryKeys( const QString &ownerName, const QStri
QStringList result; QStringList result;
if ( !exec( qry, QString( "SELECT column_name" if ( !LoggedExecPrivate( QStringLiteral( "QgsOracleConn" ), qry, QStringLiteral( "SELECT column_name"
" FROM all_cons_columns a" " FROM all_cons_columns a"
" JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner" " JOIN all_constraints b ON a.constraint_name=b.constraint_name AND a.owner=b.owner"
" WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ), " WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ),
QVariantList() << ownerName << tableName ) ) QVariantList() << ownerName << tableName ) )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() ) .arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return result; return result;
} }

View File

@ -31,6 +31,7 @@
#include "qgslogger.h" #include "qgslogger.h"
#include "qgsdatasourceuri.h" #include "qgsdatasourceuri.h"
#include "qgsvectordataprovider.h" #include "qgsvectordataprovider.h"
#include "qgsdbquerylog.h"
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QSqlQuery> #include <QSqlQuery>
@ -112,6 +113,13 @@ struct QgsOracleLayerProperty
#endif #endif
}; };
#include "qgsconfig.h"
constexpr int sOracleConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR );
#define LoggedExec(_class, query) execLogged( query, true, nullptr, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
#define LoggedExecPrivate(_class, query, sql, params ) execLogged( query, sql, params, _class, QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
/** /**
* Wraps acquireConnection() and releaseConnection() from a QgsOracleConnPool. * Wraps acquireConnection() and releaseConnection() from a QgsOracleConnPool.
* This can be used to ensure a connection is correctly released when scope ends * This can be used to ensure a connection is correctly released when scope ends
@ -157,6 +165,7 @@ class QgsOracleConn : public QObject
static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ); static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid );
bool exec( const QString &query, bool logError = true, QString *errorMessage = nullptr ); bool exec( const QString &query, bool logError = true, QString *errorMessage = nullptr );
bool execLogged( const QString &sql, bool logError = true, QString *errorMessage = nullptr, const QString &originatorClass = QString(), const QString &queryOrigin = QString() );
bool begin( QSqlDatabase &db ); bool begin( QSqlDatabase &db );
bool commit( QSqlDatabase &db ); bool commit( QSqlDatabase &db );
@ -253,11 +262,14 @@ class QgsOracleConn : public QObject
operator QSqlDatabase() { return mDatabase; } operator QSqlDatabase() { return mDatabase; }
static QString getLastExecutedQuery( const QSqlQuery &query );
private: private:
explicit QgsOracleConn( QgsDataSourceUri uri, bool transaction ); explicit QgsOracleConn( QgsDataSourceUri uri, bool transaction );
~QgsOracleConn() override; ~QgsOracleConn() override;
bool exec( QSqlQuery &qry, const QString &sql, const QVariantList &params ); bool exec( QSqlQuery &qry, const QString &sql, const QVariantList &params );
bool execLogged( QSqlQuery &qry, const QString &sql, const QVariantList &params, const QString &originatorClass = QString(), const QString &queryOrigin = QString() );
//! reference count //! reference count
int mRef; int mRef;
@ -280,6 +292,9 @@ class QgsOracleConn : public QObject
static QMap<QString, QgsOracleConn *> sConnections; static QMap<QString, QgsOracleConn *> sConnections;
static int snConnections; static int snConnections;
static QMap<QString, QDateTime> sBrokenConnections; static QMap<QString, QDateTime> sBrokenConnections;
// Connection URI string representation for query logger
QString mConnInfo;
}; };
#endif #endif

View File

@ -60,10 +60,10 @@ bool deleteLayer( const QString &uri, QString &errCause )
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
// check the geometry column count // check the geometry column count
if ( !QgsOracleProvider::exec( qry, QString( "SELECT count(*)" if ( !QgsOracleProvider::execLoggedStatic( qry, QString( "SELECT count(*)"
" FROM user_tab_columns" " FROM user_tab_columns"
" WHERE table_name=? AND data_type='SDO_GEOMETRY' AND data_type_owner='MDSYS'" ), " WHERE table_name=? AND data_type='SDO_GEOMETRY' AND data_type_owner='MDSYS'" ),
QVariantList() << tableName ) QVariantList() << tableName, dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN )
|| !qry.next() ) || !qry.next() )
{ {
errCause = QObject::tr( "Unable to determine number of geometry columns of layer %1.%2: \n%3" ) errCause = QObject::tr( "Unable to determine number of geometry columns of layer %1.%2: \n%3" )
@ -97,7 +97,7 @@ bool deleteLayer( const QString &uri, QString &errCause )
args << tableName; args << tableName;
} }
if ( !QgsOracleProvider::exec( qry, dropTable, QVariantList() ) ) if ( !QgsOracleProvider::execLoggedStatic( qry, dropTable, QVariantList(), dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN ) )
{ {
errCause = QObject::tr( "Unable to delete layer %1.%2: \n%3" ) errCause = QObject::tr( "Unable to delete layer %1.%2: \n%3" )
.arg( ownerName ) .arg( ownerName )
@ -107,7 +107,7 @@ bool deleteLayer( const QString &uri, QString &errCause )
return false; return false;
} }
if ( !QgsOracleProvider::exec( qry, cleanView, args ) ) if ( !QgsOracleProvider::execLoggedStatic( qry, cleanView, args, dsUri.uri(), QStringLiteral( "QgsOracleLayerItem" ), QGS_QUERY_LOG_ORIGIN ) )
{ {
errCause = QObject::tr( "Unable to clean metadata %1.%2: \n%3" ) errCause = QObject::tr( "Unable to clean metadata %1.%2: \n%3" )
.arg( ownerName ) .arg( ownerName )
@ -544,7 +544,7 @@ QVector<QgsDataItem *> QgsOracleRootItem::createChildren()
{ {
QVector<QgsDataItem *> connections; QVector<QgsDataItem *> connections;
const QStringList list = QgsOracleConn::connectionList(); const QStringList list = QgsOracleConn::connectionList();
for ( QString connName : list ) for ( const QString &connName : std::as_const( list ) )
{ {
connections << new QgsOracleConnectionItem( this, connName, mPath + '/' + connName ); connections << new QgsOracleConnectionItem( this, connName, mPath + '/' + connName );
} }

View File

@ -296,9 +296,10 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature &feature )
mRewind = false; mRewind = false;
if ( !execQuery( mSql, mArgs, 1 ) ) if ( !execQuery( mSql, mArgs, 1 ) )
{ {
QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" )
.arg( mQry.lastQuery(), .arg( mQry.lastQuery(),
mQry.lastError().text() ), mQry.lastError().text() ) };
QgsMessageLog::logMessage( error,
QObject::tr( "Oracle" ) ); QObject::tr( "Oracle" ) );
return false; return false;
} }
@ -531,13 +532,16 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar
QgsDebugMsgLevel( QStringLiteral( "Fetch features: %1" ).arg( query ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Fetch features: %1" ).arg( query ), 2 );
mSql = query; mSql = query;
mArgs = args; mArgs = args;
if ( !execQuery( query, args, 1 ) ) if ( !execQuery( query, args, 1 ) )
{ {
const QString error { QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" )
.arg( mQry.lastQuery(),
mQry.lastError().text() ) };
if ( showLog ) if ( showLog )
{ {
QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) QgsMessageLog::logMessage( error,
.arg( mQry.lastQuery(),
mQry.lastError().text() ),
QObject::tr( "Oracle" ) ); QObject::tr( "Oracle" ) );
} }
return false; return false;
@ -554,7 +558,7 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar
bool QgsOracleFeatureIterator::execQuery( const QString &query, const QVariantList &args, int retryCount ) bool QgsOracleFeatureIterator::execQuery( const QString &query, const QVariantList &args, int retryCount )
{ {
lock(); lock();
if ( !QgsOracleProvider::exec( mQry, query, args ) ) if ( !QgsOracleProvider::execLoggedStatic( mQry, query, args, mSource->mUri.uri(), QStringLiteral( "QgsOracleFeatureIterator" ), QGS_QUERY_LOG_ORIGIN ) )
{ {
unlock(); unlock();
if ( retryCount != 0 ) if ( retryCount != 0 )

View File

@ -29,6 +29,7 @@
#include "qgscoordinatereferencesystem.h" #include "qgscoordinatereferencesystem.h"
#include "qgsvectorlayerexporter.h" #include "qgsvectorlayerexporter.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgsdbquerylog.h"
#include "qgsoracleprovider.h" #include "qgsoracleprovider.h"
#include "qgsoracletablemodel.h" #include "qgsoracletablemodel.h"
@ -49,6 +50,10 @@
#include "ocispatial/wkbptr.h" #include "ocispatial/wkbptr.h"
#define LoggedExecStatic(query, sql, args, uri ) execLoggedStatic( query, sql, args, uri, QStringLiteral( "QgsOracleProvider" ), QString(QString( __FILE__ ).mid( sOracleConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
const QString ORACLE_KEY = "oracle"; const QString ORACLE_KEY = "oracle";
const QString ORACLE_DESCRIPTION = "Oracle data provider"; const QString ORACLE_DESCRIPTION = "Oracle data provider";
@ -279,13 +284,14 @@ QgsOracleConn *QgsOracleProvider::connectionRO() const
return mTransaction ? mTransaction->connection() : QgsOracleConn::connectDb( mUri, false ); return mTransaction ? mTransaction->connection() : QgsOracleConn::connectDb( mUri, false );
} }
bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &args ) bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass, const QString &queryOrigin )
{ {
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); QgsDatabaseQueryLogWrapper logWrapper { sql, uri, QStringLiteral( "oracle" ), originatorClass, queryOrigin };
qry.setForwardOnly( true ); qry.setForwardOnly( true );
bool res = qry.prepare( sql ); bool res = qry.prepare( sql );
if ( res ) if ( res )
{ {
for ( const auto &arg : args ) for ( const auto &arg : args )
@ -303,6 +309,18 @@ bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &a
.arg( qry.lastError().text() ) ); .arg( qry.lastError().text() ) );
} }
logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) );
logWrapper.setError( qry.lastError().text() );
// ORACLE does not support size so this will always be -1
// we leave it here in case this changes in the future
if ( qry.isSelect() )
{
logWrapper.setFetchedRows( qry.size() );
}
else
{
logWrapper.setFetchedRows( qry.numRowsAffected() );
}
return res; return res;
} }
@ -565,13 +583,13 @@ bool QgsOracleProvider::loadFields()
{ {
QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 );
if ( exec( qry, QString( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ), if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ),
QVariantList() << mOwnerName << mTableName ) ) QVariantList() << mOwnerName << mTableName, mUri.uri() ) )
{ {
if ( qry.next() ) if ( qry.next() )
mDataComment = qry.value( 0 ).toString(); mDataComment = qry.value( 0 ).toString();
else if ( exec( qry, QString( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ), else if ( LoggedExecStatic( qry, QStringLiteral( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ),
QVariantList() << mOwnerName << mTableName ) ) QVariantList() << mOwnerName << mTableName, mUri.uri() ) )
{ {
if ( qry.next() ) if ( qry.next() )
mDataComment = qry.value( 0 ).toString(); mDataComment = qry.value( 0 ).toString();
@ -579,28 +597,31 @@ bool QgsOracleProvider::loadFields()
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Loading comment for table %1.%2 failed [%3]" ) const QString error { tr( "Loading comment for table %1.%2 failed [%3]" )
.arg( mOwnerName ) .arg( mOwnerName )
.arg( mTableName ) .arg( mTableName )
.arg( qry.lastError().text() ), .arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) ); tr( "Oracle" ) );
} }
qry.finish(); qry.finish();
if ( exec( qry, QString( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ), if ( LoggedExecStatic( qry, QStringLiteral( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ),
QVariantList() << mOwnerName << mTableName ) ) QVariantList() << mOwnerName << mTableName, mUri.uri() ) )
{ {
while ( qry.next() ) while ( qry.next() )
{ {
if ( qry.value( 0 ).toString() == mGeometryColumn ) if ( qry.value( 0 ).toString() == mGeometryColumn )
continue; continue;
comments.insert( qry.value( 0 ).toString(), qry.value( 1 ).toString() ); comments.insert( qry.value( 0 ).toString(), qry.value( 1 ).toString() );
} }
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ), tr( "Oracle" ) ); const QString error { tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
} }
qry.finish(); qry.finish();
@ -638,10 +659,12 @@ bool QgsOracleProvider::loadFields()
sql += " ORDER BY t.column_id"; sql += " ORDER BY t.column_id";
if ( exec( qry, sql, args ) ) if ( LoggedExecStatic( qry, sql, args, mUri.uri() ) )
{ {
long long fetchedRows { 0 };
while ( qry.next() ) while ( qry.next() )
{ {
fetchedRows++;
QString name = qry.value( 0 ).toString(); QString name = qry.value( 0 ).toString();
QString type = qry.value( 1 ).toString(); QString type = qry.value( 1 ).toString();
int prec = qry.value( 2 ).toInt(); int prec = qry.value( 2 ).toInt();
@ -678,13 +701,16 @@ bool QgsOracleProvider::loadFields()
defvalues.insert( name, defValue ); defvalues.insert( name, defValue );
alwaysGenerated.insert( name, alwaysGen ); alwaysGenerated.insert( name, alwaysGen );
} }
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Loading field types for table %1.%2 failed [%3]" ) const QString error { tr( "Loading field types for table %1.%2 failed [%3]" )
.arg( mOwnerName ) .arg( mOwnerName )
.arg( mTableName ) .arg( mTableName )
.arg( qry.lastError().text() ), .arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) ); tr( "Oracle" ) );
} }
@ -698,10 +724,12 @@ bool QgsOracleProvider::loadFields()
{ {
if ( !mHasSpatialIndex ) if ( !mHasSpatialIndex )
{ {
mHasSpatialIndex = qry.exec( QString( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" ) const QString sql{ QStringLiteral( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" )
.arg( mQuery ) .arg( mQuery )
.arg( quotedIdentifier( mGeometryColumn ) ) .arg( quotedIdentifier( mGeometryColumn ) )
.arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) ); .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) };
mHasSpatialIndex = LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() );
} }
if ( !mHasSpatialIndex ) if ( !mHasSpatialIndex )
@ -716,9 +744,12 @@ bool QgsOracleProvider::loadFields()
qry.finish(); qry.finish();
if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) ) const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ), tr( "Oracle" ) ); const QString error { tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return false; return false;
} }
@ -763,6 +794,7 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
mEnabledCapabilities |= QgsVectorDataProvider::CircularGeometries; mEnabledCapabilities |= QgsVectorDataProvider::CircularGeometries;
QgsOracleConn *conn = connectionRO(); QgsOracleConn *conn = connectionRO();
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !mIsQuery ) if ( !mIsQuery )
{ {
@ -778,58 +810,66 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
| QgsVectorDataProvider::RenameAttributes | QgsVectorDataProvider::RenameAttributes
; ;
} }
else if ( exec( qry, QString( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ),
QVariantList() << mOwnerName << mTableName ) )
{
// check grants
while ( qry.next() )
{
QString priv = qry.value( 0 ).toString();
if ( priv == "DELETE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
}
else if ( priv == "UPDATE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
}
else if ( priv == "INSERT" )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
}
else if ( priv == "ALTER TABLE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
}
}
if ( !mGeometryColumn.isNull() )
{
if ( exec( qry, QString( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ),
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) )
{
if ( qry.next() )
mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
}
else
{
QgsMessageLog::logMessage( tr( "Unable to determine geometry column access privileges for column %1.%2.\nThe error message from the database was:\n%3.\nSQL: %4" )
.arg( mQuery )
.arg( mGeometryColumn )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ),
tr( "Oracle" ) );
}
}
}
else else
{ {
QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the table %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) if ( LoggedExecStatic( qry, QStringLiteral( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ),
.arg( mQuery ) QVariantList() << mOwnerName << mTableName, mUri.uri() ) )
.arg( qry.lastError().text() ) {
.arg( qry.lastQuery() ), // check grants
tr( "Oracle" ) ); long long fetchedRows { 0 };
while ( qry.next() )
{
fetchedRows++;
QString priv = qry.value( 0 ).toString();
if ( priv == "DELETE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
}
else if ( priv == "UPDATE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
}
else if ( priv == "INSERT" )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
}
else if ( priv == "ALTER TABLE" )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
}
}
if ( !mGeometryColumn.isNull() )
{
if ( LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ),
QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) )
{
if ( qry.next() )
mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
}
else
{
const QString error { tr( "Unable to determine geometry column access privileges for column %1.%2.\nThe error message from the database was:\n%3.\nSQL: %4" )
.arg( mQuery )
.arg( mGeometryColumn )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) );
}
}
}
else
{
const QString error { tr( "Unable to determine table access privileges for the table %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
.arg( mQuery )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error,
tr( "Oracle" ) );
}
} }
} }
else else
@ -841,11 +881,13 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
return false; return false;
} }
if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) ) const QString sql { QStringLiteral( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() ) .arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
return false; return false;
} }
} }
@ -897,21 +939,25 @@ bool QgsOracleProvider::determinePrimaryKey()
{ {
mPrimaryKeyType = ( mPrimaryKeyAttrs.size() == 1 && isInt ) ? PktInt : PktFidMap; mPrimaryKeyType = ( mPrimaryKeyAttrs.size() == 1 && isInt ) ? PktInt : PktFidMap;
} }
else if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName ) )
{
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) );
}
else if ( qry.next() )
{
// is table
QgsMessageLog::logMessage( tr( "No primary key found, using ROWID." ), tr( "Oracle" ) );
mPrimaryKeyType = PktRowId;
}
else else
{ {
determinePrimaryKeyFromUriKeyColumn(); if ( !LoggedExecStatic( qry, QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName, mUri.uri() ) )
{
const QString error { tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ) };
QgsMessageLog::logMessage( error, tr( "Oracle" ) );
}
else if ( qry.next() )
{
// is table
QgsMessageLog::logMessage( tr( "No primary key found, using ROWID." ), tr( "Oracle" ) );
mPrimaryKeyType = PktRowId;
}
else
{
determinePrimaryKeyFromUriKeyColumn();
}
} }
} }
else else
@ -997,7 +1043,7 @@ bool QgsOracleProvider::uniqueData( QString query, QString colName )
QString sql = QString( "SELECT (SELECT count(distinct %1) FROM %2)-(SELECT count(%1) FROM %2) FROM dual" ) QString sql = QString( "SELECT (SELECT count(distinct %1) FROM %2)-(SELECT count(%1) FROM %2) FROM dual" )
.arg( colName.startsWith( QLatin1String( "qgis_generated_uid_" ) ) ? colName : quotedIdentifier( colName ), mQuery ); .arg( colName.startsWith( QLatin1String( "qgis_generated_uid_" ) ) ? colName : quotedIdentifier( colName ), mQuery );
if ( !exec( qry, sql, QVariantList() ) || !qry.next() ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) || !qry.next() )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) );
@ -1028,7 +1074,7 @@ QVariant QgsOracleProvider::minimumValue( int index ) const
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) );
@ -1078,7 +1124,7 @@ QSet<QVariant> QgsOracleProvider::uniqueValues( int index, int limit ) const
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) );
@ -1120,7 +1166,7 @@ QVariant QgsOracleProvider::maximumValue( int index ) const
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastError().text(), qry.lastQuery() ), tr( "Oracle" ) );
@ -1201,7 +1247,10 @@ QVariant QgsOracleProvider::evaluateDefaultExpression( const QString &value, con
QgsOracleConn *conn = connectionRO(); QgsOracleConn *conn = connectionRO();
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !exec( qry, QString( "SELECT %1 FROM dual" ).arg( value ), QVariantList() ) || !qry.next() )
const QString sql { QStringLiteral( "SELECT %1 FROM dual" ).arg( value ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) || !qry.next() )
{ {
throw OracleException( tr( "Evaluation of default value failed" ), qry ); throw OracleException( tr( "Evaluation of default value failed" ), qry );
} }
@ -1317,6 +1366,7 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag
insert += values + ")"; insert += values + ")";
QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( insert ), 4 ); QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( insert ), 4 );
if ( !ins.prepare( insert ) ) if ( !ins.prepare( insert ) )
{ {
throw OracleException( tr( "Could not prepare insert statement" ), ins ); throw OracleException( tr( "Could not prepare insert statement" ), ins );
@ -1350,7 +1400,9 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag
} }
if ( !ins.exec() ) if ( !ins.exec() )
{
throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins ); throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins );
}
if ( !( flags & QgsFeatureSink::FastInsert ) ) if ( !( flags & QgsFeatureSink::FastInsert ) )
{ {
@ -1364,7 +1416,10 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag
if ( ins.lastInsertId().isValid() ) if ( ins.lastInsertId().isValid() )
{ {
getfid.addBindValue( QVariant( ins.lastInsertId() ) ); getfid.addBindValue( QVariant( ins.lastInsertId() ) );
if ( !getfid.exec() || !getfid.next() )
const bool result { getfid.exec() };
if ( !result || !getfid.next() )
throw OracleException( tr( "Could not retrieve feature id %1" ).arg( features->id() ), getfid ); throw OracleException( tr( "Could not retrieve feature id %1" ).arg( features->id() ), getfid );
int col = 0; int col = 0;
@ -1462,12 +1517,14 @@ bool QgsOracleProvider::deleteFeatures( const QgsFeatureIds &id )
for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it ) for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
{ {
QVariantList args; QVariantList args;
QString sql = QString( "DELETE FROM %1 WHERE %2" ) QString sql = QStringLiteral( "DELETE FROM %1 WHERE %2" )
.arg( mQuery, whereClause( *it, args ) ); .arg( mQuery, whereClause( *it, args ) );
QgsDebugMsgLevel( "delete sql: " + sql, 2 ); QgsDebugMsgLevel( "delete sql: " + sql, 2 );
if ( !exec( qry, sql, args ) ) if ( !LoggedExecStatic( qry, sql, args, mUri.uri() ) )
{
throw OracleException( tr( "Deletion of feature %1 failed" ).arg( *it ), qry ); throw OracleException( tr( "Deletion of feature %1 failed" ).arg( *it ), qry );
}
mShared->removeFid( *it ); mShared->removeFid( *it );
} }
@ -1537,15 +1594,20 @@ bool QgsOracleProvider::addAttributes( const QList<QgsField> &attributes )
.arg( mQuery, quotedIdentifier( iter->name() ), type ); .arg( mQuery, quotedIdentifier( iter->name() ), type );
QgsDebugMsgLevel( sql, 2 ); QgsDebugMsgLevel( sql, 2 );
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{
throw OracleException( tr( "Adding attribute %1 failed" ).arg( iter->name() ), qry ); throw OracleException( tr( "Adding attribute %1 failed" ).arg( iter->name() ), qry );
}
if ( !iter->comment().isEmpty() ) if ( !iter->comment().isEmpty() )
{ {
sql = QString( "COMMENT ON COLUMN %1.%2 IS ?" ) sql = QString( "COMMENT ON COLUMN %1.%2 IS ?" )
.arg( mQuery, quotedIdentifier( iter->name() ) ); .arg( mQuery, quotedIdentifier( iter->name() ) );
if ( !exec( qry, sql, QVariantList() << iter->comment() ) )
if ( !LoggedExecStatic( qry, sql, QVariantList() << iter->comment(), mUri.uri() ) )
{
throw OracleException( tr( "Setting comment on %1 failed" ).arg( iter->name() ), qry ); throw OracleException( tr( "Setting comment on %1 failed" ).arg( iter->name() ), qry );
}
} }
qry.finish(); qry.finish();
@ -1609,8 +1671,10 @@ bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids )
.arg( mQuery, quotedIdentifier( fld.name() ) ); .arg( mQuery, quotedIdentifier( fld.name() ) );
//send sql statement and do error handling //send sql statement and do error handling
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{
throw OracleException( tr( "Dropping column %1 failed" ).arg( fld.name() ), qry ); throw OracleException( tr( "Dropping column %1 failed" ).arg( fld.name() ), qry );
}
//delete the attribute from mAttributeFields //delete the attribute from mAttributeFields
mAttributeFields.remove( id ); mAttributeFields.remove( id );
@ -1685,11 +1749,12 @@ bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttribut
for ( renameIt = renamedAttributes.constBegin(); renameIt != renamedAttributes.constEnd(); ++renameIt ) for ( renameIt = renamedAttributes.constBegin(); renameIt != renamedAttributes.constEnd(); ++renameIt )
{ {
QString src( mAttributeFields.at( renameIt.key() ).name() ); QString src( mAttributeFields.at( renameIt.key() ).name() );
const QString sql { QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3" )
.arg( mQuery,
quotedIdentifier( src ),
quotedIdentifier( renameIt.value() ) ) };
if ( !exec( qry, QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3" ) if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
.arg( mQuery,
quotedIdentifier( src ),
quotedIdentifier( renameIt.value() ) ), QVariantList() ) )
{ {
throw OracleException( tr( "Renaming column %1 to %2 failed" ) throw OracleException( tr( "Renaming column %1 to %2 failed" )
.arg( quotedIdentifier( src ), .arg( quotedIdentifier( src ),
@ -1827,10 +1892,17 @@ bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &at
for ( const auto &arg : std::as_const( args ) ) for ( const auto &arg : std::as_const( args ) )
qry.addBindValue( arg ); qry.addBindValue( arg );
QgsDatabaseQueryLogWrapper logWrapper { sql, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN };
if ( !qry.exec() ) if ( !qry.exec() )
{
logWrapper.setError( qry.lastError().text() );
logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) );
throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry );
}
qry.finish(); qry.finish();
logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) );
// update feature id map if key was changed // update feature id map if key was changed
if ( pkChanged && mPrimaryKeyType == PktFidMap ) if ( pkChanged && mPrimaryKeyType == PktFidMap )
@ -2304,8 +2376,15 @@ bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map
appendGeomParam( iter.value(), qry ); appendGeomParam( iter.value(), qry );
appendPkParams( iter.key(), qry ); appendPkParams( iter.key(), qry );
QgsDatabaseQueryLogWrapper logWrapper { update, mUri.uri(), QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleProvider" ), QGS_QUERY_LOG_ORIGIN };
if ( !qry.exec() ) if ( !qry.exec() )
{
logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) );
logWrapper.setError( qry.lastError().text() );
throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry ); throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry );
}
logWrapper.setQuery( QgsOracleConn::getLastExecutedQuery( qry ) );
} }
qry.finish(); qry.finish();
@ -2359,7 +2438,9 @@ bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatu
sql += "1=0"; sql += "1=0";
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) )
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
pushError( qry.lastError().text() ); pushError( qry.lastError().text() );
mSqlWhereClause = prevWhere; mSqlWhereClause = prevWhere;
@ -2423,10 +2504,12 @@ long long QgsOracleProvider::featureCount() const
} }
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( exec( qry, sql, args ) && qry.next() )
if ( LoggedExecStatic( qry, sql, QVariantList( ), mUri.uri() ) && qry.next() )
{ {
mFeaturesCounted = qry.value( 0 ).toLongLong(); mFeaturesCounted = qry.value( 0 ).toLongLong();
} }
qry.finish(); qry.finish();
QgsDebugMsgLevel( "number of features: " + QString::number( mFeaturesCounted ), 2 ); QgsDebugMsgLevel( "number of features: " + QString::number( mFeaturesCounted ), 2 );
@ -2451,15 +2534,18 @@ QgsRectangle QgsOracleProvider::extent() const
if ( mUseEstimatedMetadata ) if ( mUseEstimatedMetadata )
{ {
// TODO: make SDO_DIMNAME values configurable (#16252) // TODO: make SDO_DIMNAME values configurable (#16252)
if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ),
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) && const QString sql { QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ) };
if ( LoggedExecStatic( qry, sql,
QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) &&
qry.next() ) qry.next() )
{ {
mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() ); mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() );
mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() ); mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() );
if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='Y'" ), if ( LoggedExecStatic( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='Y'" ),
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) && QVariantList() << mOwnerName << mTableName << mGeometryColumn, mUri.uri() ) &&
qry.next() ) qry.next() )
{ {
mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() ); mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() );
@ -2471,9 +2557,10 @@ QgsRectangle QgsOracleProvider::extent() const
if ( mHasSpatialIndex && mUseEstimatedMetadata ) if ( mHasSpatialIndex && mUseEstimatedMetadata )
{ {
ok = exec( qry, const QString sql { QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ) };
QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ), ok = LoggedExecStatic( qry,
QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn ); sql,
QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn, mUri.uri() );
} }
} }
@ -2484,7 +2571,7 @@ QgsRectangle QgsOracleProvider::extent() const
if ( !mSqlWhereClause.isEmpty() ) if ( !mSqlWhereClause.isEmpty() )
sql += QString( " WHERE %1" ).arg( mSqlWhereClause ); sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
ok = exec( qry, sql, QVariantList() ); ok = LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() );
} }
if ( ok && qry.next() ) if ( ok && qry.next() )
@ -2533,7 +2620,9 @@ bool QgsOracleProvider::getGeometryDetails()
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
if ( mIsQuery ) if ( mIsQuery )
{ {
if ( !exec( qry, QString( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ), QVariantList() ) ) const QString sql { QStringLiteral( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Could not execute query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Could not execute query.\nThe error message from the database was:\n%1.\nSQL: %2" )
.arg( qry.lastError().text() ) .arg( qry.lastError().text() )
@ -2558,32 +2647,41 @@ bool QgsOracleProvider::getGeometryDetails()
if ( !ownerName.isEmpty() ) if ( !ownerName.isEmpty() )
{ {
if ( exec( qry, QString( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ),
QVariantList() << ownerName << tableName << geomCol ) )
{ {
if ( qry.next() ) const QString sql {QStringLiteral( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ) };
if ( LoggedExecStatic( qry, sql,
QVariantList() << ownerName << tableName << geomCol, mUri.uri() ) )
{ {
detectedSrid = qry.value( 0 ).toInt(); if ( qry.next() )
{
detectedSrid = qry.value( 0 ).toInt();
}
else
{
QgsMessageLog::logMessage( tr( "Could not retrieve SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
.arg( mQuery )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) );
}
} }
else else
{ {
QgsMessageLog::logMessage( tr( "Could not retrieve SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" ) QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
.arg( mQuery ) .arg( mQuery )
.arg( qry.lastError().text() ) .arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) ); .arg( qry.lastQuery() ), tr( "Oracle" ) );
} }
} }
else
{
QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
.arg( mQuery )
.arg( qry.lastError().text() )
.arg( qry.lastQuery() ), tr( "Oracle" ) );
}
if ( exec( qry, QString( mUseEstimatedMetadata QString sql { mUseEstimatedMetadata ?
? "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<100) WHERE rownum<=2" QStringLiteral( "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<100) WHERE rownum<=2" ) :
: "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ).arg( quotedIdentifier( geomCol ) ).arg( mQuery ), QVariantList() ) ) QStringLiteral( "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ) };
sql = sql.arg( quotedIdentifier( geomCol ), mQuery );
if ( LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
if ( qry.next() ) if ( qry.next() )
{ {
@ -2705,30 +2803,41 @@ bool QgsOracleProvider::createSpatialIndex()
// TODO: make precision configurable // TODO: make precision configurable
// TODO: make SDO_DIMNAME values configurable (#16252) // TODO: make SDO_DIMNAME values configurable (#16252)
QgsRectangle r( extent() ); QgsRectangle r( extent() );
if ( !exec( qry, QString( "UPDATE mdsys.user_sdo_geom_metadata SET diminfo=mdsys.sdo_dim_array("
"mdsys.sdo_dim_element('X', ?, ?, 0.001)," const QString sql { QStringLiteral( "UPDATE mdsys.user_sdo_geom_metadata SET diminfo=mdsys.sdo_dim_array("
"mdsys.sdo_dim_element('Y', ?, ?, 0.001)" "mdsys.sdo_dim_element('X', ?, ?, 0.001),"
") WHERE table_name=? AND column_name=?" ), "mdsys.sdo_dim_element('Y', ?, ?, 0.001)"
QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn ) ") WHERE table_name=? AND column_name=?" ) };
)
{ {
QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" )
.arg( mTableName ) if ( !LoggedExecStatic( qry, sql,
.arg( mGeometryColumn ) QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn, mUri.uri() )
.arg( qry.lastQuery() ) )
.arg( qry.lastError().text() ), {
tr( "Oracle" ) ); QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" )
return false; .arg( mTableName )
.arg( mGeometryColumn )
.arg( qry.lastQuery() )
.arg( qry.lastError().text() ),
tr( "Oracle" ) );
return false;
}
} }
if ( qry.numRowsAffected() == 0 ) if ( qry.numRowsAffected() == 0 )
{ {
if ( !exec( qry, QString( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,mdsys.sdo_dim_array("
"mdsys.sdo_dim_element('X', ?, ?, 0.001)," const QString sql { QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,mdsys.sdo_dim_array("
"mdsys.sdo_dim_element('Y', ?, ?, 0.001)" "mdsys.sdo_dim_element('X', ?, ?, 0.001),"
"))" ), "mdsys.sdo_dim_element('Y', ?, ?, 0.001)"
QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid ) "))" ) };
<< r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() )
if ( !LoggedExecStatic( qry, sql,
QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid )
<< r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum(), mUri.uri() )
) )
{ {
QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" ) QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" )
@ -2756,7 +2865,10 @@ bool QgsOracleProvider::createSpatialIndex()
} }
else else
{ {
if ( !exec( qry, QString( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ), QVariantList() ) )
const QString sql { QStringLiteral( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), mUri.uri() ) )
{ {
QgsMessageLog::logMessage( tr( "Rebuild of spatial index failed.\nSQL: %1\nError: %2" ) QgsMessageLog::logMessage( tr( "Rebuild of spatial index failed.\nSQL: %1\nError: %2" )
.arg( qry.lastQuery() ) .arg( qry.lastQuery() )
@ -2928,11 +3040,15 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer(
throw OracleException( tr( "Could not start transaction" ), db ); throw OracleException( tr( "Could not start transaction" ), db );
} }
if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ),
QVariantList() << ownerName << tableName
) )
{ {
throw OracleException( tr( "Could not determine table existence." ), qry ); const QString sql { QStringLiteral( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ) };
if ( !LoggedExecStatic( qry, sql,
QVariantList() << ownerName << tableName, uri ) )
{
throw OracleException( tr( "Could not determine table existence." ), qry );
}
} }
bool exists = qry.next(); bool exists = qry.next();
@ -2942,7 +3058,11 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer(
if ( overwrite ) if ( overwrite )
{ {
// delete the table if exists, then re-create it // delete the table if exists, then re-create it
if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) )
const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) )
{ {
throw OracleException( tr( "Table %1 could not be dropped." ).arg( ownerTableName ), qry ); throw OracleException( tr( "Table %1 could not be dropped." ).arg( ownerTableName ), qry );
} }
@ -2953,7 +3073,7 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer(
} }
} }
QString sql = QString( "CREATE TABLE %1(" ).arg( ownerTableName ); QString sql = QStringLiteral( "CREATE TABLE %1(" ).arg( ownerTableName );
QString delim; QString delim;
if ( hasPrimaryKey ) if ( hasPrimaryKey )
@ -2971,7 +3091,7 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer(
sql += ")"; sql += ")";
if ( !exec( qry, sql, QVariantList() ) ) if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) )
{ {
throw OracleException( tr( "Table creation failed." ), qry ); throw OracleException( tr( "Table creation failed." ), qry );
} }
@ -2999,7 +3119,10 @@ Qgis::VectorExportResult QgsOracleProvider::createEmptyLayer(
if ( created ) if ( created )
{ {
if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) )
const QString sql { QStringLiteral( "DROP TABLE %1" ).arg( ownerTableName ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), uri ) )
{ {
QgsMessageLog::logMessage( tr( "Drop created table %1 failed.\nSQL: %2\nError: %3" ) QgsMessageLog::logMessage( tr( "Drop created table %1 failed.\nSQL: %2\nError: %3" )
.arg( qry.lastQuery() ) .arg( qry.lastQuery() )
@ -3171,9 +3294,16 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString &
QgsDebugMsgLevel( QStringLiteral( "%1:%2 not found in mdsys.cs_srs - trying WKT" ).arg( parts[0] ).arg( parts[1] ), 2 ); QgsDebugMsgLevel( QStringLiteral( "%1:%2 not found in mdsys.cs_srs - trying WKT" ).arg( parts[0] ).arg( parts[1] ), 2 );
QString wkt = srs.toWkt(); QString wkt = srs.toWkt();
if ( !exec( qry, QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ), QVariantList() << wkt ) )
const QString sql { QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ) };
{ {
throw OracleException( tr( "Could not lookup WKT." ), qry );
if ( !LoggedExecStatic( qry, sql, QVariantList() << wkt, conn->connInfo() ) )
{
throw OracleException( tr( "Could not lookup WKT." ), qry );
}
} }
if ( qry.next() ) if ( qry.next() )
@ -3182,17 +3312,25 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString &
} }
else else
{ {
if ( !exec( qry, QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ), QVariantList() ) || !qry.next() )
{ {
throw OracleException( tr( "Could not determine new srid." ), qry ); const QString sql { QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ) };
if ( !LoggedExecStatic( qry, sql, QVariantList(), conn->connInfo() ) || !qry.next() )
{
throw OracleException( tr( "Could not determine new srid." ), qry );
}
} }
srid = qry.value( 0 ).toInt(); srid = qry.value( 0 ).toInt();
if ( !exec( qry, QStringLiteral( "INSERT" const QString sql { QStringLiteral( "INSERT"
" INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)" " INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)"
" VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ), " VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ) };
QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt ) )
if ( !LoggedExecStatic( qry, sql,
QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt, conn->connInfo() ) )
{ {
throw OracleException( tr( "CRS not found and could not be created." ), qry ); throw OracleException( tr( "CRS not found and could not be created." ), qry );
} }
@ -3203,8 +3341,10 @@ void QgsOracleProvider::insertGeomMetadata( QgsOracleConn *conn, const QString &
throw OracleException( tr( "Cannot insert geometry metadata for table '%1' and geometry column '%2'. Both needs to be uppercase" ).arg( throw OracleException( tr( "Cannot insert geometry metadata for table '%1' and geometry column '%2'. Both needs to be uppercase" ).arg(
tableName, geometryColumn ), qry ); tableName, geometryColumn ), qry );
if ( !exec( qry, QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,%1)" ).arg( diminfo ), const QString sql { QStringLiteral( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,%1)" ).arg( diminfo ) };
QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid ) )
if ( !LoggedExecStatic( qry, sql,
QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid, conn->connInfo() ) )
{ {
throw OracleException( tr( "Could not insert metadata." ), qry ); throw OracleException( tr( "Could not insert metadata." ), qry );
} }
@ -3217,7 +3357,7 @@ QgsCoordinateReferenceSystem QgsOracleProvider::lookupCrs( QgsOracleConn *conn,
QSqlQuery qry( *conn ); QSqlQuery qry( *conn );
// apparently some EPSG codes don't have the auth_name setup in cs_srs // apparently some EPSG codes don't have the auth_name setup in cs_srs
if ( exec( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << srsid ) ) if ( LoggedExecStatic( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << srsid, conn->connInfo() ) )
{ {
if ( qry.next() ) if ( qry.next() )
{ {

View File

@ -50,6 +50,7 @@ enum QgsOraclePrimaryKeyType
PktFidMap PktFidMap
}; };
/** /**
* \class QgsOracleProvider * \class QgsOracleProvider
* \brief Data provider for oracle layers. * \brief Data provider for oracle layers.
@ -173,7 +174,7 @@ class QgsOracleProvider final: public QgsVectorDataProvider
QString description() const override; QString description() const override;
QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override; QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override;
static bool exec( QSqlQuery &qry, QString sql, const QVariantList &args ); static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() );
bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; } bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; }
void setTransaction( QgsTransaction *transaction ) override; void setTransaction( QgsTransaction *transaction ) override;

View File

@ -1365,15 +1365,22 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsOracleProviderConnection::
QSqlQuery qry( *pconn.get() ); QSqlQuery qry( *pconn.get() );
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
QgsDatabaseQueryLogWrapper logWrapper { sql, uri(), providerKey(), QStringLiteral( "QgsAbstractDatabaseProviderConnection" ), QGS_QUERY_LOG_ORIGIN };
if ( !qry.exec( sql ) ) if ( !qry.exec( sql ) )
{ {
logWrapper.setError( qry.lastError().text() );
throw QgsProviderConnectionException( QObject::tr( "SQL error: %1 returned %2" ) throw QgsProviderConnectionException( QObject::tr( "SQL error: %1 returned %2" )
.arg( qry.lastQuery(), .arg( qry.lastQuery(),
qry.lastError().text() ) ); qry.lastError().text() ) );
} }
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{
logWrapper.setCanceled();
return QgsAbstractDatabaseProviderConnection::QueryResult(); return QgsAbstractDatabaseProviderConnection::QueryResult();
}
if ( qry.isActive() ) if ( qry.isActive() )
{ {

View File

@ -61,6 +61,7 @@ bool QgsOracleTableModel::searchableColumn( int column ) const
case DbtmSelectAtId: case DbtmSelectAtId:
return false; return false;
} }
return false;
} }
void QgsOracleTableModel::addTableEntry( const QgsOracleLayerProperty &layerProperty ) void QgsOracleTableModel::addTableEntry( const QgsOracleLayerProperty &layerProperty )

View File

@ -73,7 +73,13 @@ bool QgsOracleTransaction::executeSql( const QString &sql, QString &errorMsg, bo
} }
QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) );
QgsDatabaseQueryLogWrapper logWrapper { sql, mConnString, QStringLiteral( "oracle" ), QStringLiteral( "QgsOracleConn" ), QGS_QUERY_LOG_ORIGIN };
const bool res = mConn->exec( sql, true, &errorMsg ); const bool res = mConn->exec( sql, true, &errorMsg );
if ( ! errorMsg.isEmpty() )
{
logWrapper.setError( errorMsg );
}
if ( !res ) if ( !res )
{ {
if ( isDirty ) if ( isDirty )

View File

@ -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>
@ -412,8 +413,8 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s
if ( mPostgresqlVersion >= 90000 ) if ( mPostgresqlVersion >= 90000 )
{ {
PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) ); LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "SET application_name='QGIS'" ) );
PQexecNR( QStringLiteral( "SET extra_float_digits=3" ) ); LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "SET extra_float_digits=3" ) );
} }
PQsetNoticeProcessor( mConn, noticeProcessor, nullptr ); PQsetNoticeProcessor( mConn, noticeProcessor, nullptr );
@ -498,7 +499,7 @@ void QgsPostgresConn::addColumnInfo( QgsPostgresLayerProperty &layerProperty, co
.arg( quotedIdentifier( schemaName ), .arg( quotedIdentifier( schemaName ),
quotedIdentifier( viewName ) ); quotedIdentifier( viewName ) );
QgsDebugMsgLevel( "getting column info: " + sql, 2 ); QgsDebugMsgLevel( "getting column info: " + sql, 2 );
QgsPostgresResult colRes( PQexec( sql ) ); QgsPostgresResult colRes( LoggedPQexec( "QgsPostgresConn", sql ) );
layerProperty.pkCols.clear(); layerProperty.pkCols.clear();
layerProperty.nSpCols = 0; layerProperty.nSpCols = 0;
@ -656,7 +657,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP
QgsDebugMsgLevel( "getting table info from layer registries: " + query, 2 ); QgsDebugMsgLevel( "getting table info from layer registries: " + query, 2 );
result = PQexec( query, true ); result = LoggedPQexec( "QgsPostgresConn", query );
// NOTE: we intentionally continue if the query fails // NOTE: we intentionally continue if the query fails
// (for example because PostGIS is not installed) // (for example because PostGIS is not installed)
for ( int idx = 0; idx < result.PQntuples(); idx++ ) for ( int idx = 0; idx < result.PQntuples(); idx++ )
@ -810,14 +811,16 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP
QgsDebugMsgLevel( "getting spatial table info from pg_catalog: " + sql, 2 ); QgsDebugMsgLevel( "getting spatial table info from pg_catalog: " + sql, 2 );
result = PQexec( sql );
result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined. The error message from the database was:\n%1\n" ) 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" ) ); LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "COMMIT" ) );
return false; return false;
} }
@ -922,7 +925,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP
QgsDebugMsgLevel( "getting non-spatial table info: " + sql, 2 ); QgsDebugMsgLevel( "getting non-spatial table info: " + sql, 2 );
result = PQexec( sql ); result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
@ -1018,10 +1021,10 @@ bool QgsPostgresConn::getSchemas( QList<QgsPostgresSchemaProperty> &schemas )
QString sql = QStringLiteral( "SELECT nspname, pg_get_userbyid(nspowner), pg_catalog.obj_description(oid) FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema' ORDER BY nspname" ); QString sql = QStringLiteral( "SELECT nspname, pg_get_userbyid(nspowner), pg_catalog.obj_description(oid) FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema' ORDER BY nspname" );
result = PQexec( sql, true ); result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
PQexecNR( QStringLiteral( "COMMIT" ) ); LoggedPQexecNR( "QgsPostgresConn", QStringLiteral( "COMMIT" ) );
return false; return false;
} }
@ -1084,7 +1087,7 @@ QString QgsPostgresConn::postgisVersion() const
mPostgresqlVersion = PQserverVersion( mConn ); mPostgresqlVersion = PQserverVersion( mConn );
QgsPostgresResult result( PQexec( QStringLiteral( "SELECT postgis_version()" ), false ) ); QgsPostgresResult result( LoggedPQexecNoLogError( QStringLiteral( "QgsPostgresConn" ), QStringLiteral( "SELECT postgis_version()" ) ) );
if ( result.PQntuples() != 1 ) if ( result.PQntuples() != 1 )
{ {
QgsMessageLog::logMessage( tr( "No PostGIS support in the database." ), tr( "PostGIS" ) ); QgsMessageLog::logMessage( tr( "No PostGIS support in the database." ), tr( "PostGIS" ) );
@ -1121,7 +1124,7 @@ QString QgsPostgresConn::postgisVersion() const
// apparently PostGIS 1.5.2 doesn't report capabilities in postgis_version() anymore // apparently PostGIS 1.5.2 doesn't report capabilities in postgis_version() anymore
if ( mPostgisVersionMajor > 1 || ( mPostgisVersionMajor == 1 && mPostgisVersionMinor >= 5 ) ) if ( mPostgisVersionMajor > 1 || ( mPostgisVersionMajor == 1 && mPostgisVersionMinor >= 5 ) )
{ {
result = PQexec( QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) ); result = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "SELECT postgis_geos_version(), postgis_proj_version()" ) );
mGeosAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ); mGeosAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 );
mProjAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 1 ); mProjAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 1 );
QgsDebugMsgLevel( QStringLiteral( "geos:%1 proj:%2" ) QgsDebugMsgLevel( QStringLiteral( "geos:%1 proj:%2" )
@ -1146,19 +1149,18 @@ QString QgsPostgresConn::postgisVersion() const
mTopologyAvailable = false; mTopologyAvailable = false;
if ( mPostgisVersionMajor > 1 ) if ( mPostgisVersionMajor > 1 )
{ {
QgsPostgresResult result( const QString query = QStringLiteral(
PQexec( "SELECT has_schema_privilege(n.oid, 'usage')"
QStringLiteral( " AND has_table_privilege(t.oid, 'select')"
"SELECT has_schema_privilege(n.oid, 'usage')" " AND has_table_privilege(l.oid, 'select')"
" AND has_table_privilege(t.oid, 'select')" " FROM pg_namespace n, pg_class t, pg_class l"
" AND has_table_privilege(l.oid, 'select')" " WHERE n.nspname = 'topology'"
" FROM pg_namespace n, pg_class t, pg_class l" " AND t.relnamespace = n.oid"
" WHERE n.nspname = 'topology'" " AND l.relnamespace = n.oid"
" AND t.relnamespace = n.oid" " AND t.relname = 'topology'"
" AND l.relnamespace = n.oid" " AND l.relname = 'layer'"
" AND t.relname = 'topology'" );
" AND l.relname = 'layer'" QgsPostgresResult result( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), query ) );
) ) );
if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) )
{ {
mTopologyAvailable = true; mTopologyAvailable = true;
@ -1179,17 +1181,17 @@ QString QgsPostgresConn::postgisVersion() const
if ( mPostgresqlVersion >= 90000 ) if ( mPostgresqlVersion >= 90000 )
{ {
QgsDebugMsgLevel( QStringLiteral( "Checking for pointcloud support" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Checking for pointcloud support" ), 2 );
result = PQexec( QStringLiteral( result = LoggedPQexecNoLogError( QStringLiteral( "QgsPostresConn" ), QStringLiteral(
"SELECT has_table_privilege(c.oid, 'select')" "SELECT has_table_privilege(c.oid, 'select')"
" AND has_table_privilege(f.oid, 'select')" " AND has_table_privilege(f.oid, 'select')"
" FROM pg_class c, pg_class f, pg_namespace n, pg_extension e" " FROM pg_class c, pg_class f, pg_namespace n, pg_extension e"
" WHERE c.relnamespace = n.oid" " WHERE c.relnamespace = n.oid"
" AND c.relname = 'pointcloud_columns'" " AND c.relname = 'pointcloud_columns'"
" AND f.relnamespace = n.oid" " AND f.relnamespace = n.oid"
" AND f.relname = 'pointcloud_formats'" " AND f.relname = 'pointcloud_formats'"
" AND n.oid = e.extnamespace" " AND n.oid = e.extnamespace"
" AND e.extname = 'pointcloud'" " AND e.extname = 'pointcloud'"
), false ); ) );
if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) )
{ {
mPointcloudAvailable = true; mPointcloudAvailable = true;
@ -1200,14 +1202,14 @@ QString QgsPostgresConn::postgisVersion() const
QgsDebugMsgLevel( QStringLiteral( "Checking for raster support" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Checking for raster support" ), 2 );
if ( mPostgisVersionMajor >= 2 ) if ( mPostgisVersionMajor >= 2 )
{ {
result = PQexec( QStringLiteral( result = LoggedPQexecNoLogError( QStringLiteral( "QgsPostresConn" ), QStringLiteral(
"SELECT has_table_privilege(c.oid, 'select')" "SELECT has_table_privilege(c.oid, 'select')"
" FROM pg_class c, pg_namespace n, pg_type t" " FROM pg_class c, pg_namespace n, pg_type t"
" WHERE c.relnamespace = n.oid" " WHERE c.relnamespace = n.oid"
" AND n.oid = t.typnamespace" " AND n.oid = t.typnamespace"
" AND c.relname = 'raster_columns'" " AND c.relname = 'raster_columns'"
" AND t.typname = 'raster'" " AND t.typname = 'raster'"
), false ); ) );
if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) if ( result.PQntuples() >= 1 && result.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) )
{ {
mRasterAvailable = true; mRasterAvailable = true;
@ -1327,12 +1329,14 @@ QString QgsPostgresConn::quotedJsonValue( const QVariant &value )
return quotedString( QString::fromStdString( j.dump() ) ); return quotedString( QString::fromStdString( j.dump() ) );
} }
PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool retry ) const PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool retry, const QString &originatorClass, const QString &queryOrigin ) const
{ {
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 ); QgsDebugMsgLevel( QStringLiteral( "Executing SQL: %1" ).arg( query ), 3 );
std::unique_ptr<QgsDatabaseQueryLogWrapper> logWrapper = std::make_unique<QgsDatabaseQueryLogWrapper>( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin );
PGresult *res = ::PQexec( mConn, query.toUtf8() ); PGresult *res = ::PQexec( mConn, query.toUtf8() );
// libpq may return a non null ptr with conn status not OK so we need to check for it to allow a retry below // libpq may return a non null ptr with conn status not OK so we need to check for it to allow a retry below
@ -1341,11 +1345,13 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret
int errorStatus = PQresultStatus( res ); int errorStatus = PQresultStatus( res );
if ( errorStatus != PGRES_COMMAND_OK && errorStatus != PGRES_TUPLES_OK ) if ( errorStatus != PGRES_COMMAND_OK && errorStatus != PGRES_TUPLES_OK )
{ {
const QString error { tr( "Erroneous query: %1 returned %2 [%3]" )
.arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) };
logWrapper->setError( error );
if ( logError ) if ( logError )
{ {
QgsMessageLog::logMessage( tr( "Erroneous query: %1 returned %2 [%3]" ) QgsMessageLog::logMessage( error, tr( "PostGIS" ) );
.arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ),
tr( "PostGIS" ) );
} }
else else
{ {
@ -1353,14 +1359,17 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret
.arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) ); .arg( query ).arg( errorStatus ).arg( PQresultErrorMessage( res ) ) );
} }
} }
logWrapper->setFetchedRows( PQntuples( res ) );
return res; return res;
} }
if ( PQstatus() != CONNECTION_OK ) if ( PQstatus() != CONNECTION_OK )
{ {
const QString error { tr( "Connection error: %1 returned %2 [%3]" )
.arg( query ).arg( PQstatus() ).arg( PQerrorMessage() ) };
logWrapper->setError( error );
if ( logError ) if ( logError )
{ {
QgsMessageLog::logMessage( tr( "Connection error: %1 returned %2 [%3]" ) QgsMessageLog::logMessage( error,
.arg( query ).arg( PQstatus() ).arg( PQerrorMessage() ),
tr( "PostGIS" ) ); tr( "PostGIS" ) );
} }
else else
@ -1371,9 +1380,11 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret
} }
else else
{ {
const QString error { tr( "Query failed: %1\nError: no result buffer" ).arg( query ) };
logWrapper->setError( error );
if ( logError ) if ( logError )
{ {
QgsMessageLog::logMessage( tr( "Query failed: %1\nError: no result buffer" ).arg( query ), tr( "PostGIS" ) ); QgsMessageLog::logMessage( error, tr( "PostGIS" ) );
} }
else else
{ {
@ -1385,6 +1396,7 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret
{ {
QgsMessageLog::logMessage( tr( "resetting bad connection." ), tr( "PostGIS" ) ); QgsMessageLog::logMessage( tr( "resetting bad connection." ), tr( "PostGIS" ) );
::PQreset( mConn ); ::PQreset( mConn );
logWrapper.reset( new QgsDatabaseQueryLogWrapper( query, mConnInfo, QStringLiteral( "postgres" ), originatorClass, queryOrigin ) );
res = PQexec( query, logError, false ); res = PQexec( query, logError, false );
if ( PQstatus() == CONNECTION_OK ) if ( PQstatus() == CONNECTION_OK )
{ {
@ -1395,13 +1407,17 @@ PGresult *QgsPostgresConn::PQexec( const QString &query, bool logError, bool ret
} }
else else
{ {
QgsMessageLog::logMessage( tr( "retry after reset failed again." ), tr( "PostGIS" ) ); const QString error { tr( "retry after reset failed again." ) };
logWrapper->setError( error );
QgsMessageLog::logMessage( error, tr( "PostGIS" ) );
return nullptr; return nullptr;
} }
} }
else else
{ {
QgsMessageLog::logMessage( tr( "connection still bad after reset." ), tr( "PostGIS" ) ); const QString error { tr( "connection still bad after reset." ) };
logWrapper->setError( error );
QgsMessageLog::logMessage( error, tr( "PostGIS" ) );
} }
} }
else else
@ -1442,8 +1458,8 @@ 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" ). return LoggedPQexecNR( "QgsPostgresConn", 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 ) );
} }
bool QgsPostgresConn::closeCursor( const QString &cursorName ) bool QgsPostgresConn::closeCursor( const QString &cursorName )
@ -1457,7 +1473,7 @@ bool QgsPostgresConn::closeCursor( const QString &cursorName )
postStr = QStringLiteral( ";COMMIT" ); postStr = QStringLiteral( ";COMMIT" );
} }
if ( !PQexecNR( QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) ) if ( !LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "CLOSE %1%2" ).arg( cursorName, postStr ) ) )
return false; return false;
return true; return true;
@ -1469,11 +1485,11 @@ QString QgsPostgresConn::uniqueCursorName()
return QStringLiteral( "qgis_%1" ).arg( ++mNextCursorId ); return QStringLiteral( "qgis_%1" ).arg( ++mNextCursorId );
} }
bool QgsPostgresConn::PQexecNR( const QString &query ) bool QgsPostgresConn::PQexecNR( const QString &query, const QString &originatorClass, const QString &queryOrigin )
{ {
QMutexLocker locker( &mLock ); // to protect access to mOpenCursors QMutexLocker locker( &mLock ); // to protect access to mOpenCursors
QgsPostgresResult res( PQexec( query, false ) ); QgsPostgresResult res( PQexec( query, false, true, originatorClass, queryOrigin ) );
ExecStatusType errorStatus = res.PQresultStatus(); ExecStatusType errorStatus = res.PQresultStatus();
if ( errorStatus == PGRES_COMMAND_OK ) if ( errorStatus == PGRES_COMMAND_OK )
@ -1495,7 +1511,7 @@ bool QgsPostgresConn::PQexecNR( const QString &query )
if ( PQstatus() == CONNECTION_OK ) if ( PQstatus() == CONNECTION_OK )
{ {
PQexecNR( QStringLiteral( "ROLLBACK" ) ); LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK" ) );
} }
return false; return false;
@ -1575,11 +1591,11 @@ bool QgsPostgresConn::begin()
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return PQexecNR( QStringLiteral( "SAVEPOINT transaction_savepoint" ) ); return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "SAVEPOINT transaction_savepoint" ) );
} }
else else
{ {
return PQexecNR( QStringLiteral( "BEGIN" ) ); return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "BEGIN" ) );
} }
} }
@ -1588,11 +1604,11 @@ bool QgsPostgresConn::commit()
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) );
} }
else else
{ {
return PQexecNR( QStringLiteral( "COMMIT" ) ); return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "COMMIT" ) );
} }
} }
@ -1601,12 +1617,12 @@ bool QgsPostgresConn::rollback()
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
if ( mTransaction ) if ( mTransaction )
{ {
return PQexecNR( QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) ) return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK TO SAVEPOINT transaction_savepoint" ) )
&& PQexecNR( QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) ); && LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "RELEASE SAVEPOINT transaction_savepoint" ) );
} }
else else
{ {
return PQexecNR( QStringLiteral( "ROLLBACK" ) ); return LoggedPQexecNR( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "ROLLBACK" ) );
} }
} }
@ -1892,7 +1908,7 @@ void QgsPostgresConn::deduceEndian()
.arg( queryCounter ) .arg( queryCounter )
.arg( errorCounter ), 2 ); .arg( errorCounter ), 2 );
QgsPostgresResult res( PQexec( QStringLiteral( "select regclass('pg_class')::oid" ) ) ); QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "select regclass('pg_class')::oid" ) ) );
QString oidValue = res.PQgetvalue( 0, 0 ); QString oidValue = res.PQgetvalue( 0, 0 );
QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Creating binary cursor" ), 2 );
@ -1902,7 +1918,7 @@ void QgsPostgresConn::deduceEndian()
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 );
res = PQexec( QStringLiteral( "fetch forward 1 from oidcursor" ) ); res = LoggedPQexec( QStringLiteral( "QgsPostresConn" ), QStringLiteral( "fetch forward 1 from oidcursor" ) );
mSwapEndian = true; mSwapEndian = true;
if ( res.PQntuples() > 0 ) if ( res.PQntuples() > 0 )
@ -2084,7 +2100,7 @@ void QgsPostgresConn::retrieveLayerTypes( QVector<QgsPostgresLayerProperty *> &l
QgsDebugMsgLevel( "Layer types,srids and dims query: " + query, 3 ); QgsDebugMsgLevel( "Layer types,srids and dims query: " + query, 3 );
QgsPostgresResult res( PQexec( query ) ); QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), query ) );
if ( res.PQresultStatus() != PGRES_TUPLES_OK ) if ( res.PQresultStatus() != PGRES_TUPLES_OK )
{ {
// TODO: print some error here ? // TODO: print some error here ?
@ -2678,7 +2694,7 @@ QString QgsPostgresConn::currentDatabase() const
QMutexLocker locker( &mLock ); QMutexLocker locker( &mLock );
QString database; QString database;
QString sql = "SELECT current_database()"; QString sql = "SELECT current_database()";
QgsPostgresResult res( PQexec( sql ) ); QgsPostgresResult res( LoggedPQexec( QStringLiteral( "QgsPostresConn" ), sql ) );
if ( res.PQresultStatus() == PGRES_TUPLES_OK ) if ( res.PQresultStatus() == PGRES_TUPLES_OK )
{ {

View File

@ -200,6 +200,12 @@ class QgsPoolPostgresConn
class QgsPostgresConn *get() const { return mPgConn; } class QgsPostgresConn *get() const { return mPgConn; }
}; };
#include "qgsconfig.h"
constexpr int sPostgresConQueryLogFilePrefixLength = CMAKE_SOURCE_DIR[sizeof( CMAKE_SOURCE_DIR ) - 1] == '/' ? sizeof( CMAKE_SOURCE_DIR ) + 1 : sizeof( CMAKE_SOURCE_DIR );
#define LoggedPQexecNR(_class, query) PQexecNR( query, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
#define LoggedPQexec(_class, query) PQexec( query, true, true, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
#define LoggedPQexecNoLogError(_class, query ) PQexec( query, false, true, _class, QString(QString( __FILE__ ).mid( sPostgresConQueryLogFilePrefixLength ) + ':' + QString::number( __LINE__ ) + " (" + __FUNCTION__ + ")") )
class QgsPostgresConn : public QObject class QgsPostgresConn : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -247,7 +253,7 @@ class QgsPostgresConn : public QObject
int pgVersion() const { return mPostgresqlVersion; } int pgVersion() const { return mPostgresqlVersion; }
//! run a query and free result buffer //! run a query and free result buffer
bool PQexecNR( const QString &query ); bool PQexecNR( const QString &query, const QString &originatorClass = QString(), const QString &queryOrigin = QString() );
//! cursor handling //! cursor handling
bool openCursor( const QString &cursorName, const QString &declare ); bool openCursor( const QString &cursorName, const QString &declare );
@ -264,7 +270,7 @@ class QgsPostgresConn : public QObject
// //
// run a query and check for errors, thread-safe // run a query and check for errors, thread-safe
PGresult *PQexec( const QString &query, bool logError = true, bool retry = true ) const; PGresult *PQexec( const QString &query, bool logError = true, bool retry = true, const QString &originatorClass = QString(), const QString &queryOrigin = QString() ) const;
int PQCancel(); int PQCancel();
void PQfinish(); void PQfinish();
QString PQerrorMessage() const; QString PQerrorMessage() const;

View File

@ -252,7 +252,7 @@ void QgsPostgresDataItemGuiProvider::createSchema( QgsDataItem *item, QgsDataIte
//create the schema //create the schema
const QString sql = QStringLiteral( "CREATE SCHEMA %1" ).arg( QgsPostgresConn::quotedIdentifier( schemaName ) ); const QString sql = QStringLiteral( "CREATE SCHEMA %1" ).arg( QgsPostgresConn::quotedIdentifier( schemaName ) );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
notify( tr( "New Schema" ), tr( "Unable to create schema '%1'\n%2" ).arg( schemaName, notify( tr( "New Schema" ), tr( "Unable to create schema '%1'\n%2" ).arg( schemaName,
@ -284,7 +284,7 @@ void QgsPostgresDataItemGuiProvider::deleteSchema( QgsPGSchemaItem *schemaItem,
} }
const QString sql = QStringLiteral( "SELECT table_name FROM information_schema.tables WHERE table_schema='%1'" ).arg( schemaItem->name() ); const QString sql = QStringLiteral( "SELECT table_name FROM information_schema.tables WHERE table_schema='%1'" ).arg( schemaItem->name() );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
notify( tr( "Delete Schema" ), tr( "Unable to delete schema." ), context, Qgis::MessageLevel::Warning ); notify( tr( "Delete Schema" ), tr( "Unable to delete schema." ), context, Qgis::MessageLevel::Warning );
@ -360,7 +360,7 @@ void QgsPostgresDataItemGuiProvider::renameSchema( QgsPGSchemaItem *schemaItem,
const QString sql = QStringLiteral( "ALTER SCHEMA %1 RENAME TO %2" ) const QString sql = QStringLiteral( "ALTER SCHEMA %1 RENAME TO %2" )
.arg( schemaName, QgsPostgresConn::quotedIdentifier( dlg.name() ) ); .arg( schemaName, QgsPostgresConn::quotedIdentifier( dlg.name() ) );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
notify( tr( "Rename Schema" ), tr( "Unable to rename schema '%1'\n%2" ).arg( schemaItem->name(), notify( tr( "Rename Schema" ), tr( "Unable to rename schema '%1'\n%2" ).arg( schemaItem->name(),
@ -418,7 +418,7 @@ void QgsPostgresDataItemGuiProvider::renameLayer( QgsPGLayerItem *layerItem, Qgs
sql = QStringLiteral( "ALTER TABLE %1 RENAME TO %2" ).arg( oldName, newName ); sql = QStringLiteral( "ALTER TABLE %1 RENAME TO %2" ).arg( oldName, newName );
} }
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
notify( tr( "Rename %1" ).arg( typeName ), tr( "Unable to rename '%1' %2\n%3" ).arg( lowerTypeName, layerItem->name(), notify( tr( "Rename %1" ).arg( typeName ), tr( "Unable to rename '%1' %2\n%3" ).arg( lowerTypeName, layerItem->name(),
@ -463,7 +463,7 @@ void QgsPostgresDataItemGuiProvider::truncateTable( QgsPGLayerItem *layerItem, Q
const QString sql = QStringLiteral( "TRUNCATE TABLE %1" ).arg( tableRef ); const QString sql = QStringLiteral( "TRUNCATE TABLE %1" ).arg( tableRef );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
notify( tr( "Truncate Table" ), tr( "Unable to truncate '%1'\n%2" ).arg( tableName, notify( tr( "Truncate Table" ), tr( "Unable to truncate '%1'\n%2" ).arg( tableName,
@ -503,7 +503,7 @@ void QgsPostgresDataItemGuiProvider::refreshMaterializedView( QgsPGLayerItem *la
const QString sql = QStringLiteral( "REFRESH MATERIALIZED VIEW CONCURRENTLY %1" ).arg( tableRef ); const QString sql = QStringLiteral( "REFRESH MATERIALIZED VIEW CONCURRENTLY %1" ).arg( tableRef );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresDataItemGuiProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
notify( tr( "Refresh View" ), tr( "Unable to refresh the view '%1'\n%2" ).arg( tableRef, notify( tr( "Refresh View" ), tr( "Unable to refresh the view '%1'\n%2" ).arg( tableRef,

View File

@ -34,7 +34,7 @@
bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause ) bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
{ {
QgsDebugMsgLevel( "deleting layer " + uri, 2 ); QgsDebugMsg( "deleting layer " + uri );
QgsDataSourceUri dsUri( uri ); QgsDataSourceUri dsUri( uri );
QString schemaName = dsUri.schema(); QString schemaName = dsUri.schema();
@ -58,12 +58,12 @@ 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 ) );
QgsPostgresResult resViewCheck( conn->PQexec( sqlViewCheck ) ); QgsPostgresResult resViewCheck( conn->LoggedPQexec( "QgsPostgresUtils", 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 );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
errCause = QObject::tr( "Unable to delete view %1: \n%2" ) errCause = QObject::tr( "Unable to delete view %1: \n%2" )
@ -85,7 +85,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
"AND f_table_schema=%1 AND f_table_name=%2" ) "AND f_table_schema=%1 AND f_table_name=%2" )
.arg( QgsPostgresConn::quotedValue( schemaName ), .arg( QgsPostgresConn::quotedValue( schemaName ),
QgsPostgresConn::quotedValue( tableName ) ); QgsPostgresConn::quotedValue( tableName ) );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
errCause = QObject::tr( "Unable to delete layer %1: \n%2" ) errCause = QObject::tr( "Unable to delete layer %1: \n%2" )
@ -113,7 +113,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
QgsPostgresConn::quotedValue( tableName ) ); QgsPostgresConn::quotedValue( tableName ) );
} }
result = conn->PQexec( sql ); result = conn->LoggedPQexec( "QgsPostgresUtils", sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
errCause = QObject::tr( "Unable to delete layer %1: \n%2" ) errCause = QObject::tr( "Unable to delete layer %1: \n%2" )
@ -129,7 +129,7 @@ bool QgsPostgresUtils::deleteLayer( const QString &uri, QString &errCause )
bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceUri &uri, QString &errCause, bool cascade ) bool QgsPostgresUtils::deleteSchema( const QString &schema, const QgsDataSourceUri &uri, QString &errCause, bool cascade )
{ {
QgsDebugMsgLevel( "deleting schema " + schema, 2 ); QgsDebugMsg( "deleting schema " + schema );
if ( schema.isEmpty() ) if ( schema.isEmpty() )
return false; return false;
@ -147,7 +147,7 @@ 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() );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresUtils", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
errCause = QObject::tr( "Unable to delete schema %1: \n%2" ) errCause = QObject::tr( "Unable to delete schema %1: \n%2" )
@ -262,7 +262,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 ); QgsDebugMsg( "URI " + uri.uri( false ) );
if ( !toSchema.isNull() ) if ( !toSchema.isNull() )
{ {
@ -365,7 +365,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 ); QgsDebugMsg( QStringLiteral( "layer uri: %1" ).arg( uri.uri( false ) ) );
return uri.uri( false ); return uri.uri( false );
} }

View File

@ -19,6 +19,7 @@
#include "qgspostgresprovider.h" #include "qgspostgresprovider.h"
#include "qgspostgrestransaction.h" #include "qgspostgrestransaction.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgsdbquerylog.h"
#include "qgsmessagelog.h" #include "qgsmessagelog.h"
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsexception.h" #include "qgsexception.h"
@ -292,12 +293,18 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature )
QgsDebugMsgLevel( QStringLiteral( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 ); QgsDebugMsgLevel( QStringLiteral( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
lock(); lock();
QgsDatabaseQueryLogWrapper logWrapper { fetch, mSource->mConnInfo, QStringLiteral( "postgres" ), QStringLiteral( "QgsPostgresFeatureIterator" ), QGS_QUERY_LOG_ORIGIN };
if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
{ {
QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); const QString error { QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ) };
QgsMessageLog::logMessage( error, QObject::tr( "PostGIS" ) );
logWrapper.setError( error );
} }
QgsPostgresResult queryResult; QgsPostgresResult queryResult;
long long fetchedRows { 0 };
for ( ;; ) for ( ;; )
{ {
queryResult = mConn->PQgetResult(); queryResult = mConn->PQgetResult();
@ -313,6 +320,8 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature )
int rows = queryResult.PQntuples(); int rows = queryResult.PQntuples();
if ( rows == 0 ) if ( rows == 0 )
continue; continue;
else
fetchedRows += rows;
mLastFetch = rows < mFeatureQueueSize; mLastFetch = rows < mFeatureQueueSize;
@ -324,6 +333,11 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature )
} }
unlock(); unlock();
if ( fetchedRows > 0 )
{
logWrapper.setFetchedRows( fetchedRows );
}
#if 0 //disabled dynamic queue size #if 0 //disabled dynamic queue size
if ( timer.elapsed() > 500 && mFeatureQueueSize > 1 ) if ( timer.elapsed() > 500 && mFeatureQueueSize > 1 )
{ {
@ -423,7 +437,7 @@ bool QgsPostgresFeatureIterator::rewind()
// move cursor to first record // move cursor to first record
mConn->PQexecNR( QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) ); mConn->LoggedPQexecNR( "QgsPostgresFeatureIterator", QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) );
mFeatureQueue.clear(); mFeatureQueue.clear();
mFetched = 0; mFetched = 0;
mLastFetch = false; mLastFetch = false;

View File

@ -41,6 +41,7 @@
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsstringutils.h" #include "qgsstringutils.h"
#include "qgsjsonutils.h" #include "qgsjsonutils.h"
#include "qgsdbquerylog.h"
#include "qgspostgresprovider.h" #include "qgspostgresprovider.h"
#include "qgsprovidermetadata.h" #include "qgsprovidermetadata.h"
@ -65,13 +66,13 @@ inline qint32 FID2PKINT( qint64 x )
static bool tableExists( QgsPostgresConn &conn, const QString &name ) static bool tableExists( QgsPostgresConn &conn, const QString &name )
{ {
QgsPostgresResult res( conn.PQexec( "SELECT EXISTS ( SELECT oid FROM pg_catalog.pg_class WHERE relname=" + QgsPostgresConn::quotedValue( name ) + ")" ) ); QgsPostgresResult res( conn.LoggedPQexec( QStringLiteral( "tableExists" ), "SELECT EXISTS ( SELECT oid FROM pg_catalog.pg_class WHERE relname=" + QgsPostgresConn::quotedValue( name ) + ")" ) );
return res.PQgetvalue( 0, 0 ).startsWith( 't' ); return res.PQgetvalue( 0, 0 ).startsWith( 't' );
} }
static bool columnExists( QgsPostgresConn &conn, const QString &table, const QString &column ) static bool columnExists( QgsPostgresConn &conn, const QString &table, const QString &column )
{ {
QgsPostgresResult res( conn.PQexec( "SELECT COUNT(*) FROM information_schema.columns WHERE table_name=" + QgsPostgresConn::quotedValue( table ) + " and column_name=" + QgsPostgresConn::quotedValue( column ) ) ); QgsPostgresResult res( conn.LoggedPQexec( QStringLiteral( "columnExists" ), "SELECT COUNT(*) FROM information_schema.columns WHERE table_name=" + QgsPostgresConn::quotedValue( table ) + " and column_name=" + QgsPostgresConn::quotedValue( column ) ) );
return res.PQgetvalue( 0, 0 ).toInt() > 0; return res.PQgetvalue( 0, 0 ).toInt() > 0;
} }
@ -434,7 +435,7 @@ QgsCoordinateReferenceSystem QgsPostgresProvider::sridToCrs( int srid, QgsPostgr
{ {
if ( conn ) if ( conn )
{ {
QgsPostgresResult result( conn->PQexec( QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) ); QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) );
if ( result.PQresultStatus() == PGRES_TUPLES_OK ) if ( result.PQresultStatus() == PGRES_TUPLES_OK )
{ {
if ( result.PQntuples() > 0 ) if ( result.PQntuples() > 0 )
@ -925,7 +926,7 @@ bool QgsPostgresProvider::loadFields()
// Get the table description // Get the table description
sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=regclass(%1)::oid AND objsubid=0" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=regclass(%1)::oid AND objsubid=0" ).arg( quotedValue( mQuery ) );
QgsPostgresResult tresult( connectionRO()->PQexec( sql ) ); QgsPostgresResult tresult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( tresult.PQntuples() > 0 ) if ( tresult.PQntuples() > 0 )
{ {
mDataComment = tresult.PQgetvalue( 0, 0 ); mDataComment = tresult.PQgetvalue( 0, 0 );
@ -937,7 +938,7 @@ bool QgsPostgresProvider::loadFields()
// field name, type, length, and precision (if numeric) // field name, type, length, and precision (if numeric)
sql = QStringLiteral( "SELECT * FROM %1 LIMIT 0" ).arg( mQuery ); sql = QStringLiteral( "SELECT * FROM %1 LIMIT 0" ).arg( mQuery );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
QMap<Oid, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap, identityMap, generatedMap; QMap<Oid, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap, identityMap, generatedMap;
QMap<Oid, QMap<int, Oid> > attTypeIdMap; QMap<Oid, QMap<int, Oid> > attTypeIdMap;
@ -988,7 +989,7 @@ bool QgsPostgresProvider::loadFields()
connectionRO()->pgVersion() >= 120000 ? QStringLiteral( ", attgenerated" ) : QString(), connectionRO()->pgVersion() >= 120000 ? QStringLiteral( ", attgenerated" ) : QString(),
tableoidsFilter ); tableoidsFilter );
QgsPostgresResult fmtFieldTypeResult( connectionRO()->PQexec( sql ) ); QgsPostgresResult fmtFieldTypeResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i ) for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i )
{ {
Oid attrelid = fmtFieldTypeResult.PQgetvalue( i, 0 ).toUInt(); Oid attrelid = fmtFieldTypeResult.PQgetvalue( i, 0 ).toUInt();
@ -1036,7 +1037,7 @@ bool QgsPostgresProvider::loadFields()
// Collect type info // Collect type info
sql = QStringLiteral( "SELECT oid,typname,typtype,typelem,typlen FROM pg_type %1" ).arg( attroidsFilter ); sql = QStringLiteral( "SELECT oid,typname,typtype,typelem,typlen FROM pg_type %1" ).arg( attroidsFilter );
QgsPostgresResult typeResult( connectionRO()->PQexec( sql ) ); QgsPostgresResult typeResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
QMap<Oid, PGTypeInfo> typeMap; QMap<Oid, PGTypeInfo> typeMap;
for ( int i = 0; i < typeResult.PQntuples(); ++i ) for ( int i = 0; i < typeResult.PQntuples(); ++i )
@ -1080,7 +1081,7 @@ bool QgsPostgresProvider::loadFields()
{ {
// get correct formatted field type for domain // get correct formatted field type for domain
sql = QStringLiteral( "SELECT format_type(%1, %2)" ).arg( fldtyp ).arg( fldMod ); sql = QStringLiteral( "SELECT format_type(%1, %2)" ).arg( fldtyp ).arg( fldMod );
QgsPostgresResult fmtFieldModResult( connectionRO()->PQexec( sql ) ); QgsPostgresResult fmtFieldModResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( fmtFieldModResult.PQntuples() > 0 ) if ( fmtFieldModResult.PQntuples() > 0 )
{ {
formattedFieldType = fmtFieldModResult.PQgetvalue( 0, 0 ); formattedFieldType = fmtFieldModResult.PQgetvalue( 0, 0 );
@ -1413,7 +1414,7 @@ void QgsPostgresProvider::setEditorWidgets()
"AND field_name IN ( %4 )" ) . "AND field_name IN ( %4 )" ) .
arg( EDITOR_WIDGET_STYLES_TABLE, quotedValue( mSchemaName ), arg( EDITOR_WIDGET_STYLES_TABLE, quotedValue( mSchemaName ),
quotedValue( mTableName ), quotedFnames.join( "," ) ); quotedValue( mTableName ), quotedFnames.join( "," ) );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
for ( int i = 0; i < result.PQntuples(); ++i ) for ( int i = 0; i < result.PQntuples(); ++i )
{ {
if ( result.PQgetisnull( i, 2 ) ) continue; // config can be null and it's OK if ( result.PQgetisnull( i, 2 ) ) continue; // config can be null and it's OK
@ -1459,7 +1460,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
{ {
// Check that we can read from the table (i.e., we have select permission). // Check that we can read from the table (i.e., we have select permission).
QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery ); QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery );
QgsPostgresResult testAccess( connectionRO()->PQexec( sql ) ); QgsPostgresResult testAccess( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( tr( "Unable to access the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" ) QgsMessageLog::logMessage( tr( "Unable to access the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
@ -1473,7 +1474,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
if ( connectionRO()->pgVersion() >= 90000 ) if ( connectionRO()->pgVersion() >= 90000 )
{ {
testAccess = connectionRO()->PQexec( QStringLiteral( "SELECT pg_is_in_recovery()" ) ); testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", QStringLiteral( "SELECT pg_is_in_recovery()" ) );
if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK || testAccess.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) ) if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK || testAccess.PQgetvalue( 0, 0 ) == QLatin1String( "t" ) )
{ {
QgsMessageLog::logMessage( tr( "PostgreSQL is still in recovery after a database crash\n(or you are connected to a (read-only) standby server).\nWrite accesses will be denied." ), tr( "PostGIS" ) ); QgsMessageLog::logMessage( tr( "PostgreSQL is still in recovery after a database crash\n(or you are connected to a (read-only) standby server).\nWrite accesses will be denied." ), tr( "PostGIS" ) );
@ -1517,7 +1518,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
.arg( quotedValue( mQuery ) ); .arg( quotedValue( mQuery ) );
} }
testAccess = connectionRO()->PQexec( sql ); testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" ) QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
@ -1563,7 +1564,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
.arg( quotedValue( mTableName ), .arg( quotedValue( mTableName ),
quotedValue( mSchemaName ), quotedValue( mSchemaName ),
connectionRO()->pgVersion() < 80100 ? "pg_get_userbyid(relowner)=current_user" : "pg_has_role(relowner,'MEMBER')" ); connectionRO()->pgVersion() < 80100 ? "pg_get_userbyid(relowner)=current_user" : "pg_has_role(relowner,'MEMBER')" );
testAccess = connectionRO()->PQexec( sql ); testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( testAccess.PQresultStatus() == PGRES_TUPLES_OK && testAccess.PQntuples() == 1 ) if ( testAccess.PQresultStatus() == PGRES_TUPLES_OK && testAccess.PQntuples() == 1 )
{ {
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes; mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
@ -1599,7 +1600,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery ); QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery );
testAccess = connectionRO()->PQexec( sql ); testAccess = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK ) if ( testAccess.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" ) QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
@ -1652,13 +1653,13 @@ bool QgsPostgresProvider::determinePrimaryKey()
{ {
sql = QStringLiteral( "SELECT count(*) FROM pg_inherits WHERE inhparent=%1::regclass" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT count(*) FROM pg_inherits WHERE inhparent=%1::regclass" ).arg( quotedValue( mQuery ) );
QgsDebugMsgLevel( QStringLiteral( "Checking whether %1 is a parent table" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Checking whether %1 is a parent table" ).arg( sql ), 2 );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
bool isParentTable( res.PQntuples() == 0 || res.PQgetvalue( 0, 0 ).toInt() > 0 ); bool isParentTable( res.PQntuples() == 0 || res.PQgetvalue( 0, 0 ).toInt() > 0 );
sql = QStringLiteral( "SELECT indexrelid FROM pg_index WHERE indrelid=%1::regclass AND (indisprimary OR indisunique) ORDER BY CASE WHEN indisprimary THEN 1 ELSE 2 END LIMIT 1" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT indexrelid FROM pg_index WHERE indrelid=%1::regclass AND (indisprimary OR indisunique) ORDER BY CASE WHEN indisprimary THEN 1 ELSE 2 END LIMIT 1" ).arg( quotedValue( mQuery ) );
QgsDebugMsgLevel( QStringLiteral( "Retrieving first primary or unique index: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Retrieving first primary or unique index: %1" ).arg( sql ), 2 );
res = connectionRO()->PQexec( sql ); res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 );
QStringList log; QStringList log;
@ -1686,7 +1687,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
{ {
// If there is an generated id on the table, use that instead, // If there is an generated id on the table, use that instead,
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attidentity IN ('a','d') AND attrelid=regclass(%1) LIMIT 1" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attidentity IN ('a','d') AND attrelid=regclass(%1) LIMIT 1" ).arg( quotedValue( mQuery ) );
res = connectionRO()->PQexec( sql ); res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( res.PQntuples() == 1 ) if ( res.PQntuples() == 1 )
{ {
// Could warn the user here that performance will suffer if // Could warn the user here that performance will suffer if
@ -1703,7 +1704,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
// If there is an oid on the table, use that instead, // If there is an oid on the table, use that instead,
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) );
res = connectionRO()->PQexec( sql ); res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( res.PQntuples() == 1 ) if ( res.PQntuples() == 1 )
{ {
// Could warn the user here that performance will suffer if // Could warn the user here that performance will suffer if
@ -1717,7 +1718,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
{ {
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) );
res = connectionRO()->PQexec( sql ); res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( res.PQntuples() == 1 ) if ( res.PQntuples() == 1 )
{ {
mPrimaryKeyType = PktTid; mPrimaryKeyType = PktTid;
@ -1750,7 +1751,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
sql = QStringLiteral( "SELECT attname,attnotnull FROM pg_index,pg_attribute WHERE indexrelid=%1 AND indrelid=attrelid AND pg_attribute.attnum=any(pg_index.indkey)" ).arg( indrelid ); sql = QStringLiteral( "SELECT attname,attnotnull FROM pg_index,pg_attribute WHERE indexrelid=%1 AND indrelid=attrelid AND pg_attribute.attnum=any(pg_index.indkey)" ).arg( indrelid );
QgsDebugMsgLevel( "Retrieving key columns: " + sql, 2 ); QgsDebugMsgLevel( "Retrieving key columns: " + sql, 2 );
res = connectionRO()->PQexec( sql ); res = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Got %1 rows." ).arg( res.PQntuples() ), 2 );
bool mightBeNull = false; bool mightBeNull = false;
@ -1934,7 +1935,7 @@ bool QgsPostgresProvider::uniqueData( const QString &quotedColNames )
mQuery, mQuery,
filterWhereClause() ); filterWhereClause() );
QgsPostgresResult unique( connectionRO()->PQexec( sql ) ); QgsPostgresResult unique( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( unique.PQresultStatus() != PGRES_TUPLES_OK ) if ( unique.PQresultStatus() != PGRES_TUPLES_OK )
{ {
@ -1962,7 +1963,7 @@ QVariant QgsPostgresProvider::minimumValue( int index ) const
sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql );
QgsPostgresResult rmin( connectionRO()->PQexec( sql ) ); QgsPostgresResult rmin( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
return convertValue( fld.type(), fld.subType(), rmin.PQgetvalue( 0, 0 ), fld.typeName() ); return convertValue( fld.type(), fld.subType(), rmin.PQgetvalue( 0, 0 ), fld.typeName() );
} }
catch ( PGFieldNotFound ) catch ( PGFieldNotFound )
@ -1998,7 +1999,7 @@ QSet<QVariant> QgsPostgresProvider::uniqueValues( int index, int limit ) const
sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( res.PQresultStatus() == PGRES_TUPLES_OK ) if ( res.PQresultStatus() == PGRES_TUPLES_OK )
{ {
for ( int i = 0; i < res.PQntuples(); i++ ) for ( int i = 0; i < res.PQntuples(); i++ )
@ -2040,7 +2041,7 @@ QStringList QgsPostgresProvider::uniqueStringsMatching( int index, const QString
sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), sql ) );
if ( res.PQresultStatus() == PGRES_TUPLES_OK ) if ( res.PQresultStatus() == PGRES_TUPLES_OK )
{ {
for ( int i = 0; i < res.PQntuples(); i++ ) for ( int i = 0; i < res.PQntuples(); i++ )
@ -2081,7 +2082,7 @@ void QgsPostgresProvider::enumValues( int index, QStringList &enumList ) const
//is type an enum? //is type an enum?
const QString typeSql = QStringLiteral( "SELECT typtype FROM pg_type WHERE typname=%1" ).arg( quotedValue( typeName ) ); const QString typeSql = QStringLiteral( "SELECT typtype FROM pg_type WHERE typname=%1" ).arg( quotedValue( typeName ) );
QgsPostgresResult typeRes( connectionRO()->PQexec( typeSql ) ); QgsPostgresResult typeRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), typeSql ) );
if ( typeRes.PQresultStatus() != PGRES_TUPLES_OK || typeRes.PQntuples() < 1 ) if ( typeRes.PQresultStatus() != PGRES_TUPLES_OK || typeRes.PQntuples() < 1 )
{ {
mShared->setFieldSupportsEnumValues( index, false ); mShared->setFieldSupportsEnumValues( index, false );
@ -2114,7 +2115,7 @@ bool QgsPostgresProvider::parseEnumRange( QStringList &enumValues, const QString
QString enumRangeSql = QStringLiteral( "SELECT enumlabel FROM pg_catalog.pg_enum WHERE enumtypid=(SELECT atttypid::regclass FROM pg_attribute WHERE attrelid=%1::regclass AND attname=%2)" ) QString enumRangeSql = QStringLiteral( "SELECT enumlabel FROM pg_catalog.pg_enum WHERE enumtypid=(SELECT atttypid::regclass FROM pg_attribute WHERE attrelid=%1::regclass AND attname=%2)" )
.arg( quotedValue( mQuery ), .arg( quotedValue( mQuery ),
quotedValue( attributeName ) ); quotedValue( attributeName ) );
QgsPostgresResult enumRangeRes( connectionRO()->PQexec( enumRangeSql ) ); QgsPostgresResult enumRangeRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), enumRangeSql ) );
if ( enumRangeRes.PQresultStatus() != PGRES_TUPLES_OK ) if ( enumRangeRes.PQresultStatus() != PGRES_TUPLES_OK )
return false; return false;
@ -2132,7 +2133,7 @@ bool QgsPostgresProvider::parseDomainCheckConstraint( QStringList &enumValues, c
//is it a domain type with a check constraint? //is it a domain type with a check constraint?
QString domainSql = QStringLiteral( "SELECT domain_name, domain_schema FROM information_schema.columns WHERE table_name=%1 AND column_name=%2" ).arg( quotedValue( mTableName ), quotedValue( attributeName ) ); QString domainSql = QStringLiteral( "SELECT domain_name, domain_schema FROM information_schema.columns WHERE table_name=%1 AND column_name=%2" ).arg( quotedValue( mTableName ), quotedValue( attributeName ) );
QgsPostgresResult domainResult( connectionRO()->PQexec( domainSql ) ); QgsPostgresResult domainResult( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), domainSql ) );
if ( domainResult.PQresultStatus() == PGRES_TUPLES_OK && domainResult.PQntuples() > 0 && !domainResult.PQgetvalue( 0, 0 ).isNull() ) if ( domainResult.PQresultStatus() == PGRES_TUPLES_OK && domainResult.PQntuples() > 0 && !domainResult.PQgetvalue( 0, 0 ).isNull() )
{ {
QString domainCheckDefinitionSql; QString domainCheckDefinitionSql;
@ -2168,7 +2169,7 @@ bool QgsPostgresProvider::parseDomainCheckConstraint( QStringList &enumValues, c
} }
QgsPostgresResult domainCheckRes( connectionRO()->PQexec( domainCheckDefinitionSql ) ); QgsPostgresResult domainCheckRes( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), domainCheckDefinitionSql ) );
if ( domainCheckRes.PQresultStatus() == PGRES_TUPLES_OK && domainCheckRes.PQntuples() > 0 ) if ( domainCheckRes.PQresultStatus() == PGRES_TUPLES_OK && domainCheckRes.PQntuples() > 0 )
{ {
QString checkDefinition = domainCheckRes.PQgetvalue( 0, 0 ); QString checkDefinition = domainCheckRes.PQgetvalue( 0, 0 );
@ -2230,7 +2231,7 @@ QVariant QgsPostgresProvider::maximumValue( int index ) const
sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql ); sql = QStringLiteral( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql );
QgsPostgresResult rmax( connectionRO()->PQexec( sql ) ); QgsPostgresResult rmax( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
return convertValue( fld.type(), fld.subType(), rmax.PQgetvalue( 0, 0 ), fld.typeName() ); return convertValue( fld.type(), fld.subType(), rmax.PQgetvalue( 0, 0 ), fld.typeName() );
} }
@ -2278,7 +2279,7 @@ QVariant QgsPostgresProvider::defaultValue( int fieldId ) const
{ {
QgsField fld = field( fieldId ); QgsField fld = field( fieldId );
QgsPostgresResult res( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defVal ) ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT %1" ).arg( defVal ) ) );
if ( res.result() ) if ( res.result() )
{ {
@ -2315,7 +2316,7 @@ QString QgsPostgresProvider::paramValue( const QString &fieldValue, const QStrin
if ( fieldValue == defaultValue && !defaultValue.isNull() ) if ( fieldValue == defaultValue && !defaultValue.isNull() )
{ {
QgsPostgresResult result( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defaultValue ) ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT %1" ).arg( defaultValue ) ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
@ -2336,7 +2337,7 @@ bool QgsPostgresProvider::getTopoLayerInfo()
.arg( quotedValue( mSchemaName ), .arg( quotedValue( mSchemaName ),
quotedValue( mTableName ), quotedValue( mTableName ),
quotedValue( mGeometryColumn ) ); quotedValue( mGeometryColumn ) );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
throw PGException( result ); // we should probably not do this throw PGException( result ); // we should probably not do this
@ -2369,7 +2370,7 @@ void QgsPostgresProvider::dropOrphanedTopoGeoms()
QgsDebugMsgLevel( "TopoGeom orphans cleanup query: " + sql, 2 ); QgsDebugMsgLevel( "TopoGeom orphans cleanup query: " + sql, 2 );
connectionRW()->PQexecNR( sql ); connectionRW()->LoggedPQexecNR( "QgsPostgresProvider", sql );
} }
QString QgsPostgresProvider::geomParam( int offset ) const QString QgsPostgresProvider::geomParam( int offset ) const
@ -2748,7 +2749,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
} }
} }
conn->PQexecNR( QStringLiteral( "DEALLOCATE addfeatures" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE addfeatures" ) );
returnvalue &= conn->commit(); returnvalue &= conn->commit();
if ( mTransaction ) if ( mTransaction )
@ -2760,7 +2761,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
{ {
pushError( tr( "PostGIS error while adding features: %1" ).arg( e.errorMessage() ) ); pushError( tr( "PostGIS error while adding features: %1" ).arg( e.errorMessage() ) );
conn->rollback(); conn->rollback();
conn->PQexecNR( QStringLiteral( "DEALLOCATE addfeatures" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE addfeatures" ) );
returnvalue = false; returnvalue = false;
} }
@ -2808,7 +2809,7 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds &ids )
QgsDebugMsgLevel( "delete sql: " + sql, 2 ); QgsDebugMsgLevel( "delete sql: " + sql, 2 );
//send DELETE statement and do error handling //send DELETE statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
@ -2872,7 +2873,7 @@ bool QgsPostgresProvider::truncate()
QgsDebugMsgLevel( "truncate sql: " + sql, 2 ); QgsDebugMsgLevel( "truncate sql: " + sql, 2 );
//send truncate statement and do error handling //send truncate statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
@ -2947,7 +2948,7 @@ bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes )
} }
//send sql statement and do error handling //send sql statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result ); throw PGException( result );
@ -2959,7 +2960,7 @@ bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes )
.arg( mQuery, .arg( mQuery,
quotedIdentifier( iter->name() ), quotedIdentifier( iter->name() ),
quotedValue( iter->comment() ) ); quotedValue( iter->comment() ) );
result = conn->PQexec( sql ); result = conn->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result ); throw PGException( result );
} }
@ -3014,7 +3015,7 @@ bool QgsPostgresProvider::deleteAttributes( const QgsAttributeIds &ids )
quotedIdentifier( column ) ); quotedIdentifier( column ) );
//send sql statement and do error handling //send sql statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result ); throw PGException( result );
@ -3081,7 +3082,7 @@ bool QgsPostgresProvider::renameAttributes( const QgsFieldNameMap &renamedAttrib
{ {
conn->begin(); conn->begin();
//send sql statement and do error handling //send sql statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result ); throw PGException( result );
returnvalue = conn->commit(); returnvalue = conn->commit();
@ -3208,7 +3209,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &
// or if the user only changed GENERATED fields in the form/attribute table. // or if the user only changed GENERATED fields in the form/attribute table.
if ( numChangedFields > 0 ) if ( numChangedFields > 0 )
{ {
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
} }
@ -3408,7 +3409,7 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m
.arg( quotedIdentifier( mTopoLayerInfo.topologyName ) ) .arg( quotedIdentifier( mTopoLayerInfo.topologyName ) )
.arg( mTopoLayerInfo.layerId ) .arg( mTopoLayerInfo.layerId )
.arg( old_tg_id ); .arg( old_tg_id );
result = conn->PQexec( replace ); result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), replace );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" ) QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" )
@ -3423,7 +3424,7 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m
.arg( mTopoLayerInfo.layerId ) .arg( mTopoLayerInfo.layerId )
.arg( new_tg_id ); .arg( new_tg_id );
QgsDebugMsgLevel( "relation swap: " + replace, 2 ); QgsDebugMsgLevel( "relation swap: " + replace, 2 );
result = conn->PQexec( replace ); result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), replace );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" ) QgsDebugMsg( QStringLiteral( "Exception thrown due to PQexec of this query returning != PGRES_COMMAND_OK (%1 != expected %2): %3" )
@ -3434,11 +3435,11 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m
} // for each feature } // for each feature
conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeatures" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeatures" ) );
if ( mSpatialColType == SctTopoGeometry ) if ( mSpatialColType == SctTopoGeometry )
{ {
connectionRO()->PQexecNR( QStringLiteral( "DEALLOCATE getid" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE getid" ) );
conn->PQexecNR( QStringLiteral( "DEALLOCATE replacetopogeom" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE replacetopogeom" ) );
} }
returnvalue &= conn->commit(); returnvalue &= conn->commit();
@ -3449,11 +3450,11 @@ bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_m
{ {
pushError( tr( "PostGIS error while changing geometry values: %1" ).arg( e.errorMessage() ) ); pushError( tr( "PostGIS error while changing geometry values: %1" ).arg( e.errorMessage() ) );
conn->rollback(); conn->rollback();
conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeatures" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeatures" ) );
if ( mSpatialColType == SctTopoGeometry ) if ( mSpatialColType == SctTopoGeometry )
{ {
connectionRO()->PQexecNR( QStringLiteral( "DEALLOCATE getid" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE getid" ) );
conn->PQexecNR( QStringLiteral( "DEALLOCATE replacetopogeom" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE replacetopogeom" ) );
} }
returnvalue = false; returnvalue = false;
} }
@ -3575,7 +3576,7 @@ bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_ma
{ {
sql += QStringLiteral( " WHERE %1" ).arg( whereClause( fid ) ); sql += QStringLiteral( " WHERE %1" ).arg( whereClause( fid ) );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK && result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
} }
@ -3609,7 +3610,7 @@ bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_ma
throw PGException( result ); throw PGException( result );
} }
conn->PQexecNR( QStringLiteral( "DEALLOCATE updatefeature" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "DEALLOCATE updatefeature" ) );
} }
// update feature id map if key was changed // update feature id map if key was changed
@ -3694,7 +3695,7 @@ bool QgsPostgresProvider::setSubsetString( const QString &theSQL, bool updateFea
sql += QLatin1String( " LIMIT 0" ); sql += QLatin1String( " LIMIT 0" );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( res.PQresultStatus() != PGRES_TUPLES_OK ) if ( res.PQresultStatus() != PGRES_TUPLES_OK )
{ {
pushError( res.PQresultErrorMessage() ); pushError( res.PQresultErrorMessage() );
@ -3759,7 +3760,7 @@ long long QgsPostgresProvider::featureCount() const
// parse explain output to estimate feature count // parse explain output to estimate feature count
// we don't use pg_class reltuples because it returns 0 for view // we don't use pg_class reltuples because it returns 0 for view
sql = QStringLiteral( "EXPLAIN (FORMAT JSON) SELECT 1 FROM %1%2" ).arg( mQuery, filterWhereClause() ); sql = QStringLiteral( "EXPLAIN (FORMAT JSON) SELECT 1 FROM %1%2" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
const QString json = result.PQgetvalue( 0, 0 ); const QString json = result.PQgetvalue( 0, 0 );
const QVariantList explain = QgsJsonUtils::parseJson( json ).toList(); const QVariantList explain = QgsJsonUtils::parseJson( json ).toList();
@ -3774,14 +3775,14 @@ long long QgsPostgresProvider::featureCount() const
else else
{ {
sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
num = result.PQgetvalue( 0, 0 ).toLongLong(); num = result.PQgetvalue( 0, 0 ).toLongLong();
} }
} }
else else
{ {
sql = QStringLiteral( "SELECT count(*) FROM %1%2" ).arg( mQuery, filterWhereClause() ); sql = QStringLiteral( "SELECT count(*) FROM %1%2" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
QgsDebugMsgLevel( "number of features as text: " + result.PQgetvalue( 0, 0 ), 2 ); QgsDebugMsgLevel( "number of features as text: " + result.PQgetvalue( 0, 0 ), 2 );
@ -3798,7 +3799,7 @@ long long QgsPostgresProvider::featureCount() const
bool QgsPostgresProvider::empty() const bool QgsPostgresProvider::empty() const
{ {
QString sql = QStringLiteral( "SELECT EXISTS (SELECT * FROM %1%2 LIMIT 1)" ).arg( mQuery, filterWhereClause() ); QString sql = QStringLiteral( "SELECT EXISTS (SELECT * FROM %1%2 LIMIT 1)" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( res.PQresultStatus() != PGRES_TUPLES_OK ) if ( res.PQresultStatus() != PGRES_TUPLES_OK )
{ {
pushError( res.PQresultErrorMessage() ); pushError( res.PQresultErrorMessage() );
@ -3830,13 +3831,13 @@ QgsRectangle QgsPostgresProvider::extent() const
.arg( quotedValue( mSchemaName ), .arg( quotedValue( mSchemaName ),
quotedValue( mTableName ), quotedValue( mTableName ),
quotedValue( mGeometryColumn ) ); quotedValue( mGeometryColumn ) );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 ) if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 )
{ {
if ( result.PQgetvalue( 0, 0 ).toInt() > 0 ) if ( result.PQgetvalue( 0, 0 ).toInt() > 0 )
{ {
sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() == PGRES_TUPLES_OK if ( result.PQresultStatus() == PGRES_TUPLES_OK
&& result.PQntuples() == 1 && result.PQntuples() == 1
&& result.PQgetvalue( 0, 0 ).toLong() > 0 ) && result.PQgetvalue( 0, 0 ).toLong() > 0 )
@ -3847,7 +3848,7 @@ QgsRectangle QgsPostgresProvider::extent() const
quotedValue( mSchemaName ), quotedValue( mSchemaName ),
quotedValue( mTableName ), quotedValue( mTableName ),
quotedValue( mGeometryColumn ) ); quotedValue( mGeometryColumn ) );
result = mConnectionRO->PQexec( sql ); result = mConnectionRO->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) )
{ {
ext = result.PQgetvalue( 0, 0 ); ext = result.PQgetvalue( 0, 0 );
@ -3884,9 +3885,9 @@ QgsRectangle QgsPostgresProvider::extent() const
mQuery, mQuery,
filterWhereClause() ); filterWhereClause() );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
connectionRO()->PQexecNR( QStringLiteral( "ROLLBACK" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "ROLLBACK" ) );
else if ( result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) else if ( result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) )
ext = result.PQgetvalue( 0, 0 ); ext = result.PQgetvalue( 0, 0 );
} }
@ -3960,7 +3961,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
QgsDebugMsgLevel( QStringLiteral( "Getting the spatial column type: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting the spatial column type: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( PGRES_TUPLES_OK == result.PQresultStatus() ) if ( PGRES_TUPLES_OK == result.PQresultStatus() )
{ {
geomColType = result.PQgetvalue( 0, 0 ); geomColType = result.PQgetvalue( 0, 0 );
@ -3996,17 +3997,17 @@ bool QgsPostgresProvider::getGeometryDetails()
QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 );
QgsPostgresResult result( connectionRO()->PQexec( sql ) ); QgsPostgresResult result( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( PGRES_TUPLES_OK == result.PQresultStatus() ) if ( PGRES_TUPLES_OK == result.PQresultStatus() )
{ {
Oid tableoid = result.PQftable( 0 ); Oid tableoid = result.PQftable( 0 );
int column = result.PQftablecol( 0 ); int column = result.PQftablecol( 0 );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( tableoid > 0 && PGRES_TUPLES_OK == result.PQresultStatus() ) if ( tableoid > 0 && PGRES_TUPLES_OK == result.PQresultStatus() )
{ {
sql = QStringLiteral( "SELECT pg_namespace.nspname,pg_class.relname FROM pg_class,pg_namespace WHERE pg_class.relnamespace=pg_namespace.oid AND pg_class.oid=%1" ).arg( tableoid ); sql = QStringLiteral( "SELECT pg_namespace.nspname,pg_class.relname FROM pg_class,pg_namespace WHERE pg_class.relnamespace=pg_namespace.oid AND pg_class.oid=%1" ).arg( tableoid );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() ) if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() )
{ {
@ -4014,7 +4015,7 @@ bool QgsPostgresProvider::getGeometryDetails()
tableName = result.PQgetvalue( 0, 1 ); tableName = result.PQgetvalue( 0, 1 );
sql = QStringLiteral( "SELECT a.attname, t.typname FROM pg_attribute a, pg_type t WHERE a.attrelid=%1 AND a.attnum=%2 AND a.atttypid = t.oid" ).arg( tableoid ).arg( column ); sql = QStringLiteral( "SELECT a.attname, t.typname FROM pg_attribute a, pg_type t WHERE a.attrelid=%1 AND a.attnum=%2 AND a.atttypid = t.oid" ).arg( tableoid ).arg( column );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() ) if ( PGRES_TUPLES_OK == result.PQresultStatus() && 1 == result.PQntuples() )
{ {
geomCol = result.PQgetvalue( 0, 0 ); geomCol = result.PQgetvalue( 0, 0 );
@ -4061,7 +4062,7 @@ bool QgsPostgresProvider::getGeometryDetails()
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting geometry column: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Geometry column query returned %1 rows" ).arg( result.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Geometry column query returned %1 rows" ).arg( result.PQntuples() ), 2 );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
@ -4086,7 +4087,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
if ( detectedType.isEmpty() ) if ( detectedType.isEmpty() )
@ -4098,7 +4099,7 @@ bool QgsPostgresProvider::getGeometryDetails()
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsDebugMsgLevel( QStringLiteral( "Getting geography column: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting geography column: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql, false ); result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Geography column query returned %1" ).arg( result.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Geography column query returned %1" ).arg( result.PQntuples() ), 2 );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
@ -4111,7 +4112,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
} }
@ -4131,7 +4132,7 @@ bool QgsPostgresProvider::getGeometryDetails()
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsDebugMsgLevel( QStringLiteral( "Getting TopoGeometry column: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting TopoGeometry column: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql, false ); result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "TopoGeometry column query returned %1" ).arg( result.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "TopoGeometry column query returned %1" ).arg( result.PQntuples() ), 2 );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
@ -4142,7 +4143,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
} }
@ -4155,7 +4156,7 @@ bool QgsPostgresProvider::getGeometryDetails()
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsDebugMsgLevel( QStringLiteral( "Getting pointcloud column: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting pointcloud column: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql, false ); result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Pointcloud column query returned %1" ).arg( result.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Pointcloud column query returned %1" ).arg( result.PQntuples() ), 2 );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
@ -4166,7 +4167,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
} }
@ -4181,7 +4182,7 @@ bool QgsPostgresProvider::getGeometryDetails()
quotedValue( geomCol ), quotedValue( geomCol ),
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsDebugMsgLevel( QStringLiteral( "Getting column datatype: %1" ).arg( sql ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Getting column datatype: %1" ).arg( sql ), 2 );
result = connectionRO()->PQexec( sql, false ); result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql );
QgsDebugMsgLevel( QStringLiteral( "Column datatype query returned %1" ).arg( result.PQntuples() ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Column datatype query returned %1" ).arg( result.PQntuples() ), 2 );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
{ {
@ -4197,19 +4198,19 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
} }
} }
else else
{ {
sql = QStringLiteral( "SELECT %1 FROM %2 LIMIT 0" ).arg( quotedIdentifier( mGeometryColumn ), mQuery ); sql = QStringLiteral( "SELECT %1 FROM %2 LIMIT 0" ).arg( quotedIdentifier( mGeometryColumn ), mQuery );
result = connectionRO()->PQexec( sql ); result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql );
if ( PGRES_TUPLES_OK == result.PQresultStatus() ) if ( PGRES_TUPLES_OK == result.PQresultStatus() )
{ {
sql = QStringLiteral( "SELECT (SELECT t.typname FROM pg_type t WHERE oid = %1), upper(postgis_typmod_type(%2)), postgis_typmod_srid(%2)" ) sql = QStringLiteral( "SELECT (SELECT t.typname FROM pg_type t WHERE oid = %1), upper(postgis_typmod_type(%2)), postgis_typmod_srid(%2)" )
.arg( QString::number( result.PQftype( 0 ) ), QString::number( result.PQfmod( 0 ) ) ); .arg( QString::number( result.PQftype( 0 ) ), QString::number( result.PQfmod( 0 ) ) );
result = connectionRO()->PQexec( sql, false ); result = connectionRO()->LoggedPQexecNoLogError( "QgsPostgresProvider", sql );
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )
{ {
geomColType = result.PQgetvalue( 0, 0 ); geomColType = result.PQgetvalue( 0, 0 );
@ -4231,7 +4232,7 @@ bool QgsPostgresProvider::getGeometryDetails()
} }
else else
{ {
connectionRO()->PQexecNR( QStringLiteral( "COMMIT" ) ); connectionRO()->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
detectedType = mRequestedGeomType == QgsWkbTypes::Unknown ? QString() : QgsPostgresConn::postgisWkbTypeName( mRequestedGeomType ); detectedType = mRequestedGeomType == QgsWkbTypes::Unknown ? QString() : QgsPostgresConn::postgisWkbTypeName( mRequestedGeomType );
} }
} }
@ -4587,13 +4588,13 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
try try
{ {
conn->PQexecNR( QStringLiteral( "BEGIN" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "BEGIN" ) );
// We want a valid schema name ... // We want a valid schema name ...
if ( schemaName.isEmpty() ) if ( schemaName.isEmpty() )
{ {
QString sql = QString( "SELECT current_schema" ); QString sql = QString( "SELECT current_schema" );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
schemaName = result.PQgetvalue( 0, 0 ); schemaName = result.PQgetvalue( 0, 0 );
@ -4610,7 +4611,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
.arg( quotedValue( tableName ), .arg( quotedValue( tableName ),
quotedValue( schemaName ) ); quotedValue( schemaName ) );
QgsPostgresResult result( conn->PQexec( sql ) ); QgsPostgresResult result( conn->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
@ -4626,7 +4627,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
.arg( quotedValue( schemaName ), .arg( quotedValue( schemaName ),
quotedValue( tableName ) ); quotedValue( tableName ) );
result = conn->PQexec( sql ); result = conn->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
} }
@ -4658,7 +4659,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
} }
sql += QStringLiteral( ", PRIMARY KEY (%1) )" ) .arg( pk ); sql += QStringLiteral( ", PRIMARY KEY (%1) )" ) .arg( pk );
result = conn->PQexec( sql ); result = conn->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result ); throw PGException( result );
@ -4679,7 +4680,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
.arg( quotedValue( geometryType ) ) .arg( quotedValue( geometryType ) )
.arg( dim ); .arg( dim );
result = conn->PQexec( sql ); result = conn->LoggedPQexec( "QgsPostgresProvider", sql );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
throw PGException( result ); throw PGException( result );
} }
@ -4688,7 +4689,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
geometryColumn.clear(); geometryColumn.clear();
} }
conn->PQexecNR( QStringLiteral( "COMMIT" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "COMMIT" ) );
} }
catch ( PGException &e ) catch ( PGException &e )
{ {
@ -4697,7 +4698,7 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
.arg( schemaTableName, .arg( schemaTableName,
e.errorMessage() ); e.errorMessage() );
conn->PQexecNR( QStringLiteral( "ROLLBACK" ) ); conn->LoggedPQexecNR( "QgsPostgresProvider", QStringLiteral( "ROLLBACK" ) );
conn->unref(); conn->unref();
return Qgis::VectorExportResult::ErrorCreatingLayer; return Qgis::VectorExportResult::ErrorCreatingLayer;
} }
@ -4843,7 +4844,7 @@ QString QgsPostgresProvider::description() const
QgsPostgresResult result; QgsPostgresResult result;
/* TODO: expose a cached QgsPostgresConn::version() ? */ /* TODO: expose a cached QgsPostgresConn::version() ? */
result = lConnectionRO->PQexec( QStringLiteral( "SELECT version()" ) ); result = lConnectionRO->LoggedPQexec( QStringLiteral( "QgsPostgresProvider" ), QStringLiteral( "SELECT version()" ) );
if ( result.PQresultStatus() == PGRES_TUPLES_OK ) if ( result.PQresultStatus() == PGRES_TUPLES_OK )
{ {
pgVersion = result.PQgetvalue( 0, 0 ); pgVersion = result.PQgetvalue( 0, 0 );
@ -4879,7 +4880,7 @@ QString QgsPostgresProvider::getNextString( const QString &txt, int &i, const QS
} }
i += match.captured( 1 ).length() + 2; i += match.captured( 1 ).length() + 2;
jumpSpace( txt, i ); jumpSpace( txt, i );
if ( !QStringView{txt}.mid( i ).startsWith( sep ) && i < txt.length() ) if ( !QStringView{txt} .mid( i ).startsWith( sep ) && i < txt.length() )
{ {
QgsMessageLog::logMessage( tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), tr( "PostGIS" ) ); QgsMessageLog::logMessage( tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), tr( "PostGIS" ) );
return QString(); return QString();
@ -4892,9 +4893,9 @@ QString QgsPostgresProvider::getNextString( const QString &txt, int &i, const QS
int start = i; int start = i;
for ( ; i < txt.length(); i++ ) for ( ; i < txt.length(); i++ )
{ {
if ( QStringView{txt}.mid( i ).startsWith( sep ) ) if ( QStringView{txt} .mid( i ).startsWith( sep ) )
{ {
QStringView v( QStringView{txt}.mid( start, i - start ) ); QStringView v( QStringView{txt} .mid( start, i - start ) );
i += sep.length(); i += sep.length();
return v.trimmed().toString(); return v.trimmed().toString();
} }
@ -5130,7 +5131,7 @@ QList<QgsRelation> QgsPostgresProvider::discoverRelations( const QgsVectorLayer
" fk.conname ;" " fk.conname ;"
); );
QgsPostgresResult sqlResult( connectionRO()->PQexec( sql ) ); QgsPostgresResult sqlResult( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
if ( sqlResult.PQresultStatus() != PGRES_TUPLES_OK ) if ( sqlResult.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsLogger::warning( "Error getting the foreign keys of " + mTableName ); QgsLogger::warning( "Error getting the foreign keys of " + mTableName );
@ -5224,7 +5225,7 @@ QgsPostgresProvider::Relkind QgsPostgresProvider::relkind() const
else else
{ {
QString sql = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) ); QString sql = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
QgsPostgresResult res( connectionRO()->PQexec( sql ) ); QgsPostgresResult res( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ) );
QString type = res.PQgetvalue( 0, 0 ); QString type = res.PQgetvalue( 0, 0 );
mKind = Relkind::Unknown; mKind = Relkind::Unknown;
@ -5356,7 +5357,7 @@ bool QgsPostgresProviderMetadata::styleExists( const QString &uri, const QString
.arg( wkbTypeString ) .arg( wkbTypeString )
.arg( QgsPostgresConn::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) ); .arg( QgsPostgresConn::quotedValue( styleId.isEmpty() ? dsUri.table() : styleId ) );
QgsPostgresResult res( conn->PQexec( checkQuery ) ); QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), checkQuery ) );
if ( res.PQresultStatus() == PGRES_TUPLES_OK ) if ( res.PQresultStatus() == PGRES_TUPLES_OK )
{ {
return res.PQntuples() > 0; return res.PQntuples() > 0;
@ -5389,22 +5390,23 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString &
if ( !tableExists( *conn, QStringLiteral( "layer_styles" ) ) ) if ( !tableExists( *conn, QStringLiteral( "layer_styles" ) ) )
{ {
QgsPostgresResult res( conn->PQexec( "CREATE TABLE layer_styles(" QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ),
"id SERIAL PRIMARY KEY" "CREATE TABLE layer_styles("
",f_table_catalog varchar" "id SERIAL PRIMARY KEY"
",f_table_schema varchar" ",f_table_catalog varchar"
",f_table_name varchar" ",f_table_schema varchar"
",f_geometry_column varchar" ",f_table_name varchar"
",styleName text" ",f_geometry_column varchar"
",styleQML xml" ",styleName text"
",styleSLD xml" ",styleQML xml"
",useAsDefault boolean" ",styleSLD xml"
",description text" ",useAsDefault boolean"
",owner varchar(63) DEFAULT CURRENT_USER" ",description text"
",ui xml" ",owner varchar(63) DEFAULT CURRENT_USER"
",update_time timestamp DEFAULT CURRENT_TIMESTAMP" ",ui xml"
",type varchar" ",update_time timestamp DEFAULT CURRENT_TIMESTAMP"
")" ) ); ",type varchar"
")" ) );
if ( res.PQresultStatus() != PGRES_COMMAND_OK ) if ( res.PQresultStatus() != PGRES_COMMAND_OK )
{ {
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() ); errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() );
@ -5416,7 +5418,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString &
{ {
if ( !columnExists( *conn, QStringLiteral( "layer_styles" ), QStringLiteral( "type" ) ) ) if ( !columnExists( *conn, QStringLiteral( "layer_styles" ), QStringLiteral( "type" ) ) )
{ {
QgsPostgresResult res( conn->PQexec( "ALTER TABLE layer_styles ADD COLUMN type varchar NULL" ) ); QgsPostgresResult res( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), "ALTER TABLE layer_styles ADD COLUMN type varchar NULL" ) );
if ( res.PQresultStatus() != PGRES_COMMAND_OK ) if ( res.PQresultStatus() != PGRES_COMMAND_OK )
{ {
errCause = QObject::tr( "Unable to add column type to layer_styles table. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() ); errCause = QObject::tr( "Unable to add column type to layer_styles table. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() );
@ -5481,7 +5483,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString &
.arg( wkbTypeString ) .arg( wkbTypeString )
.arg( QgsPostgresConn::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) ); .arg( QgsPostgresConn::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) );
QgsPostgresResult res( conn->PQexec( checkQuery ) ); QgsPostgresResult res( conn->LoggedPQexec( "QgsPostgresProviderMetadata", checkQuery ) );
if ( res.PQntuples() > 0 ) if ( res.PQntuples() > 0 )
{ {
sql = QString( "UPDATE layer_styles" sql = QString( "UPDATE layer_styles"
@ -5529,7 +5531,7 @@ bool QgsPostgresProviderMetadata::saveStyle( const QString &uri, const QString &
sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql ); sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql );
} }
res = conn->PQexec( sql ); res = conn->LoggedPQexec( "QgsPostgresProviderMetadata", sql );
bool saved = res.PQresultStatus() == PGRES_COMMAND_OK; bool saved = res.PQresultStatus() == PGRES_COMMAND_OK;
if ( !saved ) if ( !saved )
@ -5610,7 +5612,7 @@ QString QgsPostgresProviderMetadata::loadStyle( const QString &uri, QString &err
.arg( wkbTypeString ); .arg( wkbTypeString );
} }
QgsPostgresResult result( conn->PQexec( selectQmlQuery ) ); QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectQmlQuery ) );
QString style = result.PQntuples() == 1 ? result.PQgetvalue( 0, 0 ) : QString(); QString style = result.PQntuples() == 1 ? result.PQgetvalue( 0, 0 ) : QString();
conn->unref(); conn->unref();
@ -5660,7 +5662,7 @@ int QgsPostgresProviderMetadata::listStyles( const QString &uri, QStringList &id
QString( "f_geometry_column=%1" ).arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) ) ) QString( "f_geometry_column=%1" ).arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) ) )
.arg( wkbTypeString ); .arg( wkbTypeString );
QgsPostgresResult result( conn->PQexec( selectRelatedQuery ) ); QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectRelatedQuery ) );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectRelatedQuery ) ); QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectRelatedQuery ) );
@ -5687,7 +5689,7 @@ int QgsPostgresProviderMetadata::listStyles( const QString &uri, QStringList &id
.arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) ) .arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) )
.arg( wkbTypeString ); .arg( wkbTypeString );
result = conn->PQexec( selectOthersQuery ); result = conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectOthersQuery );
if ( result.PQresultStatus() != PGRES_TUPLES_OK ) if ( result.PQresultStatus() != PGRES_TUPLES_OK )
{ {
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectOthersQuery ) ); QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectOthersQuery ) );
@ -5723,7 +5725,7 @@ bool QgsPostgresProviderMetadata::deleteStyleById( const QString &uri, const QSt
{ {
QString deleteStyleQuery = QStringLiteral( "DELETE FROM layer_styles WHERE id=%1" ).arg( QString deleteStyleQuery = QStringLiteral( "DELETE FROM layer_styles WHERE id=%1" ).arg(
QgsPostgresConn::quotedValue( styleId ) ); QgsPostgresConn::quotedValue( styleId ) );
QgsPostgresResult result( conn->PQexec( deleteStyleQuery ) ); QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), deleteStyleQuery ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK ) if ( result.PQresultStatus() != PGRES_COMMAND_OK )
{ {
QgsDebugMsg( QgsDebugMsg(
@ -5755,7 +5757,7 @@ QString QgsPostgresProviderMetadata::getStyleById( const QString &uri, const QSt
QString style; QString style;
QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsPostgresConn::quotedValue( styleId ) ); QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsPostgresConn::quotedValue( styleId ) );
QgsPostgresResult result( conn->PQexec( selectQmlQuery ) ); QgsPostgresResult result( conn->LoggedPQexec( QStringLiteral( "QgsPostgresProviderMetadata" ), selectQmlQuery ) );
if ( result.PQresultStatus() == PGRES_TUPLES_OK ) if ( result.PQresultStatus() == PGRES_TUPLES_OK )
{ {
if ( result.PQntuples() == 1 ) if ( result.PQntuples() == 1 )

View File

@ -257,7 +257,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection
} }
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::unique_ptr<QgsPostgresResult> res = std::make_unique<QgsPostgresResult>( conn->PQexec( sql ) ); std::unique_ptr<QgsPostgresResult> res = std::make_unique<QgsPostgresResult>( conn->LoggedPQexec( "QgsPostgresProviderConnection", sql ) );
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
results.setQueryExecutionTime( std::chrono::duration_cast<std::chrono::milliseconds>( end - begin ).count() ); results.setQueryExecutionTime( std::chrono::duration_cast<std::chrono::milliseconds>( end - begin ).count() );

View File

@ -72,7 +72,7 @@ bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg,
} }
QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) ); QgsDebugMsg( QStringLiteral( "Transaction sql: %1" ).arg( sql ) );
QgsPostgresResult r( mConn->PQexec( sql, true ) ); QgsPostgresResult r( mConn->LoggedPQexec( "QgsPostgresTransaction", sql ) );
if ( r.PQresultStatus() == PGRES_BAD_RESPONSE || if ( r.PQresultStatus() == PGRES_BAD_RESPONSE ||
r.PQresultStatus() == PGRES_FATAL_ERROR ) r.PQresultStatus() == PGRES_FATAL_ERROR )
{ {

View File

@ -235,6 +235,10 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
sqliteStatement = nullptr; sqliteStatement = nullptr;
close(); close();
} }
else
{
mQueryLogWrapper = std::make_unique<QgsDatabaseQueryLogWrapper>( mLastSql, mSource->mSqlitePath, QStringLiteral( "spatialite" ), QStringLiteral( "QgsSpatiaLiteFeatureIterator" ), QGS_QUERY_LOG_ORIGIN );
}
} }
} }
@ -395,6 +399,7 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString &whereClause,
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), QObject::tr( "SpatiaLite" ) ); QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), QObject::tr( "SpatiaLite" ) );
return false; return false;
} }
mLastSql = sql;
} }
catch ( QgsSpatiaLiteProvider::SLFieldNotFound ) catch ( QgsSpatiaLiteProvider::SLFieldNotFound )
{ {

View File

@ -18,6 +18,7 @@
#include "qgsfeatureiterator.h" #include "qgsfeatureiterator.h"
#include "qgsfields.h" #include "qgsfields.h"
#include "qgscoordinatetransform.h" #include "qgscoordinatetransform.h"
#include "qgsdbquerylog.h"
extern "C" extern "C"
{ {
@ -113,6 +114,11 @@ class QgsSpatiaLiteFeatureIterator final: public QgsAbstractFeatureIteratorFromS
QgsCoordinateTransform mTransform; QgsCoordinateTransform mTransform;
QgsGeometry mDistanceWithinGeom; QgsGeometry mDistanceWithinGeom;
std::unique_ptr< QgsGeometryEngine > mDistanceWithinEngine; std::unique_ptr< QgsGeometryEngine > mDistanceWithinEngine;
// Last prepared sql statement for logging purposes
QString mLastSql;
std::unique_ptr<QgsDatabaseQueryLogWrapper> mQueryLogWrapper;
}; };
#endif // QGSSPATIALITEFEATUREITERATOR_H #endif // QGSSPATIALITEFEATUREITERATOR_H

View File

@ -33,6 +33,7 @@ email : a.furieri@lqt.it
#include "qgsspatialiteconnection.h" #include "qgsspatialiteconnection.h"
#include "qgsspatialitetransaction.h" #include "qgsspatialitetransaction.h"
#include "qgsspatialiteproviderconnection.h" #include "qgsspatialiteproviderconnection.h"
#include "qgsdbquerylog.h"
#include "qgsjsonutils.h" #include "qgsjsonutils.h"
#include "qgsvectorlayer.h" #include "qgsvectorlayer.h"
@ -225,7 +226,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
try try
{ {
int ret = sqlite3_exec( sqliteHandle, "BEGIN", nullptr, nullptr, &errMsg ); int ret = exec_sql( sqliteHandle, "BEGIN", uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
@ -237,14 +238,14 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
sql = QStringLiteral( "DROP TABLE IF EXISTS %1" ) sql = QStringLiteral( "DROP TABLE IF EXISTS %1" )
.arg( QgsSqliteUtils::quotedIdentifier( tableName ) ); .arg( QgsSqliteUtils::quotedIdentifier( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
sql = QStringLiteral( "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%1)" ) sql = QStringLiteral( "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%1)" )
.arg( QgsSqliteUtils::quotedString( tableName ) ); .arg( QgsSqliteUtils::quotedString( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
} }
@ -255,7 +256,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
primaryKeyType, primaryKeyType,
primaryKeyType == QLatin1String( "INTEGER" ) ? QStringLiteral( " AUTOINCREMENT" ) : QString() ); primaryKeyType == QLatin1String( "INTEGER" ) ? QStringLiteral( " AUTOINCREMENT" ) : QString() );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
@ -333,7 +334,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
.arg( QgsSqliteUtils::quotedString( geometryType ) ) .arg( QgsSqliteUtils::quotedString( geometryType ) )
.arg( dim ); .arg( dim );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
} }
@ -342,7 +343,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
geometryColumn = QString(); geometryColumn = QString();
} }
ret = sqlite3_exec( sqliteHandle, "COMMIT", nullptr, nullptr, &errMsg ); ret = exec_sql( sqliteHandle, "COMMIT", uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
throw SLException( errMsg ); throw SLException( errMsg );
@ -363,7 +364,7 @@ Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString
if ( toCommit ) if ( toCommit )
{ {
// ROLLBACK after some previous error // ROLLBACK after some previous error
sqlite3_exec( sqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr ); exec_sql( sqliteHandle, "ROLLBACK", uri, nullptr, QGS_QUERY_LOG_ORIGIN );
} }
QgsSqliteHandle::closeDb( handle ); QgsSqliteHandle::closeDb( handle );
@ -486,7 +487,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri, const Provider
for ( const auto &pragma : pragmaList ) for ( const auto &pragma : pragmaList )
{ {
char *errMsg = nullptr; char *errMsg = nullptr;
int ret = exec_sql( QStringLiteral( "PRAGMA %1" ).arg( pragma ), errMsg ); int ret = exec_sql( mSqliteHandle, QStringLiteral( "PRAGMA %1" ).arg( pragma ), uri, errMsg );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
QgsDebugMsg( QStringLiteral( "PRAGMA " ) + pragma + QString( " failed : %1" ).arg( errMsg ? errMsg : "" ) ); QgsDebugMsg( QStringLiteral( "PRAGMA " ) + pragma + QString( " failed : %1" ).arg( errMsg ? errMsg : "" ) );
@ -1143,14 +1144,15 @@ void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage,
if ( ! savepointId.isEmpty() ) if ( ! savepointId.isEmpty() )
{ {
// ROLLBACK after some previous error // ROLLBACK after some previous error
( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO \"%1\"" ).arg( savepointId ) ); ( void )exec_sql( sqliteHandle(), QStringLiteral( "ROLLBACK TRANSACTION TO \"%1\"" ).arg( savepointId ), uri().uri(), nullptr, QGS_QUERY_LOG_ORIGIN );
} }
} }
int QgsSpatiaLiteProvider::exec_sql( const QString &sql, char *errMsg ) int QgsSpatiaLiteProvider::exec_sql( sqlite3 *handle, const QString &sql, const QString &uri, char *errMsg, const QString &origin )
{ {
QgsDatabaseQueryLogWrapper logWrapper( sql, uri, QStringLiteral( "spatialite" ), QStringLiteral( "QgsSpatiaLiteProvider" ), origin );
// Use transaction's handle (if any) // Use transaction's handle (if any)
return sqlite3_exec( sqliteHandle(), sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); return sqlite3_exec( handle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
} }
sqlite3 *QgsSpatiaLiteProvider::sqliteHandle() const sqlite3 *QgsSpatiaLiteProvider::sqliteHandle() const
@ -1391,7 +1393,7 @@ bool QgsSpatiaLiteProvider::hasRowid()
// table without rowid column // table without rowid column
QString sql = QStringLiteral( "SELECT rowid FROM %1 WHERE 0" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ); QString sql = QStringLiteral( "SELECT rowid FROM %1 WHERE 0" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
char *errMsg = nullptr; char *errMsg = nullptr;
return exec_sql( sql, errMsg ) == SQLITE_OK; return exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN ) == SQLITE_OK;
} }
@ -4154,7 +4156,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret == SQLITE_OK ) if ( ret == SQLITE_OK )
{ {
toCommit = true; toCommit = true;
@ -4354,7 +4356,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{ {
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
} }
} // BEGIN statement } // BEGIN statement
@ -4370,7 +4372,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
if ( toCommit ) if ( toCommit )
{ {
// ROLLBACK after some previous error // ROLLBACK after some previous error
( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO SAVEPOINT \"%1\"" ).arg( savepointId ) ); ( void )exec_sql( sqliteHandle(), QStringLiteral( "ROLLBACK TRANSACTION TO SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), nullptr, QGS_QUERY_LOG_ORIGIN );
} }
} }
else else
@ -4402,7 +4404,7 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field )
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4415,14 +4417,14 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field )
.arg( createIndexName( mTableName, fieldName ), .arg( createIndexName( mTableName, fieldName ),
mTableName, mTableName,
QgsSqliteUtils::quotedIdentifier( fieldName ) ); QgsSqliteUtils::quotedIdentifier( fieldName ) );
ret = exec_sql( sql, errMsg ); ret = exec_sql( sqliteHandle(), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
return false; return false;
} }
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4457,7 +4459,7 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4506,7 +4508,7 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
sqlite3_finalize( stmt ); sqlite3_finalize( stmt );
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4526,7 +4528,7 @@ bool QgsSpatiaLiteProvider::truncate()
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4534,14 +4536,14 @@ bool QgsSpatiaLiteProvider::truncate()
} }
sql = QStringLiteral( "DELETE FROM %1" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ); sql = QStringLiteral( "DELETE FROM %1" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
ret = exec_sql( sql, errMsg ); ret = exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
return false; return false;
} }
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4564,7 +4566,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4577,7 +4579,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
.arg( mTableName, .arg( mTableName,
iter->name(), iter->name(),
iter->typeName() ); iter->typeName() );
ret = exec_sql( sql, errMsg ); ret = exec_sql( sqliteHandle( ), sql, uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4585,7 +4587,7 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
} }
} }
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle( ), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4614,7 +4616,7 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle( ), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4781,7 +4783,7 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap
} }
} }
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -4802,7 +4804,7 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) }; const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); int ret = exec_sql( sqliteHandle(), QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, QString() ); handleError( sql, errMsg, QString() );
@ -4860,7 +4862,7 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
sqlite3_finalize( stmt ); sqlite3_finalize( stmt );
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg ); ret = exec_sql( sqliteHandle(), QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), uri().uri(), errMsg, QGS_QUERY_LOG_ORIGIN );
if ( ret != SQLITE_OK ) if ( ret != SQLITE_OK )
{ {
handleError( sql, errMsg, savepointId ); handleError( sql, errMsg, savepointId );
@ -6177,7 +6179,7 @@ bool QgsSpatiaLiteProviderMetadata::saveStyle( const QString &uri, const QString
",ui text" ",ui text"
",update_time timestamp DEFAULT CURRENT_TIMESTAMP" ",update_time timestamp DEFAULT CURRENT_TIMESTAMP"
")" ); ")" );
ret = sqlite3_exec( sqliteHandle, createQuery.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = QgsSpatiaLiteProvider::exec_sql( sqliteHandle, createQuery.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( SQLITE_OK != ret ) if ( SQLITE_OK != ret )
{ {
QgsSqliteHandle::closeDb( handle ); QgsSqliteHandle::closeDb( handle );
@ -6269,7 +6271,7 @@ bool QgsSpatiaLiteProviderMetadata::saveStyle( const QString &uri, const QString
sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql ); sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql );
} }
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ); ret = QgsSpatiaLiteProvider::exec_sql( sqliteHandle, sql.toUtf8().constData(), uri, errMsg, QGS_QUERY_LOG_ORIGIN );
if ( SQLITE_OK != ret ) if ( SQLITE_OK != ret )
{ {
QgsSqliteHandle::closeDb( handle ); QgsSqliteHandle::closeDb( handle );

View File

@ -193,6 +193,11 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
*/ */
QgsSqliteHandle *mHandle = nullptr; QgsSqliteHandle *mHandle = nullptr;
/**
* Sqlite exec sql wrapper for SQL logging
*/
static int exec_sql( sqlite3 *handle, const QString &sql, const QString &uri, char *errMsg = nullptr, const QString &origin = QString() );
private: private:
//! Loads fields from input file to member mAttributeFields //! Loads fields from input file to member mAttributeFields
@ -397,11 +402,6 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
*/ */
void handleError( const QString &sql, char *errorMessage, const QString &savepointId ); void handleError( const QString &sql, char *errorMessage, const QString &savepointId );
/**
* Sqlite exec sql wrapper for SQL logging
*/
int exec_sql( const QString &sql, char *errMsg = nullptr );
/** /**
* Returns the sqlite handle to be used, if we are inside a transaction it will be the transaction's handle * Returns the sqlite handle to be used, if we are inside a transaction it will be the transaction's handle
*/ */

View File

@ -23,6 +23,7 @@
#include "qgsapplication.h" #include "qgsapplication.h"
#include "qgsvectorlayer.h" #include "qgsvectorlayer.h"
#include "qgsfeedback.h" #include "qgsfeedback.h"
#include "qgsdbquerylog.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QTextCodec> #include <QTextCodec>
@ -466,8 +467,11 @@ void QgsSpatiaLiteProviderConnection::setDefaultCapabilities()
QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QString &sql, QgsFeedback *feedback ) const QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QString &sql, QgsFeedback *feedback ) const
{ {
QgsDatabaseQueryLogWrapper logWrapper( sql, uri(), providerKey(), QStringLiteral( "QgsSpatiaLiteProviderConnection" ), QGS_QUERY_LOG_ORIGIN );
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{ {
logWrapper.setCanceled();
return QgsAbstractDatabaseProviderConnection::QueryResult(); return QgsAbstractDatabaseProviderConnection::QueryResult();
} }
@ -478,6 +482,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{ {
logWrapper.setCanceled();
return QgsAbstractDatabaseProviderConnection::QueryResult(); return QgsAbstractDatabaseProviderConnection::QueryResult();
} }
@ -540,6 +545,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti
if ( ! errCause.isEmpty() ) if ( ! errCause.isEmpty() )
{ {
logWrapper.setError( errCause );
throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL statement %1: %2" ).arg( sql, errCause ) );
} }
@ -562,6 +568,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti
if ( !errCause.isEmpty() ) if ( !errCause.isEmpty() )
{ {
logWrapper.setError( errCause );
throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) ); throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql, errCause ) );
} }

View 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>