Skip to content

Commit 6fba9b0

Browse files
authored
Merge pull request #7674 from elpaso/bugfix-19611-ogr-overwrite-gpkg
[bugfix] Vector file writer: also check for layername before giving up
2 parents 66dd676 + fbfb0bc commit 6fba9b0

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

src/core/qgsvectorfilewriter.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "qgsexception.h"
3434
#include "qgssettings.h"
3535
#include "qgsgeometryengine.h"
36+
#include "qgsproviderregistry.h"
3637

3738
#include <QFile>
3839
#include <QFileInfo>
@@ -2422,6 +2423,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat
24222423
details.dataSourceUri = layer->dataProvider()->dataSourceUri();
24232424
details.storageType = layer->storageType();
24242425
details.selectedFeatureIds = layer->selectedFeatureIds();
2426+
details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
24252427

24262428
if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
24272429
{
@@ -2550,16 +2552,23 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
25502552
int lastProgressReport = 0;
25512553
long total = details.featureCount;
25522554

2555+
// Special rules for OGR layers
25532556
if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
25542557
{
2555-
QStringList theURIParts = details.dataSourceUri.split( '|' );
2556-
QString srcFileName = theURIParts[0];
2557-
2558+
QString srcFileName( details.providerUriParams.value( QLatin1String( "path" ) ).toString() );
25582559
if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
25592560
{
2560-
if ( errorMessage )
2561-
*errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
2562-
return ErrCreateDataSource;
2561+
// Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
2562+
QgsDataSourceUri uri( details.dataSourceUri );
2563+
if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
2564+
options.driverName == QLatin1String( "SpatiaLite" ) ||
2565+
options.driverName == QLatin1String( "SQLite" ) ) &&
2566+
options.layerName != details.providerUriParams.value( QLatin1String( "layerName" ) ) ) )
2567+
{
2568+
if ( errorMessage )
2569+
*errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
2570+
return ErrCreateDataSource;
2571+
}
25632572
}
25642573

