Ошибка «база данных заблокирована» при попытке записи в базу данных GeoPackage - PullRequest
0 голосов
/ 05 июня 2019

Я использую управляемый API OGR / GDAL C # для записи в базу данных GeoPackage и продолжаю сталкиваться с ошибкой «база данных заблокирована» при попытке синхронизировать базу данных с диском.После долгих экспериментов это может произойти, только если я сначала открою GeoPackage для чтения, закрою базу данных (Dispose the DataSource), снова открою базу данных и попытаюсь записать.Поиски в Интернете показывают, что эта ошибка возникает, если объекты GDAL и SQLite из чтения не удаляются перед попыткой записи.Тем не менее, я думаю, что удаляю все управляемые объекты должным образом, поэтому мне интересно, есть ли вызов, который я пропускаю.Любая помощь будет принята с благодарностью.Я включил пример консольного приложения, которое показывает ошибку, которую я получаю.Это создает новый слой, но я получаю ту же ошибку, если я пытаюсь обновить существующий слой.Я использую GeoPackage v1.2, GDAL v2.3 и SQLite v3.21.0 для Windows.

Пример консольного приложения:

using System;
using System.Collections.Generic;
using OSGeo.OGR;
namespace GeoPackageWriteTest
{
  class GeoPackageTest
  {
    public static void usage()
    {
      Console.WriteLine( "usage (to create a new layer):" );
      Console.WriteLine( "gpkgWriteTest {gpkg filename} {layername}" );
      System.Environment.Exit( 0 );
    }
    static void Main( string[] args )
    {
      if ( args.Length < 2 )
        usage();

      Ogr.RegisterAll();

      // simulate an object opening a geopackage db to do a simple read
      Console.WriteLine( "Opening database readonly: " + args[0] );
      DataSource dataSource1 = Ogr.OpenShared( args[0], 0 ); // open readonly
      if ( dataSource1 == null )
        throw new ArgumentException( string.Format( "Error opening GeoPackage database: {0}", args[0] ) );
      using ( dataSource1 )
      {
        Driver driver = dataSource1.GetDriver();
        if ( driver == null )
          throw new ArgumentException( "Error getting GDAL driver!" );
        Console.WriteLine( "Using driver: " + driver.name );

        string pfile = GetParameterTableName( dataSource1 );
        Console.WriteLine( "Parameter table: " + pfile );
      } // DataSource gets disposed and geopackage is closed
      dataSource1 = null;

      // make sure all Disposed objects are garbage collected
      // (just trying to see if it makes a difference, it doesn't)
      GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced, true, true );
      GC.WaitForPendingFinalizers();

      // simulate another object reopening a geopackage db to do a simple write
      Console.WriteLine( "Opening database readwrite: " + args[0] );
      DataSource dataSource2 = Ogr.OpenShared( args[0], 1 );  // reopen readwrite
      if ( dataSource2 == null )
        throw new ArgumentException( string.Format( "Error opening GeoPackage database: {0}", args[0] ) );
      using ( dataSource2 )
      {
        List<string> options = new List<string>();
        options.Add( "SPATIAL_INDEX=YES" );

        Console.WriteLine( "Creating layer: " + args[1] );
        Layer tableLayer = dataSource2.CreateLayer( args[1], null, wkbGeometryType.wkbNone, options.ToArray() );
        if ( tableLayer == null )
          throw new ArgumentException( string.Format( "Error creating layer: {0}", args[2] ) );

        int rc = Ogr.OGRERR_NONE;
        int approx_ok = 1;
        int fieldCount = 3;
        for ( int i = 0; i < fieldCount; i++ )
        {
          //FieldDefn fieldDefn = GetFieldDefn( args[n], args[n + 1] );
          string fname = string.Format( "TestField{0}", i );
          FieldDefn fieldDefn = GetFieldDefn( fname, "int32" );
          rc = tableLayer.CreateField( fieldDefn, approx_ok );
          if ( rc != Ogr.OGRERR_NONE )
            throw new ArgumentException( string.Format( "Error creating field: {0}", fname ) );
        }
        // write changes to disk
        Console.WriteLine( "Writing changes to disk..." );
        dataSource2.FlushCache(); // this throws database locked error
      }
    }
    static string GetParameterTableName( DataSource ogrDataSource )
    {
      try
      {
        string s = string.Empty;
        string sql = "select * from sqlite_master where type='table' and name like '%Parameter%'";
        OSGeo.OGR.Layer layer = ogrDataSource.ExecuteSQL( sql, null, "SQLITE" );
        using ( layer )
        {
          layer.ResetReading();
          OSGeo.OGR.Feature fea = layer.GetNextFeature();
          if ( fea != null )
          {
            s = fea.GetFieldAsString( "name" );
          }
        }
        return s;
      }
      catch ( Exception )
      {
        return string.Empty;
      }
    }
    private static FieldDefn GetFieldDefn( string fldName, string fldType )
    {
      //OSGeo.OGR.FieldType fieldType = GetOGRFieldType( fldType );
      OSGeo.OGR.FieldType fieldType = FieldType.OFTInteger;
      OSGeo.OGR.FieldSubType subType = FieldSubType.OFSTNone;

      FieldDefn fieldDefn = new FieldDefn( fldName, fieldType );
      fieldDefn.SetSubType( subType );
      return fieldDefn;
    }
  }
}

1 Ответ

0 голосов
/ 10 июня 2019

На всякий случай кому-нибудь было интересно. Проблема заключалась в том, как я читал слой набора результатов в первый раз. Я только прочитал 1 особенность (поскольку это было все, что меня интересовало). Очевидно, это оставляет набор результатов в состоянии занятости. Мне нужно было либо прочитать весь слой, т.е.

      OSGeo.OGR.Feature fea = layer.GetNextFeature();
      while ( fea != null )
      {
        s = fea.GetFieldAsString( "name" );
        layer.GetNextFeature();
      }

Или мне нужно было сбросить слой после чтения, т.е.

      OSGeo.OGR.Feature fea = layer.GetNextFeature();
      if ( fea != null )
      {
        s = fea.GetFieldAsString( "name" );
      }
      layer.ResetReading();
...