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