25652574
// Shapefiles might contain multi types although wkbType() only reports singles
@@ -2577,7 +2586,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
25772586
if ( options.feedback )
25782587
{
25792588
//dedicate first 5% of progress bar to this scan
2580-
int newProgress = ( 5.0 * scanned ) / total;
2589+
int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
25812590
if ( newProgress != lastProgressReport )
25822591
{
25832592
lastProgressReport = newProgress;
@@ -2675,7 +2684,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( Prepa
26752684
if ( options.feedback )
26762685
{
26772686
//avoid spamming progress reports
2678-
int newProgress = initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total;
2687+
int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
26792688
if ( newProgress < 100 && newProgress != lastProgressReport )
26802689
{
26812690
lastProgressReport = newProgress;

src/core/qgsvectorfilewriter.h

+2
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
760760
QgsFeatureIterator sourceFeatureIterator;
761761
QgsGeometry filterRectGeometry;
762762
std::unique_ptr< QgsGeometryEngine > filterRectEngine;
763+
QVariantMap providerUriParams;
763764
};
764765

765766
/**
@@ -814,6 +815,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
814815
static QStringList concatenateOptions( const QMap<QString, Option *> &options );
815816

816817
friend class QgsVectorFileWriterTask;
818+
friend class TestQgsVectorFileWriter;
817819
};
818820

819821
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )

tests/src/core/testqgsvectorfilewriter.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class TestQgsVectorFileWriter: public QObject
8080
void projectedPlygonGridTest();
8181
//! This is a regression test ticket 1141 (broken Polish characters support since r8592) https://issues.qgis.org/issues/1141
8282
void regression1141();
83+
//! Test prepareWriteAsVectorFormat
84+
void prepareWriteAsVectorFormat();
8385

8486
private:
8587
// a little util fn used by all tests
@@ -106,6 +108,7 @@ void TestQgsVectorFileWriter::initTestCase()
106108
// init QGIS's paths - true means that all path will be inited from prefix
107109
QgsApplication::init();
108110
QgsApplication::showSettings();
111+
QgsApplication::initQgis();
109112
//create some objects that will be used in all tests...
110113

111114
mEncoding = QStringLiteral( "UTF-8" );
@@ -457,5 +460,36 @@ void TestQgsVectorFileWriter::regression1141()
457460
QVERIFY( QgsVectorFileWriter::deleteShapeFile( fileName ) );
458461
}
459462

463+
void TestQgsVectorFileWriter::prepareWriteAsVectorFormat()
464+
{
465+
QgsVectorFileWriter::PreparedWriterDetails details;
466+
QgsVectorFileWriter::SaveVectorOptions options;
467+
QgsVectorLayer ml( "Point?field=firstfield:int&field=secondfield:int", "test", "memory" );
468+
QgsFeature ft( ml.fields( ) );
469+
ft.setAttribute( 0, 4 );
470+
ft.setAttribute( 1, -10 );
471+
ml.dataProvider()->addFeature( ft );
472+
QVERIFY( ml.isValid() );
473+
QTemporaryFile tmpFile( QDir::tempPath() + "/test_qgsvectorfilewriter_XXXXXX.gpkg" );
474+
tmpFile.open();
475+
QString fileName( tmpFile.fileName( ) );
476+
options.driverName = "GPKG";
477+
options.layerName = "test";
478+
QString errorMessage;
479+
QgsVectorFileWriter::WriterError error( QgsVectorFileWriter::writeAsVectorFormat(
480+
&ml,
481+
fileName,
482+
options,
483+
&errorMessage ) );
484+
485+
QCOMPARE( error, QgsVectorFileWriter::WriterError::NoError );
486+
QCOMPARE( errorMessage, fileName );
487+
QgsVectorLayer vl( QStringLiteral( "%1|layername=test" ).arg( fileName ), "src_test", "ogr" );
488+
QVERIFY( vl.isValid() );
489+
QgsVectorFileWriter::prepareWriteAsVectorFormat( &vl, options, details );
490+
QCOMPARE( details.providerUriParams.value( "layerName" ).toString(), QStringLiteral( "test" ) );
491+
QCOMPARE( details.providerUriParams.value( "path" ).toString(), fileName );
492+
}
493+
460494
QGSTEST_MAIN( TestQgsVectorFileWriter )
461495
#include "testqgsvectorfilewriter.moc"

tests/src/python/test_qgsvectorfilewriter.py

+41
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
)
3333
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
3434
import os
35+
import tempfile
3536
import osgeo.gdal # NOQA
3637
from osgeo import gdal, ogr
3738
from qgis.testing import start_app, unittest
@@ -898,6 +899,46 @@ def testSupportsFeatureStyles(self):
898899
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo File'))
899900
self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo MIF'))
900901

902+
def testOverwriteGPKG(self):
903+
"""Test that overwriting the same origin GPKG file works only if the layername is different"""
904+
905+
# Prepare test data
906+
ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory')
907+
provider = ml.dataProvider()
908+
ft = QgsFeature()
909+
ft.setAttributes([4, -10])
910+
provider.addFeatures([ft])
911+
filehandle, filename = tempfile.mkstemp('.gpkg')
912+
913+
options = QgsVectorFileWriter.SaveVectorOptions()
914+
options.driverName = 'GPKG'
915+
options.layerName = 'test'
916+
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
917+
ml,
918+
filename,
919+
options)
920+
self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
921+
922+
# Real test
923+
vl = QgsVectorLayer("%s|layername=test" % filename, 'src_test', 'ogr')
924+
self.assertTrue(vl.isValid())
925+
self.assertEqual(vl.featureCount(), 1)
926+
927+
# This must fail
928+
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
929+
vl,
930+
filename,
931+
options)
932+
self.assertEqual(write_result, QgsVectorFileWriter.ErrCreateDataSource)
933+
self.assertEqual(error_message, 'Cannot overwrite a OGR layer in place')
934+
935+
options.layerName = 'test2'
936+
write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat(
937+
vl,
938+
filename,
939+
options)
940+
self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
941+
901942

902943
if __name__ == '__main__':
903944
unittest.main()

0 commit comments

Comments
 (0)