mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #5094 from pblottiere/trust2
[FEATURE] Trust project option
This commit is contained in:
commit
20d824413e
@ -785,6 +785,31 @@ Returns the number of registered layers.
|
||||
:rtype: QgsCoordinateReferenceSystem
|
||||
%End
|
||||
|
||||
void setTrustLayerMetadata( bool trust );
|
||||
%Docstring
|
||||
Sets the trust option allowing to indicate if the extent has to be
|
||||
read from the XML document when data source has no metadata or if the
|
||||
data provider has to determine it. Moreover, when this option is
|
||||
activated, primary key unicity is not checked for views and
|
||||
materialized views with Postgres provider.
|
||||
|
||||
\param trust True to trust the project, false otherwise
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
bool trustLayerMetadata() const;
|
||||
%Docstring
|
||||
Returns true if the trust option is activated, false otherwise. This
|
||||
option allows indicateing if the extent has to be read from the XML
|
||||
document when data source has no metadata or if the data provider has
|
||||
to determine it. Moreover, when this option is activated, primary key
|
||||
unicity is not checked for views and materialized views with Postgres
|
||||
provider.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
signals:
|
||||
void readProject( const QDomDocument & );
|
||||
|
@ -524,6 +524,15 @@ Returns a list of available encodings
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
virtual bool hasMetadata() const;
|
||||
%Docstring
|
||||
Returns true if the data source has metadata, false otherwise.
|
||||
|
||||
:return: true if data source has metadata, false otherwise.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
:rtype: bool
|
||||
%End
|
||||
signals:
|
||||
|
||||
void raiseError( const QString &msg ) const;
|
||||
|
@ -315,7 +315,8 @@ class QgsVectorLayer : QgsMapLayer, QgsExpressionContextGenerator, QgsFeatureSin
|
||||
};
|
||||
|
||||
QgsVectorLayer( const QString &path = QString(), const QString &baseName = QString(),
|
||||
const QString &providerLib = "ogr", bool loadDefaultStyleFlag = true );
|
||||
const QString &providerLib = "ogr", bool loadDefaultStyleFlag = true,
|
||||
bool readExtentFromXml = false );
|
||||
%Docstring
|
||||
Constructor - creates a vector layer
|
||||
|
||||
@ -328,6 +329,7 @@ class QgsVectorLayer : QgsMapLayer, QgsExpressionContextGenerator, QgsFeatureSin
|
||||
\param baseName The name used to represent the layer in the legend
|
||||
\param providerLib The name of the data provider, e.g., "memory", "postgres"
|
||||
\param loadDefaultStyleFlag whether to load the default style
|
||||
\param readExtentFromXml Read extent from XML if true or let provider determine it if false
|
||||
%End
|
||||
|
||||
|
||||
@ -1727,6 +1729,25 @@ Returns the current blending mode for features
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
void setReadExtentFromXml( bool readExtentFromXml );
|
||||
%Docstring
|
||||
Flag allowing to indicate if the extent has to be read from the XML
|
||||
document when data source has no metadata or if the data provider has
|
||||
to determine it.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
bool readExtentFromXml() const;
|
||||
%Docstring
|
||||
Returns true if the extent is read from the XML document when data
|
||||
source has no metadata, false if it's the data provider which determines
|
||||
it.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
public slots:
|
||||
|
||||
void select( QgsFeatureId featureId );
|
||||
@ -1772,10 +1793,12 @@ Returns the current blending mode for features
|
||||
.. seealso:: selectByIds()
|
||||
%End
|
||||
|
||||
virtual void updateExtents();
|
||||
virtual void updateExtents( bool force = false );
|
||||
%Docstring
|
||||
Update the extents for the layer. This is necessary if features are
|
||||
added/deleted or the layer has been subsetted.
|
||||
|
||||
\param force true to update layer extent even if it's read from xml by default, false otherwise
|
||||
%End
|
||||
|
||||
bool startEditing();
|
||||
|
@ -708,6 +708,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
|
||||
|
||||
mAutoTransaction->setChecked( QgsProject::instance()->autoTransaction() );
|
||||
mEvaluateDefaultValues->setChecked( QgsProject::instance()->evaluateDefaultValues() );
|
||||
mTrustProjectCheckBox->setChecked( QgsProject::instance()->trustLayerMetadata() );
|
||||
|
||||
// Variables editor
|
||||
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
|
||||
@ -764,6 +765,7 @@ void QgsProjectProperties::apply()
|
||||
QgsProject::instance()->setTitle( title() );
|
||||
QgsProject::instance()->setAutoTransaction( mAutoTransaction->isChecked() );
|
||||
QgsProject::instance()->setEvaluateDefaultValues( mEvaluateDefaultValues->isChecked() );
|
||||
QgsProject::instance()->setTrustLayerMetadata( mTrustProjectCheckBox->isChecked() );
|
||||
|
||||
// set the mouse display precision method and the
|
||||
// number of decimal places for the manual option
|
||||
|
@ -1387,7 +1387,7 @@ void QgsVectorLayerProperties::setPbnQueryBuilderEnabled()
|
||||
|
||||
void QgsVectorLayerProperties::on_pbnUpdateExtents_clicked()
|
||||
{
|
||||
mLayer->updateExtents();
|
||||
mLayer->updateExtents( true ); // force update whatever options activated
|
||||
mMetadataFilled = false;
|
||||
}
|
||||
|
||||
|
@ -474,12 +474,6 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, const QgsReadWr
|
||||
setAutoRefreshInterval( layerElement.attribute( QStringLiteral( "autoRefreshTime" ), 0 ).toInt() );
|
||||
setAutoRefreshEnabled( layerElement.attribute( QStringLiteral( "autoRefreshEnabled" ), QStringLiteral( "0" ) ).toInt() );
|
||||
|
||||
QDomNode extentNode = layerElement.namedItem( QStringLiteral( "extent" ) );
|
||||
if ( !extentNode.isNull() )
|
||||
{
|
||||
setExtent( QgsXmlUtils::readRectangle( extentNode.toElement() ) );
|
||||
}
|
||||
|
||||
// set name
|
||||
mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
|
||||
mne = mnl.toElement();
|
||||
@ -575,7 +569,7 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume
|
||||
layerElement.setAttribute( QStringLiteral( "maxScale" ), QString::number( maximumScale() ) );
|
||||
layerElement.setAttribute( QStringLiteral( "minScale" ), QString::number( minimumScale() ) );
|
||||
|
||||
if ( !mExtent.isNull() )
|
||||
if ( !extent().isNull() )
|
||||
{
|
||||
layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent, document ) );
|
||||
}
|
||||
|
@ -480,6 +480,7 @@ void QgsProject::clear()
|
||||
mAutoTransaction = false;
|
||||
mEvaluateDefaultValues = false;
|
||||
mDirty = false;
|
||||
mTrustLayerMetadata = false;
|
||||
mCustomVariables.clear();
|
||||
|
||||
mEmbeddedLayers.clear();
|
||||
@ -717,6 +718,12 @@ bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &broken
|
||||
if ( type == QLatin1String( "vector" ) )
|
||||
{
|
||||
mapLayer = new QgsVectorLayer;
|
||||
|
||||
// apply specific settings to vector layer
|
||||
if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer ) )
|
||||
{
|
||||
vl->setReadExtentFromXml( mTrustLayerMetadata );
|
||||
}
|
||||
}
|
||||
else if ( type == QLatin1String( "raster" ) )
|
||||
{
|
||||
@ -892,6 +899,14 @@ bool QgsProject::readProjectFile( const QString &filename )
|
||||
mEvaluateDefaultValues = true;
|
||||
}
|
||||
|
||||
nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
|
||||
if ( nl.count() )
|
||||
{
|
||||
QDomElement trustElement = nl.at( 0 ).toElement();
|
||||
if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
|
||||
mTrustLayerMetadata = true;
|
||||
}
|
||||
|
||||
// read the layer tree from project file
|
||||
|
||||
mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
|
||||
@ -1296,6 +1311,10 @@ bool QgsProject::writeProjectFile( const QString &filename )
|
||||
evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? "1" : "0" );
|
||||
qgisNode.appendChild( evaluateDefaultValuesNode );
|
||||
|
||||
QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
|
||||
trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? "1" : "0" );
|
||||
qgisNode.appendChild( trustNode );
|
||||
|
||||
QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
|
||||
titleNode.appendChild( titleText );
|
||||
|
||||
@ -2260,3 +2279,17 @@ QgsCoordinateReferenceSystem QgsProject::defaultCrsForNewLayers() const
|
||||
|
||||
return defaultCrs;
|
||||
}
|
||||
|
||||
void QgsProject::setTrustLayerMetadata( bool trust )
|
||||
{
|
||||
mTrustLayerMetadata = trust;
|
||||
|
||||
Q_FOREACH ( QgsMapLayer *layer, mapLayers().values() )
|
||||
{
|
||||
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
|
||||
if ( vl )
|
||||
{
|
||||
vl->setReadExtentFromXml( trust );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -755,6 +755,30 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
*/
|
||||
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const;
|
||||
|
||||
/**
|
||||
* Sets the trust option allowing to indicate if the extent has to be
|
||||
* read from the XML document when data source has no metadata or if the
|
||||
* data provider has to determine it. Moreover, when this option is
|
||||
* activated, primary key unicity is not checked for views and
|
||||
* materialized views with Postgres provider.
|
||||
*
|
||||
* \param trust True to trust the project, false otherwise
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
void setTrustLayerMetadata( bool trust );
|
||||
|
||||
/**
|
||||
* Returns true if the trust option is activated, false otherwise. This
|
||||
* option allows indicateing if the extent has to be read from the XML
|
||||
* document when data source has no metadata or if the data provider has
|
||||
* to determine it. Moreover, when this option is activated, primary key
|
||||
* unicity is not checked for views and materialized views with Postgres
|
||||
* provider.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
bool trustLayerMetadata() const { return mTrustLayerMetadata; }
|
||||
|
||||
signals:
|
||||
//! emitted when project is being read
|
||||
@ -1083,6 +1107,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
bool mEvaluateDefaultValues; // evaluate default values immediately
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
bool mDirty; // project has been modified since it has been read or saved
|
||||
bool mTrustLayerMetadata = false;
|
||||
};
|
||||
|
||||
/** Return the version string found in the given DOM document
|
||||
|
@ -516,6 +516,14 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
|
||||
*/
|
||||
virtual QString translateMetadataValue( const QString &mdKey, const QVariant &value ) const { Q_UNUSED( mdKey ); return value.toString(); }
|
||||
|
||||
/** Returns true if the data source has metadata, false otherwise.
|
||||
*
|
||||
* \returns true if data source has metadata, false otherwise.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
virtual bool hasMetadata() const { return true; };
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,8 @@ typedef bool deleteStyleById_t(
|
||||
QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
|
||||
const QString &baseName,
|
||||
const QString &providerKey,
|
||||
bool loadDefaultStyleFlag )
|
||||
bool loadDefaultStyleFlag,
|
||||
bool readExtentFromXml )
|
||||
: QgsMapLayer( VectorLayer, baseName, vectorLayerPath )
|
||||
, mDataProvider( nullptr )
|
||||
, mProviderKey( providerKey )
|
||||
@ -153,6 +154,7 @@ QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
|
||||
, mLazyExtent( true )
|
||||
, mSymbolFeatureCounted( false )
|
||||
, mEditCommandActive( false )
|
||||
, mReadExtentFromXml( readExtentFromXml )
|
||||
|
||||
{
|
||||
mActions = new QgsActionManager( this );
|
||||
@ -222,6 +224,7 @@ QgsVectorLayer *QgsVectorLayer::clone() const
|
||||
layer->setAttributeTableConfig( attributeTableConfig() );
|
||||
layer->setFeatureBlendMode( featureBlendMode() );
|
||||
layer->setOpacity( opacity() );
|
||||
layer->setReadExtentFromXml( readExtentFromXml() );
|
||||
|
||||
Q_FOREACH ( const QgsAction &action, actions()->actions() )
|
||||
{
|
||||
@ -779,9 +782,11 @@ bool QgsVectorLayer::countSymbolFeatures()
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsVectorLayer::updateExtents()
|
||||
void QgsVectorLayer::updateExtents( bool force )
|
||||
{
|
||||
mValidExtent = false;
|
||||
// do not update extent by default when trust project option is activated
|
||||
if ( force || !mReadExtentFromXml || ( mReadExtentFromXml && mXmlExtent.isNull() ) )
|
||||
mValidExtent = false;
|
||||
}
|
||||
|
||||
void QgsVectorLayer::setExtent( const QgsRectangle &r )
|
||||
@ -798,6 +803,14 @@ QgsRectangle QgsVectorLayer::extent() const
|
||||
if ( !isSpatial() )
|
||||
return rect;
|
||||
|
||||
|
||||
if ( !mValidExtent && mLazyExtent && mDataProvider && !mDataProvider->hasMetadata() && mReadExtentFromXml && !mXmlExtent.isNull() )
|
||||
{
|
||||
mExtent = mXmlExtent;
|
||||
mValidExtent = true;
|
||||
mLazyExtent = false;
|
||||
}
|
||||
|
||||
if ( !mValidExtent && mLazyExtent && mDataProvider )
|
||||
{
|
||||
// get the extent
|
||||
@ -1428,6 +1441,16 @@ bool QgsVectorLayer::readXml( const QDomNode &layer_node, const QgsReadWriteCont
|
||||
|
||||
setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) );
|
||||
|
||||
// read extent
|
||||
if ( mReadExtentFromXml )
|
||||
{
|
||||
QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
|
||||
if ( !extentNode.isNull() )
|
||||
{
|
||||
mXmlExtent = QgsXmlUtils::readRectangle( extentNode.toElement() );
|
||||
}
|
||||
}
|
||||
|
||||
return mValid; // should be true if read successfully
|
||||
|
||||
} // void QgsVectorLayer::readXml
|
||||
@ -1476,12 +1499,28 @@ void QgsVectorLayer::setDataSource( const QString &dataSource, const QString &ba
|
||||
bool QgsVectorLayer::setDataProvider( QString const &provider )
|
||||
{
|
||||
mProviderKey = provider; // XXX is this necessary? Usually already set
|
||||
|
||||
// primary key unicity is tested at construction time, so it has to be set
|
||||
// before initializing postgres provider
|
||||
QString checkUnicityKey = QStringLiteral( "checkPrimaryKeyUnicity" );
|
||||
QString dataSource = mDataSource;
|
||||
if ( provider.compare( QLatin1String( "postgres" ) ) == 0 )
|
||||
{
|
||||
QgsDataSourceUri uri( dataSource );
|
||||
|
||||
if ( uri.hasParam( checkUnicityKey ) )
|
||||
uri.removeParam( checkUnicityKey );
|
||||
|
||||
uri.setParam( checkUnicityKey, mReadExtentFromXml ? "0" : "1" );
|
||||
dataSource = uri.uri( false );
|
||||
}
|
||||
|
||||
// XXX when execution gets here.
|
||||
|
||||
//XXX - This was a dynamic cast but that kills the Windows
|
||||
// version big-time with an abnormal termination error
|
||||
delete mDataProvider;
|
||||
mDataProvider = ( QgsVectorDataProvider * )( QgsProviderRegistry::instance()->createProvider( provider, mDataSource ) );
|
||||
mDataProvider = ( QgsVectorDataProvider * )( QgsProviderRegistry::instance()->createProvider( provider, dataSource ) );
|
||||
if ( !mDataProvider )
|
||||
{
|
||||
QgsDebugMsg( " unable to get data provider" );
|
||||
@ -1500,7 +1539,7 @@ bool QgsVectorLayer::setDataProvider( QString const &provider )
|
||||
}
|
||||
|
||||
// TODO: Check if the provider has the capability to send fullExtentCalculated
|
||||
connect( mDataProvider, &QgsVectorDataProvider::fullExtentCalculated, this, &QgsVectorLayer::updateExtents );
|
||||
connect( mDataProvider, &QgsVectorDataProvider::fullExtentCalculated, this, [ = ] { updateExtents(); } );
|
||||
|
||||
// get and store the feature type
|
||||
mWkbType = mDataProvider->wkbType();
|
||||
@ -1537,7 +1576,11 @@ bool QgsVectorLayer::setDataProvider( QString const &provider )
|
||||
QgsDebugMsg( "Beautified layer name " + name() );
|
||||
|
||||
// deal with unnecessary schema qualification to make v.in.ogr happy
|
||||
mDataSource = mDataProvider->dataSourceUri();
|
||||
// and remove unnecessary key
|
||||
QgsDataSourceUri dataProviderUri( mDataProvider->dataSourceUri() );
|
||||
if ( dataProviderUri.hasParam( checkUnicityKey ) )
|
||||
dataProviderUri.removeParam( checkUnicityKey );
|
||||
mDataSource = dataProviderUri.uri( false );
|
||||
}
|
||||
else if ( mProviderKey == QLatin1String( "osm" ) )
|
||||
{
|
||||
@ -4418,3 +4461,13 @@ QgsAbstractVectorLayerLabeling *QgsVectorLayer::readLabelingFromCustomProperties
|
||||
|
||||
return labeling;
|
||||
}
|
||||
|
||||
void QgsVectorLayer::setReadExtentFromXml( bool readExtentFromXml )
|
||||
{
|
||||
mReadExtentFromXml = readExtentFromXml;
|
||||
}
|
||||
|
||||
bool QgsVectorLayer::readExtentFromXml() const
|
||||
{
|
||||
return mReadExtentFromXml;
|
||||
}
|
||||
|
@ -389,10 +389,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
* \param baseName The name used to represent the layer in the legend
|
||||
* \param providerLib The name of the data provider, e.g., "memory", "postgres"
|
||||
* \param loadDefaultStyleFlag whether to load the default style
|
||||
* \param readExtentFromXml Read extent from XML if true or let provider determine it if false
|
||||
*
|
||||
*/
|
||||
QgsVectorLayer( const QString &path = QString(), const QString &baseName = QString(),
|
||||
const QString &providerLib = "ogr", bool loadDefaultStyleFlag = true );
|
||||
const QString &providerLib = "ogr", bool loadDefaultStyleFlag = true,
|
||||
bool readExtentFromXml = false );
|
||||
|
||||
|
||||
virtual ~QgsVectorLayer();
|
||||
@ -1604,6 +1606,24 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
*/
|
||||
void setEditFormConfig( const QgsEditFormConfig &editFormConfig );
|
||||
|
||||
/**
|
||||
* Flag allowing to indicate if the extent has to be read from the XML
|
||||
* document when data source has no metadata or if the data provider has
|
||||
* to determine it.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
void setReadExtentFromXml( bool readExtentFromXml );
|
||||
|
||||
/**
|
||||
* Returns true if the extent is read from the XML document when data
|
||||
* source has no metadata, false if it's the data provider which determines
|
||||
* it.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
bool readExtentFromXml() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
@ -1651,8 +1671,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
|
||||
/** Update the extents for the layer. This is necessary if features are
|
||||
* added/deleted or the layer has been subsetted.
|
||||
*
|
||||
* \param force true to update layer extent even if it's read from xml by default, false otherwise
|
||||
*/
|
||||
virtual void updateExtents();
|
||||
virtual void updateExtents( bool force = false );
|
||||
|
||||
/**
|
||||
* Make layer editable.
|
||||
@ -2041,6 +2063,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
//! True while an undo command is active
|
||||
bool mEditCommandActive;
|
||||
|
||||
bool mReadExtentFromXml;
|
||||
QgsRectangle mXmlExtent;
|
||||
|
||||
QgsFeatureIds mDeletedFids;
|
||||
|
||||
QgsAttributeTableConfig mAttributeTableConfig;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "qgspostgrestransaction.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#ifdef HAVE_GUI
|
||||
#include "qgspgsourceselect.h"
|
||||
@ -114,6 +115,18 @@ QgsPostgresProvider::QgsPostgresProvider( QString const &uri )
|
||||
mRequestedSrid = mUri.srid();
|
||||
mRequestedGeomType = mUri.wkbType();
|
||||
|
||||
if ( mUri.hasParam( QStringLiteral( "checkPrimaryKeyUnicity" ) ) )
|
||||
{
|
||||
if ( mUri.param( QStringLiteral( "checkPrimaryKeyUnicity" ) ).compare( "0" ) == 0 )
|
||||
{
|
||||
mCheckPrimaryKeyUnicity = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCheckPrimaryKeyUnicity = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mSchemaName.isEmpty() && mTableName.startsWith( '(' ) && mTableName.endsWith( ')' ) )
|
||||
{
|
||||
mIsQuery = true;
|
||||
@ -1285,11 +1298,9 @@ bool QgsPostgresProvider::determinePrimaryKey()
|
||||
// If the relation is a view try to find a suitable column to use as
|
||||
// the primary key.
|
||||
|
||||
sql = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
|
||||
res = connectionRO()->PQexec( sql );
|
||||
QString type = res.PQgetvalue( 0, 0 );
|
||||
QgsPostgresProvider::Relkind type = relkind();
|
||||
|
||||
if ( type == QLatin1String( "r" ) ) // the relation is a table
|
||||
if ( type == Relkind::OrdinaryTable )
|
||||
{
|
||||
QgsDebugMsg( "Relation is a table. Checking to see if it has an oid column." );
|
||||
|
||||
@ -1324,13 +1335,15 @@ bool QgsPostgresProvider::determinePrimaryKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( type == QLatin1String( "v" ) || type == QLatin1String( "m" ) ) // the relation is a view
|
||||
else if ( type == Relkind::View || type == Relkind::MaterializedView )
|
||||
{
|
||||
determinePrimaryKeyFromUriKeyColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "Unexpected relation type '%1'." ).arg( type ), tr( "PostGIS" ) );
|
||||
const QMetaEnum metaEnum( QMetaEnum::fromType<Relkind>() );
|
||||
QString typeName = metaEnum.valueToKey( type );
|
||||
QgsMessageLog::logMessage( tr( "Unexpected relation type '%1'." ).arg( typeName ), tr( "PostGIS" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1485,7 +1498,13 @@ void QgsPostgresProvider::determinePrimaryKeyFromUriKeyColumn()
|
||||
|
||||
if ( !mPrimaryKeyAttrs.isEmpty() )
|
||||
{
|
||||
if ( mUseEstimatedMetadata || uniqueData( primaryKey ) )
|
||||
bool unique = true;
|
||||
if ( mCheckPrimaryKeyUnicity )
|
||||
{
|
||||
unique = uniqueData( primaryKey );
|
||||
}
|
||||
|
||||
if ( mUseEstimatedMetadata || unique )
|
||||
{
|
||||
mPrimaryKeyType = PktFidMap; // Map by default
|
||||
if ( mPrimaryKeyAttrs.size() == 1 )
|
||||
@ -4247,6 +4266,63 @@ QgsAttrPalIndexNameHash QgsPostgresProvider::palAttributeIndexNames() const
|
||||
return mAttrPalIndexName;
|
||||
}
|
||||
|
||||
QgsPostgresProvider::Relkind QgsPostgresProvider::relkind() const
|
||||
{
|
||||
QString sql = QStringLiteral( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
|
||||
QgsPostgresResult res( connectionRO()->PQexec( sql ) );
|
||||
QString type = res.PQgetvalue( 0, 0 );
|
||||
|
||||
QgsPostgresProvider::Relkind kind = Relkind::Unknown;
|
||||
|
||||
if ( type == QLatin1String( "r" ) )
|
||||
{
|
||||
kind = Relkind::OrdinaryTable;
|
||||
}
|
||||
else if ( type == QLatin1String( "i" ) )
|
||||
{
|
||||
kind = Relkind::Index;
|
||||
}
|
||||
else if ( type == QLatin1String( "s" ) )
|
||||
{
|
||||
kind = Relkind::Sequence;
|
||||
}
|
||||
else if ( type == QLatin1String( "v" ) )
|
||||
{
|
||||
kind = Relkind::View;
|
||||
}
|
||||
else if ( type == QLatin1String( "m" ) )
|
||||
{
|
||||
kind = Relkind::MaterializedView;
|
||||
}
|
||||
else if ( type == QLatin1String( "c" ) )
|
||||
{
|
||||
kind = Relkind::CompositeType;
|
||||
}
|
||||
else if ( type == QLatin1String( "t" ) )
|
||||
{
|
||||
kind = Relkind::ToastTable;
|
||||
}
|
||||
else if ( type == QLatin1String( "f" ) )
|
||||
{
|
||||
kind = Relkind::ForeignTable;
|
||||
}
|
||||
|
||||
return kind;
|
||||
}
|
||||
|
||||
bool QgsPostgresProvider::hasMetadata() const
|
||||
{
|
||||
bool hasMetadata = true;
|
||||
QgsPostgresProvider::Relkind kind = relkind();
|
||||
|
||||
if ( kind == Relkind::View || kind == Relkind::MaterializedView )
|
||||
{
|
||||
hasMetadata = false;
|
||||
}
|
||||
|
||||
return hasMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class factory to return a pointer to a newly created
|
||||
* QgsPostgresProvider object
|
||||
|
@ -48,6 +48,19 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Relkind
|
||||
{
|
||||
Unknown,
|
||||
OrdinaryTable, // r
|
||||
Index, // i
|
||||
Sequence, // s
|
||||
View, // v
|
||||
MaterializedView, // m
|
||||
CompositeType, // c
|
||||
ToastTable, // t
|
||||
ForeignTable // f
|
||||
};
|
||||
Q_ENUM( Relkind );
|
||||
|
||||
/** Import a vector layer into the database
|
||||
* \param options options for provider, specified via a map of option name
|
||||
@ -184,6 +197,17 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
virtual QList<QgsRelation> discoverRelations( const QgsVectorLayer *self, const QList<QgsVectorLayer *> &layers ) const override;
|
||||
virtual QgsAttrPalIndexNameHash palAttributeIndexNames() const override;
|
||||
|
||||
/** Returns true if the data source has metadata, false otherwise. For
|
||||
* example, if the kind of relation for the layer is a view or a
|
||||
* materialized view, then no metadata are associated with the data
|
||||
* source.
|
||||
*
|
||||
* \returns true if data source has metadata, false otherwise.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
virtual bool hasMetadata() const override;
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -206,6 +230,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
void repaintRequested();
|
||||
|
||||
private:
|
||||
Relkind relkind() const;
|
||||
|
||||
bool declareCursor( const QString &cursorName,
|
||||
const QgsAttributeList &fetchAttributes,
|
||||
@ -407,6 +432,8 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
void setTransaction( QgsTransaction *transaction ) override;
|
||||
|
||||
QHash<int, QString> mDefaultValues;
|
||||
|
||||
bool mCheckPrimaryKeyUnicity = true;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2491,6 +2491,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="mTrustProjectCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Speed up project loading by skipping data checks. Useful in qgis server context or project with huge database views or materialized views.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Trust project when data source has no metadata</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="mTab_Variables">
|
||||
|
@ -28,11 +28,14 @@ from qgis.core import (
|
||||
NULL,
|
||||
QgsVectorLayerUtils,
|
||||
QgsSettings,
|
||||
QgsTransactionGroup
|
||||
QgsTransactionGroup,
|
||||
QgsReadWriteContext,
|
||||
QgsRectangle
|
||||
)
|
||||
from qgis.gui import QgsGui
|
||||
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.PyQt.QtXml import QDomDocument
|
||||
from utilities import unitTestDataPath
|
||||
from providertestbase import ProviderTestCase
|
||||
|
||||
@ -718,6 +721,94 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase):
|
||||
self.assertEqual(desclist, [])
|
||||
self.assertEqual(errmsg, "")
|
||||
|
||||
def testHasMetadata(self):
|
||||
# views don't have metadata
|
||||
vl = QgsVectorLayer('{} table="qgis_test"."{}" key="pk" sql='.format(self.dbconn, 'bikes_view'), "bikes_view", "postgres")
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertFalse(vl.dataProvider().hasMetadata())
|
||||
|
||||
# ordinary tables have metadata
|
||||
vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres")
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertTrue(vl.dataProvider().hasMetadata())
|
||||
|
||||
def testReadExtentOnView(self):
|
||||
# vector layer based on view
|
||||
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data_view" (geom) sql=', 'test', 'postgres')
|
||||
self.assertTrue(vl0.isValid())
|
||||
self.assertFalse(vl0.dataProvider().hasMetadata())
|
||||
|
||||
# set a custom extent
|
||||
originalExtent = vl0.extent()
|
||||
|
||||
customExtent = QgsRectangle(-80, 80, -70, 90)
|
||||
vl0.setExtent(customExtent)
|
||||
|
||||
# write xml
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("maplayer")
|
||||
self.assertTrue(vl0.writeLayerXml(elem, doc, QgsReadWriteContext()))
|
||||
|
||||
# read xml with the custom extent. It should not be used by default
|
||||
vl1 = QgsVectorLayer()
|
||||
vl1.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertTrue(vl1.isValid())
|
||||
|
||||
self.assertEqual(vl1.extent(), originalExtent)
|
||||
|
||||
# read xml with custom extent with readExtent option. Extent read from
|
||||
# xml document should be used because we have a view
|
||||
vl2 = QgsVectorLayer()
|
||||
vl2.setReadExtentFromXml(True)
|
||||
vl2.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertTrue(vl2.isValid())
|
||||
|
||||
self.assertEqual(vl2.extent(), customExtent)
|
||||
|
||||
# but a force update on extent should allow retrieveing the data
|
||||
# provider extent
|
||||
vl2.updateExtents()
|
||||
vl2.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertEqual(vl2.extent(), customExtent)
|
||||
|
||||
vl2.updateExtents(force=True)
|
||||
vl2.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertEqual(vl2.extent(), originalExtent)
|
||||
|
||||
def testReadExtentOnTable(self):
|
||||
# vector layer based on a standard table
|
||||
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres')
|
||||
self.assertTrue(vl0.isValid())
|
||||
self.assertTrue(vl0.dataProvider().hasMetadata())
|
||||
|
||||
# set a custom extent
|
||||
originalExtent = vl0.extent()
|
||||
|
||||
customExtent = QgsRectangle(-80, 80, -70, 90)
|
||||
vl0.setExtent(customExtent)
|
||||
|
||||
# write xml
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("maplayer")
|
||||
self.assertTrue(vl0.writeLayerXml(elem, doc, QgsReadWriteContext()))
|
||||
|
||||
# read xml with the custom extent. It should not be used by default
|
||||
vl1 = QgsVectorLayer()
|
||||
vl1.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertTrue(vl1.isValid())
|
||||
|
||||
self.assertEqual(vl1.extent(), originalExtent)
|
||||
|
||||
# read xml with custom extent with readExtent option. Extent read from
|
||||
# xml document should NOT be used because we don't have a view or a
|
||||
# materialized view
|
||||
vl2 = QgsVectorLayer()
|
||||
vl2.setReadExtentFromXml(True)
|
||||
vl2.readLayerXml(elem, QgsReadWriteContext())
|
||||
self.assertTrue(vl2.isValid())
|
||||
|
||||
self.assertEqual(vl2.extent(), originalExtent)
|
||||
|
||||
|
||||
class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase):
|
||||
|
||||
|
5
tests/testdata/provider/testdata_pg.sql
vendored
5
tests/testdata/provider/testdata_pg.sql
vendored
@ -47,6 +47,11 @@ CREATE TABLE qgis_test."some_poly_data" (
|
||||
geom public.geometry(Polygon,4326)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW qgis_test.some_poly_data_view
|
||||
AS
|
||||
SELECT *
|
||||
FROM qgis_test.some_poly_data;
|
||||
|
||||
--
|
||||
-- TOC entry 4068 (class 0 OID 377761)
|
||||
-- Dependencies: 171
|
||||
|
Loading…
x
Reference in New Issue
Block a user