Skip to content

Commit 7af8c49

Browse files
authoredApr 25, 2017
Merge pull request #4410 from rouault/fix_12695_qgis2_18
DBManager: fix importing a new layer in a GeoPackage (#16295)
2 parents eb1a78b + c851db3 commit 7af8c49

File tree

5 files changed

+175
-40
lines changed

5 files changed

+175
-40
lines changed
 

‎python/plugins/db_manager/dlg_import_vector.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,16 @@ def accept(self):
317317

318318
# get output params, update output URI
319319
self.outUri.setDataSource(schema, table, geom, "", pk)
320-
uri = self.outUri.uri(False)
321-
320+
typeName = self.db.dbplugin().typeName()
322321
providerName = self.db.dbplugin().providerName()
322+
if typeName == 'gpkg':
323+
uri = self.outUri.database()
324+
options['update'] = True
325+
options['driverName'] = 'GPKG'
326+
options['layerName'] = table
327+
else:
328+
uri = self.outUri.uri(False)
329+
323330
if self.chkDropTable.isChecked():
324331
options['overwrite'] = True
325332

@@ -367,6 +374,8 @@ def accept(self):
367374
if self.chkSpatialIndex.isEnabled() and self.chkSpatialIndex.isChecked():
368375
self.db.connector.createSpatialIndex((schema, table), geom)
369376

377+
self.db.connection().reconnect()
378+
self.db.refresh()
370379
QMessageBox.information(self, self.tr("Import to database"), self.tr("Import was successful."))
371380
return QDialog.accept(self)
372381

‎src/core/qgsvectorfilewriter.h

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,37 @@ class CORE_EXPORT QgsVectorFileWriter
433433
SymbologyExport symbologyExport = NoSymbology
434434
);
435435

436+
/** Create a new vector file writer.
437+
* \param vectorFileName file name to write to
438+
* \param fileEncoding encoding to use
439+
* \param fields fields to write
440+
* \param geometryType geometry type of output file
441+
* \param srs spatial reference system of output file
442+
* \param driverName OGR driver to use
443+
* \param datasourceOptions list of OGR data source creation options
444+
* \param layerOptions list of OGR layer creation options
445+
* \param newFilename potentially modified file name (output parameter)
446+
* \param symbologyExport symbology to export
447+
* \param fieldValueConverter field value converter (added in QGIS 2.16)
448+
* \param layerName layer name. If let empty, it will be derived from the filename (added in QGIS 3.0)
449+
* \param action action on existing file (added in QGIS 3.0)
450+
* \note not available in Python bindings
451+
*/
452+
QgsVectorFileWriter( const QString &vectorFileName,
453+
const QString &fileEncoding,
454+
const QgsFields &fields,
455+
QgsWKBTypes::Type geometryType,
456+
const QgsCoordinateReferenceSystem* srs,
457+
const QString &driverName,
458+
const QStringList &datasourceOptions,
459+
const QStringList &layerOptions,
460+
QString *newFilename,
461+
SymbologyExport symbologyExport,
462+
FieldValueConverter *fieldValueConverter,
463+
const QString &layerName,
464+
ActionOnExistingFile action
465+
);
466+
436467
/** Returns map with format filter string as key and OGR format key as value*/
437468
static QMap< QString, QString> supportedFiltersAndFormats();
438469

@@ -548,42 +579,13 @@ class CORE_EXPORT QgsVectorFileWriter
548579

549580
private:
550581

551-
/** Create a new vector file writer.
552-
* @param vectorFileName file name to write to
553-
* @param fileEncoding encoding to use
554-
* @param fields fields to write
555-
* @param geometryType geometry type of output file
556-
* @param srs spatial reference system of output file
557-
* @param driverName OGR driver to use
558-
* @param datasourceOptions list of OGR data source creation options
559-
* @param layerOptions list of OGR layer creation options
560-
* @param newFilename potentially modified file name (output parameter)
561-
* @param symbologyExport symbology to export
562-
* @param fieldValueConverter field value converter (added in QGIS 2.16)
563-
* @param layerName layer name. If let empty, it will be derived from the filename (added in QGIS 3.0)
564-
* @param action action on existing file (added in QGIS 3.0)
565-
*/
566-
QgsVectorFileWriter( const QString& vectorFileName,
567-
const QString& fileEncoding,
568-
const QgsFields& fields,
569-
QgsWKBTypes::Type geometryType,
570-
const QgsCoordinateReferenceSystem* srs,
571-
const QString& driverName,
572-
const QStringList &datasourceOptions,
573-
const QStringList &layerOptions,
574-
QString *newFilename,
575-
SymbologyExport symbologyExport,
576-
FieldValueConverter* fieldValueConverter,
577-
const QString& layerName,
578-
ActionOnExistingFile action
579-
);
580-
581582
void init( QString vectorFileName, QString fileEncoding, const QgsFields& fields,
582583
QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs,
583584
const QString& driverName, QStringList datasourceOptions,
584585
QStringList layerOptions, QString* newFilename,
585586
FieldValueConverter* fieldValueConverter,
586587
const QString& layerName,
588+
587589
ActionOnExistingFile action );
588590
void resetMap( const QgsAttributeList &attributes );
589591

