1 /***************************************************************************
2 qgsogrprovider.cpp Data provider for OGR supported formats
3 Formerly known as qgsshapefileprovider.cpp
4 begin : Oct 29, 2003
5 copyright : (C) 2003 by Gary E.Sherman
6 email : sherman at mrcc.com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgsogrprovider.h"
19 ///@cond PRIVATE
20
21 #include "qgscplerrorhandler.h"
22 #include "qgsgdalutils.h"
23 #include "qgsogrfeatureiterator.h"
24 #include "qgslogger.h"
25 #include "qgsmessagelog.h"
26 #include "qgslocalec.h"
27 #include "qgsfeedback.h"
28 #include "qgssettings.h"
29 #include "qgsapplication.h"
30 #include "qgsauthmanager.h"
31 #include "qgsdataitem.h"
32 #include "qgsdataprovider.h"
33 #include "qgsfeature.h"
34 #include "qgsfields.h"
35 #include "qgsgeometry.h"
36 #include "qgscoordinatereferencesystem.h"
37 #include "qgsvectorlayerexporter.h"
38 #include "qgsdataitemprovider.h"
39 #include "qgsogrdataitems.h"
40 #include "qgsgeopackagedataitems.h"
41 #include "qgswkbtypes.h"
42 #include "qgsnetworkaccessmanager.h"
43 #include "qgsogrtransaction.h"
44 #include "qgsgeopackageprojectstorage.h"
45 #include "qgsprojectstorageregistry.h"
46 #include "qgsprovidermetadata.h"
47 #include "qgsogrdbconnection.h"
48 #include "qgsgeopackageproviderconnection.h"
49 #include "qgis.h"
50
51
52 #define CPL_SUPRESS_CPLUSPLUS //#spellok
53 #include <gdal.h> // to collect version information
54 #include <ogr_api.h>
55 #include <ogr_srs_api.h>
56 #include <cpl_string.h>
57
58 // Temporary solution until GDAL Unique support is available
59 #include "qgssqliteutils.h"
60 #include <sqlite3.h>
61 // end temporary
62
63 #include <limits>
64 #include <memory>
65
66 #include <QtDebug>
67 #include <QFile>
68 #include <QDir>
69 #include <QFileInfo>
70 #include <QMap>
71 #include <QMessageBox>
72 #include <QString>
73 #include <QTextCodec>
74
75
76 #ifdef Q_OS_WIN
77 #include <windows.h>
78 #endif
79 #ifdef Q_OS_LINUX
80 #include <sys/vfs.h>
81 #endif
82
83 // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
84 // whereas previously there was only unset fields. For QGIS purposes, both
85 // states (unset/null) are equivalent.
86 #ifndef OGRNullMarker
87 #define OGR_F_IsFieldSetAndNotNull OGR_F_IsFieldSet
88 #endif
89
90 #define TEXT_PROVIDER_KEY QStringLiteral( "ogr" )
91 #define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "OGR data provider" )
92
93 static OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString &typeName );
94
95 static bool IsLocalFile( const QString &path );
96
97 Q_GLOBAL_STATIC_WITH_ARGS( QMutex, sGlobalMutex, ( QMutex::Recursive ) )
98
99 //! Map a dataset name to the number of opened GDAL dataset objects on it (if opened with GDALOpenWrapper, only for GPKG)
100 typedef QMap< QString, int > OpenedDsCountMap;
101 Q_GLOBAL_STATIC( OpenedDsCountMap, sMapCountOpenedDS )
102
103 QMap< QgsOgrProviderUtils::DatasetIdentification,
104 QList<QgsOgrProviderUtils::DatasetWithLayers *> > QgsOgrProviderUtils::sMapSharedDS;
105
106 typedef QHash< GDALDatasetH, bool> DsHandleToUpdateModeHash;
107 Q_GLOBAL_STATIC( DsHandleToUpdateModeHash, sMapDSHandleToUpdateMode )
108
109 typedef QMap< QString, QDateTime > DsNameToLastModifiedDateMap;
Q_GLOBAL_STATIC(DsNameToLastModifiedDateMap,sMapDSNameToLastModifiedDate)110 Q_GLOBAL_STATIC( DsNameToLastModifiedDateMap, sMapDSNameToLastModifiedDate )
111
112 bool QgsOgrProvider::convertField( QgsField &field, const QTextCodec &encoding )
113 {
114 OGRFieldType ogrType = OFTString; //default to string
115 OGRFieldSubType ogrSubType = OFSTNone;
116 int ogrWidth = field.length();
117 int ogrPrecision = field.precision();
118 if ( ogrPrecision > 0 )
119 ogrWidth += 1;
120 switch ( field.type() )
121 {
122 case QVariant::LongLong:
123 ogrType = OFTInteger64;
124 ogrPrecision = 0;
125 ogrWidth = ogrWidth > 0 && ogrWidth <= 21 ? ogrWidth : 21;
126 break;
127
128 case QVariant::String:
129 ogrType = OFTString;
130 if ( ogrWidth < 0 || ogrWidth > 255 )
131 ogrWidth = 255;
132 break;
133
134 case QVariant::Int:
135 ogrType = OFTInteger;
136 ogrWidth = ogrWidth > 0 && ogrWidth <= 10 ? ogrWidth : 10;
137 ogrPrecision = 0;
138 break;
139
140 case QVariant::Bool:
141 ogrType = OFTInteger;
142 ogrSubType = OFSTBoolean;
143 ogrWidth = 1;
144 ogrPrecision = 0;
145 break;
146
147 case QVariant::Double:
148 ogrType = OFTReal;
149 break;
150
151 case QVariant::Date:
152 ogrType = OFTDate;
153 break;
154
155 case QVariant::Time:
156 ogrType = OFTTime;
157 break;
158
159 case QVariant::DateTime:
160 ogrType = OFTDateTime;
161 break;
162
163 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
164 case QVariant::List:
165 if ( field.subType() == QVariant::String )
166 {
167 ogrType = OFTStringList;
168 }
169 else
170 {
171 // only string lists are supported at this moment
172 return false;
173 }
174 break;
175 #endif
176
177 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
178 case QVariant::Map:
179 ogrType = OFTString;
180 ogrSubType = OFSTJSON;
181 break;
182 #endif
183 default:
184 return false;
185 }
186
187 if ( ogrSubType != OFSTNone )
188 field.setTypeName( encoding.toUnicode( OGR_GetFieldSubTypeName( ogrSubType ) ) );
189 else
190 field.setTypeName( encoding.toUnicode( OGR_GetFieldTypeName( ogrType ) ) );
191
192 field.setLength( ogrWidth );
193 field.setPrecision( ogrPrecision );
194 return true;
195 }
196
repack()197 void QgsOgrProvider::repack()
198 {
199 if ( !mValid || mGDALDriverName != QLatin1String( "ESRI Shapefile" ) || !mOgrOrigLayer )
200 return;
201
202 // run REPACK on shape files
203 QByteArray sql = QByteArray( "REPACK " ) + mOgrOrigLayer->name(); // don't quote the layer name as it works with spaces in the name and won't work if the name is quoted
204 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( QString::fromUtf8( sql ) ), 2 );
205 CPLErrorReset();
206 mOgrOrigLayer->ExecuteSQLNoReturn( sql );
207 if ( CPLGetLastErrorType() != CE_None )
208 {
209 pushError( tr( "OGR[%1] error %2: %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
210 }
211
212 if ( mFilePath.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) || mFilePath.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) )
213 {
214 QString packedDbf( mFilePath.left( mFilePath.size() - 4 ) + "_packed.dbf" );
215 if ( QFile::exists( packedDbf ) )
216 {
217 QgsMessageLog::logMessage( tr( "Possible corruption after REPACK detected. %1 still exists. This may point to a permission or locking problem of the original DBF." ).arg( packedDbf ), tr( "OGR" ), Qgis::Critical );
218
219 mOgrSqlLayer.reset();
220 mOgrOrigLayer.reset();
221
222 QString errCause;
223 if ( mLayerName.isNull() )
224 {
225 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, true, mOpenOptions, mLayerIndex, errCause, true );
226 }
227 else
228 {
229 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, true, mOpenOptions, mLayerName, errCause, true );
230 }
231
232 if ( !mOgrOrigLayer )
233 {
234 QgsMessageLog::logMessage( tr( "Original layer could not be reopened." ) + " " + errCause, tr( "OGR" ), Qgis::Critical );
235 mValid = false;
236 }
237
238 mOgrLayer = mOgrOrigLayer.get();
239 }
240
241 }
242
243 long oldcount = mFeaturesCounted;
244 recalculateFeatureCount();
245 if ( oldcount != mFeaturesCounted )
246 emit dataChanged();
247 }
248
249
createEmptyLayer(const QString & uri,const QgsFields & fields,QgsWkbTypes::Type wkbType,const QgsCoordinateReferenceSystem & srs,bool overwrite,QMap<int,int> & oldToNewAttrIdxMap,QString & errorMessage,const QMap<QString,QVariant> * options)250 QgsVectorLayerExporter::ExportError QgsOgrProviderMetadata::createEmptyLayer( const QString &uri,
251 const QgsFields &fields,
252 QgsWkbTypes::Type wkbType,
253 const QgsCoordinateReferenceSystem &srs,
254 bool overwrite,
255 QMap<int, int> &oldToNewAttrIdxMap,
256 QString &errorMessage,
257 const QMap<QString, QVariant> *options )
258 {
259 return QgsOgrProvider::createEmptyLayer(
260 uri, fields, wkbType, srs, overwrite,
261 &oldToNewAttrIdxMap, &errorMessage, options
262 );
263 }
264
AnalyzeURI(QString const & uri,bool & isSubLayer,int & layerIndex,QString & layerName,QString & subsetString,OGRwkbGeometryType & ogrGeometryTypeFilter,QStringList & openOptions)265 static QString AnalyzeURI( QString const &uri,
266 bool &isSubLayer,
267 int &layerIndex,
268 QString &layerName,
269 QString &subsetString,
270 OGRwkbGeometryType &ogrGeometryTypeFilter,
271 QStringList &openOptions )
272 {
273 isSubLayer = false;
274 layerIndex = 0;
275 layerName = QString();
276 subsetString = QString();
277 ogrGeometryTypeFilter = wkbUnknown;
278 openOptions.clear();
279
280 QgsDebugMsgLevel( "Data source uri is [" + uri + ']', 2 );
281
282 QVariantMap parts = QgsOgrProviderMetadata().decodeUri( uri );
283
284 if ( parts.contains( QStringLiteral( "layerName" ) ) )
285 {
286 layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
287 isSubLayer = !layerName.isEmpty();
288 }
289
290 if ( parts.contains( QStringLiteral( "layerId" ) ) &&
291 parts.value( QStringLiteral( "layerId" ) ) != QVariant() )
292 {
293 bool ok;
294 layerIndex = parts.value( QStringLiteral( "layerId" ) ).toInt( &ok );
295 if ( ok && layerIndex >= 0 )
296 isSubLayer = true;
297 else
298 layerIndex = -1;
299 }
300
301 if ( parts.contains( QStringLiteral( "subset" ) ) )
302 {
303 subsetString = parts.value( QStringLiteral( "subset" ) ).toString();
304 }
305
306 if ( parts.contains( QStringLiteral( "geometryType" ) ) )
307 {
308 ogrGeometryTypeFilter = ogrWkbGeometryTypeFromName( parts.value( QStringLiteral( "geometryType" ) ).toString() );
309 }
310
311 if ( parts.contains( QStringLiteral( "openOptions" ) ) )
312 {
313 openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
314 }
315
316 return parts.value( QStringLiteral( "path" ) ).toString();
317 }
318
createEmptyLayer(const QString & uri,const QgsFields & fields,QgsWkbTypes::Type wkbType,const QgsCoordinateReferenceSystem & srs,bool overwrite,QMap<int,int> * oldToNewAttrIdxMap,QString * errorMessage,const QMap<QString,QVariant> * options)319 QgsVectorLayerExporter::ExportError QgsOgrProvider::createEmptyLayer( const QString &uri,
320 const QgsFields &fields,
321 QgsWkbTypes::Type wkbType,
322 const QgsCoordinateReferenceSystem &srs,
323 bool overwrite,
324 QMap<int, int> *oldToNewAttrIdxMap,
325 QString *errorMessage,
326 const QMap<QString, QVariant> *options )
327 {
328 QString encoding;
329 QString driverName = QStringLiteral( "GPKG" );
330 QStringList dsOptions, layerOptions;
331 QString layerName;
332
333 if ( options )
334 {
335 if ( options->contains( QStringLiteral( "fileEncoding" ) ) )
336 encoding = options->value( QStringLiteral( "fileEncoding" ) ).toString();
337
338 if ( options->contains( QStringLiteral( "driverName" ) ) )
339 driverName = options->value( QStringLiteral( "driverName" ) ).toString();
340
341 if ( options->contains( QStringLiteral( "datasourceOptions" ) ) )
342 dsOptions << options->value( QStringLiteral( "datasourceOptions" ) ).toStringList();
343
344 if ( options->contains( QStringLiteral( "layerOptions" ) ) )
345 layerOptions << options->value( QStringLiteral( "layerOptions" ) ).toStringList();
346
347 if ( options->contains( QStringLiteral( "layerName" ) ) )
348 layerName = options->value( QStringLiteral( "layerName" ) ).toString();
349 }
350
351 oldToNewAttrIdxMap->clear();
352 if ( errorMessage )
353 errorMessage->clear();
354
355 QgsVectorFileWriter::ActionOnExistingFile action( QgsVectorFileWriter::CreateOrOverwriteFile );
356
357 bool update = false;
358 if ( options && options->contains( QStringLiteral( "update" ) ) )
359 {
360 update = options->value( QStringLiteral( "update" ) ).toBool();
361 if ( update )
362 {
363 if ( !overwrite && !layerName.isEmpty() )
364 {
365 gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
366 if ( hDS )
367 {
368 if ( GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() ) )
369 {
370 if ( errorMessage )
371 *errorMessage += QObject::tr( "Layer %2 of %1 exists and overwrite flag is false." )
372 .arg( uri, layerName );
373 return QgsVectorLayerExporter::ErrCreateDataSource;
374 }
375 }
376 }
377 if ( QFileInfo::exists( uri ) )
378 action = QgsVectorFileWriter::CreateOrOverwriteLayer;
379 }
380 }
381
382 if ( !overwrite && !update )
383 {
384 if ( QFileInfo::exists( uri ) )
385 {
386 if ( errorMessage )
387 *errorMessage += QObject::tr( "Unable to create the datasource. %1 exists and overwrite flag is false." )
388 .arg( uri );
389 return QgsVectorLayerExporter::ErrCreateDataSource;
390 }
391 }
392
393 QString newLayerName( layerName );
394
395 QgsVectorFileWriter::SaveVectorOptions saveOptions;
396 saveOptions.layerName = layerName;
397 saveOptions.fileEncoding = encoding;
398 saveOptions.driverName = driverName;
399 saveOptions.datasourceOptions = dsOptions;
400 saveOptions.layerOptions = layerOptions;
401 saveOptions.actionOnExistingFile = action;
402 saveOptions.symbologyExport = QgsVectorFileWriter::NoSymbology;
403 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( uri, fields, wkbType, srs, QgsCoordinateTransformContext(), saveOptions, QgsFeatureSink::SinkFlags(), nullptr, &newLayerName ) );
404 layerName = newLayerName;
405
406 QgsVectorFileWriter::WriterError error = writer->hasError();
407 if ( error )
408 {
409 if ( errorMessage )
410 *errorMessage += writer->errorMessage();
411
412 return static_cast<QgsVectorLayerExporter::ExportError>( error );
413 }
414
415 QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
416 writer.reset();
417
418 {
419 bool firstFieldIsFid = false;
420 bool fidColumnIsField = false;
421 if ( !layerName.isEmpty() )
422 {
423 gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
424 if ( hDS )
425 {
426 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() );
427 if ( hLayer )
428 {
429 // Expose the OGR FID if it comes from a "real" column (typically GPKG)
430 // and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
431 const QString ogrFidColumnName { OGR_L_GetFIDColumn( hLayer ) };
432 firstFieldIsFid = !( EQUAL( OGR_L_GetFIDColumn( hLayer ), "" ) ) &&
433 OGR_FD_GetFieldIndex( OGR_L_GetLayerDefn( hLayer ), ogrFidColumnName.toUtf8() ) < 0 &&
434 fields.indexFromName( ogrFidColumnName.toUtf8() ) < 0;
435 // At this point we must check if there is a real FID field in the the fields argument,
436 // because in that case we don't want to shift all fields (see issue GH #34333)
437 // Check for unique values should be performed in client code.
438 for ( const auto &f : qgis::as_const( fields ) )
439 {
440 if ( f.name().compare( ogrFidColumnName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
441 {
442 fidColumnIsField = true;
443 break;
444 }
445 }
446 }
447 }
448 }
449
450 const bool shiftColumnsByOne { firstFieldIsFid &&( ! fidColumnIsField ) };
451
452 for ( QMap<int, int>::const_iterator attrIt = attrIdxMap.constBegin(); attrIt != attrIdxMap.constEnd(); ++attrIt )
453 {
454 oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt + ( shiftColumnsByOne ? 1 : 0 ) );
455 }
456 }
457
458 QgsOgrProviderUtils::invalidateCachedLastModifiedDate( uri );
459
460 return QgsVectorLayerExporter::NoError;
461 }
462
QgsOgrProvider(QString const & uri,const ProviderOptions & options,QgsDataProvider::ReadFlags flags)463 QgsOgrProvider::QgsOgrProvider( QString const &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
464 : QgsVectorDataProvider( uri, options, flags )
465 {
466 QgsApplication::registerOgrDrivers();
467
468 QgsSettings settings;
469 // we always disable GDAL side shapefile encoding handling, and do it on the QGIS side.
470 // why? it's not the ideal choice, but...
471 // - if we DON'T disable GDAL side encoding support, then there's NO way to change the encoding used when reading
472 // shapefiles. And unfortunately the embedded encoding (which is read by GDAL) is sometimes wrong, so we need
473 // to expose support for users to be able to change and correct this
474 // - we can't change this setting on-the-fly. If we don't set it upfront, we can't reverse this decision later when
475 // a user does want/need to manually specify the encoding
476 CPLSetConfigOption( "SHAPE_ENCODING", "" );
477
478 #ifndef QT_NO_NETWORKPROXY
479 QgsGdalUtils::setupProxy();
480 #endif
481
482 // make connection to the data source
483
484 QgsDebugMsgLevel( "Data source uri is [" + uri + ']', 2 );
485
486 mFilePath = AnalyzeURI( uri,
487 mIsSubLayer,
488 mLayerIndex,
489 mLayerName,
490 mSubsetString,
491 mOgrGeometryTypeFilter,
492 mOpenOptions );
493
494 open( OpenModeInitial );
495
496 int nMaxIntLen = 11;
497 int nMaxInt64Len = 21;
498 int nMaxDoubleLen = 20;
499 int nMaxDoublePrec = 15;
500 int nDateLen = 8;
501 if ( mGDALDriverName == QLatin1String( "GPKG" ) )
502 {
503 // GPKG only supports field length for text (and binary)
504 nMaxIntLen = 0;
505 nMaxInt64Len = 0;
506 nMaxDoubleLen = 0;
507 nMaxDoublePrec = 0;
508 nDateLen = 0;
509 }
510
511 QList<NativeType> nativeTypes;
512 nativeTypes
513 << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, nMaxIntLen )
514 << QgsVectorDataProvider::NativeType( tr( "Whole number (integer 64 bit)" ), QStringLiteral( "integer64" ), QVariant::LongLong, 0, nMaxInt64Len )
515 << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, nMaxDoubleLen, 0, nMaxDoublePrec )
516 << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 65535 );
517
518 if ( mGDALDriverName == QLatin1String( "GPKG" ) )
519 nativeTypes << QgsVectorDataProvider::NativeType( tr( "JSON (string)" ), QStringLiteral( "JSON" ), QVariant::Map, 0, 0, 0, 0, QVariant::String );
520
521 bool supportsDate = true;
522 bool supportsTime = mGDALDriverName != QLatin1String( "ESRI Shapefile" ) && mGDALDriverName != QLatin1String( "GPKG" );
523 bool supportsDateTime = mGDALDriverName != QLatin1String( "ESRI Shapefile" );
524 bool supportsBinary = false;
525 bool supportsStringList = false;
526 const char *pszDataTypes = nullptr;
527 if ( mOgrOrigLayer )
528 {
529 pszDataTypes = GDALGetMetadataItem( mOgrOrigLayer->driver(), GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
530 }
531 // For drivers that advertise their data type, use that instead of the
532 // above hardcoded defaults.
533 if ( pszDataTypes )
534 {
535 char **papszTokens = CSLTokenizeString2( pszDataTypes, " ", 0 );
536 supportsDate = CSLFindString( papszTokens, "Date" ) >= 0;
537 supportsTime = CSLFindString( papszTokens, "Time" ) >= 0;
538 supportsDateTime = CSLFindString( papszTokens, "DateTime" ) >= 0;
539 supportsBinary = CSLFindString( papszTokens, "Binary" ) >= 0;
540 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
541 supportsStringList = CSLFindString( papszTokens, "StringList" ) >= 0;
542 #endif
543 CSLDestroy( papszTokens );
544 }
545
546 // Older versions of GDAL incorrectly report that shapefiles support
547 // DateTime.
548 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
549 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
550 {
551 supportsDateTime = false;
552 }
553 #endif
554
555 if ( supportsDate )
556 {
557 nativeTypes
558 << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, nDateLen, nDateLen );
559 }
560 if ( supportsTime )
561 {
562 nativeTypes
563 << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time );
564 }
565 if ( supportsDateTime )
566 {
567 nativeTypes
568 << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime );
569 }
570 if ( supportsBinary )
571 {
572 nativeTypes
573 << QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "binary" ), QVariant::ByteArray );
574 }
575 if ( supportsStringList )
576 {
577 nativeTypes
578 << QgsVectorDataProvider::NativeType( tr( "String List" ), QStringLiteral( "stringlist" ), QVariant::List, 0, 0, 0, 0, QVariant::String );
579 }
580
581 bool supportsBoolean = false;
582
583 // layer metadata
584 mLayerMetadata.setType( QStringLiteral( "dataset" ) );
585 if ( mOgrOrigLayer )
586 {
587 QMutex *mutex = nullptr;
588 OGRLayerH layer = mOgrOrigLayer->getHandleAndMutex( mutex );
589 QMutexLocker locker( mutex );
590 const QString identifier = GDALGetMetadataItem( layer, "IDENTIFIER", "" );
591 if ( !identifier.isEmpty() )
592 mLayerMetadata.setTitle( identifier ); // see geopackage specs -- "'identifier' is analogous to 'title'"
593 const QString abstract = GDALGetMetadataItem( layer, "DESCRIPTION", "" );
594 if ( !abstract.isEmpty() )
595 mLayerMetadata.setAbstract( abstract );
596 }
597
598 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0)
599 if ( mOgrOrigLayer )
600 {
601 const char *pszDataSubTypes = GDALGetMetadataItem( mOgrOrigLayer->driver(), GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
602 if ( pszDataSubTypes && strstr( pszDataSubTypes, "Boolean" ) )
603 supportsBoolean = true;
604 }
605 #else
606 if ( mGDALDriverName == QLatin1String( "GeoJSON" ) ||
607 mGDALDriverName == QLatin1String( "GML" ) ||
608 mGDALDriverName == QLatin1String( "CSV" ) ||
609 mGDALDriverName == QLatin1String( "PostgreSQL" ) ||
610 mGDALDriverName == QLatin1String( "PGDump" ) ||
611 mGDALDriverName == QLatin1String( "SQLite" ) ||
612 mGDALDriverName == QLatin1String( "GPKG" ) )
613 {
614 supportsBoolean = true;
615 }
616 #endif
617
618 if ( supportsBoolean )
619 {
620 // boolean data type
621 nativeTypes
622 << QgsVectorDataProvider::NativeType( tr( "Boolean" ), QStringLiteral( "bool" ), QVariant::Bool );
623 }
624
625 setNativeTypes( nativeTypes );
626
627 QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
628 }
629
~QgsOgrProvider()630 QgsOgrProvider::~QgsOgrProvider()
631 {
632 QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
633 // We must also make sure to flush unusef cached connections so that
634 // the file can be removed (#15137)
635 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
636
637 // Do that as last step for final cleanup that might be prevented by
638 // still opened datasets.
639 close();
640 }
641
dataSourceUri(bool expandAuthConfig) const642 QString QgsOgrProvider::dataSourceUri( bool expandAuthConfig ) const
643 {
644 if ( expandAuthConfig && QgsDataProvider::dataSourceUri( ).contains( QLatin1String( "authcfg" ) ) )
645 {
646 return QgsOgrProviderUtils::expandAuthConfig( QgsDataProvider::dataSourceUri( ) );
647 }
648 else
649 {
650 return QgsDataProvider::dataSourceUri( );
651 }
652 }
653
transaction() const654 QgsTransaction *QgsOgrProvider::transaction() const
655 {
656 return static_cast<QgsTransaction *>( mTransaction );
657 }
658
setTransaction(QgsTransaction * transaction)659 void QgsOgrProvider::setTransaction( QgsTransaction *transaction )
660 {
661 QgsDebugMsgLevel( QStringLiteral( "set transaction %1" ).arg( transaction != nullptr ), 1 );
662 // static_cast since layers cannot be added to a transaction of a non-matching provider
663 mTransaction = static_cast<QgsOgrTransaction *>( transaction );
664 }
665
featureSource() const666 QgsAbstractFeatureSource *QgsOgrProvider::featureSource() const
667 {
668 return new QgsOgrFeatureSource( this );
669 }
670
setSubsetString(const QString & theSQL,bool updateFeatureCount)671 bool QgsOgrProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
672 {
673 return _setSubsetString( theSQL, updateFeatureCount, true );
674 }
675
subsetString() const676 QString QgsOgrProvider::subsetString() const
677 {
678 return mSubsetString;
679 }
680
ogrWkbGeometryTypeName(OGRwkbGeometryType type) const681 QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
682 {
683 QString geom;
684
685 // GDAL 2.1 can return M/ZM geometries
686 if ( wkbHasM( type ) )
687 {
688 geom = ogrWkbGeometryTypeName( wkbFlatten( type ) );
689 if ( wkbHasZ( type ) )
690 geom += QLatin1Char( 'Z' );
691 if ( wkbHasM( type ) )
692 geom += QLatin1Char( 'M' );
693 return geom;
694 }
695
696 switch ( static_cast<unsigned>( type ) )
697 {
698 case wkbUnknown:
699 geom = QStringLiteral( "Unknown" );
700 break;
701 case wkbPoint:
702 geom = QStringLiteral( "Point" );
703 break;
704 case wkbLineString:
705 geom = QStringLiteral( "LineString" );
706 break;
707 case wkbPolygon:
708 geom = QStringLiteral( "Polygon" );
709 break;
710 case wkbMultiPoint:
711 geom = QStringLiteral( "MultiPoint" );
712 break;
713 case wkbMultiLineString:
714 geom = QStringLiteral( "MultiLineString" );
715 break;
716 case wkbMultiPolygon:
717 geom = QStringLiteral( "MultiPolygon" );
718 break;
719 case wkbGeometryCollection:
720 geom = QStringLiteral( "GeometryCollection" );
721 break;
722 case wkbCircularString:
723 geom = QStringLiteral( "CircularString" );
724 break;
725 case wkbCompoundCurve:
726 geom = QStringLiteral( "CompoundCurve" );
727 break;
728 case wkbCurvePolygon:
729 geom = QStringLiteral( "CurvePolygon" );
730 break;
731 case wkbMultiCurve:
732 geom = QStringLiteral( "MultiCurve" );
733 break;
734 case wkbMultiSurface:
735 geom = QStringLiteral( "MultiSurface" );
736 break;
737 case wkbCircularStringZ:
738 geom = QStringLiteral( "CircularStringZ" );
739 break;
740 case wkbCompoundCurveZ:
741 geom = QStringLiteral( "CompoundCurveZ" );
742 break;
743 case wkbCurvePolygonZ:
744 geom = QStringLiteral( "CurvePolygonZ" );
745 break;
746 case wkbMultiCurveZ:
747 geom = QStringLiteral( "MultiCurveZ" );
748 break;
749 case wkbMultiSurfaceZ:
750 geom = QStringLiteral( "MultiSurfaceZ" );
751 break;
752 case wkbNone:
753 geom = QStringLiteral( "None" );
754 break;
755 case static_cast<unsigned>( wkbUnknown ) | static_cast<unsigned>( wkb25DBit ):
756 geom = QStringLiteral( "Unknown25D" );
757 break;
758 case static_cast<unsigned>( wkbPoint25D ):
759 geom = QStringLiteral( "Point25D" );
760 break;
761 case static_cast<unsigned>( wkbLineString25D ):
762 geom = QStringLiteral( "LineString25D" );
763 break;
764 case static_cast<unsigned>( wkbPolygon25D ):
765 geom = QStringLiteral( "Polygon25D" );
766 break;
767 case static_cast<unsigned>( wkbMultiPoint25D ):
768 geom = QStringLiteral( "MultiPoint25D" );
769 break;
770 case static_cast<unsigned>( wkbMultiLineString25D ):
771 geom = QStringLiteral( "MultiLineString25D" );
772 break;
773 case static_cast<unsigned>( wkbMultiPolygon25D ):
774 geom = QStringLiteral( "MultiPolygon25D" );
775 break;
776 case static_cast<unsigned>( wkbGeometryCollection25D ):
777 geom = QStringLiteral( "GeometryCollection25D" );
778 break;
779 default:
780 // Do not use ':', as it will mess with the separator used by QgsSublayersDialog::populateLayers()
781 geom = QStringLiteral( "Unknown WKB (%1)" ).arg( type );
782 }
783 return geom;
784 }
785
ogrWkbGeometryTypeFromName(const QString & typeName)786 static OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString &typeName )
787 {
788 if ( typeName == QLatin1String( "Point" ) ) return wkbPoint;
789 else if ( typeName == QLatin1String( "LineString" ) ) return wkbLineString;
790 else if ( typeName == QLatin1String( "Polygon" ) ) return wkbPolygon;
791 else if ( typeName == QLatin1String( "MultiPoint" ) ) return wkbMultiPoint;
792 else if ( typeName == QLatin1String( "MultiLineString" ) ) return wkbMultiLineString;
793 else if ( typeName == QLatin1String( "MultiPolygon" ) ) return wkbMultiPolygon;
794 else if ( typeName == QLatin1String( "GeometryCollection" ) ) return wkbGeometryCollection;
795 else if ( typeName == QLatin1String( "None" ) ) return wkbNone;
796 else if ( typeName == QLatin1String( "Point25D" ) ) return wkbPoint25D;
797 else if ( typeName == QLatin1String( "LineString25D" ) ) return wkbLineString25D;
798 else if ( typeName == QLatin1String( "Polygon25D" ) ) return wkbPolygon25D;
799 else if ( typeName == QLatin1String( "MultiPoint25D" ) ) return wkbMultiPoint25D;
800 else if ( typeName == QLatin1String( "MultiLineString25D" ) ) return wkbMultiLineString25D;
801 else if ( typeName == QLatin1String( "MultiPolygon25D" ) ) return wkbMultiPolygon25D;
802 else if ( typeName == QLatin1String( "GeometryCollection25D" ) ) return wkbGeometryCollection25D;
803 QgsDebugMsg( QStringLiteral( "unknown geometry type: %1" ).arg( typeName ) );
804 return wkbUnknown;
805 }
806
addSubLayerDetailsToSubLayerList(int i,QgsOgrLayer * layer,bool withFeatureCount) const807 void QgsOgrProvider::addSubLayerDetailsToSubLayerList( int i, QgsOgrLayer *layer, bool withFeatureCount ) const
808 {
809 QString layerName = QString::fromUtf8( layer->name() );
810
811 if ( !mIsSubLayer && ( layerName == QLatin1String( "layer_styles" ) ||
812 layerName == QLatin1String( "qgis_projects" ) ) )
813 {
814 // Ignore layer_styles (coming from QGIS styling support) and
815 // qgis_projects (coming from http://plugins.qgis.org/plugins/QgisGeopackage/)
816 return;
817 }
818 // Get first column name,
819 // TODO: add support for multiple
820 QString geometryColumnName;
821 OGRwkbGeometryType layerGeomType = wkbUnknown;
822 const bool slowGeomTypeRetrieval =
823 mGDALDriverName == QLatin1String( "OAPIF" ) || mGDALDriverName == QLatin1String( "WFS3" ) || mGDALDriverName == QLatin1String( "PGeo" );
824 if ( !slowGeomTypeRetrieval )
825 {
826 QgsOgrFeatureDefn &fdef = layer->GetLayerDefn();
827 if ( fdef.GetGeomFieldCount() )
828 {
829 OGRGeomFieldDefnH geomH = fdef.GetGeomFieldDefn( 0 );
830 geometryColumnName = QString::fromUtf8( OGR_GFld_GetNameRef( geomH ) );
831 }
832 layerGeomType = fdef.GetGeomType();
833 }
834
835 QString longDescription;
836 if ( mGDALDriverName == QLatin1String( "OAPIF" ) || mGDALDriverName == QLatin1String( "WFS3" ) )
837 {
838 longDescription = layer->GetMetadataItem( "TITLE" );
839 }
840
841 QgsDebugMsgLevel( QStringLiteral( "id = %1 name = %2 layerGeomType = %3 longDescription = %4" ).arg( i ).arg( layerName ).arg( layerGeomType ). arg( longDescription ), 2 );
842
843 if ( slowGeomTypeRetrieval || wkbFlatten( layerGeomType ) != wkbUnknown )
844 {
845 int layerFeatureCount = withFeatureCount ? layer->GetApproxFeatureCount() : -1;
846
847 QString geom = ogrWkbGeometryTypeName( layerGeomType );
848
849 // For feature count, -1 indicates an unknown count state
850 QStringList parts = QStringList()
851 << QString::number( i )
852 << layerName
853 << QString::number( layerFeatureCount )
854 << geom
855 << geometryColumnName
856 << longDescription;
857
858 mSubLayerList << parts.join( sublayerSeparator() );
859 }
860 else
861 {
862 QgsDebugMsgLevel( QStringLiteral( "Unknown geometry type, count features for each geometry type" ), 2 );
863 // Add virtual sublayers for supported geometry types if layer type is unknown
864 // Count features for geometry types
865 QMap<OGRwkbGeometryType, int> fCount;
866 // TODO: avoid reading attributes, setRelevantFields cannot be called here because it is not constant
867
868 layer->ResetReading();
869 gdal::ogr_feature_unique_ptr fet;
870 while ( fet.reset( layer->GetNextFeature() ), fet )
871 {
872 OGRGeometryH geom = OGR_F_GetGeometryRef( fet.get() );
873 if ( geom )
874 {
875 OGRwkbGeometryType gType = ogrWkbSingleFlatten( OGR_G_GetGeometryType( geom ) );
876 fCount[gType] = fCount.value( gType ) + 1;
877 }
878 }
879 layer->ResetReading();
880 // it may happen that there are no features in the layer, in that case add unknown type
881 // to show to user that the layer exists but it is empty
882 if ( fCount.isEmpty() )
883 {
884 fCount[wkbUnknown] = 0;
885 }
886
887 // List TIN and PolyhedralSurface as Polygon
888 if ( fCount.contains( wkbTIN ) )
889 {
890 fCount[wkbPolygon] = fCount.value( wkbPolygon ) + fCount[wkbTIN];
891 fCount.remove( wkbTIN );
892 }
893 if ( fCount.contains( wkbPolyhedralSurface ) )
894 {
895 fCount[wkbPolygon] = fCount.value( wkbPolygon ) + fCount[wkbPolyhedralSurface];
896 fCount.remove( wkbPolyhedralSurface );
897 }
898 // When there are CurvePolygons, promote Polygons
899 if ( fCount.contains( wkbPolygon ) && fCount.contains( wkbCurvePolygon ) )
900 {
901 fCount[wkbCurvePolygon] += fCount.value( wkbPolygon );
902 fCount.remove( wkbPolygon );
903 }
904 // When there are CompoundCurves, promote LineStrings and CircularStrings
905 if ( fCount.contains( wkbLineString ) && fCount.contains( wkbCompoundCurve ) )
906 {
907 fCount[wkbCompoundCurve] += fCount.value( wkbLineString );
908 fCount.remove( wkbLineString );
909 }
910 if ( fCount.contains( wkbCircularString ) && fCount.contains( wkbCompoundCurve ) )
911 {
912 fCount[wkbCompoundCurve] += fCount.value( wkbCircularString );
913 fCount.remove( wkbCircularString );
914 }
915
916 bool bIs25D = wkbHasZ( layerGeomType );
917 QMap<OGRwkbGeometryType, int>::const_iterator countIt = fCount.constBegin();
918 for ( ; countIt != fCount.constEnd(); ++countIt )
919 {
920 QString geom = ogrWkbGeometryTypeName( ( bIs25D ) ? wkbSetZ( countIt.key() ) : countIt.key() );
921
922 QStringList parts = QStringList()
923 << QString::number( i )
924 << layerName
925 << QString::number( fCount.value( countIt.key() ) )
926 << geom
927 << geometryColumnName
928 << longDescription;
929
930 QString sl = parts.join( sublayerSeparator() );
931 QgsDebugMsgLevel( "sub layer: " + sl, 2 );
932 mSubLayerList << sl;
933 }
934 }
935 }
936
subLayerCount() const937 uint QgsOgrProvider::subLayerCount() const
938 {
939 uint count = layerCount();
940
941 QString errCause;
942 QgsOgrLayerUniquePtr layerStyles = QgsOgrProviderUtils::getLayer( mFilePath, "layer_styles", errCause );
943 if ( layerStyles )
944 {
945 count--;
946 }
947 return count;
948 }
949
subLayers() const950 QStringList QgsOgrProvider::subLayers() const
951 {
952 return _subLayers( true );
953 }
954
layerMetadata() const955 QgsLayerMetadata QgsOgrProvider::layerMetadata() const
956 {
957 return mLayerMetadata;
958 }
959
subLayersWithoutFeatureCount() const960 QStringList QgsOgrProvider::subLayersWithoutFeatureCount() const
961 {
962 return _subLayers( false );
963 }
964
_subLayers(bool withFeatureCount) const965 QStringList QgsOgrProvider::_subLayers( bool withFeatureCount ) const
966 {
967 if ( !mValid )
968 {
969 return QStringList();
970 }
971
972 if ( !mSubLayerList.isEmpty() )
973 return mSubLayerList;
974
975 if ( mOgrLayer && ( mIsSubLayer || layerCount() == 1 ) )
976 {
977 addSubLayerDetailsToSubLayerList( mLayerIndex, mOgrLayer, withFeatureCount );
978 }
979 else
980 {
981 // In case there is no free opened dataset in the cache, keep the first
982 // layer alive while we iterate over the other layers, so that we can
983 // reuse the same dataset. Can help in a particular with a FileGDB with
984 // the FileGDB driver
985 QgsOgrLayerUniquePtr firstLayer;
986 for ( unsigned int i = 0; i < layerCount() ; i++ )
987 {
988 QString errCause;
989 QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( mOgrOrigLayer->datasetName(),
990 mOgrOrigLayer->updateMode(),
991 mOgrOrigLayer->options(),
992 i,
993 errCause,
994 // do not check timestamp beyond the first
995 // layer
996 firstLayer == nullptr );
997 if ( !layer )
998 continue;
999
1000 addSubLayerDetailsToSubLayerList( i, layer.get(), withFeatureCount );
1001 if ( firstLayer == nullptr )
1002 {
1003 firstLayer = std::move( layer );
1004 }
1005 }
1006 }
1007 return mSubLayerList;
1008 }
1009
setEncoding(const QString & e)1010 void QgsOgrProvider::setEncoding( const QString &e )
1011 {
1012 QgsSettings settings;
1013
1014 // if the layer has the OLCStringsAsUTF8 capability, we CANNOT override the
1015 // encoding on the QGIS side!
1016 if ( mOgrLayer && !mOgrLayer->TestCapability( OLCStringsAsUTF8 ) )
1017 {
1018 QgsVectorDataProvider::setEncoding( e );
1019 }
1020 else
1021 {
1022 QgsVectorDataProvider::setEncoding( QStringLiteral( "UTF-8" ) );
1023 }
1024 loadFields();
1025 }
1026
1027 // This is reused by dataItem
getOgrGeomType(const QString & driverName,OGRLayerH ogrLayer)1028 OGRwkbGeometryType QgsOgrProvider::getOgrGeomType( const QString &driverName, OGRLayerH ogrLayer )
1029 {
1030 OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( ogrLayer );
1031 OGRwkbGeometryType geomType = wkbUnknown;
1032 if ( fdef )
1033 {
1034 geomType = OGR_FD_GetGeomType( fdef );
1035
1036 // Handle wkbUnknown and its Z/M variants. QGIS has no unknown Z/M variants,
1037 // so just use flat wkbUnknown
1038 if ( wkbFlatten( geomType ) == wkbUnknown )
1039 geomType = wkbUnknown;
1040
1041 // Some ogr drivers (e.g. GML) are not able to determine the geometry type of a layer like this.
1042 // In such cases, we use virtual sublayers for each geometry if the layer contains
1043 // multiple geometries (see subLayers) otherwise we guess geometry type from the first
1044 // feature that has a geometry (limit us to a few features, not the whole layer)
1045 //
1046 // For ESRI formats with a GeometryCollection25D type we also query features for the geometry type,
1047 // as they may be ESRI MultiPatch files which we want to report as MultiPolygon25D types
1048 if ( geomType == wkbUnknown
1049 || ( geomType == wkbGeometryCollection25D && ( driverName == QLatin1String( "ESRI Shapefile" ) || driverName == QLatin1String( "OpenFileGDB" ) || driverName == QLatin1String( "FileGDB" ) ) ) )
1050 {
1051 geomType = wkbNone;
1052 OGR_L_ResetReading( ogrLayer );
1053 for ( int i = 0; i < 10; i++ )
1054 {
1055 gdal::ogr_feature_unique_ptr nextFeature( OGR_L_GetNextFeature( ogrLayer ) );
1056 if ( !nextFeature )
1057 break;
1058
1059 OGRGeometryH geometry = OGR_F_GetGeometryRef( nextFeature.get() );
1060 if ( geometry )
1061 {
1062 geomType = OGR_G_GetGeometryType( geometry );
1063
1064 // ESRI MultiPatch can be reported as GeometryCollectionZ of TINZ
1065 if ( wkbFlatten( geomType ) == wkbGeometryCollection &&
1066 ( driverName == QLatin1String( "ESRI Shapefile" ) || driverName == QLatin1String( "OpenFileGDB" ) || driverName == QLatin1String( "FileGDB" ) ) &&
1067 OGR_G_GetGeometryCount( geometry ) >= 1 &&
1068 wkbFlatten( OGR_G_GetGeometryType( OGR_G_GetGeometryRef( geometry, 0 ) ) ) == wkbTIN )
1069 {
1070 geomType = wkbMultiPolygon25D;
1071 }
1072 }
1073 if ( geomType != wkbNone )
1074 break;
1075 }
1076 OGR_L_ResetReading( ogrLayer );
1077 }
1078 }
1079 return geomType;
1080 }
1081
loadFields()1082 void QgsOgrProvider::loadFields()
1083 {
1084 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
1085 //the attribute fields need to be read again when the encoding changes
1086 mAttributeFields.clear();
1087 mDefaultValues.clear();
1088 mPrimaryKeyAttrs.clear();
1089 if ( !mOgrLayer )
1090 return;
1091
1092 if ( mOgrGeometryTypeFilter != wkbUnknown )
1093 {
1094 mOGRGeomType = mOgrGeometryTypeFilter;
1095 }
1096 else
1097 {
1098 QMutex *mutex = nullptr;
1099 OGRLayerH ogrLayer = mOgrLayer->getHandleAndMutex( mutex );
1100 QMutexLocker locker( mutex );
1101 mOGRGeomType = getOgrGeomType( mGDALDriverName, ogrLayer );
1102 }
1103 QgsOgrFeatureDefn &fdef = mOgrLayer->GetLayerDefn();
1104
1105 // Expose the OGR FID if it comes from a "real" column (typically GPKG)
1106 // and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
1107 QByteArray fidColumn( mOgrLayer->GetFIDColumn() );
1108 mFirstFieldIsFid = !fidColumn.isEmpty() &&
1109 fdef.GetFieldIndex( fidColumn ) < 0;
1110
1111 // This is a temporary solution until GDAL Unique support is available
1112 QSet<QString> uniqueFieldNames;
1113
1114
1115 if ( mGDALDriverName == QLatin1String( "GPKG" ) )
1116 {
1117 sqlite3_database_unique_ptr dsPtr;
1118 if ( dsPtr.open_v2( mFilePath, SQLITE_OPEN_READONLY, nullptr ) == SQLITE_OK )
1119 {
1120 QString errMsg;
1121 uniqueFieldNames = QgsSqliteUtils::uniqueFields( dsPtr.get(), mOgrLayer->name(), errMsg );
1122 if ( ! errMsg.isEmpty() )
1123 {
1124 QgsMessageLog::logMessage( tr( "GPKG error searching for unique constraints on fields for table %1. (%2)" ).arg( QString( mOgrLayer->name() ), errMsg ), tr( "OGR" ) );
1125 }
1126 }
1127 }
1128
1129 int createdFields = 0;
1130 if ( mFirstFieldIsFid )
1131 {
1132 QgsField fidField(
1133 fidColumn,
1134 QVariant::LongLong,
1135 QStringLiteral( "Integer64" )
1136 );
1137 // Set constraints for feature id
1138 QgsFieldConstraints constraints = fidField.constraints();
1139 constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
1140 constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
1141 fidField.setConstraints( constraints );
1142 mAttributeFields.append(
1143 fidField
1144 );
1145 mDefaultValues.insert( 0, tr( "Autogenerate" ) );
1146 createdFields++;
1147 mPrimaryKeyAttrs << 0;
1148 }
1149
1150 for ( int i = 0; i < fdef.GetFieldCount(); ++i )
1151 {
1152 OGRFieldDefnH fldDef = fdef.GetFieldDefn( i );
1153 OGRFieldType ogrType = OGR_Fld_GetType( fldDef );
1154 OGRFieldSubType ogrSubType = OFSTNone;
1155
1156 QVariant::Type varType;
1157 QVariant::Type varSubType = QVariant::Invalid;
1158 switch ( ogrType )
1159 {
1160 case OFTInteger:
1161 if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
1162 {
1163 varType = QVariant::Bool;
1164 ogrSubType = OFSTBoolean;
1165 }
1166 else
1167 varType = QVariant::Int;
1168 break;
1169 case OFTInteger64:
1170 varType = QVariant::LongLong;
1171 break;
1172 case OFTReal:
1173 varType = QVariant::Double;
1174 break;
1175 case OFTDate:
1176 varType = QVariant::Date;
1177 break;
1178 case OFTTime:
1179 varType = QVariant::Time;
1180 break;
1181 case OFTDateTime:
1182 varType = QVariant::DateTime;
1183 break;
1184
1185 case OFTBinary:
1186 varType = QVariant::ByteArray;
1187 break;
1188
1189 case OFTString:
1190 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1191 if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
1192 {
1193 ogrSubType = OFSTJSON;
1194 varType = QVariant::Map;
1195 varSubType = QVariant::String;
1196 }
1197 else
1198 {
1199 varType = QVariant::String;
1200 }
1201 break;
1202 #endif
1203
1204 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1205 case OFTStringList:
1206 varType = QVariant::List;
1207 varSubType = QVariant::String;
1208 break;
1209 #endif
1210
1211 default:
1212 varType = QVariant::String; // other unsupported, leave it as a string
1213 }
1214
1215 //TODO: fix this hack
1216 #ifdef ANDROID
1217 QString name = OGR_Fld_GetNameRef( fldDef );
1218 #else
1219 QString name = textEncoding()->toUnicode( OGR_Fld_GetNameRef( fldDef ) );
1220 #endif
1221
1222 if ( mAttributeFields.indexFromName( name ) != -1 )
1223 {
1224
1225 QString tmpname = name + "_%1";
1226 int fix = 0;
1227
1228 while ( mAttributeFields.indexFromName( name ) != -1 )
1229 {
1230 name = tmpname.arg( ++fix );
1231 }
1232 }
1233
1234 int width = OGR_Fld_GetWidth( fldDef );
1235 int prec = OGR_Fld_GetPrecision( fldDef );
1236 if ( prec > 0 )
1237 width -= 1;
1238
1239 QString typeName = OGR_GetFieldTypeName( ogrType );
1240 if ( ogrSubType != OFSTNone )
1241 typeName = OGR_GetFieldSubTypeName( ogrSubType );
1242
1243 QgsField newField = QgsField(
1244 name,
1245 varType,
1246 #ifdef ANDROID
1247 typeName,
1248 #else
1249 textEncoding()->toUnicode( typeName.toStdString().c_str() ),
1250 #endif
1251 width, prec, QString(), varSubType
1252 );
1253
1254 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,2,0)
1255 const QString alias = textEncoding()->toUnicode( OGR_Fld_GetAlternativeNameRef( fldDef ) );
1256 if ( !alias.isEmpty() )
1257 {
1258 newField.setAlias( alias );
1259 }
1260 #endif
1261
1262 // check if field is nullable
1263 bool nullable = OGR_Fld_IsNullable( fldDef );
1264 if ( !nullable )
1265 {
1266 QgsFieldConstraints constraints;
1267 constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
1268 newField.setConstraints( constraints );
1269 }
1270
1271 if ( uniqueFieldNames.contains( OGR_Fld_GetNameRef( fldDef ) ) )
1272 {
1273 QgsFieldConstraints constraints = newField.constraints();
1274 constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
1275 newField.setConstraints( constraints );
1276 }
1277
1278 // check if field has default value
1279 QString defaultValue = textEncoding()->toUnicode( OGR_Fld_GetDefault( fldDef ) );
1280 if ( !defaultValue.isEmpty() && !OGR_Fld_IsDefaultDriverSpecific( fldDef ) )
1281 {
1282 if ( defaultValue.startsWith( '\'' ) )
1283 {
1284 defaultValue = defaultValue.remove( 0, 1 );
1285 defaultValue.chop( 1 );
1286 defaultValue.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
1287 }
1288 mDefaultValues.insert( createdFields, defaultValue );
1289 }
1290
1291 mAttributeFields.append( newField );
1292 createdFields++;
1293 }
1294 }
1295
1296
storageType() const1297 QString QgsOgrProvider::storageType() const
1298 {
1299 // Delegate to the driver loaded in by OGR
1300 return mGDALDriverName;
1301 }
1302
1303
setRelevantFields(bool fetchGeometry,const QgsAttributeList & fetchAttributes)1304 void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes )
1305 {
1306 QMutex *mutex = nullptr;
1307 OGRLayerH ogrLayer = mOgrLayer->getHandleAndMutex( mutex );
1308 QMutexLocker locker( mutex );
1309 QgsOgrProviderUtils::setRelevantFields( ogrLayer, mAttributeFields.count(), fetchGeometry, fetchAttributes, mFirstFieldIsFid, mSubsetString );
1310 }
1311
1312
setRelevantFields(OGRLayerH ogrLayer,int fieldCount,bool fetchGeometry,const QgsAttributeList & fetchAttributes,bool firstAttrIsFid,const QString & subsetString)1313 void QgsOgrProviderUtils::setRelevantFields( OGRLayerH ogrLayer, int fieldCount,
1314 bool fetchGeometry,
1315 const QgsAttributeList &fetchAttributes,
1316 bool firstAttrIsFid,
1317 const QString &subsetString )
1318 {
1319 if ( OGR_L_TestCapability( ogrLayer, OLCIgnoreFields ) )
1320 {
1321 QVector<const char *> ignoredFields;
1322 OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn( ogrLayer );
1323 for ( int i = ( firstAttrIsFid ? 1 : 0 ); i < fieldCount; i++ )
1324 {
1325 if ( !fetchAttributes.contains( i ) )
1326 {
1327 // add to ignored fields
1328 if ( OGRFieldDefnH field = OGR_FD_GetFieldDefn( featDefn, firstAttrIsFid ? i - 1 : i ) )
1329 {
1330 const char *fieldName = OGR_Fld_GetNameRef( field );
1331 // This is implemented a bit in a hacky way, but in case we are acting on a layer
1332 // with a subset filter, do not ignore fields that are found in the
1333 // where clause. We do this in a rough way, by looking, in a case
1334 // insensitive way, if the current field name is in the subsetString,
1335 // so we potentially don't ignore fields we could, in situations like
1336 // subsetFilter == "foobar = 2", and there's a "foo" or "bar" field.
1337 // Better be safe than sorry.
1338 // We could argue that OGR_L_SetIgnoredFields() should be aware of
1339 // the fields of the attribute filter, and do not ignore them.
1340 if ( subsetString.isEmpty() ||
1341 subsetString.indexOf( QString::fromUtf8( fieldName ), 0, Qt::CaseInsensitive ) < 0 )
1342 {
1343 ignoredFields.append( fieldName );
1344 }
1345 }
1346 }
1347 }
1348
1349 if ( !fetchGeometry )
1350 ignoredFields.append( "OGR_GEOMETRY" );
1351 ignoredFields.append( "OGR_STYLE" ); // not used by QGIS
1352 ignoredFields.append( nullptr );
1353
1354 OGR_L_SetIgnoredFields( ogrLayer, ignoredFields.data() );
1355 }
1356 }
1357
getFeatures(const QgsFeatureRequest & request) const1358 QgsFeatureIterator QgsOgrProvider::getFeatures( const QgsFeatureRequest &request ) const
1359 {
1360 return QgsFeatureIterator( new QgsOgrFeatureIterator( static_cast<QgsOgrFeatureSource *>( featureSource() ), true, request, mTransaction ) );
1361 }
1362
1363
getGeometryPointer(OGRFeatureH fet)1364 unsigned char *QgsOgrProvider::getGeometryPointer( OGRFeatureH fet )
1365 {
1366 OGRGeometryH geom = OGR_F_GetGeometryRef( fet );
1367 unsigned char *gPtr = nullptr;
1368
1369 if ( !geom )
1370 return nullptr;
1371
1372 // get the wkb representation
1373 gPtr = new unsigned char[OGR_G_WkbSize( geom )];
1374
1375 OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), gPtr );
1376 return gPtr;
1377 }
1378
1379
extent() const1380 QgsRectangle QgsOgrProvider::extent() const
1381 {
1382 if ( !mExtent )
1383 {
1384 mExtent.reset( new OGREnvelope() );
1385
1386 // get the extent_ (envelope) of the layer
1387 QgsDebugMsgLevel( QStringLiteral( "Starting get extent" ), 3 );
1388
1389 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,2)
1390 if ( mForceRecomputeExtent && mValid && mGDALDriverName == QLatin1String( "GPKG" ) && mOgrOrigLayer )
1391 {
1392 // works with unquoted layerName
1393 QByteArray sql = QByteArray( "RECOMPUTE EXTENT ON " ) + mOgrOrigLayer->name();
1394 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( QString::fromUtf8( sql ) ), 2 );
1395 mOgrOrigLayer->ExecuteSQLNoReturn( sql );
1396 }
1397 #endif
1398
1399 mExtent->MinX = std::numeric_limits<double>::max();
1400 mExtent->MinY = std::numeric_limits<double>::max();
1401 mExtent->MaxX = -std::numeric_limits<double>::max();
1402 mExtent->MaxY = -std::numeric_limits<double>::max();
1403
1404 // TODO: This can be expensive, do we really need it!
1405 if ( mOgrLayer == mOgrOrigLayer.get() && mSubsetString.isEmpty() )
1406 {
1407 if ( ( mGDALDriverName == QLatin1String( "OAPIF" ) || mGDALDriverName == QLatin1String( "WFS3" ) ) &&
1408 !mOgrLayer->TestCapability( OLCFastGetExtent ) )
1409 {
1410 // When the extent is not in the metadata, retrieving it would be
1411 // super slow
1412 mExtent->MinX = -180;
1413 mExtent->MinY = -90;
1414 mExtent->MaxX = 180;
1415 mExtent->MaxY = 90;
1416 }
1417 else
1418 {
1419 mOgrLayer->GetExtent( mExtent.get(), true );
1420 }
1421 }
1422 else
1423 {
1424 gdal::ogr_feature_unique_ptr f;
1425
1426 mOgrLayer->ResetReading();
1427 while ( f.reset( mOgrLayer->GetNextFeature() ), f )
1428 {
1429 OGRGeometryH g = OGR_F_GetGeometryRef( f.get() );
1430 if ( g && !OGR_G_IsEmpty( g ) )
1431 {
1432 OGREnvelope env;
1433 OGR_G_GetEnvelope( g, &env );
1434
1435 mExtent->MinX = std::min( mExtent->MinX, env.MinX );
1436 mExtent->MinY = std::min( mExtent->MinY, env.MinY );
1437 mExtent->MaxX = std::max( mExtent->MaxX, env.MaxX );
1438 mExtent->MaxY = std::max( mExtent->MaxY, env.MaxY );
1439 }
1440 }
1441 mOgrLayer->ResetReading();
1442 }
1443
1444 QgsDebugMsgLevel( QStringLiteral( "Finished get extent" ), 4 );
1445 }
1446
1447 mExtentRect.set( mExtent->MinX, mExtent->MinY, mExtent->MaxX, mExtent->MaxY );
1448 return mExtentRect;
1449 }
1450
defaultValue(int fieldId) const1451 QVariant QgsOgrProvider::defaultValue( int fieldId ) const
1452 {
1453 if ( fieldId < 0 || fieldId >= mAttributeFields.count() )
1454 return QVariant();
1455
1456 QString defaultVal = mDefaultValues.value( fieldId, QString() );
1457 if ( defaultVal.isEmpty() )
1458 return QVariant();
1459
1460 QVariant resultVar = defaultVal;
1461 if ( defaultVal == QLatin1String( "CURRENT_TIMESTAMP" ) )
1462 resultVar = QDateTime::currentDateTime();
1463 else if ( defaultVal == QLatin1String( "CURRENT_DATE" ) )
1464 resultVar = QDate::currentDate();
1465 else if ( defaultVal == QLatin1String( "CURRENT_TIME" ) )
1466 resultVar = QTime::currentTime();
1467
1468 // Get next sequence value for sqlite in case we are inside a transaction
1469 if ( mOgrOrigLayer &&
1470 mTransaction &&
1471 mDefaultValues.value( fieldId, QString() ) == tr( "Autogenerate" ) &&
1472 providerProperty( EvaluateDefaultValues, false ).toBool() &&
1473 ( mGDALDriverName == QLatin1String( "GPKG" ) ||
1474 mGDALDriverName == QLatin1String( "SQLite" ) ) &&
1475 mFirstFieldIsFid &&
1476 fieldId == 0 )
1477 {
1478 QgsOgrLayerUniquePtr resultLayer = mOgrOrigLayer->ExecuteSQL( QByteArray( "SELECT seq FROM sqlite_sequence WHERE name = " ) + QgsSqliteUtils::quotedValue( mOgrOrigLayer->name() ).toUtf8() );
1479 if ( resultLayer )
1480 {
1481 gdal::ogr_feature_unique_ptr f;
1482 if ( f.reset( resultLayer->GetNextFeature() ), f )
1483 {
1484 bool ok { true };
1485 const QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(),
1486 fields().at( 0 ),
1487 0, textEncoding(), &ok );
1488 if ( ok )
1489 {
1490 long long nextVal { res.toLongLong( &ok ) };
1491 if ( ok )
1492 {
1493 // Increment
1494 resultVar = ++nextVal;
1495 mOgrOrigLayer->ExecuteSQLNoReturn( QByteArray( "UPDATE sqlite_sequence SET seq = seq + 1 WHERE name = " ) + QgsSqliteUtils::quotedValue( mOgrOrigLayer->name() ).toUtf8() );
1496 }
1497 }
1498
1499 if ( ! ok )
1500 {
1501 QgsMessageLog::logMessage( tr( "Error retrieving next sequence value for %1" ).arg( QString::fromUtf8( mOgrOrigLayer->name() ) ), tr( "OGR" ) );
1502 }
1503 }
1504 else // no sequence!
1505 {
1506 resultVar = 1;
1507 mOgrOrigLayer->ExecuteSQLNoReturn( QByteArray( "INSERT INTO sqlite_sequence (name, seq) VALUES( " +
1508 QgsSqliteUtils::quotedValue( mOgrOrigLayer->name() ).toUtf8() ) + ", 1)" );
1509 }
1510 }
1511 else
1512 {
1513 QgsMessageLog::logMessage( tr( "Error retrieving default value for %1" ).arg( mLayerName ), tr( "OGR" ) );
1514 }
1515 }
1516
1517 ( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar );
1518 return resultVar;
1519 }
1520
defaultValueClause(int fieldIndex) const1521 QString QgsOgrProvider::defaultValueClause( int fieldIndex ) const
1522 {
1523 // Return empty clause to force defaultValue calls for sqlite in case we are inside a transaction
1524 if ( mTransaction &&
1525 mDefaultValues.value( fieldIndex, QString() ) == tr( "Autogenerate" ) &&
1526 providerProperty( EvaluateDefaultValues, false ).toBool() &&
1527 ( mGDALDriverName == QLatin1String( "GPKG" ) ||
1528 mGDALDriverName == QLatin1String( "SQLite" ) ) &&
1529 mFirstFieldIsFid &&
1530 fieldIndex == 0 )
1531 return QString();
1532 else
1533 return mDefaultValues.value( fieldIndex, QString() );
1534 }
1535
skipConstraintCheck(int fieldIndex,QgsFieldConstraints::Constraint constraint,const QVariant & value) const1536 bool QgsOgrProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const
1537 {
1538 Q_UNUSED( constraint )
1539 if ( providerProperty( EvaluateDefaultValues, false ).toBool() )
1540 {
1541 return ! mDefaultValues.value( fieldIndex ).isEmpty();
1542 }
1543 else
1544 {
1545 // stricter check
1546 return mDefaultValues.contains( fieldIndex ) && mDefaultValues.value( fieldIndex ) == value.toString() && !value.isNull();
1547 }
1548 }
1549
updateExtents()1550 void QgsOgrProvider::updateExtents()
1551 {
1552 invalidateCachedExtent( true );
1553 }
1554
invalidateCachedExtent(bool bForceRecomputeExtent)1555 void QgsOgrProvider::invalidateCachedExtent( bool bForceRecomputeExtent )
1556 {
1557 mForceRecomputeExtent = bForceRecomputeExtent;
1558 mExtent.reset();
1559 }
1560
layerCount() const1561 size_t QgsOgrProvider::layerCount() const
1562 {
1563 if ( !mValid )
1564 return 0;
1565 return mOgrLayer->GetLayerCount();
1566 }
1567
1568 /**
1569 * Returns the feature type
1570 */
wkbType() const1571 QgsWkbTypes::Type QgsOgrProvider::wkbType() const
1572 {
1573 QgsWkbTypes::Type wkb = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( mOGRGeomType );
1574 const QgsWkbTypes::Type wkbFlat = QgsWkbTypes::flatType( wkb );
1575 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) && ( wkbFlat == QgsWkbTypes::LineString || wkbFlat == QgsWkbTypes::Polygon ) )
1576 {
1577 wkb = QgsWkbTypes::multiType( wkb );
1578 }
1579 if ( mOGRGeomType % 1000 == wkbPolyhedralSurface ) // is PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM => map to MultiPolygon
1580 {
1581 wkb = static_cast<QgsWkbTypes::Type>( mOGRGeomType - ( wkbPolyhedralSurface - wkbMultiPolygon ) );
1582 }
1583 else if ( mOGRGeomType % 1000 == wkbTIN ) // is TIN, TINZ, TINM or TINZM => map to MultiPolygon
1584 {
1585 wkb = static_cast<QgsWkbTypes::Type>( mOGRGeomType - ( wkbTIN - wkbMultiPolygon ) );
1586 }
1587 return wkb;
1588 }
1589
1590 /**
1591 * Returns the feature count
1592 */
featureCount() const1593 long QgsOgrProvider::featureCount() const
1594 {
1595 return mFeaturesCounted;
1596 }
1597
1598
fields() const1599 QgsFields QgsOgrProvider::fields() const
1600 {
1601 return mAttributeFields;
1602 }
1603
1604
1605 //TODO - add sanity check for shape file layers, to include checking to
1606 // see if the .shp, .dbf, .shx files are all present and the layer
1607 // actually has features
isValid() const1608 bool QgsOgrProvider::isValid() const
1609 {
1610 return mValid;
1611 }
1612
1613 // Drivers may be more tolerant than we really wish (e.g. GeoPackage driver
1614 // may accept any geometry type)
ConvertGeometryIfNecessary(OGRGeometryH hGeom)1615 OGRGeometryH QgsOgrProvider::ConvertGeometryIfNecessary( OGRGeometryH hGeom )
1616 {
1617 if ( !hGeom )
1618 return hGeom;
1619 OGRwkbGeometryType layerGeomType = mOgrLayer->GetLayerDefn().GetGeomType();
1620 OGRwkbGeometryType flattenLayerGeomType = wkbFlatten( layerGeomType );
1621 OGRwkbGeometryType geomType = OGR_G_GetGeometryType( hGeom );
1622 OGRwkbGeometryType flattenGeomType = wkbFlatten( geomType );
1623
1624 if ( flattenLayerGeomType == wkbUnknown || flattenLayerGeomType == flattenGeomType )
1625 {
1626 return hGeom;
1627 }
1628 if ( flattenLayerGeomType == wkbMultiPolygon && flattenGeomType == wkbPolygon )
1629 {
1630 return OGR_G_ForceToMultiPolygon( hGeom );
1631 }
1632 if ( flattenLayerGeomType == wkbMultiLineString && flattenGeomType == wkbLineString )
1633 {
1634 return OGR_G_ForceToMultiLineString( hGeom );
1635 }
1636
1637 return OGR_G_ForceTo( hGeom, layerGeomType, nullptr );
1638 }
1639
jsonStringValue(const QVariant & value) const1640 QString QgsOgrProvider::jsonStringValue( const QVariant &value ) const
1641 {
1642 QString stringValue = QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson().constData() );
1643 if ( stringValue.isEmpty() )
1644 {
1645 //store as string, because it's no valid QJson value
1646 stringValue = value.toString();
1647 }
1648 return stringValue;
1649 }
1650
addFeaturePrivate(QgsFeature & f,Flags flags)1651 bool QgsOgrProvider::addFeaturePrivate( QgsFeature &f, Flags flags )
1652 {
1653 bool returnValue = true;
1654 QgsOgrFeatureDefn &featureDefinition = mOgrLayer->GetLayerDefn();
1655 gdal::ogr_feature_unique_ptr feature( featureDefinition.CreateFeature() );
1656
1657 if ( f.hasGeometry() )
1658 {
1659 QByteArray wkb( f.geometry().asWkb() );
1660 OGRGeometryH geom = nullptr;
1661
1662 if ( !wkb.isEmpty() )
1663 {
1664 if ( OGR_G_CreateFromWkb( reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), nullptr, &geom, wkb.length() ) != OGRERR_NONE )
1665 {
1666 pushError( tr( "OGR error creating wkb for feature %1: %2" ).arg( f.id() ).arg( CPLGetLastErrorMsg() ) );
1667 return false;
1668 }
1669
1670 geom = ConvertGeometryIfNecessary( geom );
1671
1672 OGR_F_SetGeometryDirectly( feature.get(), geom );
1673 }
1674 }
1675
1676 QgsAttributes attributes = f.attributes();
1677 const QgsFields qgisFields { f.fields() };
1678
1679 QgsLocaleNumC l;
1680
1681 int qgisAttributeId = ( mFirstFieldIsFid ) ? 1 : 0;
1682 // If the first attribute is the FID and the user has set it, then use it
1683 if ( mFirstFieldIsFid && attributes.count() > 0 )
1684 {
1685 QVariant attrFid = attributes.at( 0 );
1686 if ( !attrFid.isNull() )
1687 {
1688 bool ok = false;
1689 qlonglong id = attrFid.toLongLong( &ok );
1690 if ( ok )
1691 {
1692 OGR_F_SetFID( feature.get(), static_cast<GIntBig>( id ) );
1693 }
1694 }
1695 }
1696
1697 //add possible attribute information
1698 for ( int ogrAttributeId = 0; qgisAttributeId < attributes.count(); ++qgisAttributeId, ++ogrAttributeId )
1699 {
1700 // Skip fields that have no provider origin
1701 if ( qgisFields.exists( qgisAttributeId ) && qgisFields.fieldOrigin( qgisAttributeId ) != QgsFields::FieldOrigin::OriginProvider )
1702 {
1703 qgisAttributeId++;
1704 continue;
1705 }
1706
1707 // don't try to set field from attribute map if it's not present in layer
1708 if ( ogrAttributeId >= featureDefinition.GetFieldCount() )
1709 {
1710 pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( featureDefinition.GetFieldCount() ).arg( f.attributes().count() ) );
1711 continue;
1712 }
1713
1714 //if(!s.isEmpty())
1715 // continue;
1716 //
1717 OGRFieldDefnH fldDef = featureDefinition.GetFieldDefn( ogrAttributeId );
1718 OGRFieldType type = OGR_Fld_GetType( fldDef );
1719
1720 QVariant attrVal = attributes.at( qgisAttributeId );
1721 // The field value is equal to the default (that might be a provider-side expression)
1722 if ( mDefaultValues.contains( qgisAttributeId ) && attrVal.toString() == mDefaultValues.value( qgisAttributeId ) )
1723 {
1724 OGR_F_UnsetField( feature.get(), ogrAttributeId );
1725 }
1726 else if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) )
1727 {
1728 // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
1729 // whereas previously there was only unset fields. For a GeoJSON output,
1730 // leaving a field unset will cause it to not appear at all in the output
1731 // feature.
1732 // When all features of a layer have a field unset, this would cause the
1733 // field to not be present at all in the output, and thus on reading to
1734 // have disappeared. #16812
1735 #ifdef OGRNullMarker
1736 OGR_F_SetFieldNull( feature.get(), ogrAttributeId );
1737 #else
1738 OGR_F_UnsetField( feature.get(), ogrAttId );
1739 #endif
1740 }
1741 else
1742 {
1743 switch ( type )
1744 {
1745 case OFTInteger:
1746 OGR_F_SetFieldInteger( feature.get(), ogrAttributeId, attrVal.toInt() );
1747 break;
1748
1749
1750 case OFTInteger64:
1751 OGR_F_SetFieldInteger64( feature.get(), ogrAttributeId, attrVal.toLongLong() );
1752 break;
1753
1754 case OFTReal:
1755 OGR_F_SetFieldDouble( feature.get(), ogrAttributeId, attrVal.toDouble() );
1756 break;
1757
1758 case OFTDate:
1759 OGR_F_SetFieldDateTime( feature.get(), ogrAttributeId,
1760 attrVal.toDate().year(),
1761 attrVal.toDate().month(),
1762 attrVal.toDate().day(),
1763 0, 0, 0,
1764 0 );
1765 break;
1766
1767 case OFTTime:
1768 OGR_F_SetFieldDateTime( feature.get(), ogrAttributeId,
1769 0, 0, 0,
1770 attrVal.toTime().hour(),
1771 attrVal.toTime().minute(),
1772 attrVal.toTime().second(),
1773 0 );
1774 break;
1775
1776 case OFTDateTime:
1777 OGR_F_SetFieldDateTime( feature.get(), ogrAttributeId,
1778 attrVal.toDateTime().date().year(),
1779 attrVal.toDateTime().date().month(),
1780 attrVal.toDateTime().date().day(),
1781 attrVal.toDateTime().time().hour(),
1782 attrVal.toDateTime().time().minute(),
1783 attrVal.toDateTime().time().second(),
1784 0 );
1785 break;
1786
1787 case OFTString:
1788 {
1789 QString stringValue;
1790
1791 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1792 if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
1793 stringValue = jsonStringValue( attrVal );
1794 else
1795 {
1796 stringValue = attrVal.toString();
1797 }
1798 #else
1799 stringValue = attrVal.toString();
1800 #endif
1801 QgsDebugMsgLevel( QStringLiteral( "Writing string attribute %1 with %2, encoding %3" )
1802 .arg( qgisAttributeId )
1803 .arg( attrVal.toString(),
1804 textEncoding()->name().data() ), 3 );
1805 OGR_F_SetFieldString( feature.get(), ogrAttributeId, textEncoding()->fromUnicode( stringValue ).constData() );
1806 break;
1807 }
1808 case OFTBinary:
1809 {
1810 const QByteArray ba = attrVal.toByteArray();
1811 OGR_F_SetFieldBinary( feature.get(), ogrAttributeId, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
1812 break;
1813 }
1814
1815 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1816 case OFTStringList:
1817 {
1818 QStringList list = attrVal.toStringList();
1819 int count = list.count();
1820 char **lst = new char *[count + 1];
1821 if ( count > 0 )
1822 {
1823 int pos = 0;
1824 for ( QString string : list )
1825 {
1826 lst[pos] = textEncoding()->fromUnicode( string ).data();
1827 pos++;
1828 }
1829 }
1830 lst[count] = nullptr;
1831 OGR_F_SetFieldStringList( feature.get(), ogrAttributeId, lst );
1832 break;
1833 }
1834 #endif
1835
1836 default:
1837 QgsMessageLog::logMessage( tr( "type %1 for attribute %2 not found" ).arg( type ).arg( qgisAttributeId ), tr( "OGR" ) );
1838 break;
1839 }
1840 }
1841 }
1842
1843 if ( mOgrLayer->CreateFeature( feature.get() ) != OGRERR_NONE )
1844 {
1845 pushError( tr( "OGR error creating feature %1: %2" ).arg( f.id() ).arg( CPLGetLastErrorMsg() ) );
1846 returnValue = false;
1847 }
1848 else if ( !( flags & QgsFeatureSink::FastInsert ) )
1849 {
1850 QgsFeatureId id = static_cast<QgsFeatureId>( OGR_F_GetFID( feature.get() ) );
1851 if ( id >= 0 )
1852 {
1853 f.setId( id );
1854
1855 if ( mFirstFieldIsFid && attributes.count() > 0 )
1856 {
1857 f.setAttribute( 0, id );
1858 }
1859 }
1860 }
1861
1862 return returnValue;
1863 }
1864
1865
addFeatures(QgsFeatureList & flist,Flags flags)1866 bool QgsOgrProvider::addFeatures( QgsFeatureList &flist, Flags flags )
1867 {
1868 if ( !doInitialActionsForEdition() )
1869 return false;
1870
1871 setRelevantFields( true, attributeIndexes() );
1872
1873 const bool inTransaction = startTransaction();
1874
1875 bool returnvalue = true;
1876 for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end(); ++it )
1877 {
1878 if ( !addFeaturePrivate( *it, flags ) )
1879 {
1880 returnvalue = false;
1881 }
1882 }
1883
1884 if ( inTransaction )
1885 {
1886 if ( returnvalue )
1887 returnvalue = commitTransaction();
1888 else
1889 rollbackTransaction();
1890 }
1891
1892 if ( !syncToDisc() )
1893 {
1894 returnvalue = false;
1895 }
1896
1897 if ( mFeaturesCounted != QgsVectorDataProvider::Uncounted &&
1898 mFeaturesCounted != QgsVectorDataProvider::UnknownCount )
1899 {
1900 if ( returnvalue )
1901 mFeaturesCounted += flist.size();
1902 else
1903 recalculateFeatureCount();
1904 }
1905
1906 if ( returnvalue )
1907 clearMinMaxCache();
1908
1909 if ( mTransaction )
1910 mTransaction->dirtyLastSavePoint();
1911
1912 return returnvalue;
1913 }
1914
addAttributeOGRLevel(const QgsField & field,bool & ignoreErrorOut)1915 bool QgsOgrProvider::addAttributeOGRLevel( const QgsField &field, bool &ignoreErrorOut )
1916 {
1917 ignoreErrorOut = false;
1918
1919 OGRFieldType type;
1920
1921 switch ( field.type() )
1922 {
1923 case QVariant::Int:
1924 case QVariant::Bool:
1925 type = OFTInteger;
1926 break;
1927 case QVariant::LongLong:
1928 {
1929 const char *pszDataTypes = GDALGetMetadataItem( mOgrLayer->driver(), GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
1930 if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
1931 type = OFTInteger64;
1932 else
1933 {
1934 type = OFTReal;
1935 }
1936 break;
1937 }
1938 case QVariant::Double:
1939 type = OFTReal;
1940 break;
1941 case QVariant::Date:
1942 type = OFTDate;
1943 break;
1944 case QVariant::Time:
1945 type = OFTTime;
1946 break;
1947 case QVariant::DateTime:
1948 type = OFTDateTime;
1949 break;
1950 case QVariant::String:
1951 type = OFTString;
1952 break;
1953 case QVariant::ByteArray:
1954 type = OFTBinary;
1955 break;
1956 case QVariant::Map:
1957 type = OFTString;
1958 break;
1959 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1960 case QVariant::List:
1961 // only string list supported at the moment, fall through to default for other types
1962 if ( field.subType() == QVariant::String )
1963 {
1964 type = OFTStringList;
1965 break;
1966 }
1967 //intentional fall-through
1968 FALLTHROUGH
1969 #endif
1970 default:
1971 pushError( tr( "type %1 for field %2 not found" ).arg( field.typeName(), field.name() ) );
1972 ignoreErrorOut = true;
1973 return false;
1974 }
1975
1976 gdal::ogr_field_def_unique_ptr fielddefn( OGR_Fld_Create( textEncoding()->fromUnicode( field.name() ).constData(), type ) );
1977 int width = field.length();
1978 // Increase width by 1 for OFTReal to make room for the decimal point
1979 if ( type == OFTReal && field.precision() )
1980 width += 1;
1981 OGR_Fld_SetWidth( fielddefn.get(), width );
1982 OGR_Fld_SetPrecision( fielddefn.get(), field.precision() );
1983
1984 switch ( field.type() )
1985 {
1986 case QVariant::Bool:
1987 OGR_Fld_SetSubType( fielddefn.get(), OFSTBoolean );
1988 break;
1989 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
1990 case QVariant::Map:
1991 OGR_Fld_SetSubType( fielddefn.get(), OFSTJSON );
1992 break;
1993 #endif
1994 default:
1995 break;
1996 }
1997
1998 if ( mOgrLayer->CreateField( fielddefn.get(), true ) != OGRERR_NONE )
1999 {
2000 pushError( tr( "OGR error creating field %1: %2" ).arg( field.name(), CPLGetLastErrorMsg() ) );
2001 return false;
2002 }
2003 return true;
2004 }
2005
addAttributes(const QList<QgsField> & attributes)2006 bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
2007 {
2008 if ( !doInitialActionsForEdition() )
2009 return false;
2010
2011 if ( mGDALDriverName == QLatin1String( "MapInfo File" ) )
2012 {
2013 // adding attributes in mapinfo requires to be able to delete the .dat file
2014 // so drop any cached connections.
2015 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
2016 }
2017
2018 bool returnvalue = true;
2019
2020 QMap< QString, QgsField > mapFieldNameToOriginalField;
2021
2022 for ( const auto &field : attributes )
2023 {
2024 mapFieldNameToOriginalField[ field.name()] = field;
2025
2026 bool ignoreErrorOut = false;
2027 if ( !addAttributeOGRLevel( field, ignoreErrorOut ) )
2028 {
2029 returnvalue = false;
2030 if ( !ignoreErrorOut )
2031 {
2032 break;
2033 }
2034 }
2035 }
2036
2037 // Backup existing fields. We need them to 'restore' field type, length, precision
2038 QgsFields oldFields = mAttributeFields;
2039
2040 loadFields();
2041
2042 // The check in QgsVectorLayerEditBuffer::commitChanges() is questionable with
2043 // real-world drivers that might only be able to satisfy request only partially.
2044 // So to avoid erroring out, patch field type, width and precision to match
2045 // what was requested.
2046 // For example in case of Integer64->Real mapping so that QVariant::LongLong is
2047 // still returned to the caller
2048 // Or if a field width was specified but not strictly enforced by the driver (#15614)
2049 for ( QMap< QString, QgsField >::const_iterator it = mapFieldNameToOriginalField.constBegin();
2050 it != mapFieldNameToOriginalField.constEnd(); ++it )
2051 {
2052 int idx = mAttributeFields.lookupField( it.key() );
2053 if ( idx >= 0 )
2054 {
2055 mAttributeFields[ idx ].setType( it->type() );
2056 mAttributeFields[ idx ].setLength( it->length() );
2057 mAttributeFields[ idx ].setPrecision( it->precision() );
2058 }
2059 }
2060
2061 // Restore field type, length, precision of existing fields as well
2062 // We need that in scenarios where the user adds a int field with length != 0
2063 // in a editing session, and repeat that again in another editing session
2064 // Without the below hack, the length of the first added field would have
2065 // been reset to zero, and QgsVectorLayerEditBuffer::commitChanges() would
2066 // error out because of this.
2067 // See https://github.com/qgis/QGIS/issues/26840
2068 for ( auto field : oldFields )
2069 {
2070 int idx = mAttributeFields.lookupField( field.name() );
2071 if ( idx >= 0 )
2072 {
2073 mAttributeFields[ idx ].setType( field.type() );
2074 mAttributeFields[ idx ].setLength( field.length() );
2075 mAttributeFields[ idx ].setPrecision( field.precision() );
2076 }
2077 }
2078
2079 if ( mTransaction )
2080 mTransaction->dirtyLastSavePoint();
2081
2082 return returnvalue;
2083 }
2084
deleteAttributes(const QgsAttributeIds & attributes)2085 bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
2086 {
2087 if ( !doInitialActionsForEdition() )
2088 return false;
2089
2090 bool res = true;
2091 QList<int> attrsLst = qgis::setToList( attributes );
2092 // sort in descending order
2093 std::sort( attrsLst.begin(), attrsLst.end(), std::greater<int>() );
2094 const auto constAttrsLst = attrsLst;
2095 for ( int attr : constAttrsLst )
2096 {
2097 if ( mFirstFieldIsFid )
2098 {
2099 if ( attr == 0 )
2100 {
2101 pushError( tr( "Cannot delete feature id column" ) );
2102 res = false;
2103 break;
2104 }
2105 else
2106 {
2107 --attr;
2108 }
2109 }
2110 if ( mOgrLayer->DeleteField( attr ) != OGRERR_NONE )
2111 {
2112 pushError( tr( "OGR error deleting field %1: %2" ).arg( attr ).arg( CPLGetLastErrorMsg() ) );
2113 res = false;
2114 }
2115 }
2116 loadFields();
2117
2118 if ( mTransaction )
2119 mTransaction->dirtyLastSavePoint();
2120
2121 return res;
2122 }
2123
renameAttributes(const QgsFieldNameMap & renamedAttributes)2124 bool QgsOgrProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
2125 {
2126 if ( !doInitialActionsForEdition() )
2127 return false;
2128
2129 QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
2130 bool result = true;
2131 for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
2132 {
2133 int fieldIndex = renameIt.key();
2134 if ( fieldIndex < 0 || fieldIndex >= mAttributeFields.count() )
2135 {
2136 pushError( tr( "Invalid attribute index" ) );
2137 result = false;
2138 continue;
2139 }
2140 if ( mAttributeFields.indexFromName( renameIt.value() ) >= 0 )
2141 {
2142 //field name already in use
2143 pushError( tr( "Error renaming field %1: name '%2' already exists" ).arg( fieldIndex ).arg( renameIt.value() ) );
2144 result = false;
2145 continue;
2146 }
2147 int ogrFieldIndex = fieldIndex;
2148 if ( mFirstFieldIsFid )
2149 {
2150 ogrFieldIndex -= 1;
2151 if ( ogrFieldIndex < 0 )
2152 {
2153 pushError( tr( "Invalid attribute index" ) );
2154 result = false;
2155 continue;
2156 }
2157 }
2158
2159 //type does not matter, it will not be used
2160 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( textEncoding()->fromUnicode( renameIt.value() ), OFTReal ) );
2161 if ( mOgrLayer->AlterFieldDefn( ogrFieldIndex, fld.get(), ALTER_NAME_FLAG ) != OGRERR_NONE )
2162 {
2163 pushError( tr( "OGR error renaming field %1: %2" ).arg( fieldIndex ).arg( CPLGetLastErrorMsg() ) );
2164 result = false;
2165 }
2166 }
2167 loadFields();
2168
2169 if ( mTransaction )
2170 mTransaction->dirtyLastSavePoint();
2171
2172 return result;
2173 }
2174
startTransaction()2175 bool QgsOgrProvider::startTransaction()
2176 {
2177 bool inTransaction = false;
2178 if ( mTransaction == nullptr && mOgrLayer->TestCapability( OLCTransactions ) )
2179 {
2180 // A transaction might already be active, so be robust on failed
2181 // StartTransaction.
2182 CPLPushErrorHandler( CPLQuietErrorHandler );
2183 inTransaction = ( mOgrLayer->StartTransaction() == OGRERR_NONE );
2184 CPLPopErrorHandler();
2185 }
2186 return inTransaction;
2187 }
2188
2189
commitTransaction()2190 bool QgsOgrProvider::commitTransaction()
2191 {
2192 if ( mOgrLayer->CommitTransaction() != OGRERR_NONE )
2193 {
2194 pushError( tr( "OGR error committing transaction: %1" ).arg( CPLGetLastErrorMsg() ) );
2195 return false;
2196 }
2197 return true;
2198 }
2199
2200
rollbackTransaction()2201 bool QgsOgrProvider::rollbackTransaction()
2202 {
2203 if ( mOgrLayer->RollbackTransaction() != OGRERR_NONE )
2204 {
2205 pushError( tr( "OGR error rolling back transaction: %1" ).arg( CPLGetLastErrorMsg() ) );
2206 return false;
2207 }
2208 return true;
2209 }
2210
_setSubsetString(const QString & theSQL,bool updateFeatureCount,bool updateCapabilities,bool hasExistingRef)2211 bool QgsOgrProvider::_setSubsetString( const QString &theSQL, bool updateFeatureCount, bool updateCapabilities, bool hasExistingRef )
2212 {
2213 QgsCPLErrorHandler handler;
2214
2215 if ( !mOgrOrigLayer )
2216 return false;
2217
2218 if ( theSQL == mSubsetString && mFeaturesCounted != QgsVectorDataProvider::Uncounted )
2219 return true;
2220
2221 const bool subsetStringHasChanged { theSQL != mSubsetString };
2222
2223 if ( !theSQL.isEmpty() )
2224 {
2225 QMutex *mutex = nullptr;
2226 OGRLayerH layer = mOgrOrigLayer->getHandleAndMutex( mutex );
2227 GDALDatasetH ds = mOgrOrigLayer->getDatasetHandleAndMutex( mutex );
2228 OGRLayerH subsetLayerH;
2229 {
2230 QMutexLocker locker( mutex );
2231 subsetLayerH = QgsOgrProviderUtils::setSubsetString( layer, ds, textEncoding(), theSQL );
2232 }
2233 if ( !subsetLayerH )
2234 {
2235 pushError( tr( "OGR[%1] error %2: %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
2236 return false;
2237 }
2238 if ( layer != subsetLayerH )
2239 {
2240 mOgrSqlLayer = QgsOgrProviderUtils::getSqlLayer( mOgrOrigLayer.get(), subsetLayerH, theSQL );
2241 Q_ASSERT( mOgrSqlLayer.get() );
2242 mOgrLayer = mOgrSqlLayer.get();
2243 }
2244 else
2245 {
2246 mOgrSqlLayer.reset();
2247 mOgrLayer = mOgrOrigLayer.get();
2248 }
2249 }
2250 else
2251 {
2252 mOgrSqlLayer.reset();
2253 mOgrLayer = mOgrOrigLayer.get();
2254 QMutex *mutex = nullptr;
2255 OGRLayerH layer = mOgrOrigLayer->getHandleAndMutex( mutex );
2256 {
2257 QMutexLocker locker( mutex );
2258 OGR_L_SetAttributeFilter( layer, nullptr );
2259 }
2260 }
2261 mSubsetString = theSQL;
2262
2263 QVariantMap parts;
2264 parts.insert( QStringLiteral( "path" ), mFilePath );
2265
2266 if ( !mLayerName.isNull() )
2267 {
2268 parts.insert( QStringLiteral( "layerName" ), mLayerName );
2269 }
2270 else if ( mIsSubLayer && mLayerIndex >= 0 )
2271 {
2272 parts.insert( QStringLiteral( "layerId" ), mLayerIndex );
2273 }
2274
2275 if ( !mSubsetString.isEmpty() )
2276 {
2277 parts.insert( QStringLiteral( "subset" ), mSubsetString );
2278 }
2279
2280 if ( mOgrGeometryTypeFilter != wkbUnknown )
2281 {
2282 parts.insert( QStringLiteral( "geometryType" ), ogrWkbGeometryTypeName( mOgrGeometryTypeFilter ) );
2283 }
2284
2285 if ( !mOpenOptions.isEmpty() )
2286 {
2287 parts.insert( QStringLiteral( "openOptions" ), mOpenOptions );
2288 }
2289
2290 QString uri = QgsOgrProviderMetadata().encodeUri( parts );
2291 if ( uri != dataSourceUri() )
2292 {
2293 if ( hasExistingRef )
2294 QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
2295 setDataSourceUri( uri );
2296 if ( hasExistingRef )
2297 QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
2298 }
2299
2300 mOgrLayer->ResetReading();
2301
2302 // getting the total number of features in the layer
2303 // TODO: This can be expensive, do we really need it!
2304 if ( updateFeatureCount )
2305 {
2306 recalculateFeatureCount();
2307 }
2308
2309 // check the validity of the layer if subset string has changed
2310 if ( subsetStringHasChanged )
2311 {
2312 loadFields();
2313 }
2314
2315 invalidateCachedExtent( false );
2316
2317 // Changing the filter may change capabilities
2318 if ( updateCapabilities )
2319 computeCapabilities();
2320
2321 emit dataChanged();
2322
2323 return true;
2324
2325 }
2326
2327
changeAttributeValues(const QgsChangedAttributesMap & attr_map)2328 bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
2329 {
2330 if ( !doInitialActionsForEdition() )
2331 return false;
2332
2333 if ( attr_map.isEmpty() )
2334 return true;
2335
2336 bool returnValue = true;
2337
2338 clearMinMaxCache();
2339
2340 setRelevantFields( true, attributeIndexes() );
2341
2342 const bool inTransaction = startTransaction();
2343
2344 // Some drivers may need to call ResetReading() after GetFeature(), such
2345 // as GPKG in GDAL < 2.3.0 to avoid letting the database in a locked state.
2346 // But this is undesirable in general, so don't do this when we know that
2347 // we don't need to.
2348 bool mayNeedResetReadingAfterGetFeature = true;
2349 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
2350 {
2351 mayNeedResetReadingAfterGetFeature = false;
2352 }
2353 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0)
2354 else if ( mGDALDriverName == QLatin1String( "GPKG" ) )
2355 {
2356 mayNeedResetReadingAfterGetFeature = false;
2357 }
2358 #endif
2359
2360 for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
2361 {
2362 QgsFeatureId fid = it.key();
2363
2364 const QgsAttributeMap &attr = it.value();
2365 if ( attr.isEmpty() )
2366 continue;
2367
2368 gdal::ogr_feature_unique_ptr of( mOgrLayer->GetFeature( FID_TO_NUMBER( fid ) ) );
2369 if ( !of )
2370 {
2371 pushError( tr( "Feature %1 for attribute update not found." ).arg( fid ) );
2372 continue;
2373 }
2374
2375 if ( mayNeedResetReadingAfterGetFeature )
2376 {
2377 mOgrLayer->ResetReading();
2378 }
2379
2380 QgsLocaleNumC l;
2381
2382 for ( QgsAttributeMap::const_iterator it2 = attr.begin(); it2 != attr.end(); ++it2 )
2383 {
2384 int f = it2.key();
2385 if ( mFirstFieldIsFid )
2386 {
2387 if ( f == 0 )
2388 {
2389 if ( it2->toLongLong() != fid )
2390 {
2391 pushError( tr( "Changing feature id of feature %1 is not allowed." ).arg( fid ) );
2392 returnValue = false;
2393 }
2394 continue;
2395 }
2396 else
2397 {
2398 --f;
2399 }
2400 }
2401
2402 OGRFieldDefnH fd = OGR_F_GetFieldDefnRef( of.get(), f );
2403 if ( !fd )
2404 {
2405 pushError( tr( "Field %1 of feature %2 doesn't exist." ).arg( f ).arg( fid ) );
2406 continue;
2407 }
2408
2409 OGRFieldType type = OGR_Fld_GetType( fd );
2410
2411 if ( it2->isNull() || ( type != OFTString && it2->toString().isEmpty() ) )
2412 {
2413 // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
2414 // whereas previously there was only unset fields. For a GeoJSON output,
2415 // leaving a field unset will cause it to not appear at all in the output
2416 // feature.
2417 // When all features of a layer have a field unset, this would cause the
2418 // field to not be present at all in the output, and thus on reading to
2419 // have disappeared. #16812
2420 #ifdef OGRNullMarker
2421 OGR_F_SetFieldNull( of.get(), f );
2422 #else
2423 OGR_F_UnsetField( of.get(), f );
2424 #endif
2425 }
2426 else
2427 {
2428
2429 switch ( type )
2430 {
2431 case OFTInteger:
2432 OGR_F_SetFieldInteger( of.get(), f, it2->toInt() );
2433 break;
2434 case OFTInteger64:
2435 OGR_F_SetFieldInteger64( of.get(), f, it2->toLongLong() );
2436 break;
2437 case OFTReal:
2438 OGR_F_SetFieldDouble( of.get(), f, it2->toDouble() );
2439 break;
2440 case OFTDate:
2441 OGR_F_SetFieldDateTime( of.get(), f,
2442 it2->toDate().year(),
2443 it2->toDate().month(),
2444 it2->toDate().day(),
2445 0, 0, 0,
2446 0 );
2447 break;
2448 case OFTTime:
2449 OGR_F_SetFieldDateTime( of.get(), f,
2450 0, 0, 0,
2451 it2->toTime().hour(),
2452 it2->toTime().minute(),
2453 it2->toTime().second(),
2454 0 );
2455 break;
2456 case OFTDateTime:
2457 OGR_F_SetFieldDateTime( of.get(), f,
2458 it2->toDateTime().date().year(),
2459 it2->toDateTime().date().month(),
2460 it2->toDateTime().date().day(),
2461 it2->toDateTime().time().hour(),
2462 it2->toDateTime().time().minute(),
2463 it2->toDateTime().time().second(),
2464 0 );
2465 break;
2466 case OFTString:
2467 {
2468 QString stringValue;
2469 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
2470 if ( OGR_Fld_GetSubType( fd ) == OFSTJSON )
2471 stringValue = jsonStringValue( it2.value() );
2472 else
2473 stringValue = it2->toString();
2474 #else
2475 stringValue = it2->toString();
2476 #endif
2477 OGR_F_SetFieldString( of.get(), f, textEncoding()->fromUnicode( stringValue ).constData() );
2478 break;
2479 }
2480
2481 case OFTBinary:
2482 {
2483 const QByteArray ba = it2->toByteArray();
2484 OGR_F_SetFieldBinary( of.get(), f, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
2485 break;
2486 }
2487
2488 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
2489 case OFTStringList:
2490 {
2491 QStringList list = it2->toStringList();
2492 int count = list.count();
2493 char **lst = new char *[count + 1];
2494 if ( count > 0 )
2495 {
2496 int pos = 0;
2497 for ( QString string : list )
2498 {
2499 lst[pos] = textEncoding()->fromUnicode( string ).data();
2500 pos++;
2501 }
2502 }
2503 lst[count] = nullptr;
2504 OGR_F_SetFieldStringList( of.get(), f, lst );
2505 break;
2506 }
2507 #endif
2508
2509 default:
2510 pushError( tr( "Type %1 of attribute %2 of feature %3 unknown." ).arg( type ).arg( fid ).arg( f ) );
2511 break;
2512 }
2513 }
2514 }
2515
2516 if ( mOgrLayer->SetFeature( of.get() ) != OGRERR_NONE )
2517 {
2518 pushError( tr( "OGR error setting feature %1: %2" ).arg( fid ).arg( CPLGetLastErrorMsg() ) );
2519 returnValue = false;
2520 }
2521 }
2522
2523 if ( inTransaction )
2524 {
2525 if ( returnValue )
2526 returnValue = commitTransaction();
2527 else
2528 rollbackTransaction();
2529 }
2530
2531 if ( mTransaction )
2532 mTransaction->dirtyLastSavePoint();
2533
2534 if ( !syncToDisc() )
2535 {
2536 pushError( tr( "OGR error syncing to disk: %1" ).arg( CPLGetLastErrorMsg() ) );
2537 }
2538 return returnValue;
2539 }
2540
changeGeometryValues(const QgsGeometryMap & geometry_map)2541 bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
2542 {
2543 if ( !doInitialActionsForEdition() )
2544 return false;
2545
2546 setRelevantFields( true, attributeIndexes() );
2547
2548 const bool inTransaction = startTransaction();
2549
2550 // Some drivers may need to call ResetReading() after GetFeature(), such
2551 // as GPKG in GDAL < 2.3.0 to avoid letting the database in a locked state.
2552 // But this is undesirable in general, so don't do this when we know that
2553 // we don't need to.
2554 bool mayNeedResetReadingAfterGetFeature = true;
2555 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
2556 {
2557 mayNeedResetReadingAfterGetFeature = false;
2558 }
2559 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0)
2560 else if ( mGDALDriverName == QLatin1String( "GPKG" ) )
2561 {
2562 mayNeedResetReadingAfterGetFeature = false;
2563 }
2564 #endif
2565
2566 bool returnvalue = true;
2567 for ( QgsGeometryMap::const_iterator it = geometry_map.constBegin(); it != geometry_map.constEnd(); ++it )
2568 {
2569 gdal::ogr_feature_unique_ptr theOGRFeature( mOgrLayer->GetFeature( FID_TO_NUMBER( it.key() ) ) );
2570 if ( !theOGRFeature )
2571 {
2572 pushError( tr( "OGR error changing geometry: feature %1 not found" ).arg( it.key() ) );
2573 returnvalue = false;
2574 continue;
2575 }
2576
2577 if ( mayNeedResetReadingAfterGetFeature )
2578 {
2579 mOgrLayer->ResetReading(); // needed for SQLite-based to clear iterator, which could let the database in a locked state otherwise
2580 }
2581
2582 OGRGeometryH newGeometry = nullptr;
2583 QByteArray wkb = it->asWkb();
2584 // We might receive null geometries. It is OK, but don't go through the
2585 // OGR_G_CreateFromWkb() route then
2586 if ( !wkb.isEmpty() )
2587 {
2588 //create an OGRGeometry
2589 if ( OGR_G_CreateFromWkb( reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ),
2590 mOgrLayer->GetSpatialRef(),
2591 &newGeometry,
2592 wkb.length() ) != OGRERR_NONE )
2593 {
2594 pushError( tr( "OGR error creating geometry for feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
2595 OGR_G_DestroyGeometry( newGeometry );
2596 returnvalue = false;
2597 continue;
2598 }
2599
2600 if ( !newGeometry )
2601 {
2602 pushError( tr( "OGR error in feature %1: geometry is null" ).arg( it.key() ) );
2603 returnvalue = false;
2604 continue;
2605 }
2606
2607 newGeometry = ConvertGeometryIfNecessary( newGeometry );
2608 }
2609
2610 //set the new geometry
2611 if ( OGR_F_SetGeometryDirectly( theOGRFeature.get(), newGeometry ) != OGRERR_NONE )
2612 {
2613 pushError( tr( "OGR error setting geometry of feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
2614 // Shouldn't happen normally. If it happens, ownership of the geometry
2615 // may be not really well defined, so better not destroy it, but just
2616 // the feature.
2617 returnvalue = false;
2618 continue;
2619 }
2620
2621
2622 if ( mOgrLayer->SetFeature( theOGRFeature.get() ) != OGRERR_NONE )
2623 {
2624 pushError( tr( "OGR error setting feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
2625 returnvalue = false;
2626 continue;
2627 }
2628 mShapefileMayBeCorrupted = true;
2629
2630 invalidateCachedExtent( true );
2631 }
2632
2633 if ( inTransaction )
2634 {
2635 if ( returnvalue )
2636 returnvalue = commitTransaction();
2637 else
2638 rollbackTransaction();
2639 }
2640
2641 if ( mTransaction )
2642 mTransaction->dirtyLastSavePoint();
2643
2644 if ( !syncToDisc() )
2645 {
2646 pushError( tr( "OGR error syncing to disk: %1" ).arg( CPLGetLastErrorMsg() ) );
2647 }
2648 return returnvalue;
2649 }
2650
createSpatialIndex()2651 bool QgsOgrProvider::createSpatialIndex()
2652 {
2653 if ( !mOgrOrigLayer )
2654 return false;
2655 if ( !doInitialActionsForEdition() )
2656 return false;
2657
2658 QByteArray layerName = mOgrOrigLayer->name();
2659 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
2660 {
2661 QByteArray sql = QByteArray( "CREATE SPATIAL INDEX ON " ) + quotedIdentifier( layerName ); // quote the layer name so spaces are handled
2662 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( QString::fromUtf8( sql ) ), 2 );
2663 mOgrOrigLayer->ExecuteSQLNoReturn( sql );
2664
2665 if ( !mFilePath.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
2666 return true;
2667
2668 QFileInfo fi( mFilePath ); // to get the base name
2669 //find out, if the .qix file is there
2670 return QFileInfo::exists( fi.path().append( '/' ).append( fi.completeBaseName() ).append( ".qix" ) );
2671 }
2672 else if ( mGDALDriverName == QLatin1String( "GPKG" ) ||
2673 mGDALDriverName == QLatin1String( "SQLite" ) )
2674 {
2675 QMutex *mutex = nullptr;
2676 OGRLayerH layer = mOgrOrigLayer->getHandleAndMutex( mutex );
2677 QByteArray sql = QByteArray( "SELECT CreateSpatialIndex(" + quotedIdentifier( layerName ) + ","
2678 + quotedIdentifier( OGR_L_GetGeometryColumn( layer ) ) + ") " ); // quote the layer name so spaces are handled
2679 mOgrOrigLayer->ExecuteSQLNoReturn( sql );
2680 return true;
2681 }
2682 return false;
2683 }
2684
createIndexName(QString tableName,QString field)2685 QString QgsOgrProvider::createIndexName( QString tableName, QString field )
2686 {
2687 QRegularExpression safeExp( QStringLiteral( "[^a-zA-Z0-9]" ) );
2688 tableName.replace( safeExp, QStringLiteral( "_" ) );
2689 field.replace( safeExp, QStringLiteral( "_" ) );
2690 return tableName + "_" + field + "_idx";
2691 }
2692
createAttributeIndex(int field)2693 bool QgsOgrProvider::createAttributeIndex( int field )
2694 {
2695 if ( field < 0 || field >= mAttributeFields.count() )
2696 return false;
2697
2698 if ( !doInitialActionsForEdition() )
2699 return false;
2700
2701 QByteArray quotedLayerName = quotedIdentifier( mOgrOrigLayer->name() );
2702 if ( mGDALDriverName == QLatin1String( "GPKG" ) ||
2703 mGDALDriverName == QLatin1String( "SQLite" ) )
2704 {
2705 if ( field == 0 && mFirstFieldIsFid )
2706 {
2707 // already an index on this field, no need to re-created
2708 return false;
2709 }
2710
2711 QString indexName = createIndexName( mOgrOrigLayer->name(), fields().at( field ).name() );
2712 QByteArray createSql = "CREATE INDEX IF NOT EXISTS " + textEncoding()->fromUnicode( indexName ) + " ON " + quotedLayerName + " (" + textEncoding()->fromUnicode( fields().at( field ).name() ) + ")";
2713 mOgrOrigLayer->ExecuteSQLNoReturn( createSql );
2714 return true;
2715 }
2716 else
2717 {
2718 QByteArray dropSql = "DROP INDEX ON " + quotedLayerName;
2719 mOgrOrigLayer->ExecuteSQLNoReturn( dropSql );
2720 QByteArray createSql = "CREATE INDEX ON " + quotedLayerName + " USING " + textEncoding()->fromUnicode( fields().at( field ).name() );
2721 mOgrOrigLayer->ExecuteSQLNoReturn( createSql );
2722
2723 QFileInfo fi( mFilePath ); // to get the base name
2724 //find out, if the .idm/.ind file is there
2725 QString idmFile( fi.path().append( '/' ).append( fi.completeBaseName() ).append( ".idm" ) );
2726 QString indFile( fi.path().append( '/' ).append( fi.completeBaseName() ).append( ".ind" ) );
2727 return QFile::exists( idmFile ) || QFile::exists( indFile );
2728 }
2729 }
2730
deleteFeatures(const QgsFeatureIds & id)2731 bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds &id )
2732 {
2733 if ( !doInitialActionsForEdition() )
2734 return false;
2735
2736 const bool inTransaction = startTransaction();
2737
2738 bool returnvalue = true;
2739 for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
2740 {
2741 if ( !deleteFeature( *it ) )
2742 {
2743 returnvalue = false;
2744 }
2745 }
2746
2747 if ( inTransaction )
2748 {
2749 if ( returnvalue )
2750 returnvalue = commitTransaction();
2751 else
2752 rollbackTransaction();
2753 }
2754
2755 if ( mTransaction )
2756 mTransaction->dirtyLastSavePoint();
2757
2758 if ( !syncToDisc() )
2759 {
2760 returnvalue = false;
2761 }
2762
2763 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
2764 {
2765 // Shapefile behaves in a special way due to possible recompaction
2766 recalculateFeatureCount();
2767 }
2768 else
2769 {
2770 if ( mFeaturesCounted != QgsVectorDataProvider::Uncounted &&
2771 mFeaturesCounted != QgsVectorDataProvider::UnknownCount )
2772 {
2773 if ( returnvalue )
2774 mFeaturesCounted -= id.size();
2775 else
2776 recalculateFeatureCount();
2777 }
2778 }
2779
2780 clearMinMaxCache();
2781
2782 invalidateCachedExtent( true );
2783
2784 return returnvalue;
2785 }
2786
deleteFeature(QgsFeatureId id)2787 bool QgsOgrProvider::deleteFeature( QgsFeatureId id )
2788 {
2789 if ( !doInitialActionsForEdition() )
2790 return false;
2791
2792 if ( mOgrLayer->DeleteFeature( FID_TO_NUMBER( id ) ) != OGRERR_NONE )
2793 {
2794 pushError( tr( "OGR error deleting feature %1: %2" ).arg( id ).arg( CPLGetLastErrorMsg() ) );
2795 return false;
2796 }
2797
2798 if ( mTransaction )
2799 mTransaction->dirtyLastSavePoint();
2800
2801 mShapefileMayBeCorrupted = true;
2802
2803 return true;
2804 }
2805
doInitialActionsForEdition()2806 bool QgsOgrProvider::doInitialActionsForEdition()
2807 {
2808 if ( !mValid )
2809 return false;
2810
2811 // If mUpdateModeStackDepth > 0, it means that an updateMode is already active and that we have write access
2812 if ( mUpdateModeStackDepth == 0 )
2813 {
2814 QgsDebugMsgLevel( QStringLiteral( "Enter update mode implicitly" ), 1 );
2815 if ( !_enterUpdateMode( true ) )
2816 return false;
2817 }
2818
2819 return true;
2820 }
2821
capabilities() const2822 QgsVectorDataProvider::Capabilities QgsOgrProvider::capabilities() const
2823 {
2824 return mCapabilities;
2825 }
2826
computeCapabilities()2827 void QgsOgrProvider::computeCapabilities()
2828 {
2829 QgsVectorDataProvider::Capabilities ability = QgsVectorDataProvider::Capabilities();
2830 bool updateModeActivated = false;
2831
2832 // collect abilities reported by OGR
2833 if ( mOgrLayer )
2834 {
2835
2836 // We want the layer in rw mode or capabilities will be wrong
2837 // If mUpdateModeStackDepth > 0, it means that an updateMode is already active and that we have write access
2838 if ( mUpdateModeStackDepth == 0 )
2839 {
2840 updateModeActivated = _enterUpdateMode( true );
2841 }
2842
2843 // Whilst the OGR documentation (e.g. at
2844 // https://gdal.org/doxygen/classOGRLayer.html#aeedbda1a62f9b89b8e5f24332cf22286) states "The capability
2845 // codes that can be tested are represented as strings, but #defined
2846 // constants exists to ensure correct spelling", we always use strings
2847 // here. This is because older versions of OGR don't always have all
2848 // the #defines we want to test for here.
2849
2850 if ( mOgrLayer->TestCapability( "RandomRead" ) )
2851 // true if the GetFeature() method works *efficiently* for this layer.
2852 // TODO: Perhaps influence if QGIS caches into memory
2853 // (vs read from disk every time) based on this setting.
2854 {
2855 // the latter flag is here just for compatibility
2856 ability |= QgsVectorDataProvider::SelectAtId;
2857 }
2858
2859 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "SequentialWrite" ) )
2860 // true if the CreateFeature() method works for this layer.
2861 {
2862 ability |= QgsVectorDataProvider::AddFeatures;
2863 }
2864
2865 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "DeleteFeature" ) )
2866 // true if this layer can delete its features
2867 {
2868 ability |= DeleteFeatures;
2869 }
2870
2871 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "RandomWrite" ) )
2872 // true if the SetFeature() method is operational on this layer.
2873 {
2874 // TODO According to http://shapelib.maptools.org/ (Shapefile C Library V1.2)
2875 // TODO "You can't modify the vertices of existing structures".
2876 // TODO Need to work out versions of shapelib vs versions of GDAL/OGR
2877 // TODO And test appropriately.
2878
2879 ability |= ChangeAttributeValues;
2880 ability |= ChangeGeometries;
2881 }
2882
2883 #if 0
2884 if ( mOgrLayer->TestCapability( "FastSpatialFilter" ) )
2885 // true if this layer implements spatial filtering efficiently.
2886 // Layers that effectively read all features, and test them with the
2887 // OGRFeature intersection methods should return false.
2888 // This can be used as a clue by the application whether it should build
2889 // and maintain it's own spatial index for features in this layer.
2890 {
2891 // TODO: Perhaps use as a clue by QGIS whether it should build and maintain it's own spatial index for features in this layer.
2892 }
2893
2894 if ( mOgrLayer->TestCapability( "FastFeatureCount" ) )
2895 // true if this layer can return a feature count
2896 // (via OGRLayer::GetFeatureCount()) efficiently ... ie. without counting
2897 // the features. In some cases this will return true until a spatial
2898 // filter is installed after which it will return false.
2899 {
2900 // TODO: Perhaps use as a clue by QGIS whether it should spawn a thread to count features.
2901 }
2902
2903 if ( mOgrLayer->TestCapability( "FastGetExtent" ) )
2904 // true if this layer can return its data extent
2905 // (via OGRLayer::GetExtent()) efficiently ... ie. without scanning
2906 // all the features. In some cases this will return true until a
2907 // spatial filter is installed after which it will return false.
2908 {
2909 // TODO: Perhaps use as a clue by QGIS whether it should spawn a thread to calculate extent.
2910 }
2911
2912 if ( mOgrLayer->TestCapability( "FastSetNextByIndex" ) )
2913 // true if this layer can perform the SetNextByIndex() call efficiently.
2914 {
2915 // No use required for this QGIS release.
2916 }
2917 #endif
2918
2919 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "CreateField" ) )
2920 {
2921 ability |= AddAttributes;
2922 }
2923
2924 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "DeleteField" ) )
2925 {
2926 ability |= DeleteAttributes;
2927 }
2928
2929 if ( mWriteAccessPossible && mOgrLayer->TestCapability( "AlterFieldDefn" ) )
2930 {
2931 ability |= RenameAttributes;
2932 }
2933
2934 if ( !mOgrLayer->TestCapability( OLCStringsAsUTF8 ) )
2935 {
2936 ability |= SelectEncoding;
2937 }
2938
2939 // OGR doesn't handle shapefiles without attributes, ie. missing DBFs well, fixes #803
2940 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
2941 {
2942 ability |= CreateSpatialIndex;
2943 ability |= CreateAttributeIndex;
2944
2945 if ( mAttributeFields.size() == 0 )
2946 {
2947 QgsMessageLog::logMessage( tr( "Shapefiles without attribute are considered read-only." ), tr( "OGR" ) );
2948 ability &= ~( AddFeatures | DeleteFeatures | ChangeAttributeValues | AddAttributes | DeleteAttributes );
2949 }
2950
2951 if ( ( ability & ChangeAttributeValues ) == 0 )
2952 {
2953 // on readonly shapes OGR reports that it can delete features although it can't RandomWrite
2954 ability &= ~( AddAttributes | DeleteFeatures );
2955 }
2956 }
2957 else if ( mGDALDriverName == QLatin1String( "GPKG" ) ||
2958 mGDALDriverName == QLatin1String( "SQLite" ) )
2959 {
2960 ability |= CreateSpatialIndex;
2961 ability |= CreateAttributeIndex;
2962 }
2963
2964 /* Curve geometries are available in some drivers starting with GDAL 2.0 */
2965 if ( mOgrLayer->TestCapability( "CurveGeometries" ) )
2966 {
2967 ability |= CircularGeometries;
2968 }
2969
2970 if ( mGDALDriverName == QLatin1String( "GPKG" ) )
2971 {
2972 //supports transactions
2973 ability |= TransactionSupport;
2974 }
2975 }
2976
2977 ability |= ReadLayerMetadata;
2978
2979 if ( updateModeActivated )
2980 leaveUpdateMode();
2981
2982 mCapabilities = ability;
2983 }
2984
2985
name() const2986 QString QgsOgrProvider::name() const
2987 {
2988 return TEXT_PROVIDER_KEY;
2989 }
2990
providerKey()2991 QString QgsOgrProvider::providerKey()
2992 {
2993 return TEXT_PROVIDER_KEY;
2994 }
2995
description() const2996 QString QgsOgrProvider::description() const
2997 {
2998 return TEXT_PROVIDER_DESCRIPTION;
2999 } // QgsOgrProvider::description()
3000
3001
3002 /**
3003
3004 Convenience function for readily creating file filters.
3005
3006 Given a long name for a file filter and a regular expression, return
3007 a file filter string suitable for use in a QFileDialog::OpenFiles()
3008 call. The regular express, glob, will have both all lower and upper
3009 case versions added.
3010
3011 \note
3012
3013 Copied from qgisapp.cpp.
3014
3015 \todo XXX This should probably be generalized and moved to a standard
3016 utility type thingy.
3017
3018 */
createFileFilter_(QString const & longName,QString const & glob)3019 static QString createFileFilter_( QString const &longName, QString const &glob )
3020 {
3021 // return longName + " [OGR] (" + glob.toLower() + ' ' + glob.toUpper() + ");;";
3022 return longName + " (" + glob.toLower() + ' ' + glob.toUpper() + ");;";
3023 } // createFileFilter_
3024
3025
createFilters(const QString & type)3026 QString createFilters( const QString &type )
3027 {
3028 //! Database drivers available
3029 static QString sDatabaseDrivers;
3030 //! Protocol drivers available
3031 static QString sProtocolDrivers;
3032 //! File filters
3033 static QString sFileFilters;
3034 //! Directory drivers
3035 static QString sDirectoryDrivers;
3036 //! Extensions
3037 static QStringList sExtensions;
3038 //! Directory extensions
3039 static QStringList sDirectoryExtensions;
3040 //! Wildcards
3041 static QStringList sWildcards;
3042
3043 // if we've already built the supported vector string, just return what
3044 // we've already built
3045
3046 if ( sFileFilters.isEmpty() || sFileFilters.isNull() )
3047 {
3048 // register ogr plugins
3049 QgsApplication::registerOgrDrivers();
3050
3051 // first get the GDAL driver manager
3052 GDALDriverH driver; // current driver
3053 QString driverName; // current driver name
3054
3055 // Grind through all the drivers and their respective metadata.
3056 // We'll add a file filter for those drivers that have a file
3057 // extension defined for them; the others, welll, even though
3058 // theoreticaly we can open those files because there exists a
3059 // driver for them, the user will have to use the "All Files" to
3060 // open datasets with no explicitly defined file name extension.
3061 QgsDebugMsgLevel( QStringLiteral( "Driver count: %1" ).arg( OGRGetDriverCount() ), 3 );
3062
3063 bool kmlFound = false;
3064 bool dwgFound = false;
3065 bool dgnFound = false;
3066
3067 for ( int i = 0; i < OGRGetDriverCount(); ++i )
3068 {
3069 driver = OGRGetDriver( i );
3070
3071 Q_CHECK_PTR( driver ); // NOLINT
3072
3073 if ( !driver )
3074 {
3075 QgsMessageLog::logMessage( QObject::tr( "Unable to get driver %1" ).arg( i ), QObject::tr( "OGR" ) );
3076 continue;
3077 }
3078
3079 driverName = GDALGetDriverShortName( driver );
3080
3081 if ( driverName.startsWith( QLatin1String( "AVCBin" ) ) )
3082 {
3083 sDirectoryDrivers += QObject::tr( "Arc/Info Binary Coverage" ) + ",AVCBin;";
3084 }
3085 else if ( driverName.startsWith( QLatin1String( "AVCE00" ) ) )
3086 {
3087 sFileFilters += createFileFilter_( QObject::tr( "Arc/Info ASCII Coverage" ), QStringLiteral( "*.e00" ) );
3088 sExtensions << QStringLiteral( "e00" );
3089 }
3090 else if ( driverName.startsWith( QLatin1String( "BNA" ) ) )
3091 {
3092 sFileFilters += createFileFilter_( QObject::tr( "Atlas BNA" ), QStringLiteral( "*.bna" ) );
3093 sExtensions << QStringLiteral( "bna" );
3094 }
3095 else if ( driverName.startsWith( QLatin1String( "CSV" ) ) )
3096 {
3097 sFileFilters += createFileFilter_( QObject::tr( "Comma Separated Value" ), QStringLiteral( "*.csv" ) );
3098 sExtensions << QStringLiteral( "csv" );
3099 }
3100 else if ( driverName.startsWith( QObject::tr( "DODS" ) ) )
3101 {
3102 sProtocolDrivers += QLatin1String( "DODS/OPeNDAP,DODS;" );
3103 }
3104 else if ( driverName.startsWith( QObject::tr( "CouchDB" ) ) )
3105 {
3106 sProtocolDrivers += QLatin1String( "CouchDB;" );
3107 }
3108 else if ( driverName.startsWith( QLatin1String( "FileGDB" ) ) )
3109 {
3110 sDirectoryDrivers += QObject::tr( "ESRI FileGDB" ) + ",FileGDB;";
3111 if ( !sDirectoryExtensions.contains( QStringLiteral( "gdb" ) ) )
3112 sDirectoryExtensions << QStringLiteral( "gdb" );
3113 }
3114 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
3115 else if ( driverName.startsWith( QLatin1String( "FlatGeobuf" ) ) )
3116 {
3117 sProtocolDrivers += QLatin1String( "FlatGeobuf;" );
3118 sFileFilters += createFileFilter_( QObject::tr( "FlatGeobuf" ), QStringLiteral( "*.fgb" ) );
3119 sExtensions << QStringLiteral( "fgb" );
3120 }
3121 #endif
3122 else if ( driverName.startsWith( QLatin1String( "PGeo" ) ) )
3123 {
3124 sDatabaseDrivers += QObject::tr( "ESRI Personal GeoDatabase" ) + ",PGeo;";
3125 #ifdef Q_OS_WIN
3126 sFileFilters += createFileFilter_( QObject::tr( "ESRI Personal GeoDatabase" ), "*.mdb" );
3127 sExtensions << "mdb";
3128 #endif
3129 }
3130 else if ( driverName.startsWith( QLatin1String( "SDE" ) ) )
3131 {
3132 sDatabaseDrivers += QObject::tr( "ESRI ArcSDE" ) + ",SDE;";
3133 }
3134 else if ( driverName.startsWith( QLatin1String( "ESRI Shapefile" ) ) )
3135 {
3136 QString exts = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
3137 sFileFilters += createFileFilter_( QObject::tr( "ESRI Shapefiles" ), exts.contains( "shz" ) ? QStringLiteral( "*.shp *.shz *.shp.zip" ) : QStringLiteral( "*.shp" ) );
3138 sExtensions << QStringLiteral( "shp" ) << QStringLiteral( "dbf" );
3139 if ( exts.contains( "shz" ) )
3140 sExtensions << QStringLiteral( "shz" ) << QStringLiteral( "shp.zip" );
3141 }
3142 else if ( driverName.startsWith( QObject::tr( "FMEObjects Gateway" ) ) )
3143 {
3144 sFileFilters += createFileFilter_( QObject::tr( "FMEObjects Gateway" ), QStringLiteral( "*.fdd" ) );
3145 sExtensions << QStringLiteral( "fdd" );
3146 }
3147 else if ( driverName.startsWith( QLatin1String( "GeoJSONSeq" ) ) )
3148 {
3149 sProtocolDrivers += QLatin1String( "GeoJSON - Newline Delimited;" );
3150 sFileFilters += createFileFilter_( QObject::tr( "GeoJSON Newline Delimited JSON" ), QStringLiteral( "*.geojsonl *.geojsons *.nlgeojson *.json" ) );
3151 sExtensions << QStringLiteral( "geojsonl" ) << QStringLiteral( "geojsons" ) << QStringLiteral( "nlgeojson" ) << QStringLiteral( "json" );
3152 }
3153 else if ( driverName.startsWith( QLatin1String( "GeoJSON" ) ) )
3154 {
3155 sProtocolDrivers += QLatin1String( "GeoJSON;" );
3156 sFileFilters += createFileFilter_( QObject::tr( "GeoJSON" ), QStringLiteral( "*.geojson" ) );
3157 sExtensions << QStringLiteral( "geojson" );
3158 }
3159 else if ( driverName.startsWith( QLatin1String( "GeoRSS" ) ) )
3160 {
3161 sFileFilters += createFileFilter_( QObject::tr( "GeoRSS" ), QStringLiteral( "*.xml" ) );
3162 sExtensions << QStringLiteral( "xml" );
3163 }
3164 else if ( driverName == QLatin1String( "GML" ) )
3165 {
3166 sFileFilters += createFileFilter_( QObject::tr( "Geography Markup Language [GML]" ), QStringLiteral( "*.gml" ) );
3167 sExtensions << QStringLiteral( "gml" );
3168 }
3169 else if ( driverName == QLatin1String( "GMLAS" ) )
3170 {
3171 continue;
3172 }
3173 else if ( driverName.startsWith( QLatin1String( "GMT" ) ) )
3174 {
3175 sFileFilters += createFileFilter_( QObject::tr( "Generic Mapping Tools [GMT]" ), QStringLiteral( "*.gmt" ) );
3176 sExtensions << QStringLiteral( "gmt" );
3177 }
3178 else if ( driverName.startsWith( QLatin1String( "GPX" ) ) )
3179 {
3180 sFileFilters += createFileFilter_( QObject::tr( "GPS eXchange Format [GPX]" ), QStringLiteral( "*.gpx" ) );
3181 sExtensions << QStringLiteral( "gpx" );
3182 }
3183 else if ( driverName.startsWith( QLatin1String( "GPKG" ) ) )
3184 {
3185 sFileFilters += createFileFilter_( QObject::tr( "GeoPackage" ), QStringLiteral( "*.gpkg" ) );
3186 sExtensions << QStringLiteral( "gpkg" );
3187 }
3188 else if ( driverName.startsWith( QLatin1String( "GRASS" ) ) )
3189 {
3190 sDirectoryDrivers += QObject::tr( "Grass Vector" ) + ",GRASS;";
3191 }
3192 else if ( driverName.startsWith( QLatin1String( "IDB" ) ) )
3193 {
3194 sDatabaseDrivers += QObject::tr( "Informix DataBlade" ) + ",IDB;";
3195 }
3196 else if ( driverName.startsWith( QLatin1String( "Interlis 1" ) ) )
3197 {
3198 sFileFilters += createFileFilter_( QObject::tr( "INTERLIS 1" ), QStringLiteral( "*.itf *.xml *.ili" ) );
3199 sExtensions << QStringLiteral( "itf" ) << QStringLiteral( "xml" ) << QStringLiteral( "ili" );
3200 }
3201 else if ( driverName.startsWith( QLatin1String( "Interlis 2" ) ) )
3202 {
3203 sFileFilters += createFileFilter_( QObject::tr( "INTERLIS 2" ), QStringLiteral( "*.xtf *.xml *.ili" ) );
3204 sExtensions << QStringLiteral( "xtf" ) << QStringLiteral( "xml" ) << QStringLiteral( "ili" );
3205 }
3206 else if ( driverName.startsWith( QLatin1String( "Ingres" ) ) )
3207 {
3208 sDatabaseDrivers += QObject::tr( "Ingres" ) + ",Ingres;";
3209 }
3210 else if ( driverName == QLatin1String( "KML" ) || driverName == QLatin1String( "LIBKML" ) )
3211 {
3212 if ( kmlFound )
3213 continue;
3214 kmlFound = true;
3215 sFileFilters += createFileFilter_( QObject::tr( "Keyhole Markup Language [KML]" ), QStringLiteral( "*.kml *.kmz" ) );
3216 sExtensions << QStringLiteral( "kml" ) << QStringLiteral( "kmz" );
3217 }
3218 else if ( driverName.startsWith( QLatin1String( "MapInfo File" ) ) )
3219 {
3220 sFileFilters += createFileFilter_( QObject::tr( "Mapinfo File" ), QStringLiteral( "*.mif *.tab" ) );
3221 sExtensions << QStringLiteral( "mif" ) << QStringLiteral( "tab" );
3222 }
3223 else if ( driverName == QLatin1String( "DGN" ) || driverName == QLatin1String( "DGNV8" ) )
3224 {
3225 if ( dgnFound )
3226 continue;
3227 dgnFound = true;
3228 sFileFilters += createFileFilter_( QObject::tr( "Microstation DGN" ), QStringLiteral( "*.dgn" ) );
3229 sExtensions << QStringLiteral( "dgn" );
3230 }
3231 else if ( driverName.startsWith( QLatin1String( "MySQL" ) ) )
3232 {
3233 sDatabaseDrivers += QObject::tr( "MySQL" ) + ",MySQL;";
3234 }
3235 else if ( driverName.startsWith( QLatin1String( "MSSQL" ) ) )
3236 {
3237 sDatabaseDrivers += QObject::tr( "MSSQL" ) + ",MSSQL;";
3238 }
3239 else if ( driverName.startsWith( QLatin1String( "OCI" ) ) )
3240 {
3241 sDatabaseDrivers += QObject::tr( "Oracle Spatial" ) + ",OCI;";
3242 }
3243 else if ( driverName.startsWith( QLatin1String( "ODBC" ) ) )
3244 {
3245 sDatabaseDrivers += QObject::tr( "ODBC" ) + ",ODBC;";
3246 }
3247 else if ( driverName.startsWith( QLatin1String( "OGDI" ) ) )
3248 {
3249 sDatabaseDrivers += QObject::tr( "OGDI Vectors" ) + ",OGDI;";
3250 }
3251 else if ( driverName.startsWith( QLatin1String( "OpenFileGDB" ) ) )
3252 {
3253 sDirectoryDrivers += QObject::tr( "OpenFileGDB" ) + ",OpenFileGDB;";
3254 if ( !sDirectoryExtensions.contains( QStringLiteral( "gdb" ) ) )
3255 sDirectoryExtensions << QStringLiteral( "gdb" );
3256 }
3257 else if ( driverName.startsWith( QLatin1String( "PostgreSQL" ) ) )
3258 {
3259 sDatabaseDrivers += QObject::tr( "PostgreSQL" ) + ",PostgreSQL;";
3260 }
3261 else if ( driverName.startsWith( QLatin1String( "S57" ) ) )
3262 {
3263 sFileFilters += createFileFilter_( QObject::tr( "S-57 Base file" ),
3264 QStringLiteral( "*.000" ) );
3265 sExtensions << QStringLiteral( "000" );
3266 }
3267 else if ( driverName.startsWith( QLatin1String( "SDTS" ) ) )
3268 {
3269 sFileFilters += createFileFilter_( QObject::tr( "Spatial Data Transfer Standard [SDTS]" ),
3270 QStringLiteral( "*catd.ddf" ) );
3271 sWildcards << QStringLiteral( "*catd.ddf" );
3272 }
3273 else if ( driverName.startsWith( QLatin1String( "SOSI" ) ) )
3274 {
3275 sFileFilters += createFileFilter_( QObject::tr( "Systematic Organization of Spatial Information [SOSI]" ), QStringLiteral( "*.sos" ) );
3276 sExtensions << QStringLiteral( "sos" );
3277 }
3278 else if ( driverName.startsWith( QLatin1String( "SQLite" ) ) )
3279 {
3280 sFileFilters += createFileFilter_( QObject::tr( "SQLite/SpatiaLite" ), QStringLiteral( "*.sqlite *.db *.sqlite3 *.db3 *.s3db *.sl3" ) );
3281 sExtensions << QStringLiteral( "sqlite" ) << QStringLiteral( "db" ) << QStringLiteral( "sqlite3" ) << QStringLiteral( "db3" ) << QStringLiteral( "s3db" ) << QStringLiteral( "sl3" );
3282 }
3283 else if ( driverName.startsWith( QLatin1String( "SXF" ) ) )
3284 {
3285 sFileFilters += createFileFilter_( QObject::tr( "Storage and eXchange Format" ), QStringLiteral( "*.sxf" ) );
3286 sExtensions << QStringLiteral( "sxf" );
3287 }
3288 else if ( driverName.startsWith( QLatin1String( "UK .NTF" ) ) )
3289 {
3290 sDirectoryDrivers += QObject::tr( "UK. NTF2" ) + ",UK. NTF;";
3291 }
3292 else if ( driverName.startsWith( QLatin1String( "TIGER" ) ) )
3293 {
3294 sDirectoryDrivers += QObject::tr( "U.S. Census TIGER/Line" ) + ",TIGER;";
3295 }
3296 else if ( driverName.startsWith( QLatin1String( "OGR_VRT" ) ) )
3297 {
3298 sFileFilters += createFileFilter_( QObject::tr( "VRT - Virtual Datasource" ),
3299 QStringLiteral( "*.vrt *.ovf" ) );
3300 sExtensions << QStringLiteral( "vrt" ) << QStringLiteral( "ovf" );
3301 }
3302 else if ( driverName.startsWith( QLatin1String( "XPlane" ) ) )
3303 {
3304 sFileFilters += createFileFilter_( QObject::tr( "X-Plane/Flightgear" ),
3305 QStringLiteral( "apt.dat nav.dat fix.dat awy.dat" ) );
3306 sWildcards << QStringLiteral( "apt.dat" ) << QStringLiteral( "nav.dat" ) << QStringLiteral( "fix.dat" ) << QStringLiteral( "awy.dat" );
3307 }
3308 else if ( driverName.startsWith( QLatin1String( "Geoconcept" ) ) )
3309 {
3310 sFileFilters += createFileFilter_( QObject::tr( "Geoconcept" ), QStringLiteral( "*.gxt *.txt" ) );
3311 sExtensions << QStringLiteral( "gxt" ) << QStringLiteral( "txt" );
3312 }
3313 else if ( driverName.startsWith( QLatin1String( "DXF" ) ) )
3314 {
3315 sFileFilters += createFileFilter_( QObject::tr( "AutoCAD DXF" ), QStringLiteral( "*.dxf" ) );
3316 sExtensions << QStringLiteral( "dxf" );
3317 }
3318 else if ( driverName.startsWith( QLatin1String( "ODS" ) ) )
3319 {
3320 sFileFilters += createFileFilter_( QObject::tr( "Open Document Spreadsheet" ), QStringLiteral( "*.ods" ) );
3321 sExtensions << QStringLiteral( "ods" );
3322 }
3323 else if ( driverName.startsWith( QLatin1String( "XLSX" ) ) )
3324 {
3325 sFileFilters += createFileFilter_( QObject::tr( "MS Office Open XML spreadsheet" ), QStringLiteral( "*.xlsx" ) );
3326 sExtensions << QStringLiteral( "xlsx" );
3327 }
3328 else if ( driverName.endsWith( QLatin1String( "XLS" ) ) )
3329 {
3330 sFileFilters += createFileFilter_( QObject::tr( "MS Excel format" ), QStringLiteral( "*.xls" ) );
3331 sExtensions << QStringLiteral( "xls" );
3332 }
3333 else if ( driverName.startsWith( QLatin1String( "EDIGEO" ) ) )
3334 {
3335 sFileFilters += createFileFilter_( QObject::tr( "EDIGEO" ), QStringLiteral( "*.thf" ) );
3336 sExtensions << QStringLiteral( "thf" );
3337 }
3338 else if ( driverName.startsWith( QLatin1String( "NAS" ) ) )
3339 {
3340 sFileFilters += createFileFilter_( QObject::tr( "NAS - ALKIS" ), QStringLiteral( "*.xml" ) );
3341 sExtensions << QStringLiteral( "xml" );
3342 }
3343 else if ( driverName.startsWith( QLatin1String( "WAsP" ) ) )
3344 {
3345 sFileFilters += createFileFilter_( QObject::tr( "WAsP" ), QStringLiteral( "*.map" ) );
3346 sExtensions << QStringLiteral( "map" );
3347 }
3348 else if ( driverName.startsWith( QLatin1String( "PCIDSK" ) ) )
3349 {
3350 sFileFilters += createFileFilter_( QObject::tr( "PCI Geomatics Database File" ), QStringLiteral( "*.pix" ) );
3351 sExtensions << QStringLiteral( "pix" );
3352 }
3353 else if ( driverName.startsWith( QLatin1String( "GPSTrackMaker" ) ) )
3354 {
3355 sFileFilters += createFileFilter_( QObject::tr( "GPSTrackMaker" ), QStringLiteral( "*.gtm *.gtz" ) );
3356 sExtensions << QStringLiteral( "gtm" ) << QStringLiteral( "gtz" );
3357 }
3358 else if ( driverName.startsWith( QLatin1String( "VFK" ) ) )
3359 {
3360 sFileFilters += createFileFilter_( QObject::tr( "Czech Cadastral Exchange Data Format" ), QStringLiteral( "*.vfk" ) );
3361 sExtensions << QStringLiteral( "vfk" );
3362 }
3363 else if ( driverName.startsWith( QLatin1String( "OSM" ) ) )
3364 {
3365 sFileFilters += createFileFilter_( QObject::tr( "OpenStreetMap" ), QStringLiteral( "*.osm *.pbf" ) );
3366 sExtensions << QStringLiteral( "osm" ) << QStringLiteral( "pbf" );
3367 }
3368 else if ( driverName.startsWith( QLatin1String( "SUA" ) ) )
3369 {
3370 sFileFilters += createFileFilter_( QObject::tr( "Special Use Airspace Format" ), QStringLiteral( "*.sua" ) );
3371 sExtensions << QStringLiteral( "sua" );
3372 }
3373 else if ( driverName.startsWith( QLatin1String( "OpenAir" ) ) )
3374 {
3375 sFileFilters += createFileFilter_( QObject::tr( "OpenAir Special Use Airspace Format" ), QStringLiteral( "*.txt" ) );
3376 sExtensions << QStringLiteral( "txt" );
3377 }
3378 else if ( driverName.startsWith( QLatin1String( "PDS" ) ) )
3379 {
3380 sFileFilters += createFileFilter_( QObject::tr( "Planetary Data Systems TABLE" ), QStringLiteral( "*.xml" ) );
3381 sExtensions << QStringLiteral( "xml" );
3382 }
3383 else if ( driverName.startsWith( QLatin1String( "HTF" ) ) )
3384 {
3385 sFileFilters += createFileFilter_( QObject::tr( "Hydrographic Transfer Format" ), QStringLiteral( "*.htf" ) );
3386 sExtensions << QStringLiteral( "htf" );
3387 }
3388 else if ( driverName.startsWith( QLatin1String( "SVG" ) ) )
3389 {
3390 sFileFilters += createFileFilter_( QObject::tr( "Scalable Vector Graphics" ), QStringLiteral( "*.svg" ) );
3391 sExtensions << QStringLiteral( "svg" );
3392 }
3393 else if ( driverName.startsWith( QLatin1String( "ARCGEN" ) ) )
3394 {
3395 sFileFilters += createFileFilter_( QObject::tr( "Arc/Info Generate" ), QStringLiteral( "*.gen" ) );
3396 sExtensions << QStringLiteral( "gen" );
3397 }
3398 else if ( driverName.startsWith( QLatin1String( "PDF" ) ) )
3399 {
3400 sFileFilters += createFileFilter_( QObject::tr( "Geospatial PDF" ), QStringLiteral( "*.pdf" ) );
3401 sExtensions << QStringLiteral( "pdf" );
3402 }
3403 else if ( driverName.startsWith( QLatin1String( "SEGY" ) ) )
3404 {
3405 sFileFilters += createFileFilter_( QObject::tr( "SEG-Y" ), QStringLiteral( "*.sgy *.segy" ) );
3406 sExtensions << QStringLiteral( "sgy" ) << QStringLiteral( "segy" );
3407 }
3408 else if ( driverName.startsWith( QLatin1String( "SEGUKOOA" ) ) )
3409 {
3410 sFileFilters += createFileFilter_( QObject::tr( "SEG-P1" ), QStringLiteral( "*.seg *.seg1 *.sp1" ) );
3411 sFileFilters += createFileFilter_( QObject::tr( "UKOOA P1/90" ), QStringLiteral( "*.uko *.ukooa" ) );
3412 sExtensions << QStringLiteral( "seg" ) << QStringLiteral( "seg1" ) << QStringLiteral( "sp1" ) << QStringLiteral( "uko" ) << QStringLiteral( "ukooa" );
3413 }
3414 else
3415 {
3416 if ( driverName == QLatin1String( "CAD" ) || driverName == QLatin1String( "DWG" ) )
3417 {
3418 if ( dwgFound )
3419 continue;
3420 dwgFound = true;
3421 }
3422
3423 QString myGdalDriverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
3424 QString myGdalDriverLongName = GDALGetMetadataItem( driver, GDAL_DMD_LONGNAME, "" );
3425 if ( !( myGdalDriverExtensions.isEmpty() || myGdalDriverLongName.isEmpty() ) )
3426 {
3427 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
3428 const QStringList splitExtensions = myGdalDriverExtensions.split( ' ', QString::SkipEmptyParts );
3429 #else
3430 const QStringList splitExtensions = myGdalDriverExtensions.split( ' ', Qt::SkipEmptyParts );
3431 #endif
3432 QString glob;
3433
3434 for ( const QString &ext : splitExtensions )
3435 {
3436 sExtensions << ext;
3437 if ( !glob.isEmpty() )
3438 glob += QLatin1Char( ' ' );
3439 glob += "*." + ext;
3440 }
3441
3442 if ( driverName == QLatin1String( "JPEG2000" ) ||
3443 driverName.startsWith( QLatin1String( "JP2" ) ) )
3444 {
3445 // Skip over JPEG2000 drivers, as their vector capabilities are just
3446 // a marginal use case
3447 continue;
3448 }
3449
3450 sFileFilters += createFileFilter_( myGdalDriverLongName, glob );
3451
3452 }
3453 else
3454 {
3455 // NOP, we don't know anything about the current driver
3456 // with regards to a proper file filter string
3457 QgsDebugMsgLevel( QStringLiteral( "Unknown driver %1 for file filters." ).arg( driverName ), 2 );
3458 }
3459 }
3460
3461 } // each loaded OGR driver
3462
3463 // sort file filters alphabetically
3464 QgsDebugMsgLevel( "myFileFilters: " + sFileFilters, 2 );
3465 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
3466 QStringList filters = sFileFilters.split( QStringLiteral( ";;" ), QString::SkipEmptyParts );
3467 #else
3468 QStringList filters = sFileFilters.split( QStringLiteral( ";;" ), Qt::SkipEmptyParts );
3469 #endif
3470 filters.sort();
3471 sFileFilters = filters.join( QLatin1String( ";;" ) ) + ";;";
3472 QgsDebugMsgLevel( "myFileFilters: " + sFileFilters, 2 );
3473
3474 // VSIFileHandler (.zip and .gz files) - second
3475 // see http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
3476 // Requires GDAL>=1.6.0 with libz support, let's assume we have it.
3477 // This does not work for some file types, see VSIFileHandler doc.
3478 QgsSettings settings;
3479 if ( settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString() != QLatin1String( "no" ) )
3480 {
3481 sFileFilters.prepend( createFileFilter_( QObject::tr( "GDAL/OGR VSIFileHandler" ), QStringLiteral( "*.zip *.gz *.tar *.tar.gz *.tgz" ) ) );
3482 sExtensions << QStringLiteral( "zip" ) << QStringLiteral( "gz" ) << QStringLiteral( "tar" ) << QStringLiteral( "tar.gz" ) << QStringLiteral( "tgz" );
3483 }
3484
3485 // can't forget the all supported case
3486 QStringList exts;
3487 for ( const QString &ext : qgis::as_const( sExtensions ) )
3488 exts << QStringLiteral( "*.%1 *.%2" ).arg( ext, ext.toUpper() );
3489 sFileFilters.prepend( QObject::tr( "All supported files" ) + QStringLiteral( " (%1);;" ).arg( exts.join( QLatin1Char( ' ' ) ) ) );
3490
3491 // can't forget the default case - first
3492 sFileFilters.prepend( QObject::tr( "All files" ) + " (*);;" );
3493
3494
3495 // cleanup
3496 if ( sFileFilters.endsWith( QLatin1String( ";;" ) ) ) sFileFilters.chop( 2 );
3497
3498 QgsDebugMsgLevel( "myFileFilters: " + sFileFilters, 2 );
3499 }
3500
3501 if ( type == QLatin1String( "file" ) )
3502 {
3503 return sFileFilters;
3504 }
3505 if ( type == QLatin1String( "database" ) )
3506 {
3507 return sDatabaseDrivers;
3508 }
3509 if ( type == QLatin1String( "protocol" ) )
3510 {
3511 return sProtocolDrivers;
3512 }
3513 if ( type == QLatin1String( "directory" ) )
3514 {
3515 return sDirectoryDrivers;
3516 }
3517 if ( type == QLatin1String( "extensions" ) )
3518 {
3519 return sExtensions.join( QLatin1Char( '|' ) );
3520 }
3521 if ( type == QLatin1String( "directory_extensions" ) )
3522 {
3523 return sDirectoryExtensions.join( QLatin1Char( '|' ) );
3524 }
3525 if ( type == QLatin1String( "wildcards" ) )
3526 {
3527 return sWildcards.join( QLatin1Char( '|' ) );
3528 }
3529 else
3530 {
3531 return QString();
3532 }
3533 }
3534
decodeUri(const QString & uri)3535 QVariantMap QgsOgrProviderMetadata::decodeUri( const QString &uri )
3536 {
3537 QString path = uri;
3538 QString layerName;
3539 QString subset;
3540 QString geometryType;
3541 QStringList openOptions;
3542 QString databaseName;
3543
3544 int layerId = -1;
3545
3546 if ( path.contains( '|' ) )
3547 {
3548 const QRegularExpression geometryTypeRegex( QStringLiteral( "\\|geometrytype=([a-zA-Z0-9]*)" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
3549 const QRegularExpression layerNameRegex( QStringLiteral( "\\|layername=([^|]*)" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
3550 const QRegularExpression layerIdRegex( QStringLiteral( "\\|layerid=([^|]*)" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
3551 const QRegularExpression subsetRegex( QStringLiteral( "\\|subset=((?:.*[\r\n]*)*)\\Z" ) );
3552 const QRegularExpression openOptionRegex( QStringLiteral( "\\|option:([^|]*)" ) );
3553
3554
3555 // we first try to split off the geometry type component, if that's present. That's a known quantity which
3556 // will never be more than a-z characters
3557 QRegularExpressionMatch match = geometryTypeRegex.match( path );
3558 if ( match.hasMatch() )
3559 {
3560 geometryType = match.captured( 1 );
3561 path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
3562 }
3563
3564 // next, we try to find and strip out the layerid/layername component. (This logic is based on the assumption
3565 // that a layer name doesn't contain a | character!)
3566 // we prefer layer names over layer ids, since they are persistent..
3567 match = layerNameRegex.match( path );
3568 if ( match.hasMatch() )
3569 {
3570 layerName = match.captured( 1 );
3571 path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
3572 }
3573
3574 match = layerIdRegex.match( path );
3575 if ( match.hasMatch() )
3576 {
3577 layerId = match.captured( 1 ).toInt();
3578 path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
3579 }
3580
3581 while ( true )
3582 {
3583 match = openOptionRegex.match( path );
3584 if ( match.hasMatch() )
3585 {
3586 openOptions << match.captured( 1 );
3587 path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
3588 }
3589 else
3590 {
3591 break;
3592 }
3593 }
3594
3595 // lastly, try to parse out the subset component. This is the biggest unknown, because it's
3596 // quite possible that a subset string will contain a | character. If we've already parsed
3597 // out all the other known |xxx=yyy tags, then we can safely assume that everything from "|subset=" to the
3598 // end of the path is part of the subset filter
3599 match = subsetRegex.match( path );
3600 if ( match.hasMatch() )
3601 {
3602 subset = match.captured( 1 );
3603 path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
3604 }
3605 }
3606
3607 // Handles DB connections extracting database name if possible
3608 // Example: MySQL:database_name,host=localhost,port=3306 authcfg='f8wwfx8'
3609 if ( uri.startsWith( QStringLiteral( "MySQL" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3610 uri.startsWith( QStringLiteral( "PostgreSQL" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3611 uri.startsWith( QStringLiteral( "MSSQL" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3612 uri.startsWith( QStringLiteral( "ODBC" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3613 uri.startsWith( QStringLiteral( "PGeo" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3614 uri.startsWith( QStringLiteral( "SDE" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3615 uri.startsWith( QStringLiteral( "OGDI" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3616 uri.startsWith( QStringLiteral( "Ingres" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3617 uri.startsWith( QStringLiteral( "IDB" ), Qt::CaseSensitivity::CaseInsensitive ) ||
3618 uri.startsWith( QStringLiteral( "OCI" ), Qt::CaseSensitivity::CaseInsensitive ) )
3619 {
3620 auto parts( path.split( ':' ) );
3621 if ( parts.count( ) > 1 )
3622 {
3623 auto dataParts( parts.at( 1 ).split( ',' ) );
3624 if ( dataParts.count() > 0 )
3625 databaseName = dataParts.at( 0 );
3626 }
3627 }
3628
3629 QVariantMap uriComponents;
3630 uriComponents.insert( QStringLiteral( "path" ), path );
3631 uriComponents.insert( QStringLiteral( "layerName" ), layerName );
3632 uriComponents.insert( QStringLiteral( "layerId" ), layerId > -1 && layerName.isEmpty() ? layerId : QVariant() ) ;
3633 if ( !subset.isEmpty() )
3634 uriComponents.insert( QStringLiteral( "subset" ), subset );
3635 if ( !geometryType.isEmpty() )
3636 uriComponents.insert( QStringLiteral( "geometryType" ), geometryType );
3637 if ( !databaseName.isEmpty() )
3638 uriComponents.insert( QStringLiteral( "databaseName" ), databaseName );
3639 if ( !openOptions.isEmpty() )
3640 uriComponents.insert( QStringLiteral( "openOptions" ), openOptions );
3641 return uriComponents;
3642 }
3643
encodeUri(const QVariantMap & parts)3644 QString QgsOgrProviderMetadata::encodeUri( const QVariantMap &parts )
3645 {
3646 const QString path = parts.value( QStringLiteral( "path" ) ).toString();
3647 const QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
3648 const QString layerId = parts.value( QStringLiteral( "layerId" ) ).toString();
3649 const QString subset = parts.value( QStringLiteral( "subset" ) ).toString();
3650 const QString geometryType = parts.value( QStringLiteral( "geometryType" ) ).toString();
3651 const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
3652 QString uri = path
3653 + ( !layerName.isEmpty() ? QStringLiteral( "|layername=%1" ).arg( layerName ) : !layerId.isEmpty() ? QStringLiteral( "|layerid=%1" ).arg( layerId ) : QString() )
3654 + ( !geometryType.isEmpty() ? QStringLiteral( "|geometrytype=%1" ).arg( geometryType ) : QString() );
3655 for ( const QString &openOption : openOptions )
3656 {
3657 uri += QLatin1String( "|option:" );
3658 uri += openOption;
3659 }
3660 if ( !subset.isEmpty() )
3661 uri += QStringLiteral( "|subset=%1" ).arg( subset );
3662 return uri;
3663 }
3664
fileVectorFilters()3665 QString QgsOgrProviderUtils::fileVectorFilters()
3666 {
3667 return createFilters( QStringLiteral( "file" ) );
3668 }
3669
databaseDrivers()3670 QString QgsOgrProviderUtils::databaseDrivers()
3671 {
3672 return createFilters( QStringLiteral( "database" ) );
3673 }
3674
protocolDrivers()3675 QString QgsOgrProviderUtils::protocolDrivers()
3676 {
3677 return createFilters( QStringLiteral( "protocol" ) );
3678 }
3679
3680
directoryDrivers()3681 QString QgsOgrProviderUtils::directoryDrivers()
3682 {
3683 return createFilters( QStringLiteral( "directory" ) );
3684 }
3685
fileExtensions()3686 QStringList QgsOgrProviderUtils::fileExtensions()
3687 {
3688 return createFilters( QStringLiteral( "extensions" ) ).split( '|' );
3689 }
3690
directoryExtensions()3691 QStringList QgsOgrProviderUtils::directoryExtensions()
3692 {
3693 return createFilters( QStringLiteral( "directory_extensions" ) ).split( '|' );
3694 }
3695
wildcards()3696 QStringList QgsOgrProviderUtils::wildcards()
3697 {
3698 return createFilters( QStringLiteral( "wildcards" ) ).split( '|' );
3699 }
3700
3701
3702 /**
3703 * Class factory to return a pointer to a newly created
3704 * QgsOgrProvider object
3705 */
createProvider(const QString & uri,const QgsDataProvider::ProviderOptions & options,QgsDataProvider::ReadFlags flags)3706 QgsOgrProvider *QgsOgrProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options,
3707 QgsDataProvider::ReadFlags flags )
3708 {
3709 return new QgsOgrProvider( uri, options, flags );
3710 }
3711
3712 /**
3713 * Creates an empty data source
3714 \param uri location to store the file(s)
3715 \param format data format (e.g. "ESRI Shapefile")
3716 \param vectortype point/line/polygon or multitypes
3717 \param attributes a list of name/type pairs for the initial attributes
3718 \return true in case of success*/
createEmptyDataSource(const QString & uri,const QString & format,const QString & encoding,QgsWkbTypes::Type vectortype,const QList<QPair<QString,QString>> & attributes,const QgsCoordinateReferenceSystem & srs,QString & errorMessage)3719 bool QgsOgrProviderUtils::createEmptyDataSource( const QString &uri,
3720 const QString &format,
3721 const QString &encoding,
3722 QgsWkbTypes::Type vectortype,
3723 const QList< QPair<QString, QString> > &attributes,
3724 const QgsCoordinateReferenceSystem &srs,
3725 QString &errorMessage )
3726 {
3727 QgsDebugMsgLevel( QStringLiteral( "Creating empty vector layer with format: %1" ).arg( format ), 2 );
3728 errorMessage.clear();
3729
3730 QgsApplication::registerOgrDrivers();
3731 OGRSFDriverH driver = OGRGetDriverByName( format.toLatin1() );
3732 if ( !driver )
3733 {
3734 return false;
3735 }
3736
3737 QString driverName = GDALGetDriverShortName( driver );
3738
3739 if ( driverName == QLatin1String( "ESRI Shapefile" ) )
3740 {
3741 if ( !uri.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) &&
3742 !uri.endsWith( QLatin1String( ".shz" ), Qt::CaseInsensitive ) &&
3743 !uri.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) )
3744 {
3745 errorMessage = QObject::tr( "URI %1 doesn't end with .shp or .dbf" ).arg( uri );
3746 QgsDebugMsg( errorMessage );
3747 return false;
3748 }
3749
3750 // check for duplicate fieldnames
3751 QSet<QString> fieldNames;
3752 QList<QPair<QString, QString> >::const_iterator fldIt;
3753 for ( fldIt = attributes.begin(); fldIt != attributes.end(); ++fldIt )
3754 {
3755 QString name = fldIt->first.left( 10 );
3756 if ( fieldNames.contains( name ) )
3757 {
3758 errorMessage = QObject::tr( "Duplicate field (10 significant characters): %1" ).arg( name );
3759 QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
3760 return false;
3761 }
3762 fieldNames << name;
3763 }
3764
3765 QgsVectorFileWriter::deleteShapeFile( uri );
3766 }
3767 else
3768 {
3769 QFile::remove( uri );
3770 }
3771
3772 gdal::dataset_unique_ptr dataSource;
3773 dataSource.reset( OGR_Dr_CreateDataSource( driver, uri.toUtf8().constData(), nullptr ) );
3774 if ( !dataSource )
3775 {
3776 errorMessage = QObject::tr( "Creating the data source %1 failed: %2" ).arg( uri, QString::fromUtf8( CPLGetLastErrorMsg() ) );
3777 QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
3778 return false;
3779 }
3780
3781 //consider spatial reference system
3782 OGRSpatialReferenceH reference = nullptr;
3783
3784 QgsCoordinateReferenceSystem mySpatialRefSys;
3785 if ( srs.isValid() )
3786 {
3787 mySpatialRefSys = srs;
3788 }
3789 else
3790 {
3791 mySpatialRefSys.validate();
3792 }
3793
3794 QString myWkt = mySpatialRefSys.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL );
3795
3796 if ( !myWkt.isNull() && myWkt.length() != 0 )
3797 {
3798 reference = OSRNewSpatialReference( myWkt.toLocal8Bit().data() );
3799 }
3800
3801 // Map the qgis geometry type to the OGR geometry type
3802 OGRwkbGeometryType OGRvectortype = wkbUnknown;
3803 switch ( vectortype )
3804 {
3805 case QgsWkbTypes::GeometryCollection:
3806 case QgsWkbTypes::GeometryCollectionZ:
3807 case QgsWkbTypes::GeometryCollectionM:
3808 case QgsWkbTypes::GeometryCollectionZM:
3809 case QgsWkbTypes::Unknown:
3810 {
3811 errorMessage = QObject::tr( "Unknown vector type of %1" ).arg( static_cast< int >( vectortype ) );
3812 QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
3813 return false;
3814 }
3815
3816 default:
3817 OGRvectortype = QgsOgrProviderUtils::ogrTypeFromQgisType( vectortype );
3818 }
3819
3820 char **papszOptions = nullptr;
3821 if ( driverName == QLatin1String( "ESRI Shapefile" ) )
3822 {
3823 papszOptions = CSLSetNameValue( papszOptions, "ENCODING", QgsVectorFileWriter::convertCodecNameForEncodingOption( encoding ).toLocal8Bit().data() );
3824 // OGR Shapefile fails to create fields if given encoding is not supported by its side
3825 // so disable encoding conversion of OGR Shapefile layer
3826 CPLSetConfigOption( "SHAPE_ENCODING", "" );
3827 }
3828
3829 OGRLayerH layer;
3830 layer = GDALDatasetCreateLayer( dataSource.get(), QFileInfo( uri ).completeBaseName().toUtf8().constData(), reference, OGRvectortype, papszOptions );
3831 CSLDestroy( papszOptions );
3832
3833 if ( !layer )
3834 {
3835 errorMessage = QString::fromUtf8( CPLGetLastErrorMsg() );
3836 QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
3837 return false;
3838 }
3839
3840 //create the attribute fields
3841
3842 QTextCodec *codec = QTextCodec::codecForName( encoding.toLocal8Bit().data() );
3843 if ( !codec )
3844 {
3845 // fall back to "System" codec
3846 codec = QTextCodec::codecForLocale();
3847 Q_ASSERT( codec );
3848 }
3849
3850 for ( QList<QPair<QString, QString> >::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
3851 {
3852 QStringList fields = it->second.split( ';' );
3853
3854 if ( fields.isEmpty() )
3855 continue;
3856
3857 int width = fields.size() > 1 ? fields[1].toInt() : -1;
3858 int precision = fields.size() > 2 ? fields[2].toInt() : -1;
3859 if ( precision > 0 )
3860 width += 1;
3861
3862 OGRFieldDefnH field;
3863 if ( fields[0] == QLatin1String( "Real" ) )
3864 {
3865 if ( width < 0 )
3866 width = 32;
3867 if ( precision < 0 )
3868 precision = 3;
3869
3870 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTReal );
3871 OGR_Fld_SetWidth( field, width );
3872 OGR_Fld_SetPrecision( field, precision );
3873 }
3874 else if ( fields[0] == QLatin1String( "Integer" ) )
3875 {
3876 if ( width < 0 || width > 10 )
3877 width = 10;
3878
3879 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTInteger );
3880 // limit to 10. otherwise OGR sets it to 11 and recognizes as OFTDouble later
3881 OGR_Fld_SetWidth( field, width );
3882 }
3883 else if ( fields[0] == QLatin1String( "String" ) )
3884 {
3885 if ( width < 0 || width > 255 )
3886 width = 255;
3887
3888 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTString );
3889 OGR_Fld_SetWidth( field, width );
3890 }
3891 else if ( fields[0] == QLatin1String( "Date" ) )
3892 {
3893 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTDate );
3894 }
3895 else if ( fields[0] == QLatin1String( "Time" ) )
3896 {
3897 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTTime );
3898 }
3899 else if ( fields[0] == QLatin1String( "DateTime" ) )
3900 {
3901 field = OGR_Fld_Create( codec->fromUnicode( it->first ).constData(), OFTDateTime );
3902 }
3903 else
3904 {
3905 QgsMessageLog::logMessage( QObject::tr( "field %1 with unsupported type %2 skipped" ).arg( it->first, fields[0] ), QObject::tr( "OGR" ) );
3906 continue;
3907 }
3908
3909 if ( OGR_L_CreateField( layer, field, true ) != OGRERR_NONE )
3910 {
3911 QgsMessageLog::logMessage( QObject::tr( "creation of field %1 failed" ).arg( it->first ), QObject::tr( "OGR" ) );
3912 }
3913 }
3914
3915 dataSource.reset();
3916
3917 if ( driverName == QLatin1String( "ESRI Shapefile" ) )
3918 {
3919 int index = uri.indexOf( QLatin1String( ".shp" ), Qt::CaseInsensitive );
3920 if ( index > 0 )
3921 {
3922 QString layerName = uri.left( index );
3923 QFile prjFile( layerName + ".qpj" );
3924 #if PROJ_VERSION_MAJOR<6
3925 if ( prjFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3926 {
3927 QTextStream prjStream( &prjFile );
3928 prjStream << myWkt.toLocal8Bit().data() << endl;
3929 prjFile.close();
3930 }
3931 else
3932 {
3933 QgsMessageLog::logMessage( QObject::tr( "Couldn't create file %1.qpj" ).arg( layerName ), QObject::tr( "OGR" ) );
3934 }
3935 #else
3936 if ( prjFile.exists() )
3937 prjFile.remove();
3938 #endif
3939 }
3940 }
3941
3942 QgsDebugMsgLevel( QStringLiteral( "GDAL Version number %1" ).arg( GDAL_VERSION_NUM ), 2 );
3943 if ( reference )
3944 {
3945 OSRRelease( reference );
3946 }
3947 return true;
3948 }
3949
dataItemProviders() const3950 QList<QgsDataItemProvider *> QgsOgrProviderMetadata::dataItemProviders() const
3951 {
3952 QList< QgsDataItemProvider * > providers;
3953 providers << new QgsOgrDataItemProvider;
3954 providers << new QgsGeoPackageDataItemProvider;
3955 return providers;
3956 }
3957
crs() const3958 QgsCoordinateReferenceSystem QgsOgrProvider::crs() const
3959 {
3960 QgsCoordinateReferenceSystem srs;
3961 if ( !mValid || ( mOGRGeomType == wkbNone ) )
3962 return srs;
3963
3964 #if PROJ_VERSION_MAJOR<6
3965 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
3966 {
3967 int index = mFilePath.indexOf( QLatin1String( ".shp" ), Qt::CaseInsensitive );
3968 if ( index > 0 )
3969 {
3970 QString layerName = mFilePath.left( index );
3971 QFile prjFile( layerName + ".qpj" );
3972 if ( prjFile.open( QIODevice::ReadOnly ) )
3973 {
3974 QTextStream prjStream( &prjFile );
3975 QString myWktString = prjStream.readLine();
3976 prjFile.close();
3977
3978 srs = QgsCoordinateReferenceSystem::fromWkt( myWktString.toUtf8().constData() );
3979 if ( srs.isValid() )
3980 return srs;
3981 }
3982 }
3983 }
3984
3985 // add towgs84 parameter
3986 Q_NOWARN_DEPRECATED_PUSH
3987 QgsCoordinateReferenceSystem::setupESRIWktFix();
3988 Q_NOWARN_DEPRECATED_POP
3989 #endif
3990
3991 if ( OGRSpatialReferenceH spatialRefSys = mOgrLayer->GetSpatialRef() )
3992 {
3993 srs = QgsOgrUtils::OGRSpatialReferenceToCrs( spatialRefSys );
3994 }
3995 else
3996 {
3997 QgsDebugMsg( QStringLiteral( "no spatial reference found" ) );
3998 }
3999
4000 return srs;
4001 }
4002
uniqueValues(int index,int limit) const4003 QSet<QVariant> QgsOgrProvider::uniqueValues( int index, int limit ) const
4004 {
4005 QSet<QVariant> uniqueValues;
4006
4007 if ( !mValid || index < 0 || index >= mAttributeFields.count() )
4008 return uniqueValues;
4009
4010 const QgsField fld = mAttributeFields.at( index );
4011 if ( fld.name().isNull() )
4012 {
4013 return uniqueValues; //not a provider field
4014 }
4015
4016
4017 QByteArray sql = "SELECT DISTINCT " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) );
4018
4019 // GPKG/SQLite fid
4020 // For GPKG and SQLITE drivers PK fields are not exposed as real fields, (and OGR_F_GetFID only
4021 // works with GPKG), so we are adding an extra column that will become index 0
4022 // See https://github.com/qgis/QGIS/issues/29129
4023 if ( ( mGDALDriverName == QLatin1String( "GPKG" ) || mGDALDriverName == QLatin1String( "SQLite" ) )
4024 && mFirstFieldIsFid && index == 0 )
4025 {
4026 sql += ", " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) ) + " AS fid2";
4027 }
4028
4029 sql += " FROM " + quotedIdentifier( mOgrLayer->name() );
4030
4031 if ( !mSubsetString.isEmpty() )
4032 {
4033 sql += " WHERE " + textEncoding()->fromUnicode( mSubsetString );
4034 }
4035
4036 sql += " ORDER BY " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) ) + " ASC";
4037
4038 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( textEncoding()->toUnicode( sql ) ), 2 );
4039 QgsOgrLayerUniquePtr l = mOgrLayer->ExecuteSQL( sql );
4040 if ( !l )
4041 {
4042 QgsDebugMsg( QStringLiteral( "Failed to execute SQL" ) );
4043 return QgsVectorDataProvider::uniqueValues( index, limit );
4044 }
4045
4046 gdal::ogr_feature_unique_ptr f;
4047 bool ok = false;
4048 while ( f.reset( l->GetNextFeature() ), f )
4049 {
4050 const QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(), fld, 0, textEncoding(), &ok );
4051 if ( ok )
4052 uniqueValues << res;
4053
4054 if ( limit >= 0 && uniqueValues.size() >= limit )
4055 break;
4056 }
4057
4058 return uniqueValues;
4059 }
4060
uniqueStringsMatching(int index,const QString & substring,int limit,QgsFeedback * feedback) const4061 QStringList QgsOgrProvider::uniqueStringsMatching( int index, const QString &substring, int limit, QgsFeedback *feedback ) const
4062 {
4063 QStringList results;
4064
4065 if ( !mValid || index < 0 || index >= mAttributeFields.count() )
4066 return results;
4067
4068 QgsField fld = mAttributeFields.at( index );
4069 if ( fld.name().isNull() )
4070 {
4071 return results; //not a provider field
4072 }
4073
4074 // uniqueStringsMatching() is supposed to be case insensitive, so use the
4075 // ILIKE operator when it is available.
4076 // Prior to GDAL 3.1, with OGR SQL, LIKE behaved like ILIKE
4077 bool supportsILIKE = false;
4078 {
4079 QByteArray sql = "SELECT 1 FROM ";
4080 sql += quotedIdentifier( mOgrLayer->name() );
4081 sql += " WHERE 'a' ILIKE 'A' LIMIT 1";
4082 QgsOgrLayerUniquePtr l = mOgrLayer->ExecuteSQL( sql );
4083 if ( l )
4084 {
4085 gdal::ogr_feature_unique_ptr f;
4086 f.reset( l->GetNextFeature() );
4087 supportsILIKE = f != nullptr;
4088 }
4089 }
4090
4091 QByteArray sql = "SELECT DISTINCT " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) );
4092 sql += " FROM " + quotedIdentifier( mOgrLayer->name() );
4093
4094 sql += " WHERE " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) );
4095 if ( supportsILIKE )
4096 sql += " ILIKE '%";
4097 else
4098 sql += " LIKE '%";
4099 sql += textEncoding()->fromUnicode( substring ) + "%'";
4100
4101 if ( !mSubsetString.isEmpty() )
4102 {
4103 sql += " AND (" + textEncoding()->fromUnicode( mSubsetString ) + ')';
4104 }
4105
4106 sql += " ORDER BY " + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) ) + " ASC";
4107
4108 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( textEncoding()->toUnicode( sql ) ), 2 );
4109 QgsOgrLayerUniquePtr l = mOgrLayer->ExecuteSQL( sql );
4110 if ( !l )
4111 {
4112 QgsDebugMsg( QStringLiteral( "Failed to execute SQL" ) );
4113 return QgsVectorDataProvider::uniqueStringsMatching( index, substring, limit, feedback );
4114 }
4115
4116 gdal::ogr_feature_unique_ptr f;
4117 while ( f.reset( l->GetNextFeature() ), f )
4118 {
4119 if ( OGR_F_IsFieldSetAndNotNull( f.get(), 0 ) )
4120 results << textEncoding()->toUnicode( OGR_F_GetFieldAsString( f.get(), 0 ) );
4121
4122 if ( ( limit >= 0 && results.size() >= limit ) || ( feedback && feedback->isCanceled() ) )
4123 break;
4124 }
4125
4126 return results;
4127 }
4128
hasSpatialIndex() const4129 QgsFeatureSource::SpatialIndexPresence QgsOgrProvider::hasSpatialIndex() const
4130 {
4131 if ( mOgrLayer && mOgrLayer->TestCapability( OLCFastSpatialFilter ) )
4132 return QgsFeatureSource::SpatialIndexPresent;
4133 else if ( mOgrLayer )
4134 return QgsFeatureSource::SpatialIndexNotPresent;
4135 else
4136 return QgsFeatureSource::SpatialIndexUnknown;
4137 }
4138
minimumValue(int index) const4139 QVariant QgsOgrProvider::minimumValue( int index ) const
4140 {
4141 if ( !mValid || index < 0 || index >= mAttributeFields.count() )
4142 {
4143 return QVariant();
4144 }
4145 const QgsField originalField = mAttributeFields.at( index );
4146 QgsField fld = originalField;
4147
4148 // can't use native date/datetime types -- OGR converts these to string in the MAX return value
4149 if ( fld.type() == QVariant::DateTime || fld.type() == QVariant::Date )
4150 {
4151 fld.setType( QVariant::String );
4152 }
4153
4154 // Don't quote column name (see https://trac.osgeo.org/gdal/ticket/5799#comment:9)
4155 QByteArray sql = "SELECT MIN(" + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) );
4156 sql += ") FROM " + quotedIdentifier( mOgrLayer->name() );
4157
4158 if ( !mSubsetString.isEmpty() )
4159 {
4160 sql += " WHERE " + textEncoding()->fromUnicode( mSubsetString );
4161 }
4162
4163 QgsOgrLayerUniquePtr l = mOgrLayer->ExecuteSQL( sql );
4164 if ( !l )
4165 {
4166 QgsDebugMsg( QStringLiteral( "Failed to execute SQL: %1" ).arg( textEncoding()->toUnicode( sql ) ) );
4167 return QgsVectorDataProvider::minimumValue( index );
4168 }
4169
4170 gdal::ogr_feature_unique_ptr f( l->GetNextFeature() );
4171 if ( !f )
4172 {
4173 return QVariant();
4174 }
4175
4176 bool ok = false;
4177 QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(), fld, 0, textEncoding(), &ok );
4178 if ( !ok )
4179 return QVariant();
4180
4181 if ( res.type() != originalField.type() )
4182 res = convertValue( originalField.type(), res.toString() );
4183
4184 if ( originalField.type() == QVariant::DateTime )
4185 {
4186 // ensure that we treat times as local time, to match behavior when iterating features
4187 QDateTime temp = res.toDateTime();
4188 temp.setTimeSpec( Qt::LocalTime );
4189 res = temp;
4190 }
4191
4192 return res;
4193 }
4194
maximumValue(int index) const4195 QVariant QgsOgrProvider::maximumValue( int index ) const
4196 {
4197 if ( !mValid || index < 0 || index >= mAttributeFields.count() )
4198 {
4199 return QVariant();
4200 }
4201 const QgsField originalField = mAttributeFields.at( index );
4202 QgsField fld = originalField;
4203
4204 // can't use native date/datetime types -- OGR converts these to string in the MAX return value
4205 if ( fld.type() == QVariant::DateTime || fld.type() == QVariant::Date )
4206 {
4207 fld.setType( QVariant::String );
4208 }
4209
4210 // Don't quote column name (see https://trac.osgeo.org/gdal/ticket/5799#comment:9)
4211 QByteArray sql = "SELECT MAX(" + quotedIdentifier( textEncoding()->fromUnicode( fld.name() ) );
4212 sql += ") FROM " + quotedIdentifier( mOgrLayer->name() );
4213
4214 if ( !mSubsetString.isEmpty() )
4215 {
4216 sql += " WHERE " + textEncoding()->fromUnicode( mSubsetString );
4217 }
4218
4219 QgsOgrLayerUniquePtr l = mOgrLayer->ExecuteSQL( sql );
4220 if ( !l )
4221 {
4222 QgsDebugMsg( QStringLiteral( "Failed to execute SQL: %1" ).arg( textEncoding()->toUnicode( sql ) ) );
4223 return QgsVectorDataProvider::maximumValue( index );
4224 }
4225
4226 gdal::ogr_feature_unique_ptr f( l->GetNextFeature() );
4227 if ( !f )
4228 {
4229 return QVariant();
4230 }
4231
4232 bool ok = false;
4233 QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(), fld, 0, textEncoding(), &ok );
4234 if ( !ok )
4235 return QVariant();
4236
4237 if ( res.type() != originalField.type() )
4238 res = convertValue( originalField.type(), res.toString() );
4239
4240 if ( originalField.type() == QVariant::DateTime )
4241 {
4242 // ensure that we treat times as local time, to match behavior when iterating features
4243 QDateTime temp = res.toDateTime();
4244 temp.setTimeSpec( Qt::LocalTime );
4245 res = temp;
4246 }
4247
4248 return res;
4249 }
4250
quotedIdentifier(const QByteArray & field) const4251 QByteArray QgsOgrProvider::quotedIdentifier( const QByteArray &field ) const
4252 {
4253 return QgsOgrProviderUtils::quotedIdentifier( field, mGDALDriverName );
4254 }
4255
connectionPoolId(const QString & dataSourceURI,bool shareSameDatasetAmongLayers)4256 QString QgsOgrProviderUtils::connectionPoolId( const QString &dataSourceURI, bool shareSameDatasetAmongLayers )
4257 {
4258 if ( shareSameDatasetAmongLayers )
4259 {
4260 // If the file part of the URI is really a file, then use it as the
4261 // connection pool id (for example, so that all layers of a .gpkg file can
4262 // use the same GDAL dataset object)
4263 // Otherwise use the datasourceURI
4264 // Not completely sure about this logic. But at least, for GeoPackage this
4265 // works fine with multi layer datasets.
4266 QString filePath = dataSourceURI.left( dataSourceURI.indexOf( QLatin1Char( '|' ) ) );
4267 QFileInfo fi( filePath );
4268 if ( fi.isFile() )
4269 return filePath;
4270 }
4271 return dataSourceURI;
4272 }
4273
GDALOpenWrapper(const char * pszPath,bool bUpdate,char ** papszOpenOptionsIn,GDALDriverH * phDriver)4274 GDALDatasetH QgsOgrProviderUtils::GDALOpenWrapper( const char *pszPath, bool bUpdate, char **papszOpenOptionsIn, GDALDriverH *phDriver )
4275 {
4276 CPLErrorReset();
4277
4278 char **papszOpenOptions = CSLDuplicate( papszOpenOptionsIn );
4279
4280 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,3,0)
4281 {
4282 // Workaround for a bug in the GML driver that was fixed in 2.3.0 (and 2.2.X)
4283 // See https://trac.osgeo.org/gdal/ticket/7046
4284 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0)
4285 const char *apszAllowedDrivers[] = { "GML", nullptr };
4286 GDALDriverH hIdentifiedDriver =
4287 GDALIdentifyDriverEx( pszPath, GDAL_OF_VECTOR, apszAllowedDrivers, nullptr );
4288 #else
4289 GDALDriverH hIdentifiedDriver =
4290 GDALIdentifyDriver( pszPath, nullptr );
4291 #endif
4292 if ( hIdentifiedDriver &&
4293 strcmp( GDALGetDriverShortName( hIdentifiedDriver ), "GML" ) == 0 )
4294 {
4295 VSIStatBufL sStat;
4296 if ( VSIStatL( CPLResetExtension( pszPath, "gfs" ), &sStat ) != 0 )
4297 {
4298 papszOpenOptions = CSLSetNameValue( papszOpenOptions, "FORCE_SRS_DETECTION", "YES" );
4299 }
4300 }
4301 }
4302 #endif
4303
4304 QString filePath( QString::fromUtf8( pszPath ) );
4305
4306 bool bIsGpkg = QFileInfo( filePath ).suffix().compare( QLatin1String( "gpkg" ), Qt::CaseInsensitive ) == 0;
4307 bool bIsLocalGpkg = false;
4308 if ( bIsGpkg &&
4309 IsLocalFile( filePath ) &&
4310 !CPLGetConfigOption( "OGR_SQLITE_JOURNAL", nullptr ) &&
4311 QgsSettings().value( QStringLiteral( "qgis/walForSqlite3" ), true ).toBool() )
4312 {
4313 // For GeoPackage, we force opening of the file in WAL (Write Ahead Log)
4314 // mode so as to avoid readers blocking writer(s), and vice-versa.
4315 // https://www.sqlite.org/wal.html
4316 // But only do that on a local file since WAL is advertised not to work
4317 // on network shares
4318 CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", "WAL" );
4319 bIsLocalGpkg = true;
4320 }
4321 else if ( bIsGpkg )
4322 {
4323 // If WAL isn't set, we explicitly disable it, as it is persistent and it
4324 // may have been set on a previous connection.
4325 CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", "DELETE" );
4326 }
4327
4328 bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
4329 if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
4330 {
4331 CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", "NO" );
4332 }
4333
4334 const int nOpenFlags = GDAL_OF_VECTOR | ( bUpdate ? GDAL_OF_UPDATE : 0 );
4335 GDALDatasetH hDS = GDALOpenEx( pszPath, nOpenFlags, nullptr, papszOpenOptions, nullptr );
4336 CSLDestroy( papszOpenOptions );
4337
4338 CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", nullptr );
4339 if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
4340 {
4341 CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
4342 }
4343
4344 if ( !hDS )
4345 {
4346 if ( phDriver )
4347 *phDriver = nullptr;
4348 return nullptr;
4349 }
4350 GDALDriverH hDrv = GDALGetDatasetDriver( hDS );
4351 if ( bIsLocalGpkg && strcmp( GDALGetDriverShortName( hDrv ), "GPKG" ) == 0 )
4352 {
4353 QMutexLocker locker( sGlobalMutex() );
4354 ( *sMapCountOpenedDS() )[ filePath ]++;
4355 ( *sMapDSHandleToUpdateMode() )[ hDS ] = bUpdate;
4356 }
4357 if ( phDriver )
4358 *phDriver = hDrv;
4359
4360 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3, 3, 1)
4361 if ( bUpdate && bIsGpkg && strcmp( GDALGetDriverShortName( hDrv ), "GPKG" ) == 0 )
4362 {
4363 // Fix wrong gpkg_metadata_reference_column_name_update trigger that was
4364 // generated by GDAL < 2.4.0
4365 OGRLayerH hSqlLyr = GDALDatasetExecuteSQL(
4366 hDS,
4367 "SELECT sql FROM sqlite_master WHERE type = 'trigger' AND "
4368 "NAME ='gpkg_metadata_reference_column_name_update' AND "
4369 "sql LIKE '%column_nameIS%'",
4370 nullptr, nullptr );
4371 if ( hSqlLyr )
4372 {
4373 QString triggerSql;
4374 gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hSqlLyr ) );
4375 if ( hFeat )
4376 {
4377 triggerSql = OGR_F_GetFieldAsString( hFeat.get(), 0 );
4378 }
4379 GDALDatasetReleaseResultSet( hDS, hSqlLyr );
4380
4381 if ( !triggerSql.isEmpty() )
4382 {
4383 GDALDatasetExecuteSQL(
4384 hDS, "DROP TRIGGER gpkg_metadata_reference_column_name_update",
4385 nullptr, nullptr );
4386 GDALDatasetExecuteSQL(
4387 hDS,
4388 triggerSql.replace( QLatin1String( "column_nameIS" ),
4389 QLatin1String( "column_name IS" ) ).toUtf8().toStdString().c_str(),
4390 nullptr, nullptr );
4391 }
4392 }
4393 }
4394 #endif
4395
4396 return hDS;
4397 }
4398
IsLocalFile(const QString & path)4399 static bool IsLocalFile( const QString &path )
4400 {
4401 QString dirName( QFileInfo( path ).absolutePath() );
4402 // Start with the OS specific methods since the QT >= 5.4 method just
4403 // return a string and not an enumerated type.
4404 #if defined(Q_OS_WIN)
4405 if ( dirName.startsWith( "\\\\" ) || dirName.startsWith( "//" ) )
4406 return false;
4407 if ( dirName.length() >= 3 && dirName[1] == ':' &&
4408 ( dirName[2] == '\\' || dirName[2] == '/' ) )
4409 {
4410 dirName.resize( 3 );
4411 return GetDriveType( dirName.toLatin1().constData() ) != DRIVE_REMOTE;
4412 }
4413 return true;
4414 #elif defined(Q_OS_LINUX)
4415 struct statfs sStatFS;
4416 if ( statfs( dirName.toLatin1().constData(), &sStatFS ) == 0 )
4417 {
4418 // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
4419 if ( sStatFS.f_type == 0x6969 /* NFS */ ||
4420 sStatFS.f_type == 0x517b /* SMB */ ||
4421 sStatFS.f_type == 0xff534d42 /* CIFS */ ||
4422 sStatFS.f_type == 0xfe534d42 /* CIFS */ )
4423 {
4424 return false;
4425 }
4426 }
4427 return true;
4428 #else
4429 QStorageInfo info( dirName );
4430 const QString fileSystem( info.fileSystemType() );
4431 bool isLocal = path != QLatin1String( "nfs" ) && path != QLatin1String( "smbfs" );
4432 if ( !isLocal )
4433 QgsDebugMsgLevel( QStringLiteral( "Filesystem for %1 is %2" ).arg( path, fileSystem ), 2 );
4434 return isLocal;
4435 #endif
4436 }
4437
GDALCloseWrapper(GDALDatasetH hDS)4438 void QgsOgrProviderUtils::GDALCloseWrapper( GDALDatasetH hDS )
4439 {
4440 if ( !hDS )
4441 return;
4442 GDALDriverH mGDALDriver = GDALGetDatasetDriver( hDS );
4443 QString mGDALDriverName = GDALGetDriverShortName( mGDALDriver );
4444 QString datasetName( QString::fromUtf8( GDALGetDescription( hDS ) ) );
4445 if ( mGDALDriverName == QLatin1String( "GPKG" ) &&
4446 IsLocalFile( datasetName ) &&
4447 !CPLGetConfigOption( "OGR_SQLITE_JOURNAL", nullptr ) )
4448 {
4449 bool openedAsUpdate = false;
4450 bool tryReturnToWall = false;
4451 {
4452 QMutexLocker locker( sGlobalMutex() );
4453 ( *sMapCountOpenedDS() )[ datasetName ] --;
4454 if ( ( *sMapCountOpenedDS() )[ datasetName ] == 0 )
4455 {
4456 sMapCountOpenedDS()->remove( datasetName );
4457 openedAsUpdate = ( *sMapDSHandleToUpdateMode() )[hDS];
4458 tryReturnToWall = true;
4459 }
4460 sMapDSHandleToUpdateMode()->remove( hDS );
4461 }
4462 if ( tryReturnToWall )
4463 {
4464 bool bSuccess = false;
4465 if ( openedAsUpdate )
4466 {
4467 // We need to reset all iterators on layers, otherwise we will not
4468 // be able to change journal_mode.
4469 int layerCount = GDALDatasetGetLayerCount( hDS );
4470 for ( int i = 0; i < layerCount; i ++ )
4471 {
4472 OGR_L_ResetReading( GDALDatasetGetLayer( hDS, i ) );
4473 }
4474
4475 CPLPushErrorHandler( CPLQuietErrorHandler );
4476 QgsDebugMsgLevel( QStringLiteral( "GPKG: Trying to return to delete mode" ), 2 );
4477 OGRLayerH hSqlLyr = GDALDatasetExecuteSQL( hDS,
4478 "PRAGMA journal_mode = delete",
4479 nullptr, nullptr );
4480 if ( hSqlLyr )
4481 {
4482 gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hSqlLyr ) );
4483 if ( hFeat )
4484 {
4485 const char *pszRet = OGR_F_GetFieldAsString( hFeat.get(), 0 );
4486 bSuccess = EQUAL( pszRet, "delete" );
4487 QgsDebugMsgLevel( QStringLiteral( "Return: %1" ).arg( pszRet ), 2 );
4488 }
4489 }
4490 else if ( CPLGetLastErrorType() != CE_None )
4491 {
4492 QgsDebugMsg( QStringLiteral( "Return: %1" ).arg( CPLGetLastErrorMsg() ) );
4493 }
4494 GDALDatasetReleaseResultSet( hDS, hSqlLyr );
4495 CPLPopErrorHandler();
4496 }
4497 GDALClose( hDS );
4498
4499 // If the file was opened in read-only mode, or if the above failed,
4500 // we need to reopen it in update mode
4501 if ( !bSuccess )
4502 {
4503 if ( openedAsUpdate )
4504 {
4505 QgsDebugMsgLevel( QStringLiteral( "GPKG: Trying again" ), 1 );
4506 }
4507 else
4508 {
4509 QgsDebugMsgLevel( QStringLiteral( "GPKG: Trying to return to delete mode" ), 1 );
4510 }
4511 CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", "DELETE" );
4512 hDS = GDALOpenEx( datasetName.toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr );
4513 CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", nullptr );
4514 if ( hDS )
4515 {
4516 #ifdef QGISDEBUG
4517 CPLPushErrorHandler( CPLQuietErrorHandler );
4518 OGRLayerH hSqlLyr = GDALDatasetExecuteSQL( hDS,
4519 "PRAGMA journal_mode",
4520 nullptr, nullptr );
4521 CPLPopErrorHandler();
4522 if ( hSqlLyr != nullptr )
4523 {
4524 gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hSqlLyr ) );
4525 if ( hFeat != nullptr )
4526 {
4527 const char *pszRet = OGR_F_GetFieldAsString( hFeat.get(), 0 );
4528 QgsDebugMsgLevel( QStringLiteral( "Return: %1" ).arg( pszRet ), 1 );
4529 }
4530 GDALDatasetReleaseResultSet( hDS, hSqlLyr );
4531 }
4532 #endif
4533 GDALClose( hDS );
4534 }
4535 }
4536 }
4537 else
4538 {
4539 GDALClose( hDS );
4540 }
4541 }
4542
4543 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0) && GDAL_VERSION_NUM <= GDAL_COMPUTE_VERSION(3,1,3)
4544 else if ( mGDALDriverName == QLatin1String( "XLSX" ) ||
4545 mGDALDriverName == QLatin1String( "ODS" ) )
4546 {
4547 // Workaround bug in GDAL 3.1.0 to 3.1.3 that creates XLSX and ODS files incompatible with LibreOffice due to use of ZIP64
4548 CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", "NO" );
4549 GDALClose( hDS );
4550 CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", nullptr );
4551 }
4552 #endif
4553
4554 else
4555 {
4556 GDALClose( hDS );
4557 }
4558 }
4559
quotedIdentifier(QByteArray field,const QString & driverName)4560 QByteArray QgsOgrProviderUtils::quotedIdentifier( QByteArray field, const QString &driverName )
4561 {
4562 if ( driverName == QLatin1String( "MySQL" ) )
4563 {
4564 field.replace( '\\', "\\\\" );
4565 field.replace( '`', "``" );
4566 return field.prepend( '`' ).append( '`' );
4567 }
4568 else
4569 {
4570 field.replace( '\\', "\\\\" );
4571 field.replace( '"', "\\\"" );
4572 field.replace( '\'', "\\'" );
4573 return field.prepend( '\"' ).append( '\"' );
4574 }
4575 }
4576
quotedValue(const QVariant & value)4577 QString QgsOgrProviderUtils::quotedValue( const QVariant &value )
4578 {
4579 if ( value.isNull() )
4580 return QStringLiteral( "NULL" );
4581
4582 switch ( value.type() )
4583 {
4584 case QVariant::Int:
4585 case QVariant::LongLong:
4586 case QVariant::Double:
4587 return value.toString();
4588
4589 case QVariant::Bool:
4590 //OGR does not support boolean literals
4591 return value.toBool() ? "1" : "0";
4592
4593 default:
4594 case QVariant::String:
4595 QString v = value.toString();
4596 v.replace( '\'', QLatin1String( "''" ) );
4597 if ( v.contains( '\\' ) )
4598 return v.replace( '\\', QLatin1String( "\\\\" ) ).prepend( "E'" ).append( '\'' );
4599 else
4600 return v.prepend( '\'' ).append( '\'' );
4601 }
4602 }
4603
syncToDisc()4604 bool QgsOgrProvider::syncToDisc()
4605 {
4606 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
4607
4608 //for shapefiles, remove spatial index files and create a new index
4609 QgsOgrConnPool::instance()->unref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
4610 bool shapeIndex = false;
4611 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
4612 {
4613 QString sbnIndexFile;
4614 QFileInfo fi( mFilePath );
4615 int suffixLength = fi.suffix().length();
4616 sbnIndexFile = mFilePath;
4617 sbnIndexFile.chop( suffixLength );
4618 sbnIndexFile.append( "sbn" );
4619
4620 if ( QFile::exists( sbnIndexFile ) )
4621 {
4622 shapeIndex = true;
4623 close();
4624 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
4625 QFile::remove( sbnIndexFile );
4626 open( OpenModeSameAsCurrent );
4627 if ( !mValid )
4628 return false;
4629 }
4630 }
4631
4632 if ( mOgrLayer->SyncToDisk() != OGRERR_NONE )
4633 {
4634 pushError( tr( "OGR error syncing to disk: %1" ).arg( CPLGetLastErrorMsg() ) );
4635 }
4636
4637 // Repack is done automatically on OGR_L_SyncToDisk with gdal-2.2.0+
4638 #if !defined(GDAL_VERSION_NUM) || GDAL_VERSION_NUM < 2020000
4639 if ( !mDeferRepack )
4640 {
4641 if ( mShapefileMayBeCorrupted )
4642 repack();
4643
4644 mShapefileMayBeCorrupted = false;
4645 }
4646 #endif
4647
4648 QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
4649 if ( shapeIndex )
4650 {
4651 return createSpatialIndex();
4652 }
4653
4654 return true;
4655 }
4656
recalculateFeatureCount()4657 void QgsOgrProvider::recalculateFeatureCount()
4658 {
4659 if ( !mOgrLayer )
4660 {
4661 mFeaturesCounted = QgsVectorDataProvider::Uncounted;
4662 return;
4663 }
4664
4665 OGRGeometryH filter = mOgrLayer->GetSpatialFilter();
4666 if ( filter )
4667 {
4668 filter = OGR_G_Clone( filter );
4669 mOgrLayer->SetSpatialFilter( nullptr );
4670 }
4671
4672 // feature count returns number of features within current spatial filter
4673 // so we remove it if there's any and then put it back
4674 if ( mOgrGeometryTypeFilter == wkbUnknown )
4675 {
4676 mFeaturesCounted = mOgrLayer->GetApproxFeatureCount();
4677 if ( mFeaturesCounted == -1 )
4678 {
4679 mFeaturesCounted = QgsVectorDataProvider::UnknownCount;
4680 }
4681 }
4682 else
4683 {
4684 mFeaturesCounted = 0;
4685 mOgrLayer->ResetReading();
4686 setRelevantFields( true, QgsAttributeList() );
4687 mOgrLayer->ResetReading();
4688 gdal::ogr_feature_unique_ptr fet;
4689 const OGRwkbGeometryType flattenGeomTypeFilter =
4690 QgsOgrProvider::ogrWkbSingleFlatten( mOgrGeometryTypeFilter );
4691 while ( fet.reset( mOgrLayer->GetNextFeature() ), fet )
4692 {
4693 OGRGeometryH geom = OGR_F_GetGeometryRef( fet.get() );
4694 if ( geom )
4695 {
4696 OGRwkbGeometryType gType = OGR_G_GetGeometryType( geom );
4697 gType = QgsOgrProvider::ogrWkbSingleFlatten( gType );
4698 if ( gType == flattenGeomTypeFilter ) mFeaturesCounted++;
4699 }
4700 }
4701 mOgrLayer->ResetReading();
4702
4703 }
4704
4705 if ( filter )
4706 {
4707 mOgrLayer->SetSpatialFilter( filter );
4708 }
4709
4710 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
4711 }
4712
doesStrictFeatureTypeCheck() const4713 bool QgsOgrProvider::doesStrictFeatureTypeCheck() const
4714 {
4715 // FIXME probably other drivers too...
4716 return mGDALDriverName != QLatin1String( "ESRI Shapefile" ) || ( mOGRGeomType == wkbPoint || mOGRGeomType == wkbPoint25D );
4717 }
4718
ogrWkbSingleFlatten(OGRwkbGeometryType type)4719 OGRwkbGeometryType QgsOgrProvider::ogrWkbSingleFlatten( OGRwkbGeometryType type )
4720 {
4721 type = wkbFlatten( type );
4722 switch ( type )
4723 {
4724 case wkbMultiPoint:
4725 return wkbPoint;
4726 case wkbMultiLineString:
4727 return wkbLineString;
4728 case wkbMultiPolygon:
4729 return wkbPolygon;
4730 case wkbMultiCurve:
4731 return wkbCompoundCurve;
4732 case wkbMultiSurface:
4733 return wkbCurvePolygon;
4734 default:
4735 return type;
4736 }
4737 }
4738
setSubsetString(OGRLayerH layer,GDALDatasetH ds,QTextCodec * encoding,const QString & subsetString)4739 OGRLayerH QgsOgrProviderUtils::setSubsetString( OGRLayerH layer, GDALDatasetH ds, QTextCodec *encoding, const QString &subsetString )
4740 {
4741 QByteArray layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( layer ) );
4742 GDALDriverH driver = GDALGetDatasetDriver( ds );
4743 QString driverName = GDALGetDriverShortName( driver );
4744
4745 if ( driverName == QLatin1String( "ODBC" ) ) //the odbc driver does not like schema names for subset
4746 {
4747 QString layerNameString = encoding->toUnicode( layerName );
4748 int dotIndex = layerNameString.indexOf( '.' );
4749 if ( dotIndex > 1 )
4750 {
4751 QString modifiedLayerName = layerNameString.right( layerNameString.size() - dotIndex - 1 );
4752 layerName = encoding->fromUnicode( modifiedLayerName );
4753 }
4754 }
4755 OGRLayerH subsetLayer = nullptr;
4756 if ( subsetString.startsWith( QLatin1String( "SELECT " ), Qt::CaseInsensitive ) )
4757 {
4758 QByteArray sql = encoding->fromUnicode( subsetString );
4759
4760 QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( encoding->toUnicode( sql ) ), 1 );
4761 subsetLayer = GDALDatasetExecuteSQL( ds, sql.constData(), nullptr, nullptr );
4762 }
4763 else
4764 {
4765 if ( OGR_L_SetAttributeFilter( layer, encoding->fromUnicode( subsetString ).constData() ) != OGRERR_NONE )
4766 {
4767 return nullptr;
4768 }
4769 subsetLayer = layer;
4770 }
4771
4772 return subsetLayer;
4773 }
4774
open(OpenMode mode)4775 void QgsOgrProvider::open( OpenMode mode )
4776 {
4777 bool openReadOnly = false;
4778 Q_ASSERT( !mOgrSqlLayer );
4779 Q_ASSERT( !mOgrOrigLayer );
4780
4781 // Try to open using VSIFileHandler
4782 // see http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
4783 QString vsiPrefix = QgsZipItem::vsiPrefix( dataSourceUri() );
4784 if ( !vsiPrefix.isEmpty() )
4785 {
4786 // GDAL>=1.8.0 has write support for zip, but read and write operations
4787 // cannot be interleaved, so for now just use read-only.
4788 openReadOnly = true;
4789 if ( !mFilePath.startsWith( vsiPrefix ) )
4790 {
4791 mFilePath = vsiPrefix + mFilePath;
4792 setDataSourceUri( mFilePath );
4793 }
4794 QgsDebugMsgLevel( QStringLiteral( "Trying %1 syntax, mFilePath= %2" ).arg( vsiPrefix, mFilePath ), 1 );
4795 }
4796
4797 QgsDebugMsgLevel( "mFilePath: " + mFilePath, 3 );
4798 QgsDebugMsgLevel( "mLayerIndex: " + QString::number( mLayerIndex ), 3 );
4799 QgsDebugMsgLevel( "mLayerName: " + mLayerName, 3 );
4800 QgsDebugMsgLevel( "mSubsetString: " + mSubsetString, 3 );
4801 CPLSetConfigOption( "OGR_ORGANIZE_POLYGONS", "ONLY_CCW" ); // "SKIP" returns MULTIPOLYGONs for multiringed POLYGONs
4802 CPLSetConfigOption( "GPX_ELE_AS_25D", "YES" ); // use GPX elevation as z values
4803 if ( !CPLGetConfigOption( "OSM_USE_CUSTOM_INDEXING", nullptr ) )
4804 {
4805 // Disable custom/fast indexing by default, as it can prevent some .osm.pbf
4806 // files to be loaded.
4807 // See https://github.com/qgis/QGIS/issues/31062
4808 CPLSetConfigOption( "OSM_USE_CUSTOM_INDEXING", "NO" );
4809 }
4810
4811 if ( mFilePath.startsWith( QLatin1String( "MySQL:" ) ) && !mLayerName.isEmpty() && !mFilePath.endsWith( ",tables=" + mLayerName ) )
4812 {
4813 mFilePath += ",tables=" + mLayerName;
4814 }
4815
4816 if ( mode == OpenModeForceReadOnly )
4817 openReadOnly = true;
4818 else if ( mode == OpenModeSameAsCurrent && !mWriteAccess )
4819 openReadOnly = true;
4820
4821 // first try to open in update mode (unless specified otherwise)
4822 QString errCause;
4823 if ( !openReadOnly )
4824 {
4825 QStringList options( mOpenOptions );
4826 if ( mode == OpenModeForceUpdateRepackOff || ( mDeferRepack && OpenModeSameAsCurrent ) )
4827 {
4828 options << "AUTO_REPACK=OFF";
4829 }
4830 // We get the layer which was requested by the uri. The layername
4831 // has precedence over the layerid if both are given.
4832 if ( !mLayerName.isNull() )
4833 {
4834 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, true, options, mLayerName, errCause, true );
4835 }
4836 else
4837 {
4838 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, true, options, mLayerIndex, errCause, true );
4839 }
4840 }
4841
4842 mValid = false;
4843 if ( mOgrOrigLayer )
4844 {
4845 mWriteAccess = true;
4846 mWriteAccessPossible = true;
4847 }
4848 else
4849 {
4850 mWriteAccess = false;
4851 if ( !openReadOnly )
4852 {
4853 QgsDebugMsg( QStringLiteral( "OGR failed to opened in update mode, trying in read-only mode" ) );
4854 }
4855
4856 QStringList options( mOpenOptions );
4857 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
4858 // assume trusted data to get more speed
4859 if ( mGDALDriverName == QLatin1String( "FlatGeobuf" ) &&
4860 !options.contains( "VERIFY_BUFFERS=YES" ) )
4861 {
4862 options << "VERIFY_BUFFERS=NO";
4863 }
4864 #endif
4865
4866 // try to open read-only
4867 if ( !mLayerName.isNull() )
4868 {
4869 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, false, options, mLayerName, errCause, true );
4870 }
4871 else
4872 {
4873 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, false, options, mLayerIndex, errCause, true );
4874 }
4875 }
4876
4877 if ( mOgrOrigLayer )
4878 {
4879 mGDALDriverName = mOgrOrigLayer->driverName();
4880 mShareSameDatasetAmongLayers = QgsOgrProviderUtils::canDriverShareSameDatasetAmongLayers( mGDALDriverName );
4881
4882 QgsDebugMsgLevel( "OGR opened using Driver " + mGDALDriverName, 2 );
4883
4884 mOgrLayer = mOgrOrigLayer.get();
4885
4886 // check that the initial encoding setting is fit for this layer
4887
4888 if ( mode == OpenModeInitial && mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
4889 {
4890 // determine encoding from shapefile cpg or LDID information, if possible
4891 QString shpEncoding;
4892 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
4893 shpEncoding = mOgrLayer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_CPG" ), QStringLiteral( "SHAPEFILE" ) );
4894 if ( shpEncoding.isEmpty() )
4895 shpEncoding = mOgrLayer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_LDID" ), QStringLiteral( "SHAPEFILE" ) );
4896 #else
4897 shpEncoding = QgsOgrUtils::readShapefileEncoding( mFilePath );
4898 #endif
4899
4900 if ( !shpEncoding.isEmpty() )
4901 setEncoding( shpEncoding );
4902 else
4903 setEncoding( encoding() );
4904 }
4905 else
4906 {
4907 setEncoding( encoding() );
4908 }
4909
4910 // Ensure subset is set (setSubsetString does nothing if the passed sql subset string is equal to mSubsetString, which is the case when reloading the dataset)
4911 QString origSubsetString = mSubsetString;
4912 mSubsetString.clear();
4913 // Block signals to avoid endless recursion reloadData -> emit dataChanged -> reloadData
4914 blockSignals( true );
4915
4916 // Do not update capabilities: it will be done later
4917
4918 // WARNING if this is the initial open - we don't already have a connection ref, and will be creating one later. So we *mustn't* grab an extra connection ref
4919 // while setting the subset string, or we'll be left with an extra reference which is never cleared.
4920 mValid = _setSubsetString( origSubsetString, true, false, mode != OpenModeInitial );
4921
4922 blockSignals( false );
4923 if ( mValid )
4924 {
4925 if ( mode == OpenModeInitial )
4926 {
4927 computeCapabilities();
4928 }
4929 QgsDebugMsgLevel( QStringLiteral( "Data source is valid" ), 2 );
4930 }
4931 else
4932 {
4933 QgsMessageLog::logMessage( tr( "Data source is invalid (%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ), tr( "OGR" ) );
4934 }
4935 }
4936 else
4937 {
4938 QgsMessageLog::logMessage( errCause + "(" + QString::fromUtf8( CPLGetLastErrorMsg() ) + ")", tr( "OGR" ) );
4939 }
4940
4941 // For shapefiles or MapInfo .tab, so as to allow concurrent opening between
4942 // QGIS and MapInfo, we go back to read-only mode for now.
4943 // We limit to those drivers as re-opening is relatively cheap (other drivers
4944 // like GeoJSON might do full content ingestion for example)
4945 if ( mValid && mode == OpenModeInitial && mWriteAccess &&
4946 ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) || mGDALDriverName == QLatin1String( "MapInfo File" ) ) )
4947 {
4948 mOgrSqlLayer.reset();
4949 mOgrOrigLayer.reset();
4950 mOgrLayer = nullptr;
4951 mValid = false;
4952
4953 // In the case where we deal with a shapefile, it is possible that it has
4954 // pre-existing holes in the DBF (see #15407), so if using a GDAL version
4955 // recent enough to have reliable packing, do a packing at the first edit
4956 // action.
4957 if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) &&
4958 atoi( GDALVersionInfo( "VERSION_NUM" ) ) >= GDAL_COMPUTE_VERSION( 2, 1, 2 ) )
4959 {
4960 mShapefileMayBeCorrupted = true;
4961 }
4962
4963 // try to open read-only
4964 if ( !mLayerName.isNull() )
4965 {
4966 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, false, mOpenOptions, mLayerName, errCause, true );
4967 }
4968 else
4969 {
4970 mOgrOrigLayer = QgsOgrProviderUtils::getLayer( mFilePath, false, mOpenOptions, mLayerIndex, errCause, true );
4971 }
4972
4973 mWriteAccess = false;
4974 mOgrLayer = mOgrOrigLayer.get();
4975 if ( mOgrLayer )
4976 {
4977 mValid = true;
4978 mDynamicWriteAccess = true;
4979
4980 if ( !mSubsetString.isEmpty() )
4981 {
4982 int featuresCountedBackup = mFeaturesCounted;
4983 mFeaturesCounted = -1;
4984 // Do not update capabilities here
4985 // but ensure subset is set (setSubsetString does nothing if the passed sql subset string is equal to
4986 // mSubsetString, which is the case when reloading the dataset)
4987 QString origSubsetString = mSubsetString;
4988 mSubsetString.clear();
4989 mValid = _setSubsetString( origSubsetString, false, false );
4990 mFeaturesCounted = featuresCountedBackup;
4991 }
4992 }
4993 }
4994
4995 // For debug/testing purposes
4996 if ( !mValid )
4997 setProperty( "_debug_open_mode", "invalid" );
4998 else if ( mWriteAccess )
4999 setProperty( "_debug_open_mode", "read-write" );
5000 else
5001 setProperty( "_debug_open_mode", "read-only" );
5002 }
5003
close()5004 void QgsOgrProvider::close()
5005 {
5006 mOgrSqlLayer.reset();
5007 mOgrOrigLayer.reset();
5008 mOgrLayer = nullptr;
5009 mValid = false;
5010 setProperty( "_debug_open_mode", "invalid" );
5011
5012 invalidateCachedExtent( false );
5013 }
5014
reloadProviderData()5015 void QgsOgrProvider::reloadProviderData()
5016 {
5017 mFeaturesCounted = QgsVectorDataProvider::Uncounted;
5018 bool wasValid = mValid;
5019 QgsOgrConnPool::instance()->invalidateConnections( QgsOgrProviderUtils::connectionPoolId( dataSourceUri( true ), mShareSameDatasetAmongLayers ) );
5020 close();
5021 open( OpenModeSameAsCurrent );
5022 if ( !mValid && wasValid )
5023 pushError( tr( "Cannot reopen datasource %1" ).arg( dataSourceUri() ) );
5024 }
5025
_enterUpdateMode(bool implicit)5026 bool QgsOgrProvider::_enterUpdateMode( bool implicit )
5027 {
5028 if ( !mWriteAccessPossible )
5029 {
5030 return false;
5031 }
5032 if ( mWriteAccess )
5033 {
5034 ++mUpdateModeStackDepth;
5035 return true;
5036 }
5037 if ( mUpdateModeStackDepth == 0 )
5038 {
5039 Q_ASSERT( mDynamicWriteAccess );
5040 QgsDebugMsgLevel( QStringLiteral( "Reopening %1 in update mode" ).arg( dataSourceUri() ), 1 );
5041 close();
5042 open( implicit ? OpenModeForceUpdate : OpenModeForceUpdateRepackOff );
5043 if ( !mOgrLayer || !mWriteAccess )
5044 {
5045 QgsMessageLog::logMessage( tr( "Cannot reopen datasource %1 in update mode" ).arg( dataSourceUri() ), tr( "OGR" ) );
5046 pushError( tr( "Cannot reopen datasource %1 in update mode" ).arg( dataSourceUri() ) );
5047 return false;
5048 }
5049 }
5050 ++mUpdateModeStackDepth;
5051 // For implicitly entered updateMode, don't defer repacking
5052 mDeferRepack = !implicit;
5053 return true;
5054 }
5055
leaveUpdateMode()5056 bool QgsOgrProvider::leaveUpdateMode()
5057 {
5058 if ( !mWriteAccessPossible )
5059 {
5060 return false;
5061 }
5062 --mUpdateModeStackDepth;
5063 if ( mUpdateModeStackDepth < 0 )
5064 {
5065 QgsMessageLog::logMessage( tr( "Unbalanced call to leaveUpdateMode() w.r.t. enterUpdateMode()" ), tr( "OGR" ) );
5066 mUpdateModeStackDepth = 0;
5067 return false;
5068 }
5069 if ( mDeferRepack && mUpdateModeStackDepth == 0 )
5070 {
5071 // Only repack once update mode is inactive
5072 if ( mShapefileMayBeCorrupted )
5073 repack();
5074
5075 mShapefileMayBeCorrupted = false;
5076 mDeferRepack = false;
5077 }
5078 if ( !mDynamicWriteAccess )
5079 {
5080 // The GeoJSON driver only properly flushes stuff in all situations by
5081 // closing and re-opening. Starting with GDAL 2.3.1, it should be safe to
5082 // use GDALDatasetFlush().
5083 if ( mGDALDriverName == QLatin1String( "GeoJSON" ) )
5084 {
5085 // Backup fields since if we created new fields, but didn't populate it
5086 // with any feature yet, it will disappear.
5087 QgsFields oldFields = mAttributeFields;
5088 reloadData();
5089 if ( mValid )
5090 {
5091 // Make sure that new fields we added, but didn't populate yet, are
5092 // recreated at the OGR level, otherwise we won't be able to populate
5093 // them.
5094 for ( const auto &field : oldFields )
5095 {
5096 int idx = mAttributeFields.lookupField( field.name() );
5097 if ( idx < 0 )
5098 {
5099 bool ignoreErrorOut = false;
5100 addAttributeOGRLevel( field, ignoreErrorOut );
5101 }
5102 }
5103 mAttributeFields = oldFields;
5104 }
5105 }
5106 return true;
5107 }
5108 if ( mUpdateModeStackDepth == 0 )
5109 {
5110 QgsDebugMsgLevel( QStringLiteral( "Reopening %1 in read-only mode" ).arg( dataSourceUri() ), 1 );
5111 close();
5112 open( OpenModeForceReadOnly );
5113 if ( !mOgrLayer )
5114 {
5115 QgsMessageLog::logMessage( tr( "Cannot reopen datasource %1 in read-only mode" ).arg( dataSourceUri() ), tr( "OGR" ) );
5116 pushError( tr( "Cannot reopen datasource %1 in read-only mode" ).arg( dataSourceUri() ) );
5117 return false;
5118 }
5119 }
5120 return true;
5121 }
5122
isSaveAndLoadStyleToDatabaseSupported() const5123 bool QgsOgrProvider::isSaveAndLoadStyleToDatabaseSupported() const
5124 {
5125 // We could potentially extend support for styling to other drivers
5126 // with multiple layer support.
5127 return mGDALDriverName == QLatin1String( "GPKG" ) ||
5128 mGDALDriverName == QLatin1String( "SQLite" );
5129 }
5130
isDeleteStyleFromDatabaseSupported() const5131 bool QgsOgrProvider::isDeleteStyleFromDatabaseSupported() const
5132 {
5133 return isSaveAndLoadStyleToDatabaseSupported();
5134 }
5135
fileVectorFilters() const5136 QString QgsOgrProvider::fileVectorFilters() const
5137 {
5138 return QgsOgrProviderUtils::fileVectorFilters();
5139 }
5140
toString() const5141 QString QgsOgrProviderUtils::DatasetIdentification::toString() const
5142 {
5143 return dsName +
5144 ( updateMode ?
5145 QStringLiteral( "update" ) : QStringLiteral( "read-only" ) ) +
5146 options.join( ',' );
5147 }
5148
operator <(const QgsOgrProviderUtils::DatasetIdentification & other) const5149 bool QgsOgrProviderUtils::DatasetIdentification::operator<
5150 ( const QgsOgrProviderUtils::DatasetIdentification &other ) const
5151 {
5152 return toString() < other.toString();
5153 }
5154
OpenHelper(const QString & dsName,bool updateMode,const QStringList & options)5155 static GDALDatasetH OpenHelper( const QString &dsName,
5156 bool updateMode,
5157 const QStringList &options )
5158 {
5159 char **papszOpenOptions = nullptr;
5160 const auto constOptions = options;
5161 for ( QString option : constOptions )
5162 {
5163 papszOpenOptions = CSLAddString( papszOpenOptions,
5164 option.toUtf8().constData() );
5165 }
5166 GDALDatasetH hDS = QgsOgrProviderUtils::GDALOpenWrapper(
5167 QgsOgrProviderUtils::expandAuthConfig( dsName ).toUtf8().constData(), updateMode, papszOpenOptions, nullptr );
5168 CSLDestroy( papszOpenOptions );
5169 return hDS;
5170 }
5171
invalidateCachedDatasets(const QString & dsName)5172 void QgsOgrProviderUtils::invalidateCachedDatasets( const QString &dsName )
5173 {
5174 QMutexLocker locker( sGlobalMutex() );
5175 while ( true )
5176 {
5177 bool erased = false;
5178 for ( auto iter = sMapSharedDS.begin(); iter != sMapSharedDS.end(); ++iter )
5179 {
5180 if ( iter.key().dsName == dsName )
5181 {
5182 sMapSharedDS.erase( iter );
5183 erased = true;
5184 break;
5185 }
5186 }
5187 if ( !erased )
5188 break;
5189 }
5190 }
5191
5192
getAlreadyOpenedDataset(const QString & dsName)5193 QgsOgrDatasetSharedPtr QgsOgrProviderUtils::getAlreadyOpenedDataset( const QString &dsName )
5194 {
5195 QMutexLocker locker( sGlobalMutex() );
5196 for ( auto iter = sMapSharedDS.begin(); iter != sMapSharedDS.end(); ++iter )
5197 {
5198 auto ident = iter.key();
5199 if ( ident.dsName == dsName && ident.updateMode )
5200 {
5201 // Browse through this list, to look for the first available DatasetWithLayers*
5202 // instance that is in update mode (hoping there's only one...)
5203 auto &datasetList = iter.value();
5204 for ( const auto &ds : datasetList )
5205 {
5206 Q_ASSERT( ds->refCount > 0 );
5207 return QgsOgrDataset::create( ident, ds );
5208 }
5209 }
5210 }
5211 return nullptr;
5212 }
5213
5214
getLayer(const QString & dsName,int layerIndex,QString & errCause)5215 QgsOgrLayerUniquePtr QgsOgrProviderUtils::getLayer( const QString &dsName,
5216 int layerIndex,
5217 QString &errCause )
5218 {
5219 QMutexLocker locker( sGlobalMutex() );
5220 for ( auto iter = sMapSharedDS.begin(); iter != sMapSharedDS.end(); ++iter )
5221 {
5222 if ( iter.key().dsName == dsName )
5223 {
5224 // Browse through this list, to look for a DatasetWithLayers*
5225 // instance that don't use yet our layer of interest
5226 auto &datasetList = iter.value();
5227 const auto constDatasetList = datasetList;
5228 for ( QgsOgrProviderUtils::DatasetWithLayers *ds : constDatasetList )
5229 {
5230 if ( !ds->canBeShared )
5231 continue;
5232 Q_ASSERT( ds->refCount > 0 );
5233
5234 QString layerName;
5235 OGRLayerH hLayer;
5236 {
5237 QMutexLocker lockerDS( &ds->mutex );
5238 hLayer = GDALDatasetGetLayer(
5239 ds->hDS, layerIndex );
5240 if ( hLayer )
5241 {
5242 layerName = QString::fromUtf8( OGR_L_GetName( hLayer ) );
5243 }
5244 }
5245 if ( !hLayer )
5246 {
5247 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerIndex );
5248 return nullptr;
5249 }
5250 return getLayer( dsName, iter.key().updateMode, iter.key().options, layerName, errCause, true );
5251 }
5252 }
5253 }
5254 return getLayer( dsName, false, QStringList(), layerIndex, errCause, true );
5255 }
5256
getLayer(const QString & dsName,bool updateMode,const QStringList & options,int layerIndex,QString & errCause,bool checkModificationDateAgainstCache)5257 QgsOgrLayerUniquePtr QgsOgrProviderUtils::getLayer( const QString &dsName,
5258 bool updateMode,
5259 const QStringList &options,
5260 int layerIndex,
5261 QString &errCause,
5262 bool checkModificationDateAgainstCache )
5263 {
5264 QMutexLocker locker( sGlobalMutex() );
5265
5266 // The idea is that we want to minimize the number of GDALDatasetH
5267 // handles openeded. But we have constraints. We do not want that 2
5268 // callers of getLayer() with the same input parameters get the same
5269 // GDALDatasetH since iterators over features of that layer would conflict
5270
5271 QgsOgrProviderUtils::DatasetIdentification ident;
5272 ident.dsName = dsName;
5273 ident.updateMode = updateMode;
5274 ident.options = options;
5275 // Find if there's a list of DatasetWithLayers* that match our
5276 // (dsName, updateMode, options) criteria
5277 auto iter = sMapSharedDS.find( ident );
5278 if ( iter != sMapSharedDS.end() )
5279 {
5280 // Browse through this list, to look for a DatasetWithLayers*
5281 // instance that don't use yet our layer of interest
5282 auto datasetList = iter.value();
5283 const auto constDatasetList = datasetList;
5284 for ( QgsOgrProviderUtils::DatasetWithLayers *ds : constDatasetList )
5285 {
5286 if ( !ds->canBeShared )
5287 continue;
5288 Q_ASSERT( ds->refCount > 0 );
5289
5290 QString layerName;
5291 OGRLayerH hLayer;
5292 {
5293 QMutexLocker lockerDS( &ds->mutex );
5294 hLayer = GDALDatasetGetLayer(
5295 ds->hDS, layerIndex );
5296 if ( hLayer )
5297 {
5298 layerName = QString::fromUtf8( OGR_L_GetName( hLayer ) );
5299 }
5300 }
5301 if ( !hLayer )
5302 {
5303 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerIndex );
5304 return nullptr;
5305 }
5306 return getLayer( dsName, updateMode, options, layerName, errCause,
5307 checkModificationDateAgainstCache );
5308 }
5309 }
5310
5311 GDALDatasetH hDS = OpenHelper( dsName, updateMode, options );
5312 if ( !hDS )
5313 {
5314 errCause = QObject::tr( "Cannot open %1." ).arg( dsName );
5315 return nullptr;
5316 }
5317
5318 OGRLayerH hLayer = GDALDatasetGetLayer(
5319 hDS, layerIndex );
5320 if ( !hLayer )
5321 {
5322 QgsOgrProviderUtils::GDALCloseWrapper( hDS );
5323 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerIndex );
5324 return nullptr;
5325 }
5326 QString layerName = QString::fromUtf8( OGR_L_GetName( hLayer ) );
5327
5328 QgsOgrProviderUtils::DatasetWithLayers *ds =
5329 new QgsOgrProviderUtils::DatasetWithLayers;
5330 ds->hDS = hDS;
5331
5332 GDALDriverH driver = GDALGetDatasetDriver( hDS );
5333 QString driverName = GDALGetDriverShortName( driver );
5334 ds->canBeShared = canDriverShareSameDatasetAmongLayers( driverName, updateMode, dsName );
5335
5336 QgsOgrLayerUniquePtr layer = QgsOgrLayer::CreateForLayer(
5337 ident, layerName, ds, hLayer );
5338 ds->setLayers[layerName] = layer.get();
5339
5340 QList<DatasetWithLayers *> datasetList;
5341 datasetList.push_back( ds );
5342 sMapSharedDS[ident] = datasetList;
5343
5344 return layer;
5345 }
5346
getLayer(const QString & dsName,const QString & layerName,QString & errCause)5347 QgsOgrLayerUniquePtr QgsOgrProviderUtils::getLayer( const QString &dsName,
5348 const QString &layerName,
5349 QString &errCause )
5350 {
5351 QMutexLocker locker( sGlobalMutex() );
5352
5353 for ( auto iter = sMapSharedDS.begin(); iter != sMapSharedDS.end(); ++iter )
5354 {
5355 if ( iter.key().dsName == dsName )
5356 {
5357 // Browse through this list, to look for a DatasetWithLayers*
5358 // instance that don't use yet our layer of interest
5359 auto &datasetList = iter.value();
5360 const auto constDatasetList = datasetList;
5361 for ( QgsOgrProviderUtils::DatasetWithLayers *ds : constDatasetList )
5362 {
5363 if ( !ds->canBeShared )
5364 continue;
5365 Q_ASSERT( ds->refCount > 0 );
5366
5367 auto iter2 = ds->setLayers.find( layerName );
5368 if ( iter2 == ds->setLayers.end() )
5369 {
5370 OGRLayerH hLayer;
5371 {
5372 QMutexLocker lockerDS( &ds->mutex );
5373 hLayer = GDALDatasetGetLayerByName(
5374 ds->hDS, layerName.toUtf8().constData() );
5375 }
5376 if ( !hLayer )
5377 {
5378 // Shouldn't happen really !
5379 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerName );
5380 return nullptr;
5381 }
5382 OGR_L_SetAttributeFilter( hLayer, nullptr );
5383
5384 QgsOgrLayerUniquePtr layer = QgsOgrLayer::CreateForLayer(
5385 iter.key(), layerName, ds, hLayer );
5386 ds->setLayers[layerName] = layer.get();
5387 return layer;
5388 }
5389 }
5390 }
5391 }
5392 return getLayer( dsName, false, QStringList(), layerName, errCause, true );
5393 }
5394
getLastModified(const QString & dsName)5395 static QDateTime getLastModified( const QString &dsName )
5396 {
5397 if ( dsName.endsWith( ".gpkg", Qt::CaseInsensitive ) )
5398 {
5399 QFileInfo info( dsName + "-wal" );
5400 if ( info.exists() )
5401 return info.lastModified();
5402 }
5403 return QFileInfo( dsName ).lastModified();
5404 }
5405
5406 // In case we do very fast structural changes within the same second,
5407 // the last modified date might not change enough, so artificially
5408 // decrement the cache modified date, so that the file appears newer to it
invalidateCachedLastModifiedDate(const QString & dsName)5409 void QgsOgrProviderUtils::invalidateCachedLastModifiedDate( const QString &dsName )
5410 {
5411 QMutexLocker locker( sGlobalMutex() );
5412
5413 auto iter = sMapDSNameToLastModifiedDate()->find( dsName );
5414 if ( iter != sMapDSNameToLastModifiedDate()->end() )
5415 {
5416 QgsDebugMsgLevel( QStringLiteral( "invalidating last modified date for %1" ).arg( dsName ), 1 );
5417 iter.value() = iter.value().addSecs( -10 );
5418 }
5419 }
5420
ogrTypeFromQgisType(QgsWkbTypes::Type type)5421 OGRwkbGeometryType QgsOgrProviderUtils::ogrTypeFromQgisType( QgsWkbTypes::Type type )
5422 {
5423 switch ( type )
5424 {
5425 case QgsWkbTypes::Point:
5426 return wkbPoint;
5427 case QgsWkbTypes::Point25D:
5428 case QgsWkbTypes::PointZ:
5429 return wkbPoint25D;
5430 case QgsWkbTypes::PointM:
5431 return wkbPointM;
5432 case QgsWkbTypes::PointZM:
5433 return wkbPointZM;
5434
5435 case QgsWkbTypes::LineString:
5436 return wkbLineString;
5437 case QgsWkbTypes::LineString25D:
5438 case QgsWkbTypes::LineStringZ:
5439 return wkbLineString25D;
5440 case QgsWkbTypes::LineStringM:
5441 return wkbLineStringM;
5442 case QgsWkbTypes::LineStringZM:
5443 return wkbLineStringZM;
5444
5445 case QgsWkbTypes::Polygon:
5446 return wkbPolygon;
5447 case QgsWkbTypes::Polygon25D:
5448 case QgsWkbTypes::PolygonZ:
5449 return wkbPolygon25D;
5450 case QgsWkbTypes::PolygonM:
5451 return wkbPolygonM;
5452 case QgsWkbTypes::PolygonZM:
5453 return wkbPolygonZM;
5454
5455 case QgsWkbTypes::MultiPoint:
5456 return wkbMultiPoint;
5457 case QgsWkbTypes::MultiPoint25D:
5458 case QgsWkbTypes::MultiPointZ:
5459 return wkbMultiPoint25D;
5460 case QgsWkbTypes::MultiPointM:
5461 return wkbMultiPointM;
5462 case QgsWkbTypes::MultiPointZM:
5463 return wkbMultiPointZM;
5464
5465 case QgsWkbTypes::MultiLineString:
5466 return wkbMultiLineString;
5467 case QgsWkbTypes::MultiLineString25D:
5468 case QgsWkbTypes::MultiLineStringZ:
5469 return wkbMultiLineString25D;
5470 case QgsWkbTypes::MultiLineStringM:
5471 return wkbMultiLineStringM;
5472 case QgsWkbTypes::MultiLineStringZM:
5473 return wkbMultiLineStringZM;
5474
5475 case QgsWkbTypes::MultiPolygon:
5476 return wkbMultiPolygon;
5477 case QgsWkbTypes::MultiPolygon25D:
5478 case QgsWkbTypes::MultiPolygonZ:
5479 return wkbMultiPolygon25D;
5480 case QgsWkbTypes::MultiPolygonM:
5481 return wkbMultiPolygonM;
5482 case QgsWkbTypes::MultiPolygonZM:
5483 return wkbMultiPolygonZM;
5484
5485 case QgsWkbTypes::CircularString:
5486 return wkbCircularString;
5487 case QgsWkbTypes::CircularStringZ:
5488 return wkbCircularStringZ;
5489 case QgsWkbTypes::CircularStringM:
5490 return wkbCircularStringM;
5491 case QgsWkbTypes::CircularStringZM:
5492 return wkbCircularStringZM;
5493
5494 case QgsWkbTypes::CompoundCurve:
5495 return wkbCompoundCurve;
5496 case QgsWkbTypes::CompoundCurveZ:
5497 return wkbCompoundCurveZ;
5498 case QgsWkbTypes::CompoundCurveM:
5499 return wkbCompoundCurveM;
5500 case QgsWkbTypes::CompoundCurveZM:
5501 return wkbCompoundCurveZM;
5502
5503 case QgsWkbTypes::CurvePolygon:
5504 return wkbCurvePolygon;
5505 case QgsWkbTypes::CurvePolygonZ:
5506 return wkbCurvePolygonZ;
5507 case QgsWkbTypes::CurvePolygonM:
5508 return wkbCurvePolygonM;
5509 case QgsWkbTypes::CurvePolygonZM:
5510 return wkbCurvePolygonZM;
5511
5512 case QgsWkbTypes::MultiCurve:
5513 return wkbMultiCurve;
5514 case QgsWkbTypes::MultiCurveZ:
5515 return wkbMultiCurveZ;
5516 case QgsWkbTypes::MultiCurveM:
5517 return wkbMultiCurveM;
5518 case QgsWkbTypes::MultiCurveZM:
5519 return wkbMultiCurveZM;
5520
5521 case QgsWkbTypes::MultiSurface:
5522 return wkbMultiSurface;
5523 case QgsWkbTypes::MultiSurfaceZ:
5524 return wkbMultiSurfaceZ;
5525 case QgsWkbTypes::MultiSurfaceM:
5526 return wkbMultiSurfaceM;
5527 case QgsWkbTypes::MultiSurfaceZM:
5528 return wkbMultiSurfaceZM;
5529
5530 case QgsWkbTypes::Triangle:
5531 return wkbTriangle;
5532 case QgsWkbTypes::TriangleZ:
5533 return wkbTriangleZ;
5534 case QgsWkbTypes::TriangleM:
5535 return wkbTriangleM;
5536 case QgsWkbTypes::TriangleZM:
5537 return wkbTriangleZM;
5538
5539 case QgsWkbTypes::NoGeometry:
5540 return wkbNone;
5541
5542 case QgsWkbTypes::GeometryCollection:
5543 return wkbGeometryCollection;
5544 case QgsWkbTypes::GeometryCollectionZ:
5545 return wkbGeometryCollection25D;
5546 case QgsWkbTypes::GeometryCollectionM:
5547 return wkbGeometryCollectionM;
5548 case QgsWkbTypes::GeometryCollectionZM:
5549 return wkbGeometryCollectionZM;
5550
5551 case QgsWkbTypes::Unknown:
5552 return wkbUnknown;
5553 }
5554 // no warnings!
5555 return wkbUnknown;
5556 }
5557
qgisTypeFromOgrType(OGRwkbGeometryType type)5558 QgsWkbTypes::Type QgsOgrProviderUtils::qgisTypeFromOgrType( OGRwkbGeometryType type )
5559 {
5560 switch ( type )
5561 {
5562 case wkbUnknown:
5563 return QgsWkbTypes::Unknown;
5564 case wkbPoint:
5565 return QgsWkbTypes::Point;
5566 case wkbLineString:
5567 return QgsWkbTypes::LineString;
5568 case wkbPolygon:
5569 return QgsWkbTypes::Polygon;
5570 case wkbMultiPoint:
5571 return QgsWkbTypes::MultiPoint;
5572 case wkbMultiLineString:
5573 return QgsWkbTypes::MultiLineString;
5574 case wkbMultiPolygon:
5575 return QgsWkbTypes::MultiPolygon;
5576 case wkbGeometryCollection:
5577 return QgsWkbTypes::GeometryCollection;
5578 case wkbCircularString:
5579 return QgsWkbTypes::CircularString;
5580 case wkbCompoundCurve:
5581 return QgsWkbTypes::CompoundCurve;
5582 case wkbCurvePolygon:
5583 return QgsWkbTypes::CurvePolygon;
5584 case wkbMultiCurve:
5585 return QgsWkbTypes::MultiCurve;
5586 case wkbMultiSurface:
5587 return QgsWkbTypes::MultiSurface;
5588 case wkbTriangle:
5589 return QgsWkbTypes::Triangle;
5590 case wkbNone:
5591 return QgsWkbTypes::NoGeometry;
5592
5593 case wkbCircularStringZ:
5594 return QgsWkbTypes::CircularStringZ;
5595 case wkbCompoundCurveZ:
5596 return QgsWkbTypes::CompoundCurveZ;
5597 case wkbCurvePolygonZ:
5598 return QgsWkbTypes::PolygonZ;
5599 case wkbMultiCurveZ:
5600 return QgsWkbTypes::MultiCurveZ;
5601 case wkbMultiSurfaceZ:
5602 return QgsWkbTypes::MultiSurfaceZ;
5603 case wkbTriangleZ:
5604 return QgsWkbTypes::TriangleZ;
5605
5606 case wkbPointM:
5607 return QgsWkbTypes::PointM;
5608 case wkbLineStringM:
5609 return QgsWkbTypes::LineStringM;
5610 case wkbPolygonM:
5611 return QgsWkbTypes::PolygonM;
5612 case wkbMultiPointM:
5613 return QgsWkbTypes::PointM;
5614 case wkbMultiLineStringM:
5615 return QgsWkbTypes::LineStringM;
5616 case wkbMultiPolygonM:
5617 return QgsWkbTypes::PolygonM;
5618 case wkbGeometryCollectionM:
5619 return QgsWkbTypes::GeometryCollectionM;
5620 case wkbCircularStringM:
5621 return QgsWkbTypes::CircularStringM;
5622 case wkbCompoundCurveM:
5623 return QgsWkbTypes::CompoundCurveM;
5624 case wkbCurvePolygonM:
5625 return QgsWkbTypes::PolygonM;
5626 case wkbMultiCurveM:
5627 return QgsWkbTypes::MultiCurveM;
5628 case wkbMultiSurfaceM:
5629 return QgsWkbTypes::MultiSurfaceM;
5630 case wkbTriangleM:
5631 return QgsWkbTypes::TriangleM;
5632
5633 case wkbPointZM:
5634 return QgsWkbTypes::PointZM;
5635 case wkbLineStringZM:
5636 return QgsWkbTypes::LineStringZM;
5637 case wkbPolygonZM:
5638 return QgsWkbTypes::PolygonZM;
5639 case wkbMultiPointZM:
5640 return QgsWkbTypes::MultiPointZM;
5641 case wkbMultiLineStringZM:
5642 return QgsWkbTypes::MultiLineStringZM;
5643 case wkbMultiPolygonZM:
5644 return QgsWkbTypes::MultiPolygonZM;
5645 case wkbGeometryCollectionZM:
5646 return QgsWkbTypes::GeometryCollectionZM;
5647 case wkbCircularStringZM:
5648 return QgsWkbTypes::CircularStringZM;
5649 case wkbCompoundCurveZM:
5650 return QgsWkbTypes::CompoundCurveZM;
5651 case wkbCurvePolygonZM:
5652 return QgsWkbTypes::CurvePolygonZM;
5653 case wkbMultiCurveZM:
5654 return QgsWkbTypes::MultiCurveZM;
5655 case wkbMultiSurfaceZM:
5656 return QgsWkbTypes::MultiSurfaceZM;
5657 case wkbTriangleZM:
5658 return QgsWkbTypes::TriangleZM;
5659
5660 case wkbPoint25D:
5661 return QgsWkbTypes::Point25D;
5662 case wkbLineString25D:
5663 return QgsWkbTypes::LineString25D;
5664 case wkbPolygon25D:
5665 return QgsWkbTypes::Polygon25D;
5666 case wkbMultiPoint25D:
5667 return QgsWkbTypes::MultiPoint25D;
5668 case wkbMultiLineString25D:
5669 return QgsWkbTypes::MultiLineString25D;
5670 case wkbMultiPolygon25D:
5671 return QgsWkbTypes::MultiPolygon25D;
5672 case wkbGeometryCollection25D:
5673 return QgsWkbTypes::GeometryCollectionZ;
5674
5675 case wkbCurve:
5676 case wkbSurface:
5677 case wkbCurveZ:
5678 case wkbSurfaceZ:
5679 case wkbCurveM:
5680 case wkbSurfaceM:
5681 case wkbCurveZM:
5682 case wkbSurfaceZM:
5683 return QgsWkbTypes::Unknown; // abstract types - no direct mapping to QGIS types
5684
5685 case wkbLinearRing:
5686 case wkbTIN:
5687 case wkbTINZ:
5688 case wkbTINM:
5689 case wkbTINZM:
5690 case wkbPolyhedralSurface:
5691 case wkbPolyhedralSurfaceZ:
5692 case wkbPolyhedralSurfaceM:
5693 case wkbPolyhedralSurfaceZM:
5694 return QgsWkbTypes::Unknown; // unsupported types
5695 }
5696 return QgsWkbTypes::Unknown;
5697 }
5698
5699
expandAuthConfig(const QString & dsName)5700 QString QgsOgrProviderUtils::expandAuthConfig( const QString &dsName )
5701 {
5702 QString uri( dsName );
5703 // Check for authcfg
5704 QRegularExpression authcfgRe( " authcfg='([^']+)'" );
5705 QRegularExpressionMatch match;
5706 if ( uri.contains( authcfgRe, &match ) )
5707 {
5708 uri = uri.replace( match.captured( 0 ), QString() );
5709 QString configId( match.captured( 1 ) );
5710 QStringList connectionItems;
5711 connectionItems << uri;
5712 if ( QgsApplication::authManager()->updateDataSourceUriItems( connectionItems, configId, QStringLiteral( "ogr" ) ) )
5713 {
5714 uri = connectionItems.first( );
5715 }
5716 }
5717 return uri;
5718 }
5719
5720 // Must be called under the globalMutex
canUseOpenedDatasets(const QString & dsName)5721 bool QgsOgrProviderUtils::canUseOpenedDatasets( const QString &dsName )
5722 {
5723 auto iter = sMapDSNameToLastModifiedDate()->find( dsName );
5724 if ( iter == sMapDSNameToLastModifiedDate()->end() )
5725 return true;
5726 return getLastModified( dsName ) <= iter.value();
5727 }
5728
createDatasetWithLayers(const QString & dsName,bool updateMode,const QStringList & options,const QString & layerName,const DatasetIdentification & ident,QgsOgrLayerUniquePtr & layer,QString & errCause)5729 QgsOgrProviderUtils::DatasetWithLayers *QgsOgrProviderUtils::createDatasetWithLayers(
5730 const QString &dsName,
5731 bool updateMode,
5732 const QStringList &options,
5733 const QString &layerName,
5734 const DatasetIdentification &ident,
5735 QgsOgrLayerUniquePtr &layer,
5736 QString &errCause )
5737 {
5738 GDALDatasetH hDS = OpenHelper( dsName, updateMode, options );
5739 if ( !hDS )
5740 {
5741 errCause = QObject::tr( "Cannot open %1." ).arg( dsName );
5742 return nullptr;
5743 }
5744 ( *sMapDSNameToLastModifiedDate() )[dsName] = getLastModified( dsName );
5745
5746 OGRLayerH hLayer = GDALDatasetGetLayerByName(
5747 hDS, layerName.toUtf8().constData() );
5748 if ( !hLayer )
5749 {
5750 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerName );
5751 QgsOgrProviderUtils::GDALCloseWrapper( hDS );
5752 return nullptr;
5753 }
5754
5755 QgsOgrProviderUtils::DatasetWithLayers *ds =
5756 new QgsOgrProviderUtils::DatasetWithLayers;
5757 ds->hDS = hDS;
5758
5759 GDALDriverH driver = GDALGetDatasetDriver( hDS );
5760 QString driverName = GDALGetDriverShortName( driver );
5761 ds->canBeShared = canDriverShareSameDatasetAmongLayers( driverName, updateMode, dsName );
5762
5763 layer = QgsOgrLayer::CreateForLayer(
5764 ident, layerName, ds, hLayer );
5765 ds->setLayers[layerName] = layer.get();
5766 return ds;
5767 }
5768
getLayer(const QString & dsName,bool updateMode,const QStringList & options,const QString & layerName,QString & errCause,bool checkModificationDateAgainstCache)5769 QgsOgrLayerUniquePtr QgsOgrProviderUtils::getLayer( const QString &dsName,
5770 bool updateMode,
5771 const QStringList &options,
5772 const QString &layerName,
5773 QString &errCause,
5774 bool checkModificationDateAgainstCache )
5775 {
5776 QMutexLocker locker( sGlobalMutex() );
5777
5778 // The idea is that we want to minimize the number of GDALDatasetH
5779 // handles openeded. But we have constraints. We do not want that 2
5780 // callers of getLayer() with the same input parameters get the same
5781 // GDALDatasetH since iterators over features of that layer would conflict
5782
5783 QgsOgrProviderUtils::DatasetIdentification ident;
5784 ident.dsName = dsName;
5785 ident.updateMode = updateMode;
5786 ident.options = options;
5787 // Find if there's a list of DatasetWithLayers* that match our
5788 // (dsName, updateMode, options) criteria
5789 auto iter = sMapSharedDS.find( ident );
5790 if ( iter != sMapSharedDS.end() )
5791 {
5792 if ( checkModificationDateAgainstCache && !canUseOpenedDatasets( dsName ) )
5793 {
5794 QgsDebugMsg( QStringLiteral( "Cannot reuse existing opened dataset(s) on %1 since it has been modified" ).arg( dsName ) );
5795 invalidateCachedDatasets( dsName );
5796 iter = sMapSharedDS.find( ident );
5797 Q_ASSERT( iter == sMapSharedDS.end() );
5798 }
5799 }
5800 if ( iter != sMapSharedDS.end() )
5801 {
5802 // Browse through this list, to look for a DatasetWithLayers*
5803 // instance that don't use yet our layer of interest
5804 auto &datasetList = iter.value();
5805 const auto constDatasetList = datasetList;
5806 for ( QgsOgrProviderUtils::DatasetWithLayers *ds : constDatasetList )
5807 {
5808 if ( !ds->canBeShared )
5809 continue;
5810 Q_ASSERT( ds->refCount > 0 );
5811
5812 auto iter2 = ds->setLayers.find( layerName );
5813 if ( iter2 == ds->setLayers.end() )
5814 {
5815 OGRLayerH hLayer;
5816 {
5817 QMutexLocker lockerDS( &ds->mutex );
5818 hLayer = GDALDatasetGetLayerByName(
5819 ds->hDS, layerName.toUtf8().constData() );
5820 }
5821 if ( !hLayer )
5822 {
5823 // Shouldn't happen really !
5824 errCause = QObject::tr( "Cannot find layer %1." ).arg( layerName );
5825 return nullptr;
5826 }
5827 OGR_L_SetAttributeFilter( hLayer, nullptr );
5828
5829 QgsOgrLayerUniquePtr layer = QgsOgrLayer::CreateForLayer(
5830 ident, layerName, ds, hLayer );
5831 ds->setLayers[layerName] = layer.get();
5832 return layer;
5833 }
5834 }
5835
5836 // All existing DatasetWithLayers* already reference our layer of
5837 // interest, so instantiate a new DatasetWithLayers*
5838 QgsOgrLayerUniquePtr layer;
5839 QgsOgrProviderUtils::DatasetWithLayers *ds =
5840 createDatasetWithLayers( dsName, updateMode, options, layerName, ident, layer, errCause );
5841 if ( !ds )
5842 return nullptr;
5843
5844 datasetList.push_back( ds );
5845
5846 return layer;
5847 }
5848
5849 QgsOgrLayerUniquePtr layer;
5850 QgsOgrProviderUtils::DatasetWithLayers *ds =
5851 createDatasetWithLayers( dsName, updateMode, options, layerName, ident, layer, errCause );
5852 if ( !ds )
5853 return nullptr;
5854
5855 QList<DatasetWithLayers *> datasetList;
5856 datasetList.push_back( ds );
5857 sMapSharedDS[ident] = datasetList;
5858
5859 return layer;
5860 }
5861
getSqlLayer(QgsOgrLayer * baseLayer,OGRLayerH hSqlLayer,const QString & sql)5862 QgsOgrLayerUniquePtr QgsOgrProviderUtils::getSqlLayer( QgsOgrLayer *baseLayer,
5863 OGRLayerH hSqlLayer,
5864 const QString &sql )
5865 {
5866 QgsOgrProviderUtils::DatasetIdentification ident;
5867 ident.dsName = baseLayer->datasetName();
5868 ident.updateMode = baseLayer->updateMode();
5869 ident.options = baseLayer->options();
5870 return QgsOgrLayer::CreateForSql( ident, sql, baseLayer->ds, hSqlLayer );
5871 }
5872
releaseInternal(const DatasetIdentification & ident,DatasetWithLayers * ds,bool removeFromDatasetList)5873 void QgsOgrProviderUtils::releaseInternal( const DatasetIdentification &ident,
5874 DatasetWithLayers *ds,
5875 bool removeFromDatasetList )
5876 {
5877
5878 ds->refCount --;
5879 if ( ds->refCount == 0 )
5880 {
5881 Q_ASSERT( ds->setLayers.isEmpty() );
5882
5883 if ( removeFromDatasetList )
5884 {
5885 auto iter = sMapSharedDS.find( ident );
5886 if ( iter != sMapSharedDS.end() )
5887 {
5888 auto &datasetList = iter.value();
5889 int i = 0;
5890
5891 // Normally there should be a match, except for datasets that
5892 // have been invalidated
5893 const auto constDatasetList = datasetList;
5894 for ( QgsOgrProviderUtils::DatasetWithLayers *dsIter : constDatasetList )
5895 {
5896 if ( dsIter == ds )
5897 {
5898 datasetList.removeAt( i );
5899 break;
5900 }
5901 i ++;
5902 }
5903
5904 if ( datasetList.isEmpty() )
5905 sMapSharedDS.erase( iter );
5906 }
5907 }
5908 QgsOgrProviderUtils::GDALCloseWrapper( ds->hDS );
5909 delete ds;
5910 }
5911 }
5912
release(QgsOgrLayer * & layer)5913 void QgsOgrProviderUtils::release( QgsOgrLayer *&layer )
5914 {
5915 if ( !layer )
5916 return;
5917
5918 QMutexLocker locker( sGlobalMutex() );
5919
5920 if ( !layer->isSqlLayer )
5921 {
5922 layer->ds->setLayers.remove( layer->layerName );
5923 }
5924 else
5925 {
5926 QMutexLocker lockerDS( &layer->ds->mutex );
5927 GDALDatasetReleaseResultSet( layer->ds->hDS, layer->hLayer );
5928 }
5929
5930 releaseInternal( layer->ident, layer->ds, !layer->isSqlLayer );
5931
5932 delete layer;
5933 layer = nullptr;
5934 }
5935
5936
releaseDataset(QgsOgrDataset * ds)5937 void QgsOgrProviderUtils::releaseDataset( QgsOgrDataset *ds )
5938 {
5939 if ( !ds )
5940 return;
5941
5942 QMutexLocker locker( sGlobalMutex() );
5943 releaseInternal( ds->mIdent, ds->mDs, true );
5944 delete ds;
5945 }
5946
canDriverShareSameDatasetAmongLayers(const QString & driverName)5947 bool QgsOgrProviderUtils::canDriverShareSameDatasetAmongLayers( const QString &driverName )
5948 {
5949 return driverName != QLatin1String( "OSM" );
5950 }
5951
canDriverShareSameDatasetAmongLayers(const QString & driverName,bool updateMode,const QString & dsName)5952 bool QgsOgrProviderUtils::canDriverShareSameDatasetAmongLayers( const QString &driverName,
5953 bool updateMode,
5954 const QString &dsName )
5955 {
5956 // For .shp.zip with multiple layers, in update mode, we want that each
5957 // layer has its own dataset, so that when its gets closed and reopened,
5958 // the .shp.zip is updated. Otherwise if we share the same dataset, the .shp.zip
5959 // would only be updated when all layers are unloaded, and thus readers will see
5960 // an outdated version of the .shp.zip. This works only if editing operations are
5961 // done separately on layers (which is how it works from the GUI)
5962 return canDriverShareSameDatasetAmongLayers( driverName ) &&
5963 !( updateMode && dsName.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) );
5964 }
5965
5966
create(const QgsOgrProviderUtils::DatasetIdentification & ident,QgsOgrProviderUtils::DatasetWithLayers * ds)5967 QgsOgrDatasetSharedPtr QgsOgrDataset::create( const QgsOgrProviderUtils::DatasetIdentification &ident,
5968 QgsOgrProviderUtils::DatasetWithLayers *ds )
5969 {
5970 QgsOgrDatasetSharedPtr dsRet = QgsOgrDatasetSharedPtr( new QgsOgrDataset(), QgsOgrProviderUtils::releaseDataset );
5971 dsRet->mIdent = ident;
5972 dsRet->mDs = ds;
5973 dsRet->mDs->refCount ++;
5974 return dsRet;
5975 }
5976
executeSQLNoReturn(const QString & sql)5977 bool QgsOgrDataset::executeSQLNoReturn( const QString &sql )
5978 {
5979 QMutexLocker locker( &mutex() );
5980 CPLErrorReset();
5981 OGRLayerH hSqlLayer = GDALDatasetExecuteSQL(
5982 mDs->hDS, sql.toUtf8().constData(), nullptr, nullptr );
5983 bool ret = CPLGetLastErrorType() == CE_None;
5984 GDALDatasetReleaseResultSet( mDs->hDS, hSqlLayer );
5985 return ret;
5986 }
5987
5988
getLayerFromNameOrIndex(const QString & layerName,int layerIndex)5989 OGRLayerH QgsOgrDataset::getLayerFromNameOrIndex( const QString &layerName, int layerIndex )
5990 {
5991 QMutexLocker locker( &mutex() );
5992
5993 OGRLayerH layer;
5994 if ( !layerName.isEmpty() )
5995 {
5996 layer = GDALDatasetGetLayerByName( mDs->hDS, layerName.toUtf8().constData() );
5997 }
5998 else
5999 {
6000 layer = GDALDatasetGetLayer( mDs->hDS, layerIndex );
6001 }
6002 return layer;
6003 }
6004
releaseResultSet(OGRLayerH hSqlLayer)6005 void QgsOgrDataset::releaseResultSet( OGRLayerH hSqlLayer )
6006 {
6007 QMutexLocker locker( &mutex() );
6008 GDALDatasetReleaseResultSet( mDs->hDS, hSqlLayer );
6009 }
6010
QgsOgrLayer()6011 QgsOgrLayer::QgsOgrLayer()
6012 {
6013 oFDefn.layer = this;
6014 }
6015
CreateForLayer(const QgsOgrProviderUtils::DatasetIdentification & ident,const QString & layerName,QgsOgrProviderUtils::DatasetWithLayers * ds,OGRLayerH hLayer)6016 QgsOgrLayerUniquePtr QgsOgrLayer::CreateForLayer(
6017 const QgsOgrProviderUtils::DatasetIdentification &ident,
6018 const QString &layerName,
6019 QgsOgrProviderUtils::DatasetWithLayers *ds,
6020 OGRLayerH hLayer )
6021 {
6022 QgsOgrLayerUniquePtr layer( new QgsOgrLayer() );
6023 layer->ident = ident;
6024 layer->isSqlLayer = false;
6025 layer->layerName = layerName;
6026 layer->ds = ds;
6027 layer->hLayer = hLayer;
6028 {
6029 QMutexLocker locker( &ds->mutex );
6030 OGR_L_ResetReading( hLayer );
6031 }
6032 ds->refCount ++;
6033 return layer;
6034 }
6035
CreateForSql(const QgsOgrProviderUtils::DatasetIdentification & ident,const QString & sql,QgsOgrProviderUtils::DatasetWithLayers * ds,OGRLayerH hLayer)6036 QgsOgrLayerUniquePtr QgsOgrLayer::CreateForSql(
6037 const QgsOgrProviderUtils::DatasetIdentification &ident,
6038 const QString &sql,
6039 QgsOgrProviderUtils::DatasetWithLayers *ds,
6040 OGRLayerH hLayer )
6041 {
6042 QgsOgrLayerUniquePtr layer( new QgsOgrLayer() );
6043 layer->ident = ident;
6044 layer->isSqlLayer = true;
6045 layer->sql = sql;
6046 layer->ds = ds;
6047 layer->hLayer = hLayer;
6048 {
6049 QMutexLocker locker( &ds->mutex );
6050 OGR_L_ResetReading( hLayer );
6051 }
6052 ds->refCount ++;
6053 return layer;
6054 }
6055
GetLayerCount()6056 int QgsOgrLayer::GetLayerCount()
6057 {
6058 QMutexLocker locker( &ds->mutex );
6059 return GDALDatasetGetLayerCount( ds->hDS );
6060 }
6061
driver()6062 GDALDriverH QgsOgrLayer::driver()
6063 {
6064 return GDALGetDatasetDriver( ds->hDS );
6065 }
6066
driverName()6067 QString QgsOgrLayer::driverName()
6068 {
6069 return QString::fromUtf8( GDALGetDriverShortName( GDALGetDatasetDriver( ds->hDS ) ) );
6070 }
6071
name()6072 QByteArray QgsOgrLayer::name()
6073 {
6074 QMutexLocker locker( &ds->mutex );
6075 return OGR_L_GetName( hLayer );
6076 }
6077
ResetReading()6078 void QgsOgrLayer::ResetReading()
6079 {
6080 QMutexLocker locker( &ds->mutex );
6081 OGR_L_ResetReading( hLayer );
6082 }
6083
GetFIDColumn()6084 QByteArray QgsOgrLayer::GetFIDColumn()
6085 {
6086 QMutexLocker locker( &ds->mutex );
6087 return OGR_L_GetFIDColumn( hLayer );
6088 }
6089
GetSpatialRef()6090 OGRSpatialReferenceH QgsOgrLayer::GetSpatialRef()
6091 {
6092 QMutexLocker locker( &ds->mutex );
6093 return OGR_L_GetSpatialRef( hLayer );
6094 }
6095
GetNextFeature()6096 OGRFeatureH QgsOgrLayer::GetNextFeature()
6097 {
6098 QMutexLocker locker( &ds->mutex );
6099 return OGR_L_GetNextFeature( hLayer );
6100 }
6101
GetFeature(GIntBig fid)6102 OGRFeatureH QgsOgrLayer::GetFeature( GIntBig fid )
6103 {
6104 QMutexLocker locker( &ds->mutex );
6105 return OGR_L_GetFeature( hLayer, fid );
6106 }
6107
GetLayerDefn()6108 QgsOgrFeatureDefn &QgsOgrLayer::GetLayerDefn()
6109 {
6110 return oFDefn;
6111 }
6112
GetFeatureCount(bool force)6113 GIntBig QgsOgrLayer::GetFeatureCount( bool force )
6114 {
6115 QMutexLocker locker( &ds->mutex );
6116 return OGR_L_GetFeatureCount( hLayer, force );
6117 }
6118
GetApproxFeatureCount()6119 GIntBig QgsOgrLayer::GetApproxFeatureCount()
6120 {
6121 QMutexLocker locker( &ds->mutex );
6122
6123 // OGR_L_GetFeatureCount() can be super slow on huge geopackage files
6124 // so implement some approximation strategy that has reasonable runtime.
6125 QString driverName = GDALGetDriverShortName( GDALGetDatasetDriver( ds->hDS ) );
6126 if ( driverName == QLatin1String( "GPKG" ) )
6127 {
6128 CPLPushErrorHandler( CPLQuietErrorHandler );
6129 OGRLayerH hSqlLayer = GDALDatasetExecuteSQL(
6130 ds->hDS, "SELECT 1 FROM gpkg_ogr_contents LIMIT 0", nullptr, nullptr );
6131 CPLPopErrorHandler();
6132 if ( hSqlLayer )
6133 {
6134 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6135 return OGR_L_GetFeatureCount( hLayer, TRUE );
6136 }
6137
6138 // Enumerate features up to a limit of 100000.
6139 const GIntBig nLimit = CPLAtoGIntBig(
6140 CPLGetConfigOption( "QGIS_GPKG_FC_THRESHOLD", "100000" ) );
6141 QByteArray layerName = OGR_L_GetName( hLayer );
6142 QByteArray sql( "SELECT COUNT(*) FROM (SELECT 1 FROM " );
6143 sql += QgsOgrProviderUtils::quotedIdentifier( layerName, driverName );
6144 sql += " LIMIT ";
6145 sql += CPLSPrintf( CPL_FRMT_GIB, nLimit );
6146 sql += ")";
6147 hSqlLayer = GDALDatasetExecuteSQL( ds->hDS, sql, nullptr, nullptr );
6148 GIntBig res = -1;
6149 if ( hSqlLayer )
6150 {
6151 gdal::ogr_feature_unique_ptr fet( OGR_L_GetNextFeature( hSqlLayer ) );
6152 if ( fet )
6153 {
6154 res = OGR_F_GetFieldAsInteger64( fet.get(), 0 );
6155 }
6156 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6157 }
6158 if ( res >= 0 && res < nLimit )
6159 {
6160 // Less than 100000 features ? This is the final count
6161 return res;
6162 }
6163 if ( res == nLimit )
6164 {
6165 // If we reach the threshold, then use the min and max values of the rowid
6166 // hoping there are not a lot of holes.
6167 // Do it in 2 separate SQL queries otherwise SQLite apparently does a
6168 // full table scan...
6169 sql = "SELECT MAX(ROWID) FROM ";
6170 sql += QgsOgrProviderUtils::quotedIdentifier( layerName, driverName );
6171 hSqlLayer = GDALDatasetExecuteSQL( ds->hDS, sql, nullptr, nullptr );
6172 GIntBig maxrowid = -1;
6173 if ( hSqlLayer )
6174 {
6175 gdal::ogr_feature_unique_ptr fet( OGR_L_GetNextFeature( hSqlLayer ) );
6176 if ( fet )
6177 {
6178 maxrowid = OGR_F_GetFieldAsInteger64( fet.get(), 0 );
6179 }
6180 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6181 }
6182
6183 sql = "SELECT MIN(ROWID) FROM ";
6184 sql += QgsOgrProviderUtils::quotedIdentifier( layerName, driverName );
6185 hSqlLayer = GDALDatasetExecuteSQL( ds->hDS, sql, nullptr, nullptr );
6186 GIntBig minrowid = 0;
6187 if ( hSqlLayer )
6188 {
6189 gdal::ogr_feature_unique_ptr fet( OGR_L_GetNextFeature( hSqlLayer ) );
6190 if ( fet )
6191 {
6192 minrowid = OGR_F_GetFieldAsInteger64( fet.get(), 0 );
6193 }
6194 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6195 }
6196
6197 if ( maxrowid >= minrowid )
6198 {
6199 return maxrowid - minrowid + 1;
6200 }
6201 }
6202 }
6203 if ( driverName == QLatin1String( "OAPIF" ) || driverName == QLatin1String( "WFS3" ) )
6204 {
6205 return -1;
6206 }
6207
6208 return OGR_L_GetFeatureCount( hLayer, TRUE );
6209 }
6210
6211 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,4,0)
findMinOrMax(GDALDatasetH hDS,const QByteArray & rtreeName,const char * varName,bool isMin,double & val)6212 static bool findMinOrMax( GDALDatasetH hDS, const QByteArray &rtreeName,
6213 const char *varName, bool isMin, double &val )
6214 {
6215 // We proceed by dichotomic search since unfortunately SELECT MIN(minx)
6216 // in a RTree is a slow operation
6217 double minval = -1e10;
6218 double maxval = 1e10;
6219 val = 0.0;
6220 double oldval = 0.0;
6221 for ( int i = 0; i < 100 && maxval - minval > 1e-15; i++ )
6222 {
6223 val = ( minval + maxval ) / 2;
6224 if ( i > 0 && val == oldval )
6225 {
6226 break;
6227 }
6228 oldval = val;
6229 QByteArray sql = "SELECT 1 FROM ";
6230 sql += rtreeName;
6231 sql += " WHERE ";
6232 sql += varName;
6233 sql += isMin ? " < " : " > ";
6234 sql += CPLSPrintf( "%.18g", val );
6235 sql += " LIMIT 1";
6236 auto hSqlLayer = GDALDatasetExecuteSQL(
6237 hDS, sql, nullptr, nullptr );
6238 GIntBig count = -1;
6239 if ( hSqlLayer )
6240 {
6241 count = OGR_L_GetFeatureCount( hSqlLayer, true );
6242 GDALDatasetReleaseResultSet( hDS, hSqlLayer );
6243 }
6244 if ( count < 0 )
6245 {
6246 return false;
6247 }
6248 if ( ( isMin && count == 0 ) || ( !isMin && count == 1 ) )
6249 {
6250 minval = val;
6251 }
6252 else
6253 {
6254 maxval = val;
6255 }
6256 }
6257 return true;
6258 }
6259 #endif
6260
GetExtent(OGREnvelope * psExtent,bool bForce)6261 OGRErr QgsOgrLayer::GetExtent( OGREnvelope *psExtent, bool bForce )
6262 {
6263 QMutexLocker locker( &ds->mutex );
6264
6265 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,4,0)
6266 // OGR_L_GetExtent() can be super slow on huge geopackage files
6267 // so implement some approximation strategy that has reasonable runtime.
6268 // Actually this should return a rather accurante answer.
6269 QString driverName = GDALGetDriverShortName( GDALGetDatasetDriver( ds->hDS ) );
6270 if ( driverName == QLatin1String( "GPKG" ) )
6271 {
6272 QByteArray layerName = OGR_L_GetName( hLayer );
6273 QByteArray rtreeName =
6274 QgsOgrProviderUtils::quotedIdentifier( "rtree_" + layerName + "_" + OGR_L_GetGeometryColumn( hLayer ), driverName );
6275
6276 // Check if there is a non-empty RTree
6277 QByteArray sql( "SELECT 1 FROM " );
6278 sql += rtreeName;
6279 sql += " LIMIT 1";
6280 CPLPushErrorHandler( CPLQuietErrorHandler );
6281 OGRLayerH hSqlLayer = GDALDatasetExecuteSQL(
6282 ds->hDS, sql, nullptr, nullptr );
6283 CPLPopErrorHandler();
6284 if ( !hSqlLayer )
6285 {
6286 return OGR_L_GetExtent( hLayer, psExtent, bForce );
6287 }
6288 bool hasFeatures = OGR_L_GetFeatureCount( hSqlLayer, true ) > 0;
6289 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6290 if ( !hasFeatures )
6291 {
6292 return OGRERR_FAILURE;
6293 }
6294
6295 double minx, miny, maxx, maxy;
6296 if ( findMinOrMax( ds->hDS, rtreeName, "MINX", true, minx ) &&
6297 findMinOrMax( ds->hDS, rtreeName, "MINY", true, miny ) &&
6298 findMinOrMax( ds->hDS, rtreeName, "MAXX", false, maxx ) &&
6299 findMinOrMax( ds->hDS, rtreeName, "MAXY", false, maxy ) )
6300 {
6301 psExtent->MinX = minx;
6302 psExtent->MinY = miny;
6303 psExtent->MaxX = maxx;
6304 psExtent->MaxY = maxy;
6305 return OGRERR_NONE;
6306 }
6307 }
6308 #endif
6309
6310 return OGR_L_GetExtent( hLayer, psExtent, bForce );
6311 }
6312
GetSpatialFilter()6313 OGRGeometryH QgsOgrLayer::GetSpatialFilter()
6314 {
6315 QMutexLocker locker( &ds->mutex );
6316 return OGR_L_GetSpatialFilter( hLayer );
6317 }
6318
SetSpatialFilter(OGRGeometryH hGeometry)6319 void QgsOgrLayer::SetSpatialFilter( OGRGeometryH hGeometry )
6320 {
6321 QMutexLocker locker( &ds->mutex );
6322 OGR_L_SetSpatialFilter( hLayer, hGeometry );
6323 }
6324
getDatasetHandleAndMutex(QMutex * & mutex)6325 GDALDatasetH QgsOgrLayer::getDatasetHandleAndMutex( QMutex *&mutex )
6326 {
6327 mutex = &( ds->mutex );
6328 return ds->hDS;
6329 }
6330
getHandleAndMutex(QMutex * & mutex)6331 OGRLayerH QgsOgrLayer::getHandleAndMutex( QMutex *&mutex )
6332 {
6333 mutex = &( ds->mutex );
6334 return hLayer;
6335 }
6336
CreateFeature(OGRFeatureH hFeature)6337 OGRErr QgsOgrLayer::CreateFeature( OGRFeatureH hFeature )
6338 {
6339 QMutexLocker locker( &ds->mutex );
6340 return OGR_L_CreateFeature( hLayer, hFeature );
6341 }
6342
SetFeature(OGRFeatureH hFeature)6343 OGRErr QgsOgrLayer::SetFeature( OGRFeatureH hFeature )
6344 {
6345 QMutexLocker locker( &ds->mutex );
6346 return OGR_L_SetFeature( hLayer, hFeature );
6347 }
6348
DeleteFeature(GIntBig fid)6349 OGRErr QgsOgrLayer::DeleteFeature( GIntBig fid )
6350 {
6351 QMutexLocker locker( &ds->mutex );
6352 return OGR_L_DeleteFeature( hLayer, fid );
6353 }
6354
CreateField(OGRFieldDefnH hFieldDefn,bool bStrict)6355 OGRErr QgsOgrLayer::CreateField( OGRFieldDefnH hFieldDefn, bool bStrict )
6356 {
6357 QMutexLocker locker( &ds->mutex );
6358 return OGR_L_CreateField( hLayer, hFieldDefn, bStrict );
6359 }
6360
DeleteField(int iField)6361 OGRErr QgsOgrLayer::DeleteField( int iField )
6362 {
6363 QMutexLocker locker( &ds->mutex );
6364 return OGR_L_DeleteField( hLayer, iField );
6365 }
6366
AlterFieldDefn(int iField,OGRFieldDefnH hNewFieldDefn,int flags)6367 OGRErr QgsOgrLayer::AlterFieldDefn( int iField, OGRFieldDefnH hNewFieldDefn, int flags )
6368 {
6369 QMutexLocker locker( &ds->mutex );
6370 return OGR_L_AlterFieldDefn( hLayer, iField, hNewFieldDefn, flags );
6371 }
6372
TestCapability(const char * cap)6373 int QgsOgrLayer::TestCapability( const char *cap )
6374 {
6375 QMutexLocker locker( &ds->mutex );
6376 return OGR_L_TestCapability( hLayer, cap );
6377 }
6378
StartTransaction()6379 OGRErr QgsOgrLayer::StartTransaction()
6380 {
6381 QMutexLocker locker( &ds->mutex );
6382 return OGR_L_StartTransaction( hLayer );
6383 }
6384
CommitTransaction()6385 OGRErr QgsOgrLayer::CommitTransaction()
6386 {
6387 QMutexLocker locker( &ds->mutex );
6388 return OGR_L_CommitTransaction( hLayer );
6389 }
6390
RollbackTransaction()6391 OGRErr QgsOgrLayer::RollbackTransaction()
6392 {
6393 QMutexLocker locker( &ds->mutex );
6394 return OGR_L_RollbackTransaction( hLayer );
6395 }
6396
SyncToDisk()6397 OGRErr QgsOgrLayer::SyncToDisk()
6398 {
6399 QMutexLocker locker( &ds->mutex );
6400
6401 OGRErr eErr;
6402 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0) && GDAL_VERSION_NUM <= GDAL_COMPUTE_VERSION(3,1,3)
6403 // Workaround bug in GDAL 3.1.0 to 3.1.3 that creates XLSX and ODS files incompatible with LibreOffice due to use of ZIP64
6404 QString drvName = GDALGetDriverShortName( GDALGetDatasetDriver( ds->hDS ) );
6405 if ( drvName == QLatin1String( "XLSX" ) ||
6406 drvName == QLatin1String( "ODS" ) )
6407 {
6408 CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", "NO" );
6409 eErr = OGR_L_SyncToDisk( hLayer );
6410 CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", nullptr );
6411 }
6412 else
6413 #endif
6414 {
6415 eErr = OGR_L_SyncToDisk( hLayer );
6416 }
6417
6418 return eErr;
6419 }
6420
ExecuteSQLNoReturn(const QByteArray & sql)6421 void QgsOgrLayer::ExecuteSQLNoReturn( const QByteArray &sql )
6422 {
6423 QMutexLocker locker( &ds->mutex );
6424 OGRLayerH hSqlLayer = GDALDatasetExecuteSQL( ds->hDS,
6425 sql.constData(),
6426 nullptr,
6427 nullptr );
6428 GDALDatasetReleaseResultSet( ds->hDS, hSqlLayer );
6429 }
6430
ExecuteSQL(const QByteArray & sql)6431 QgsOgrLayerUniquePtr QgsOgrLayer::ExecuteSQL( const QByteArray &sql )
6432 {
6433 QMutexLocker locker( &ds->mutex );
6434 OGRLayerH hSqlLayer = GDALDatasetExecuteSQL( ds->hDS,
6435 sql.constData(),
6436 nullptr,
6437 nullptr );
6438 if ( !hSqlLayer )
6439 return nullptr;
6440
6441 return QgsOgrLayer::CreateForSql( ident,
6442 QString::fromUtf8( sql ),
6443 ds,
6444 hSqlLayer );
6445 }
6446
GetMetadataItem(const QString & key,const QString & domain)6447 QString QgsOgrLayer::GetMetadataItem( const QString &key, const QString &domain )
6448 {
6449 QMutexLocker locker( &ds->mutex );
6450 return GDALGetMetadataItem( hLayer, key.toUtf8().constData(),
6451 domain.toUtf8().constData() );
6452 }
6453
mutex()6454 QMutex &QgsOgrFeatureDefn::mutex()
6455 {
6456 return layer->mutex();
6457 }
6458
get()6459 OGRFeatureDefnH QgsOgrFeatureDefn::get()
6460 {
6461 if ( !hDefn )
6462 {
6463 QMutexLocker locker( &mutex() );
6464 hDefn = OGR_L_GetLayerDefn( layer->hLayer );
6465 }
6466 return hDefn;
6467 }
6468
6469
GetFieldCount()6470 int QgsOgrFeatureDefn::GetFieldCount()
6471 {
6472 QMutexLocker locker( &mutex() );
6473 return OGR_FD_GetFieldCount( get() );
6474 }
6475
GetFieldDefn(int idx)6476 OGRFieldDefnH QgsOgrFeatureDefn::GetFieldDefn( int idx )
6477 {
6478 QMutexLocker locker( &mutex() );
6479 return OGR_FD_GetFieldDefn( get(), idx );
6480 }
6481
GetFieldIndex(const QByteArray & name)6482 int QgsOgrFeatureDefn::GetFieldIndex( const QByteArray &name )
6483 {
6484 QMutexLocker locker( &mutex() );
6485 return OGR_FD_GetFieldIndex( get(), name.constData() );
6486 }
6487
GetGeomFieldCount()6488 int QgsOgrFeatureDefn::GetGeomFieldCount()
6489 {
6490 QMutexLocker locker( &mutex() );
6491 return OGR_FD_GetGeomFieldCount( get() );
6492 }
6493
GetGeomFieldDefn(int idx)6494 OGRGeomFieldDefnH QgsOgrFeatureDefn::GetGeomFieldDefn( int idx )
6495 {
6496 QMutexLocker locker( &mutex() );
6497 return OGR_FD_GetGeomFieldDefn( get(), idx );
6498 }
6499
GetGeomType()6500 OGRwkbGeometryType QgsOgrFeatureDefn::GetGeomType()
6501 {
6502 QMutexLocker locker( &mutex() );
6503 return OGR_FD_GetGeomType( get() );
6504 }
6505
CreateFeature()6506 OGRFeatureH QgsOgrFeatureDefn::CreateFeature()
6507 {
6508 QMutexLocker locker( &mutex() );
6509 return OGR_F_Create( get() );
6510 }
6511
6512 // ---------------------------------------------------------------------------
6513
6514 static
LoadDataSourceAndLayer(const QString & uri,QString & errCause)6515 QgsOgrLayerUniquePtr LoadDataSourceAndLayer( const QString &uri,
6516 QString &errCause )
6517 {
6518 bool isSubLayer;
6519 int layerIndex;
6520 QString layerName;
6521 QString subsetString;
6522 OGRwkbGeometryType ogrGeometryType;
6523 QStringList openOptions;
6524 QString filePath = AnalyzeURI( uri,
6525 isSubLayer,
6526 layerIndex,
6527 layerName,
6528 subsetString,
6529 ogrGeometryType,
6530 openOptions );
6531
6532 if ( !layerName.isEmpty() )
6533 {
6534 return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerName, errCause, true );
6535 }
6536 else
6537 {
6538 return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerIndex, errCause, true );
6539 }
6540 }
6541
6542
saveStyle(const QString & uri,const QString & qmlStyle,const QString & sldStyle,const QString & styleName,const QString & styleDescription,const QString & uiFileContent,bool useAsDefault,QString & errCause)6543 bool QgsOgrProviderMetadata::saveStyle(
6544 const QString &uri, const QString &qmlStyle, const QString &sldStyle,
6545 const QString &styleName, const QString &styleDescription,
6546 const QString &uiFileContent, bool useAsDefault, QString &errCause )
6547 {
6548 QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, errCause );
6549 if ( !userLayer )
6550 return false;
6551
6552 QMutex *mutex = nullptr;
6553 OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex );
6554 GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
6555 QMutexLocker locker( mutex );
6556
6557 // check if layer_styles table already exist
6558 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
6559 if ( !hLayer )
6560 {
6561 // if not create it
6562 // Note: we use the same schema as in the SpatiaLite and postgres providers
6563 //for cross interoperability
6564
6565 char **options = nullptr;
6566 // TODO: might need change if other drivers than GPKG / SQLite
6567 options = CSLSetNameValue( options, "FID", "id" );
6568 hLayer = GDALDatasetCreateLayer( hDS, "layer_styles", nullptr, wkbNone, options );
6569 QgsOgrProviderUtils::invalidateCachedDatasets( QString::fromUtf8( GDALGetDescription( hDS ) ) );
6570 CSLDestroy( options );
6571 if ( !hLayer )
6572 {
6573 errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
6574 return false;
6575 }
6576 bool ok = true;
6577 {
6578 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_catalog", OFTString ) );
6579 OGR_Fld_SetWidth( fld.get(), 256 );
6580 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6581 }
6582 {
6583 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_schema", OFTString ) );
6584 OGR_Fld_SetWidth( fld.get(), 256 );
6585 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6586 }
6587 {
6588 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_name", OFTString ) );
6589 OGR_Fld_SetWidth( fld.get(), 256 );
6590 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6591 }
6592 {
6593 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_geometry_column", OFTString ) );
6594 OGR_Fld_SetWidth( fld.get(), 256 );
6595 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6596 }
6597 {
6598 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleName", OFTString ) );
6599 OGR_Fld_SetWidth( fld.get(), 30 );
6600 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6601 }
6602 {
6603 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleQML", OFTString ) );
6604 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6605 }
6606 {
6607 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleSLD", OFTString ) );
6608 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6609 }
6610 {
6611 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "useAsDefault", OFTInteger ) );
6612 OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
6613 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6614 }
6615 {
6616 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "description", OFTString ) );
6617 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6618 }
6619 {
6620 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "owner", OFTString ) );
6621 OGR_Fld_SetWidth( fld.get(), 30 );
6622 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6623 }
6624 {
6625 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "ui", OFTString ) );
6626 OGR_Fld_SetWidth( fld.get(), 30 );
6627 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6628 }
6629 {
6630 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "update_time", OFTDateTime ) );
6631 OGR_Fld_SetDefault( fld.get(), "CURRENT_TIMESTAMP" );
6632 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
6633 }
6634 if ( !ok )
6635 {
6636 errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
6637 return false;
6638 }
6639 }
6640
6641 QString realStyleName =
6642 styleName.isEmpty() ? QString( OGR_L_GetName( hUserLayer ) ) : styleName;
6643
6644 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
6645
6646 if ( useAsDefault )
6647 {
6648 QString oldDefaultQuery = QStringLiteral( "useAsDefault = 1 AND f_table_schema=''"
6649 " AND f_table_name=%1"
6650 " AND f_geometry_column=%2" )
6651 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
6652 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) );
6653 OGR_L_SetAttributeFilter( hLayer, oldDefaultQuery.toUtf8().constData() );
6654 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
6655 if ( hFeature )
6656 {
6657 OGR_F_SetFieldInteger( hFeature.get(),
6658 OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
6659 0 );
6660 bool ok = OGR_L_SetFeature( hLayer, hFeature.get() ) == 0;
6661 if ( !ok )
6662 {
6663 QgsDebugMsg( QStringLiteral( "Could not unset previous useAsDefault style" ) );
6664 }
6665 }
6666 }
6667
6668 QString checkQuery = QStringLiteral( "f_table_schema=''"
6669 " AND f_table_name=%1"
6670 " AND f_geometry_column=%2"
6671 " AND styleName=%3" )
6672 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
6673 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) )
6674 .arg( QgsOgrProviderUtils::quotedValue( realStyleName ) );
6675 OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
6676 OGR_L_ResetReading( hLayer );
6677 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
6678 OGR_L_ResetReading( hLayer );
6679 bool bNew = true;
6680
6681 if ( hFeature )
6682 {
6683 QgsSettings settings;
6684 // Only used in tests. Do not define it for interactive implication
6685 QVariant overwriteStyle = settings.value( QStringLiteral( "qgis/overwriteStyle" ) );
6686 if ( ( !overwriteStyle.isNull() && !overwriteStyle.toBool() ) ||
6687 ( overwriteStyle.isNull() &&
6688 QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
6689 QObject::tr( "A style named \"%1\" already exists in the database for this layer. Do you want to overwrite it?" )
6690 .arg( realStyleName ),
6691 QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No ) )
6692 {
6693 errCause = QObject::tr( "Operation aborted" );
6694 return false;
6695 }
6696 bNew = false;
6697 }
6698 else
6699 {
6700 hFeature.reset( OGR_F_Create( hLayerDefn ) );
6701 OGR_F_SetFieldString( hFeature.get(),
6702 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_catalog" ),
6703 "" );
6704 OGR_F_SetFieldString( hFeature.get(),
6705 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_schema" ),
6706 "" );
6707 OGR_F_SetFieldString( hFeature.get(),
6708 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ),
6709 OGR_L_GetName( hUserLayer ) );
6710 OGR_F_SetFieldString( hFeature.get(),
6711 OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ),
6712 OGR_L_GetGeometryColumn( hUserLayer ) );
6713 OGR_F_SetFieldString( hFeature.get(),
6714 OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ),
6715 realStyleName.toUtf8().constData() );
6716 if ( !uiFileContent.isEmpty() )
6717 {
6718 OGR_F_SetFieldString( hFeature.get(),
6719 OGR_FD_GetFieldIndex( hLayerDefn, "ui" ),
6720 uiFileContent.toUtf8().constData() );
6721 }
6722 }
6723 OGR_F_SetFieldString( hFeature.get(),
6724 OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ),
6725 qmlStyle.toUtf8().constData() );
6726 OGR_F_SetFieldString( hFeature.get(),
6727 OGR_FD_GetFieldIndex( hLayerDefn, "styleSLD" ),
6728 sldStyle.toUtf8().constData() );
6729 OGR_F_SetFieldInteger( hFeature.get(),
6730 OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
6731 useAsDefault ? 1 : 0 );
6732 OGR_F_SetFieldString( hFeature.get(),
6733 OGR_FD_GetFieldIndex( hLayerDefn, "description" ),
6734 ( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ).toUtf8().constData() );
6735 OGR_F_SetFieldString( hFeature.get(),
6736 OGR_FD_GetFieldIndex( hLayerDefn, "owner" ),
6737 "" );
6738
6739 bool bFeatureOK;
6740 if ( bNew )
6741 bFeatureOK = OGR_L_CreateFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
6742 else
6743 bFeatureOK = OGR_L_SetFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
6744
6745 if ( !bFeatureOK )
6746 {
6747 QgsMessageLog::logMessage( QObject::tr( "Error updating style" ) );
6748 errCause = QObject::tr( "Error looking for style. The query was logged" );
6749 return false;
6750 }
6751
6752 return true;
6753 }
6754
6755
deleteStyleById(const QString & uri,QString styleId,QString & errCause)6756 bool QgsOgrProviderMetadata::deleteStyleById( const QString &uri, QString styleId, QString &errCause )
6757 {
6758 QgsDataSourceUri dsUri( uri );
6759 bool deleted;
6760
6761 QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, errCause );
6762 if ( !userLayer )
6763 return false;
6764
6765 QMutex *mutex = nullptr;
6766 GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
6767 QMutexLocker locker( mutex );
6768
6769 // check if layer_styles table already exist
6770 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
6771 if ( !hLayer )
6772 {
6773 errCause = QObject::tr( "Connection to database failed: %1" ).arg( dsUri.uri() );
6774 deleted = false;
6775 }
6776 else
6777 {
6778 if ( OGR_L_DeleteFeature( hLayer, styleId.toInt() ) != OGRERR_NONE )
6779 {
6780 errCause = QObject::tr( "Error executing the delete query." );
6781 deleted = false;
6782 }
6783 else
6784 {
6785 deleted = true;
6786 }
6787 }
6788 return deleted;
6789 }
6790
6791 static
LoadDataSourceLayerStylesAndLayer(const QString & uri,QgsOgrLayerUniquePtr & layerStyles,QgsOgrLayerUniquePtr & userLayer,QString & errCause)6792 bool LoadDataSourceLayerStylesAndLayer( const QString &uri,
6793 QgsOgrLayerUniquePtr &layerStyles,
6794 QgsOgrLayerUniquePtr &userLayer,
6795 QString &errCause )
6796 {
6797 bool isSubLayer;
6798 int layerIndex;
6799 QString layerName;
6800 QString subsetString;
6801 OGRwkbGeometryType ogrGeometryType;
6802 QStringList openOptions;
6803 QString filePath = AnalyzeURI( uri,
6804 isSubLayer,
6805 layerIndex,
6806 layerName,
6807 subsetString,
6808 ogrGeometryType,
6809 openOptions );
6810
6811 layerStyles =
6812 QgsOgrProviderUtils::getLayer( filePath, "layer_styles", errCause );
6813 userLayer = nullptr;
6814 if ( !layerStyles )
6815 {
6816 errCause = QObject::tr( "Cannot find layer_styles layer" );
6817 return false;
6818 }
6819
6820 if ( !layerName.isEmpty() )
6821 {
6822 userLayer = QgsOgrProviderUtils::getLayer( filePath, layerName, errCause );
6823 }
6824 else
6825 {
6826 userLayer = QgsOgrProviderUtils::getLayer( filePath, layerIndex, errCause );
6827 }
6828 if ( !userLayer )
6829 {
6830 layerStyles.reset();
6831 return false;
6832 }
6833 return true;
6834 }
6835
6836
loadStyle(const QString & uri,QString & errCause)6837 QString QgsOgrProviderMetadata::loadStyle( const QString &uri, QString &errCause )
6838 {
6839 QgsOgrLayerUniquePtr layerStyles;
6840 QgsOgrLayerUniquePtr userLayer;
6841 if ( !LoadDataSourceLayerStylesAndLayer( uri, layerStyles, userLayer, errCause ) )
6842 {
6843 return QString();
6844 }
6845
6846 QMutex *mutex1 = nullptr;
6847 OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
6848 QMutex *mutex2 = nullptr;
6849 OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex2 );
6850 QMutexLocker lock1( mutex1 );
6851 QMutexLocker lock2( mutex2 );
6852
6853 QString selectQmlQuery = QStringLiteral( "f_table_schema=''"
6854 " AND f_table_name=%1"
6855 " AND f_geometry_column=%2"
6856 " ORDER BY CASE WHEN useAsDefault THEN 1 ELSE 2 END"
6857 ",update_time DESC LIMIT 1" )
6858 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
6859 .arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) );
6860 OGR_L_SetAttributeFilter( hLayer, selectQmlQuery.toUtf8().constData() );
6861 OGR_L_ResetReading( hLayer );
6862 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
6863 QString styleQML;
6864 qlonglong moreRecentTimestamp = 0;
6865 while ( true )
6866 {
6867 gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hLayer ) );
6868 if ( !hFeat )
6869 break;
6870 if ( OGR_F_GetFieldAsInteger( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ) ) )
6871 {
6872 styleQML = QString::fromUtf8(
6873 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
6874 break;
6875 }
6876
6877 int year, month, day, hour, minute, second, TZ;
6878 OGR_F_GetFieldAsDateTime( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
6879 &year, &month, &day, &hour, &minute, &second, &TZ );
6880 qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
6881 static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
6882 if ( ts > moreRecentTimestamp )
6883 {
6884 moreRecentTimestamp = ts;
6885 styleQML = QString::fromUtf8(
6886 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
6887
6888 }
6889 }
6890 OGR_L_ResetReading( hLayer );
6891
6892 return styleQML;
6893 }
6894
listStyles(const QString & uri,QStringList & ids,QStringList & names,QStringList & descriptions,QString & errCause)6895 int QgsOgrProviderMetadata::listStyles(
6896 const QString &uri, QStringList &ids, QStringList &names,
6897 QStringList &descriptions, QString &errCause )
6898 {
6899 bool isSubLayer;
6900 int layerIndex;
6901 QString layerName;
6902 QString subsetString;
6903 OGRwkbGeometryType ogrGeometryType;
6904 QStringList openOptions;
6905 QString filePath = AnalyzeURI( uri,
6906 isSubLayer,
6907 layerIndex,
6908 layerName,
6909 subsetString,
6910 ogrGeometryType,
6911 openOptions );
6912
6913 QgsOgrLayerUniquePtr userLayer;
6914 if ( !layerName.isEmpty() )
6915 {
6916 userLayer = QgsOgrProviderUtils::getLayer( filePath, layerName, errCause );
6917 }
6918 else
6919 {
6920 userLayer = QgsOgrProviderUtils::getLayer( filePath, layerIndex, errCause );
6921 }
6922 if ( !userLayer )
6923 {
6924 return -1;
6925 }
6926
6927 QgsOgrLayerUniquePtr layerStyles =
6928 QgsOgrProviderUtils::getLayer( filePath, "layer_styles", errCause );
6929 if ( !layerStyles )
6930 {
6931 QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
6932 errCause = QObject::tr( "No styles available on DB" );
6933 return 0;
6934 }
6935
6936 QMutex *mutex1 = nullptr;
6937 OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
6938 QMutexLocker lock1( mutex1 );
6939 QMutex *mutex2 = nullptr;
6940 OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex2 );
6941 QMutexLocker lock2( mutex2 );
6942
6943 if ( OGR_L_GetFeatureCount( hLayer, TRUE ) == 0 )
6944 {
6945 QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
6946 errCause = QObject::tr( "No styles available on DB" );
6947 return 0;
6948 }
6949
6950 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
6951
6952 OGR_L_ResetReading( hLayer );
6953
6954 QList<qlonglong> listTimestamp;
6955 QMap<int, QString> mapIdToStyleName;
6956 QMap<int, QString> mapIdToDescription;
6957 QMap<qlonglong, QList<int> > mapTimestampToId;
6958 int numberOfRelatedStyles = 0;
6959 while ( true )
6960 {
6961 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
6962 if ( !hFeature )
6963 break;
6964
6965 QString tableName( QString::fromUtf8(
6966 OGR_F_GetFieldAsString( hFeature.get(),
6967 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ) ) ) );
6968 QString geometryColumn( QString::fromUtf8(
6969 OGR_F_GetFieldAsString( hFeature.get(),
6970 OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ) ) ) );
6971 QString styleName( QString::fromUtf8(
6972 OGR_F_GetFieldAsString( hFeature.get(),
6973 OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) ) );
6974 QString description( QString::fromUtf8(
6975 OGR_F_GetFieldAsString( hFeature.get(),
6976 OGR_FD_GetFieldIndex( hLayerDefn, "description" ) ) ) );
6977 int fid = static_cast<int>( OGR_F_GetFID( hFeature.get() ) );
6978 if ( tableName == QString::fromUtf8( OGR_L_GetName( hUserLayer ) ) &&
6979 geometryColumn == QString::fromUtf8( OGR_L_GetGeometryColumn( hUserLayer ) ) )
6980 {
6981 // Append first all related styles
6982 QString id( QString::number( fid ) );
6983 ids.append( id );
6984 names.append( styleName );
6985 descriptions.append( description );
6986 ++ numberOfRelatedStyles;
6987 }
6988 else
6989 {
6990 int year, month, day, hour, minute, second, TZ;
6991 OGR_F_GetFieldAsDateTime( hFeature.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
6992 &year, &month, &day, &hour, &minute, &second, &TZ );
6993 qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
6994 static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
6995
6996 listTimestamp.append( ts );
6997 mapIdToStyleName[fid] = styleName;
6998 mapIdToDescription[fid] = description;
6999 mapTimestampToId[ts].append( fid );
7000 }
7001 }
7002
7003 std::sort( listTimestamp.begin(), listTimestamp.end() );
7004 // Sort from most recent to least recent
7005 for ( int i = listTimestamp.size() - 1; i >= 0; i-- )
7006 {
7007 const QList<int> &listId = mapTimestampToId[listTimestamp[i]];
7008 for ( int j = 0; j < listId.size(); j++ )
7009 {
7010 int fid = listId[j];
7011 QString id( QString::number( fid ) );
7012 ids.append( id );
7013 names.append( mapIdToStyleName[fid] );
7014 descriptions.append( mapIdToDescription[fid] );
7015 }
7016 }
7017
7018 return numberOfRelatedStyles;
7019 }
7020
getStyleById(const QString & uri,QString styleId,QString & errCause)7021 QString QgsOgrProviderMetadata::getStyleById( const QString &uri, QString styleId, QString &errCause )
7022 {
7023 QgsOgrLayerUniquePtr layerStyles;
7024 QgsOgrLayerUniquePtr userLayer;
7025 if ( !LoadDataSourceLayerStylesAndLayer( uri, layerStyles, userLayer, errCause ) )
7026 {
7027 return QString();
7028 }
7029
7030 QMutex *mutex1 = nullptr;
7031 OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
7032 QMutexLocker lock1( mutex1 );
7033
7034 bool ok;
7035 int id = styleId.toInt( &ok );
7036 if ( !ok )
7037 {
7038 errCause = QObject::tr( "Invalid style identifier" );
7039 return QString();
7040 }
7041
7042 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetFeature( hLayer, id ) );
7043 if ( !hFeature )
7044 {
7045 errCause = QObject::tr( "No style corresponding to style identifier" );
7046 return QString();
7047 }
7048
7049 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
7050 QString styleQML( QString::fromUtf8(
7051 OGR_F_GetFieldAsString( hFeature.get(),
7052 OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) ) );
7053 OGR_L_ResetReading( hLayer );
7054
7055 return styleQML;
7056 }
7057
7058
7059 // ---------------------------------------------------------------------------
7060
deleteLayer(const QString & uri,QString & errCause)7061 bool QgsOgrProviderUtils::deleteLayer( const QString &uri, QString &errCause )
7062 {
7063 bool isSubLayer;
7064 int layerIndex;
7065 QString layerName;
7066 QString subsetString;
7067 OGRwkbGeometryType ogrGeometryType;
7068 QStringList openOptions;
7069 QString filePath = AnalyzeURI( uri,
7070 isSubLayer,
7071 layerIndex,
7072 layerName,
7073 subsetString,
7074 ogrGeometryType,
7075 openOptions );
7076
7077
7078 gdal::dataset_unique_ptr hDS( GDALOpenEx( filePath.toUtf8().constData(), GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr ) );
7079 if ( hDS && ( ! layerName.isEmpty() || layerIndex != -1 ) )
7080 {
7081 // If we have got a name we convert it into an index
7082 if ( ! layerName.isEmpty() )
7083 {
7084 layerIndex = -1;
7085 for ( int i = 0; i < GDALDatasetGetLayerCount( hDS.get() ); i++ )
7086 {
7087 OGRLayerH hL = GDALDatasetGetLayer( hDS.get(), i );
7088 if ( layerName == QString::fromUtf8( OGR_L_GetName( hL ) ) )
7089 {
7090 layerIndex = i;
7091 break;
7092 }
7093 }
7094 }
7095 // Do delete!
7096 if ( layerIndex != -1 )
7097 {
7098 OGRErr error = GDALDatasetDeleteLayer( hDS.get(), layerIndex );
7099 switch ( error )
7100 {
7101 case OGRERR_NOT_ENOUGH_DATA:
7102 errCause = QObject::tr( "Not enough data to deserialize" );
7103 break;
7104 case OGRERR_NOT_ENOUGH_MEMORY:
7105 errCause = QObject::tr( "Not enough memory" );
7106 break;
7107 case OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
7108 errCause = QObject::tr( "Unsupported geometry type" );
7109 break;
7110 case OGRERR_UNSUPPORTED_OPERATION:
7111 errCause = QObject::tr( "Unsupported operation" );
7112 break;
7113 case OGRERR_CORRUPT_DATA:
7114 errCause = QObject::tr( "Corrupt data" );
7115 break;
7116 case OGRERR_FAILURE:
7117 errCause = QObject::tr( "Failure" );
7118 break;
7119 case OGRERR_UNSUPPORTED_SRS:
7120 errCause = QObject::tr( "Unsupported SRS" );
7121 break;
7122 case OGRERR_INVALID_HANDLE:
7123 errCause = QObject::tr( "Invalid handle" );
7124 break;
7125 case OGRERR_NON_EXISTING_FEATURE:
7126 errCause = QObject::tr( "Non existing feature" );
7127 break;
7128 case OGRERR_NONE:
7129 errCause = QObject::tr( "Success" );
7130 break;
7131 }
7132 errCause = QObject::tr( "GDAL result code: %1" ).arg( errCause );
7133 return error == OGRERR_NONE;
7134 }
7135 }
7136 // This should never happen:
7137 errCause = QObject::tr( "Layer not found: %1" ).arg( uri );
7138 return false;
7139 }
7140
operator ()(QgsOgrLayer * layer)7141 void QgsOgrLayerReleaser::operator()( QgsOgrLayer *layer )
7142 {
7143 QgsOgrProviderUtils::release( layer );
7144 }
7145
createTransaction(const QString & connString)7146 QgsTransaction *QgsOgrProviderMetadata::createTransaction( const QString &connString )
7147 {
7148 auto ds = QgsOgrProviderUtils::getAlreadyOpenedDataset( connString );
7149 if ( !ds )
7150 {
7151 QgsMessageLog::logMessage( QObject::tr( "Cannot open transaction on %1, since it is is not currently opened" ).arg( connString ),
7152 QObject::tr( "OGR" ), Qgis::Critical );
7153 return nullptr;
7154 }
7155
7156 return new QgsOgrTransaction( connString, ds );
7157 }
7158
7159 QgsGeoPackageProjectStorage *gGeoPackageProjectStorage = nullptr; // when not null it is owned by QgsApplication::projectStorageRegistry()
7160
initProvider()7161 void QgsOgrProviderMetadata::initProvider()
7162 {
7163 Q_ASSERT( !gGeoPackageProjectStorage );
7164 gGeoPackageProjectStorage = new QgsGeoPackageProjectStorage;
7165 QgsApplication::projectStorageRegistry()->registerProjectStorage( gGeoPackageProjectStorage ); // takes ownership
7166 }
7167
7168
cleanupProvider()7169 void QgsOgrProviderMetadata::cleanupProvider()
7170 {
7171 QgsApplication::projectStorageRegistry()->unregisterProjectStorage( gGeoPackageProjectStorage ); // destroys the object
7172 gGeoPackageProjectStorage = nullptr;
7173 QgsOgrConnPool::cleanupInstance();
7174 // NOTE: QgsApplication takes care of
7175 // calling OGRCleanupAll();
7176 }
7177
7178
7179
QgsOgrProviderMetadata()7180 QgsOgrProviderMetadata::QgsOgrProviderMetadata()
7181 : QgsProviderMetadata( TEXT_PROVIDER_KEY, TEXT_PROVIDER_DESCRIPTION )
7182 {
7183
7184 }
7185
filters(FilterType type)7186 QString QgsOgrProviderMetadata::filters( FilterType type )
7187 {
7188 switch ( type )
7189 {
7190 case QgsProviderMetadata::FilterType::FilterVector:
7191 return QgsOgrProviderUtils::fileVectorFilters();
7192 default:
7193 return QString();
7194 }
7195 }
7196
7197
connections(bool cached)7198 QMap<QString, QgsAbstractProviderConnection *> QgsOgrProviderMetadata::connections( bool cached )
7199 {
7200 return connectionsProtected<QgsGeoPackageProviderConnection, QgsOgrDbConnection>( cached );
7201 }
7202
createConnection(const QString & connName)7203 QgsAbstractProviderConnection *QgsOgrProviderMetadata::createConnection( const QString &connName )
7204 {
7205 return new QgsGeoPackageProviderConnection( connName );
7206 }
7207
createConnection(const QString & uri,const QVariantMap & configuration)7208 QgsAbstractProviderConnection *QgsOgrProviderMetadata::createConnection( const QString &uri, const QVariantMap &configuration )
7209 {
7210 return new QgsGeoPackageProviderConnection( uri, configuration );
7211 }
7212
deleteConnection(const QString & name)7213 void QgsOgrProviderMetadata::deleteConnection( const QString &name )
7214 {
7215 deleteConnectionProtected<QgsGeoPackageProviderConnection>( name );
7216 }
7217
saveConnection(const QgsAbstractProviderConnection * conn,const QString & name)7218 void QgsOgrProviderMetadata::saveConnection( const QgsAbstractProviderConnection *conn, const QString &name )
7219 {
7220 saveConnectionProtected( conn, name );
7221 }
7222
7223 ///@endcond
7224