in

System.Data.SQLite

An open source ADO.NET provider for the SQLite database engine

Using the Backup API

Last post 02-20-2012 2:58 PM by jakublinhart. 5 replies.
Page 1 of 1 (6 items)
Sort Posts: Previous Next
  • 06-10-2010 4:18 PM

    Using the Backup API

    If you're here, you've realized that System.Data.SQLite does not support the SQLite Online Backup API. If you absolutely can't wait until it is supported, I've found a solution.

    If you want to copy a .db3 file to another .db3 file, you should either use the copy file API of your operating system, or RogerIrwin's method. Use the following method only if you need to back up your in-memory database and you like kludgey solutions.

    This will require downloading the System.Data.SQLite source, modifying it, and recompiling. Basically, you need to be able to access the _sql property of the SQLite3.cs class.

    Make the following classes public:
    • SQLite3
    • SQLiteBase
    • SQLiteConnection
    • SQLiteConvert
    • CriticalHandle and SQLiteConnectionHandle in UnsafeNativeMethods.cs

    Make a public accessor for the _sql property of the SQLiteConnection class. This will allow you to retrieve the SQLite3 object created with your connection.

    Make a public accessor for the _sql property of the SQLite3 class. Mine returns an IntPtr instead of a SQLiteConnectionHandle so it doesn't have to be converted later. This allows you to get the handle of your database to use in the API.

    When you compile, make sure to use Release mode, and select the platform you are using. The dll that is created should be closer to 800kB than 100kB.

    Now, in your project you need to link some external functions manually.

    [DllImport("System.Data.SQLite.DLL", CharSet = CharSet.Auto)]
    internal static extern int sqlite3_open(byte[] filename, out IntPtr db);

    [DllImport("System.Data.SQLite.DLL", CharSet = CharSet.Auto)]
    internal static extern int sqlite3_close(IntPtr db);

    [DllImport("System.Data.SQLite.DLL", CharSet = CharSet.Auto)]
    internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[] destname, IntPtr srcDB, byte[] srcname);

    [DllImport("System.Data.SQLite.DLL", CharSet = CharSet.Auto)]
    internal static extern int sqlite3_backup_step(IntPtr backup, int pages);

    [DllImport("System.Data.SQLite.DLL", CharSet = CharSet.Auto)]
    internal static extern int sqlite3_backup_finish(IntPtr backup);

    And here is my method to do the backup.

    /// summary
    /// Backs up the in-memory database to the specified file.
    /// /summary
    /// param name="filename" The filename. /param
    /// returns 0 if successful; otherwise, an SQLite error code. /returns
    public int BackupDatabaseToFile(string filename)
    {
      IntPtr backup = new IntPtr();
      IntPtr destDB = new IntPtr();
      IntPtr memDB = new IntPtr();
      int returnCode;
      var encoding = new System.Text.UTF8Encoding();

      byte[] destName = encoding.GetBytes(filename);

      //get the handle to the memory database
      IDbConnection iconn = ConnectionProvider.GetConnection();
      SQLiteConnection conn = (SQLiteConnection)iconn;
      SQLite3 database = conn.Sql as SQLite3;
      memDB = database.Sql;

      //the rest is similar to the SQLite C examples
      //open the file for writing
      returnCode = sqlite3_open(destName, out destDB);
      if (returnCode == 0)
      {
       //begin the backup
       backup = sqlite3_backup_init(destDB, encoding.GetBytes("main"), memDB,
        encoding.GetBytes("main"));
       if (backup != IntPtr.Zero)
       {
        sqlite3_backup_step(backup, -1);
        sqlite3_backup_finish(backup);
       }
       returnCode = sqlite3_errcode(destDB);
      }
      //close the file, but not the memory database
      sqlite3_close(destDB);
      return returnCode;
    }

    Note: Use this at your own risk. There is a reason I'm not submitting this as a patch - it's not consistent with the framework and may be insecure.
    Filed under: , , , ,
  • 07-11-2011 12:49 PM In reply to

    Re: Using the Backup API

    I am trying to do this with 1.0.66.0 and am having trouble seeing my public get properties for _sql in SQLite3 and SQLiteConnection. (getting MethodNotFoundException for SQLiteConnection.get_Sql()). Any ideas on what I'm doing wrong?
  • 07-11-2011 3:49 PM In reply to

    Re: Using the Backup API

    I haven't looked at this code for a year, but I'd make sure both your classes and get_Sql methods are set to public. Further debugging would be similar to any other project. Check that the return value of the method is the type you're using. Also check your namespaces, whether it's static, etc.

    In my example, I called my accessor SQLiteConnection.Sql instead of get_Sql, if that helps. I don't recall what version of this project I was using.

    By the way, I never mentioned why I went through this trouble in the first place. This setup allows you to have an in-memory SQLite database that you can back up in case of crashes.

  • 12-30-2011 5:22 AM In reply to

    Re: Using the Backup API

     You do not need to change System.Data.SQLite source code, use reflection instead:).

    [DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
    internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[ destname, IntPtr srcDB, byte[ srcname);
     
    [DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int sqlite3_backup_step(IntPtr backup, int pages);
     
    [DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int sqlite3_backup_finish(IntPtr backup);
     
    public static void Backup(SQLiteConnection source, SQLiteConnection destination)
    {
        IntPtr sourceHandle = GetConnectionHandle(source);
        IntPtr destinationHandle = GetConnectionHandle(destination);
     
        IntPtr backupHandle = sqlite3_backup_init(destinationHandle, SQLiteConvert.ToUTF8("main"), sourceHandle, SQLiteConvert.ToUTF8("main"));
        sqlite3_backup_step(backupHandle, -1);
        sqlite3_backup_finish(backupHandle);
     
    }
     
    private static IntPtr GetConnectionHandle(SQLiteConnection source)
    {
        object sqlLite3 = GetPrivateFieldValue(source, "_sql");
        object connectionHandle = GetPrivateFieldValue(sqlLite3, "_sql");
        IntPtr handle = (IntPtr)GetPrivateFieldValue(connectionHandle, "handle");
     
        return handle;
    }
     
    private static object GetPrivateFieldValue(object instance, string fieldName)
    {
        var filedType = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
     
        object result = filedType.GetValue(instance);
        return result;
    }
     

     

  • 02-01-2012 10:15 AM In reply to

    Re: Using the Backup API

    Jakub, Using your code I get: --- System.EntryPointNotFoundException : Unable to find an entry point named 'sqlite3_backup_init' in DLL 'System.Data.SQLite.DLL'. --- Is a certain version of System.Data.Sqlite required? Also, I could not find any help on the markup this forum used, so sorry about formatting.
  • 02-20-2012 2:58 PM In reply to

    Re: Using the Backup API

    Latest version (1.0.79.0) of System.Data.SQLite works well but the former example was tested on version 1.0.65.0. Which version do you use?

Page 1 of 1 (6 items)
Powered by Community Server (Commercial Edition), by Telligent Systems