‎src/core/qgsvectorlayerimport.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,20 @@ QgsVectorLayerImport::QgsVectorLayerImport( const QString &uri,
9797

9898
QgsDebugMsg( "Created empty layer" );
9999

100-
QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider* >( pReg->provider( providerKey, uri ) );
100+
QString uriUpdated( uri );
101+
// HACK sorry...
102+
if ( providerKey == "ogr" )
103+
{
104+
QString layerName;
105+
if ( options->contains( "layerName" ) )
106+
layerName = options->value( "layerName" ).toString();
107+
if ( !layerName.isEmpty() )
108+
{
109+
uriUpdated += "|layername=";
110+
uriUpdated += layerName;
111+
}
112+
}
113+
QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider * >( pReg->provider( providerKey, uriUpdated ) );
101114
if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
102115
{
103116
mError = ErrInvalidLayer;

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
230230
QString encoding;
231231
QString driverName = "ESRI Shapefile";
232232
QStringList dsOptions, layerOptions;
233+
QString layerName;
233234
234235
if ( options )
235236
{
@@ -244,14 +245,45 @@ QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
244245
245246
if ( options->contains( "layerOptions" ) )
246247
layerOptions << options->value( "layerOptions" ).toStringList();
248+
249+
if ( options->contains( "layerName" ) )
250+
layerName = options->value( "layerName" ).toString();
247251
}
248252
249253
if ( oldToNewAttrIdxMap )
250254
oldToNewAttrIdxMap->clear();
251255
if ( errorMessage )
252256
errorMessage->clear();
253257
254-
if ( !overwrite )
258+
QgsVectorFileWriter::ActionOnExistingFile action( QgsVectorFileWriter::CreateOrOverwriteFile );
259+
260+
bool update = false;
261+
if ( options->contains( "update" ) )
262+
{
263+
update = options->value( "update" ).toBool();
264+
if ( update )
265+
{
266+
if ( !overwrite && !layerName.isEmpty() )
267+
{
268+
OGRDataSourceH hDS = OGROpen( uri.toUtf8().constData(), TRUE, nullptr );
269+
if ( hDS )
270+
{
271+
if ( OGR_DS_GetLayerByName( hDS, layerName.toUtf8().constData() ) )
272+
{
273+
OGR_DS_Destroy( hDS );
274+
if ( errorMessage )
275+
*errorMessage += QObject::tr( "Layer %2 of %1 exists and overwrite flag is false." )
276+
.arg( uri ).arg( layerName );
277+
return QgsVectorLayerImport::ErrCreateDataSource;
278+
}
279+
OGR_DS_Destroy( hDS );
280+
}
281+
}
282+
action = QgsVectorFileWriter::CreateOrOverwriteLayer;
283+
}
284+
}
285+
286+
if ( !overwrite && !update )
255287
{
256288
QFileInfo fi( uri );
257289
if ( fi.exists() )
@@ -264,8 +296,10 @@ QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
264296
}
265297
266298
QgsVectorFileWriter *writer = new QgsVectorFileWriter(
267-
uri, encoding, fields, wkbType,
268-
srs, driverName, dsOptions, layerOptions );
299+
uri, encoding, fields, QGis::fromOldWkbType( wkbType ),
300+
srs, driverName, dsOptions, layerOptions, nullptr,
301+
QgsVectorFileWriter::NoSymbology, nullptr,
302+
layerName, action );
269303
270304
QgsVectorFileWriter::WriterError error = writer->hasError();
271305
if ( error )
@@ -277,16 +311,37 @@ QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
277311
return ( QgsVectorLayerImport::ImportError ) error;
278312
}
279313
314+
QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
315+
delete writer;
316+
280317
if ( oldToNewAttrIdxMap )
281318
{
282-
QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
319+
bool firstFieldIsFid = false;
320+
if ( !layerName.isEmpty() )
321+
{
322+
OGRDataSourceH hDS = OGROpen( uri.toUtf8().constData(), TRUE, nullptr );
323+
if ( hDS )
324+
{
325+
OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS, layerName.toUtf8().constData() );
326+
if ( hLayer )
327+
{
328+
// Expose the OGR FID if it comes from a "real" column (typically GPKG)
329+
// and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
330+
firstFieldIsFid = !( EQUAL( OGR_L_GetFIDColumn( hLayer ), "" ) ) &&
331+
OGR_FD_GetFieldIndex( OGR_L_GetLayerDefn( hLayer ), OGR_L_GetFIDColumn( hLayer ) ) < 0 &&
332+
fields.indexFromName( OGR_L_GetFIDColumn( hLayer ) ) < 0;
333+
334+
}
335+
OGR_DS_Destroy( hDS );
336+
}
337+
}
338+
283339
for ( QMap<int, int>::const_iterator attrIt = attrIdxMap.begin(); attrIt != attrIdxMap.end(); ++attrIt )
284340
{
285-
oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt );
341+
oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt + ( firstFieldIsFid ? 1 : 0 ) );
286342
}
287343
}
288344
289-
delete writer;
290345
return QgsVectorLayerImport::NoError;
291346
}
292347

