mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-03 00:02:25 -05:00
Merge pull request #7674 from elpaso/bugfix-19611-ogr-overwrite-gpkg
[bugfix] Vector file writer: also check for layername before giving up
This commit is contained in:
commit
6fba9b0b6d
@ -33,6 +33,7 @@
|
|||||||
#include "qgsexception.h"
|
#include "qgsexception.h"
|
||||||
#include "qgssettings.h"
|
#include "qgssettings.h"
|
||||||
#include "qgsgeometryengine.h"
|
#include "qgsgeometryengine.h"
|
||||||
|
#include "qgsproviderregistry.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@ -2422,6 +2423,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat
|
|||||||
details.dataSourceUri = layer->dataProvider()->dataSourceUri();
|
details.dataSourceUri = layer->dataProvider()->dataSourceUri();
|
||||||
details.storageType = layer->storageType();
|
details.storageType = layer->storageType();
|
||||||
details.selectedFeatureIds = layer->selectedFeatureIds();
|
details.selectedFeatureIds = layer->selectedFeatureIds();
|
||||||
|
details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
|
||||||
|
|
||||||
if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
|
if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
|
||||||
{
|
{
|
||||||
@ -2550,17 +2552,24 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
|
|||||||
int lastProgressReport = 0;
|
int lastProgressReport = 0;
|
||||||
long total = details.featureCount;
|
long total = details.featureCount;
|
||||||
|
|
||||||
|
// Special rules for OGR layers
|
||||||
if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
|
if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
|
||||||
{
|
{
|
||||||
QStringList theURIParts = details.dataSourceUri.split( '|' );
|
QString srcFileName( details.providerUriParams.value( QLatin1String( "path" ) ).toString() );
|
||||||
QString srcFileName = theURIParts[0];
|
|
||||||
|
|
||||||
if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
|
if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
|
||||||
|
{
|
||||||
|
// Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
|
||||||
|
QgsDataSourceUri uri( details.dataSourceUri );
|
||||||
|
if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
|
||||||
|
options.driverName == QLatin1String( "SpatiaLite" ) ||
|
||||||
|
options.driverName == QLatin1String( "SQLite" ) ) &&
|
||||||
|
options.layerName != details.providerUriParams.value( QLatin1String( "layerName" ) ) ) )
|
||||||
{
|
{
|
||||||
if ( errorMessage )
|
if ( errorMessage )
|
||||||
*errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
|
*errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
|
||||||
return ErrCreateDataSource;
|
return ErrCreateDataSource;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shapefiles might contain multi types although wkbType() only reports singles
|
// Shapefiles might contain multi types although wkbType() only reports singles
|
||||||
if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
|
if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
|
||||||
@ -2577,7 +2586,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
|
|||||||
if ( options.feedback )
|
if ( options.feedback )
|
||||||
{
|
{
|
||||||
//dedicate first 5% of progress bar to this scan
|
//dedicate first 5% of progress bar to this scan
|
||||||
int newProgress = ( 5.0 * scanned ) / total;
|
int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
|
||||||
if ( newProgress != lastProgressReport )
|
if ( newProgress != lastProgressReport )
|
||||||
{
|
{
|
||||||
lastProgressReport = newProgress;
|
lastProgressReport = newProgress;
|
||||||
@ -2675,7 +2684,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
|
|||||||
if ( options.feedback )
|
if ( options.feedback )
|
||||||
{
|
{
|
||||||
//avoid spamming progress reports
|
//avoid spamming progress reports
|
||||||
int newProgress = initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total;
|
int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
|
||||||
if ( newProgress < 100 && newProgress != lastProgressReport )
|
if ( newProgress < 100 && newProgress != lastProgressReport )
|
||||||
{
|
{
|
||||||
lastProgressReport = newProgress;
|
lastProgressReport = newProgress;
|
||||||
|
@ -760,6 +760,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
|||||||
QgsFeatureIterator sourceFeatureIterator;
|
QgsFeatureIterator sourceFeatureIterator;
|
||||||
QgsGeometry filterRectGeometry;
|
QgsGeometry filterRectGeometry;
|
||||||
std::unique_ptr< QgsGeometryEngine > filterRectEngine;
|
std::unique_ptr< QgsGeometryEngine > filterRectEngine;
|
||||||
|
QVariantMap providerUriParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -814,6 +815,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
|||||||
static QStringList concatenateOptions( const QMap<QString, Option *> &options );
|
static QStringList concatenateOptions( const QMap<QString, Option *> &options );
|
||||||
|
|
||||||
friend class QgsVectorFileWriterTask;
|
friend class QgsVectorFileWriterTask;
|
||||||
|
friend class TestQgsVectorFileWriter;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )
|
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )
|
||||||
|
@ -80,6 +80,8 @@ class TestQgsVectorFileWriter: public QObject
|
|||||||
void projectedPlygonGridTest();
|
void projectedPlygonGridTest();
|
||||||
//! This is a regression test ticket 1141 (broken Polish characters support since r8592) https://issues.qgis.org/issues/1141
|
//! This is a regression test ticket 1141 (broken Polish characters support since r8592) https://issues.qgis.org/issues/1141
|
||||||
void regression1141();
|
void regression1141();
|
||||||
|
//! Test prepareWriteAsVectorFormat
|
||||||
|
void prepareWriteAsVectorFormat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// a little util fn used by all tests
|
// a little util fn used by all tests
|
||||||
@ -106,6 +108,7 @@ void TestQgsVectorFileWriter::initTestCase()
|
|||||||
// init QGIS's paths - true means that all path will be inited from prefix
|
// init QGIS's paths - true means that all path will be inited from prefix
|
||||||
QgsApplication::init();
|
QgsApplication::init();
|
||||||
QgsApplication::showSettings();
|
QgsApplication::showSettings();
|
||||||
|
QgsApplication::initQgis();
|
||||||
//create some objects that will be used in all tests...
|
//create some objects that will be used in all tests...
|
||||||
|
|
||||||
mEncoding = QStringLiteral( "UTF-8" );
|
mEncoding = QStringLiteral( "UTF-8" );
|
||||||
@ -457,5 +460,36 @@ void TestQgsVectorFileWriter::regression1141()
|
|||||||
QVERIFY( QgsVectorFileWriter::deleteShapeFile( fileName ) );
|
QVERIFY( QgsVectorFileWriter::deleteShapeFile( fileName ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsVectorFileWriter::prepareWriteAsVectorFormat()
|
||||||
|
{
|
||||||
|
QgsVectorFileWriter::PreparedWriterDetails details;
|
||||||
|
QgsVectorFileWriter::SaveVectorOptions options;
|
||||||
|
QgsVectorLayer ml( "Point?field=firstfield:int&field=secondfield:int", "test", "memory" );
|
||||||
|
QgsFeature ft( ml.fields( ) );
|
||||||
|
ft.setAttribute( 0, 4 );
|
||||||
|
ft.setAttribute( 1, -10 );
|
||||||
|
ml.dataProvider()->addFeature( ft );
|
||||||
|
QVERIFY( ml.isValid() );
|
||||||
|
QTemporaryFile tmpFile( QDir::tempPath() + "/test_qgsvectorfilewriter_XXXXXX.gpkg" );
|
||||||
|
tmpFile.open();
|
||||||
|
QString fileName( tmpFile.fileName( ) );
|
||||||
|
options.driverName = "GPKG";
|
||||||
|
options.layerName = "test";
|
||||||
|
QString errorMessage;
|
||||||
|
QgsVectorFileWriter::WriterError error( QgsVectorFileWriter::writeAsVectorFormat(
|
||||||
|
&ml,
|
||||||
|
fileName,
|
||||||
|
options,
|
||||||
|
&errorMessage ) );
|
||||||
|
|
||||||
|
QCOMPARE( error, QgsVectorFileWriter::WriterError::NoError );
|
||||||
|
QCOMPARE( errorMessage, fileName );
|
||||||
|
QgsVectorLayer vl( QStringLiteral( "%1|layername=test" ).arg( fileName ), "src_test", "ogr" );
|
||||||
|
QVERIFY( vl.isValid() );
|
||||||
|
QgsVectorFileWriter::prepareWriteAsVectorFormat( &vl, options, details );
|
||||||
|
QCOMPARE( details.providerUriParams.value( "layerName" ).toString(), QStringLiteral( "test" ) );
|
||||||
|
QCOMPARE( details.providerUriParams.value( "path" ).toString(), fileName );
|
||||||
|
}
|
||||||
|
|
||||||
QGSTEST_MAIN( TestQgsVectorFileWriter )
|
QGSTEST_MAIN( TestQgsVectorFileWriter )
|
||||||
#include "testqgsvectorfilewriter.moc"
|
#include "testqgsvectorfilewriter.moc"
|
||||||
|
@ -32,6 +32,7 @@ from qgis.core import (QgsVectorLayer,
|
|||||||
)
|
)
|
||||||
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
|
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
import osgeo.gdal # NOQA
|
import osgeo.gdal # NOQA
|
||||||
from osgeo import gdal, ogr
|
from osgeo import gdal, ogr
|
||||||
from qgis.testing import start_app, unittest
|
from qgis.testing import start_app, unittest
|
||||||
@ -898,6 +899,46 @@ class TestQgsVectorFileWriter(unittest.TestCase):
|
|||||||
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo File'))
|
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo File'))
|
||||||
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo MIF'))
|
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo MIF'))
|
||||||
|
|
||||||
|
def testOverwriteGPKG(self):
|
||||||
|
"""Test that overwriting the same origin GPKG file works only if the layername is different"""
|
||||||
|
|
||||||
|
# Prepare test data
|
||||||
|
ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory')
|
||||||
|
provider = ml.dataProvider()
|
||||||
|
ft = QgsFeature()
|
||||||
|
ft.setAttributes([4, -10])
|
||||||
|
provider.addFeatures([ft])
|
||||||
|
filehandle, filename = tempfile.mkstemp('.gpkg')
|
||||||
|
|
||||||
|
options = QgsVectorFileWriter.SaveVectorOptions()
|
||||||
|
options.driverName = 'GPKG'
|
||||||
|
options.layerName = 'test'
|
||||||
|
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
|
||||||
|
ml,
|
||||||
|
filename,
|
||||||
|
options)
|
||||||
|
self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
|
||||||
|
|
||||||
|
# Real test
|
||||||
|
vl = QgsVectorLayer("%s|layername=test" % filename, 'src_test', 'ogr')
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
self.assertEqual(vl.featureCount(), 1)
|
||||||
|
|
||||||
|
# This must fail
|
||||||
|
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
|
||||||
|
vl,
|
||||||
|
filename,
|
||||||
|
options)
|
||||||
|
self.assertEqual(write_result, QgsVectorFileWriter.ErrCreateDataSource)
|
||||||
|
self.assertEqual(error_message, 'Cannot overwrite a OGR layer in place')
|
||||||
|
|
||||||
|
options.layerName = 'test2'
|
||||||
|
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
|
||||||
|
vl,
|
||||||
|
filename,
|
||||||
|
options)
|
||||||
|
self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user