@@ -48,6 +48,13 @@ email : sherman at mrcc.com
48
48
#include " qgsvectorlayerimport.h"
49
49
#include " qgslocalec.h"
50
50
51
+ #ifdef Q_OS_WIN
52
+ #include < windows.h>
53
+ #endif
54
+ #ifdef Q_OS_LINUX
55
+ #include < sys/vfs.h>
56
+ #endif
57
+
51
58
static const QString TEXT_PROVIDER_KEY = " ogr" ;
52
59
static const QString TEXT_PROVIDER_DESCRIPTION =
53
60
QString ( " OGR data provider" )
@@ -383,11 +390,14 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
383
390
384
391
QgsOgrProvider::~QgsOgrProvider ()
385
392
{
386
- close ();
387
393
QgsOgrConnPool::instance ()->unref ( dataSourceUri () );
388
394
// We must also make sure to flush unusef cached connections so that
389
395
// the file can be removed (#15137)
390
396
QgsOgrConnPool::instance ()->invalidateConnections ( dataSourceUri () );
397
+
398
+ // Do that as last step for final cleanup that might be prevented by
399
+ // still opened datasets.
400
+ close ();
391
401
}
392
402
393
403
QgsAbstractFeatureSource* QgsOgrProvider::featureSource () const
@@ -2627,6 +2637,152 @@ void QgsOgrProvider::forceReload()
2627
2637
QgsOgrConnPool::instance ()->invalidateConnections ( dataSourceUri () );
2628
2638
}
2629
2639
2640
+ OGRDataSourceH QgsOgrUtils::OGROpenWrapper ( const char * pszPath, bool bUpdate, OGRSFDriverH *phDriver )
2641
+ {
2642
+ CPLErrorReset ();
2643
+ OGRSFDriverH hDriver = nullptr ;
2644
+ OGRDataSourceH hDS = OGROpen ( pszPath, bUpdate, &hDriver );
2645
+ if ( phDriver )
2646
+ *phDriver = hDriver;
2647
+ if ( !hDS )
2648
+ return nullptr ;
2649
+ // GDAL < 1.11.5 has a crashing bug with GeoPackage databases with curve geometry
2650
+ // types (https://trac.osgeo.org/gdal/ticket/6558)
2651
+ #if GDAL_VERSION_MAJOR == 1 && GDAL_VERSION_MINOR == 11 && GDAL_VERSION_MACRO < 5
2652
+ const char * pszLastErrorMsg = CPLGetLastErrorMsg ();
2653
+ if ( hDriver == OGRGetDriverByName ( " GPKG" ) &&
2654
+ strstr ( pszLastErrorMsg, " geometry column" ) &&
2655
+ strstr ( pszLastErrorMsg, " of type" ) &&
2656
+ strstr ( pszLastErrorMsg, " ignored" ) )
2657
+ {
2658
+ QgsDebugMsg ( QString ( " Ignoring %1 that is a GeoPackage DB with curve geometries" ).arg ( pszPath ) );
2659
+ OGR_DS_Destroy ( hDS );
2660
+ hDS = nullptr ;
2661
+ }
2662
+ #endif
2663
+ return hDS;
2664
+ }
2665
+
2666
+ static bool IsLocalFile ( const QString& path )
2667
+ {
2668
+ QString dirName ( QFileInfo ( path ).absolutePath () );
2669
+ // Start with the OS specific methods since the QT >= 5.4 method just
2670
+ // return a string and not an enumerated type.
2671
+ #if defined(Q_OS_WIN)
2672
+ if ( dirName.startsWith ( " \\\\ " ) )
2673
+ return false ;
2674
+ if ( dirName.length () >= 3 && dirName[1 ] == ' :' &&
2675
+ ( dirName[2 ] == ' \\ ' || dirName[2 ] == ' /' ) )
2676
+ {
2677
+ dirName.resize ( 3 );
2678
+ return GetDriveType ( dirName.toAscii ().constData () ) != DRIVE_REMOTE;
2679
+ }
2680
+ return true ;
2681
+ #elif defined(Q_OS_LINUX)
2682
+ struct statfs sStatFS ;
2683
+ if ( statfs ( dirName.toAscii ().constData (), &sStatFS ) == 0 )
2684
+ {
2685
+ // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
2686
+ if ( sStatFS .f_type == 0x6969 /* NFS */ ||
2687
+ sStatFS .f_type == 0x517b /* SMB */ ||
2688
+ sStatFS .f_type == 0xff534d42 /* CIFS */ )
2689
+ {
2690
+ return false ;
2691
+ }
2692
+ }
2693
+ return true ;
2694
+ #elif QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
2695
+ QStorageInfo info ( dirName );
2696
+ QString fileSystem ( info.fileSystemType () );
2697
+ QgsDebugMsg ( QString ( " Filesystem for %1 is %2" ).arg ( path ).arg ( fileSystem ) );
2698
+ return path != " nfs" && path != " smbfs" ;
2699
+ #else
2700
+ return true ;
2701
+ #endif
2702
+ }
2703
+
2704
+ void QgsOgrUtils::OGRDestroyWrapper ( OGRDataSourceH ogrDataSource )
2705
+ {
2706
+ if ( !ogrDataSource )
2707
+ return ;
2708
+ OGRSFDriverH ogrDriver = OGR_DS_GetDriver ( ogrDataSource );
2709
+ QString ogrDriverName = OGR_Dr_GetName ( ogrDriver );
2710
+ QString datasetName ( FROM8 ( OGR_DS_GetName ( ogrDataSource ) ) );
2711
+ if ( ogrDriverName == " GPKG" &&
2712
+ IsLocalFile ( datasetName ) &&
2713
+ !CPLGetConfigOption ( " OGR_SQLITE_JOURNAL" , NULL ) )
2714
+ {
2715
+ // We need to reset all iterators on layers, otherwise we will not
2716
+ // be able to change journal_mode.
2717
+ int layerCount = OGR_DS_GetLayerCount ( ogrDataSource );
2718
+ for ( int i = 0 ; i < layerCount; i ++ )
2719
+ {
2720
+ OGR_L_ResetReading ( OGR_DS_GetLayer ( ogrDataSource, i ) );
2721
+ }
2722
+
2723
+ CPLPushErrorHandler ( CPLQuietErrorHandler );
2724
+ QgsDebugMsg ( " GPKG: Trying to return to delete mode" );
2725
+ bool bSuccess = false ;
2726
+ OGRLayerH hSqlLyr = OGR_DS_ExecuteSQL ( ogrDataSource,
2727
+ " PRAGMA journal_mode = delete" ,
2728
+ NULL , NULL );
2729
+ if ( hSqlLyr != NULL )
2730
+ {
2731
+ OGRFeatureH hFeat = OGR_L_GetNextFeature ( hSqlLyr );
2732
+ if ( hFeat != NULL )
2733
+ {
2734
+ const char * pszRet = OGR_F_GetFieldAsString ( hFeat, 0 );
2735
+ bSuccess = EQUAL ( pszRet, " delete" );
2736
+ QgsDebugMsg ( QString ( " Return: %1" ).arg ( pszRet ) );
2737
+ OGR_F_Destroy ( hFeat );
2738
+ }
2739
+ }
2740
+ else if ( CPLGetLastErrorType () != CE_None )
2741
+ {
2742
+ QgsDebugMsg ( QString ( " Return: %1" ).arg ( CPLGetLastErrorMsg () ) );
2743
+ }
2744
+ OGR_DS_ReleaseResultSet ( ogrDataSource, hSqlLyr );
2745
+ CPLPopErrorHandler ();
2746
+ OGR_DS_Destroy ( ogrDataSource );
2747
+
2748
+ // This may have not worked if the file was opened in read-only mode,
2749
+ // so retry in update mode
2750
+ if ( !bSuccess )
2751
+ {
2752
+ QgsDebugMsg ( " GPKG: Trying again" );
2753
+ CPLSetThreadLocalConfigOption ( " OGR_SQLITE_JOURNAL" , " DELETE" );
2754
+ ogrDataSource = OGROpen ( TO8F ( datasetName ), TRUE , NULL );
2755
+ CPLSetThreadLocalConfigOption ( " OGR_SQLITE_JOURNAL" , NULL );
2756
+ if ( ogrDataSource )
2757
+ {
2758
+ #ifdef QGISDEBUG
2759
+ CPLPushErrorHandler ( CPLQuietErrorHandler );
2760
+ OGRLayerH hSqlLyr = OGR_DS_ExecuteSQL ( ogrDataSource,
2761
+ " PRAGMA journal_mode" ,
2762
+ NULL , NULL );
2763
+ CPLPopErrorHandler ();
2764
+ if ( hSqlLyr != NULL )
2765
+ {
2766
+ OGRFeatureH hFeat = OGR_L_GetNextFeature ( hSqlLyr );
2767
+ if ( hFeat != NULL )
2768
+ {
2769
+ const char * pszRet = OGR_F_GetFieldAsString ( hFeat, 0 );
2770
+ QgsDebugMsg ( QString ( " Return: %1" ).arg ( pszRet ) );
2771
+ OGR_F_Destroy ( hFeat );
2772
+ }
2773
+ OGR_DS_ReleaseResultSet ( ogrDataSource, hSqlLyr );
2774
+ }
2775
+ #endif
2776
+ OGR_DS_Destroy ( ogrDataSource );
2777
+ }
2778
+ }
2779
+ }
2780
+ else
2781
+ {
2782
+ OGR_DS_Destroy ( ogrDataSource );
2783
+ }
2784
+ }
2785
+
2630
2786
QByteArray QgsOgrUtils::quotedIdentifier ( QByteArray field, const QString& ogrDriverName )
2631
2787
{
2632
2788
if ( ogrDriverName == " MySQL" )
@@ -2855,7 +3011,22 @@ void QgsOgrProvider::open( OpenMode mode )
2855
3011
2856
3012
// first try to open in update mode (unless specified otherwise)
2857
3013
if ( !openReadOnly )
2858
- ogrDataSource = OGROpen ( TO8F ( mFilePath ), true , &ogrDriver );
3014
+ {
3015
+ if ( QFileInfo ( mFilePath ).suffix ().compare ( " gpkg" , Qt::CaseInsensitive ) == 0 &&
3016
+ IsLocalFile ( mFilePath ) &&
3017
+ !CPLGetConfigOption ( " OGR_SQLITE_JOURNAL" , NULL ) &&
3018
+ QSettings ().value ( " /qgis/walForSqlite3" , true ).toBool () )
3019
+ {
3020
+ // For GeoPackage, we force opening of the file in WAL (Write Ahead Log)
3021
+ // mode so as to avoid readers blocking writer(s), and vice-versa.
3022
+ // https://www.sqlite.org/wal.html
3023
+ // But only do that on a local file since WAL is advertized not to work
3024
+ // on network shares
3025
+ CPLSetThreadLocalConfigOption ( " OGR_SQLITE_JOURNAL" , " WAL" );
3026
+ }
3027
+ ogrDataSource = QgsOgrUtils::OGROpenWrapper ( TO8F ( mFilePath ), true , &ogrDriver );
3028
+ CPLSetThreadLocalConfigOption ( " OGR_SQLITE_JOURNAL" , NULL );
3029
+ }
2859
3030
2860
3031
mValid = false ;
2861
3032
if ( ogrDataSource )
@@ -2985,7 +3156,7 @@ void QgsOgrProvider::close()
2985
3156
2986
3157
if ( ogrDataSource )
2987
3158
{
2988
- OGR_DS_Destroy ( ogrDataSource );
3159
+ QgsOgrUtils::OGRDestroyWrapper ( ogrDataSource );
2989
3160
}
2990
3161
ogrDataSource = nullptr ;
2991
3162
ogrLayer = nullptr ;
1 commit comments
nirvn commentedon Jan 15, 2018
@rouault , this has created a side issue: when adding a geopackage layer (or opening a pre-existing project) simply to display its content, the modified date for the geopackage file is modified. This is somewhat problematic for users who actually use this file system attribute to keep track of when a dataset was actually last modified.
Is there a way to fix that side issue?