‎tests/src/python/test_provider_ogr_gpkg.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from osgeo import gdal, ogr
2222

2323
from qgis.PyQt.QtCore import QCoreApplication, QSettings
24-
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsRectangle
24+
from qgis.core import QgsVectorLayer, QgsVectorLayerImport, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsRectangle
2525
from qgis.testing import start_app, unittest
2626
from utilities import unitTestDataPath
2727

@@ -268,6 +268,62 @@ def testDisablewalForSqlite3(self):
268268

269269
QSettings().setValue("/qgis/walForSqlite3", None)
270270

271+
def testSimulatedDBManagerImport(self):
272+
uri = 'point?field=f1:int'
273+
uri += '&field=f2:double(6,4)'
274+
uri += '&field=f3:string(20)'
275+
lyr = QgsVectorLayer(uri, "x", "memory")
276+
self.assertTrue(lyr.isValid())
277+
f = QgsFeature(lyr.fields())
278+
f['f1'] = 1
279+
f['f2'] = 123.456
280+
f['f3'] = '12345678.90123456789'
281+
f2 = QgsFeature(lyr.fields())
282+
f2['f1'] = 2
283+
lyr.dataProvider().addFeatures([f, f2])
284+
285+
tmpfile = os.path.join(self.basetestpath, 'testSimulatedDBManagerImport.gpkg')
286+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
287+
ds = None
288+
options = {}
289+
options['update'] = True
290+
options['driverName'] = 'GPKG'
291+
options['layerName'] = 'my_out_table'
292+
err = QgsVectorLayerImport.importLayer(lyr, tmpfile, "ogr", lyr.crs(), False, False, options)
293+
self.assertEqual(err[0], QgsVectorLayerImport.NoError,
294+
'unexpected import error {0}'.format(err))
295+
lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr")
296+
self.assertTrue(lyr.isValid())
297+
features = lyr.getFeatures()
298+
f = next(features)
299+
self.assertEqual(f['f1'], 1)
300+
self.assertEqual(f['f2'], 123.456)
301+
self.assertEqual(f['f3'], '12345678.90123456789')
302+
f = next(features)
303+
self.assertEqual(f['f1'], 2)
304+
features = None
305+
306+
# Test overwriting without overwrite option
307+
err = QgsVectorLayerImport.importLayer(lyr, tmpfile, "ogr", lyr.crs(), False, False, options)
308+
self.assertEqual(err[0], QgsVectorLayerImport.ErrCreateDataSource)
309+
310+
# Test overwriting
311+
lyr = QgsVectorLayer(uri, "x", "memory")
312+
self.assertTrue(lyr.isValid())
313+
f = QgsFeature(lyr.fields())
314+
f['f1'] = 3
315+
lyr.dataProvider().addFeatures([f])
316+
options['overwrite'] = True
317+
err = QgsVectorLayerImport.importLayer(lyr, tmpfile, "ogr", lyr.crs(), False, False, options)
318+
self.assertEqual(err[0], QgsVectorLayerImport.NoError,
319+
'unexpected import error {0}'.format(err))
320+
lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr")
321+
self.assertTrue(lyr.isValid())
322+
features = lyr.getFeatures()
323+
f = next(features)
324+
self.assertEqual(f['f1'], 3)
325+
features = None
326+
271327

272328
if __name__ == '__main__':
273329
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.