System.Data.SQLite
Check-in [6eac31ab88]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
SHA1 Hash:6eac31ab88f55b09f9ce52889973ddd8e8c25e63
Date: 2012-03-24 10:05:13
User: mistachkin
Comment:Start of work on ticket [c71846ed57], supporting the SQLite online backup API.
Tags And Properties
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to System.Data.SQLite/SQLite3.cs

  1365   1365       internal override int SetLogCallback(SQLiteLogCallback func)
  1366   1366       {
  1367   1367           int rc = UnsafeNativeMethods.sqlite3_config(
  1368   1368               (int)SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, (IntPtr)0);
  1369   1369   
  1370   1370           return rc;
  1371   1371       }
         1372  +
         1373  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         1374  +
         1375  +    /// <summary>
         1376  +    /// Creates a new SQLite backup object based on the provided destination
         1377  +    /// database connection.  The source database connection is the one
         1378  +    /// associated with this object.  The source and destination database
         1379  +    /// connections cannot be the same.
         1380  +    /// </summary>
         1381  +    /// <param name="destCnn">The destination database connection handle.</param>
         1382  +    /// <param name="destName">The destination database name.</param>
         1383  +    /// <param name="sourceName">The source database name.</param>
         1384  +    /// <returns>The newly created backup object.</returns>
         1385  +    internal override SQLiteBackup InitializeBackup(
         1386  +        SQLiteConnectionHandle destCnn,
         1387  +        string destName,
         1388  +        string sourceName
         1389  +        )
         1390  +    {
         1391  +        if (destCnn == null)
         1392  +            throw new ArgumentNullException("destCnn");
         1393  +
         1394  +        if (destName == null)
         1395  +            throw new ArgumentNullException("destName");
         1396  +
         1397  +        if (sourceName == null)
         1398  +            throw new ArgumentNullException("sourceName");
         1399  +
         1400  +        byte[] zDestName = ToUTF8(destName);
         1401  +        byte[] zSourceName = ToUTF8(sourceName);
         1402  +
         1403  +        IntPtr backup = UnsafeNativeMethods.sqlite3_backup_init(
         1404  +            destCnn, zDestName, _sql, zSourceName);
         1405  +
         1406  +        if (backup == IntPtr.Zero)
         1407  +            throw new SQLiteException(ResultCode(), SQLiteLastError());
         1408  +
         1409  +        return new SQLiteBackup(this, backup, destCnn, zDestName, _sql,
         1410  +            zSourceName);
         1411  +    }
         1412  +
         1413  +    /// <summary>
         1414  +    /// Copies up to N pages from the source database to the destination
         1415  +    /// database associated with the specified backup object.
         1416  +    /// </summary>
         1417  +    /// <param name="backup">The backup object to use.</param>
         1418  +    /// <param name="nPage">
         1419  +    /// The number of pages to copy, negative to copy all remaining pages.
         1420  +    /// </param>
         1421  +    /// <returns>
         1422  +    /// True if there are more pages to be copied, false otherwise.
         1423  +    /// </returns>
         1424  +    internal override bool StepBackup(
         1425  +        SQLiteBackup backup,
         1426  +        int nPage
         1427  +        )
         1428  +    {
         1429  +        if (backup == null)
         1430  +            throw new ArgumentNullException("backup");
         1431  +
         1432  +        SQLiteBackupHandle handle = backup._sqlite_backup;
         1433  +
         1434  +        if (handle == null)
         1435  +            throw new InvalidOperationException(
         1436  +                "Backup object has an invalid handle.");
         1437  +
         1438  +        int n = UnsafeNativeMethods.sqlite3_backup_step(handle, nPage);
         1439  +        backup._stepResult = n; /* NOTE: Save for use by FinishBackup. */
         1440  +
         1441  +        if (n == (int)SQLiteErrorCode.Ok)
         1442  +            return true;
         1443  +        else if (n == (int)SQLiteErrorCode.Done)
         1444  +            return false;
         1445  +        else
         1446  +            throw new SQLiteException(n, SQLiteLastError());
         1447  +    }
         1448  +
         1449  +    /// <summary>
         1450  +    /// Returns the number of pages remaining to be copied from the source
         1451  +    /// database to the destination database associated with the specified
         1452  +    /// backup object.
         1453  +    /// </summary>
         1454  +    /// <param name="backup">The backup object to check.</param>
         1455  +    /// <returns>The number of pages remaining to be copied.</returns>
         1456  +    internal override int RemainingBackup(
         1457  +        SQLiteBackup backup
         1458  +        )
         1459  +    {
         1460  +        if (backup == null)
         1461  +            throw new ArgumentNullException("backup");
         1462  +
         1463  +        SQLiteBackupHandle handle = backup._sqlite_backup;
         1464  +
         1465  +        if (handle == null)
         1466  +            throw new InvalidOperationException(
         1467  +                "Backup object has an invalid handle.");
         1468  +
         1469  +        return UnsafeNativeMethods.sqlite3_backup_remaining(handle);
         1470  +    }
         1471  +
         1472  +    /// <summary>
         1473  +    /// Returns the total number of pages in the source database associated
         1474  +    /// with the specified backup object.
         1475  +    /// </summary>
         1476  +    /// <param name="backup">The backup object to check.</param>
         1477  +    /// <returns>The total number of pages in the source database.</returns>
         1478  +    internal override int PageCountBackup(
         1479  +        SQLiteBackup backup
         1480  +        )
         1481  +    {
         1482  +        if (backup == null)
         1483  +            throw new ArgumentNullException("backup");
         1484  +
         1485  +        SQLiteBackupHandle handle = backup._sqlite_backup;
         1486  +
         1487  +        if (handle == null)
         1488  +            throw new InvalidOperationException(
         1489  +                "Backup object has an invalid handle.");
         1490  +
         1491  +        return UnsafeNativeMethods.sqlite3_backup_pagecount(handle);
         1492  +    }
         1493  +
         1494  +    /// <summary>
         1495  +    /// Destroys the backup object, rolling back any backup that may be in
         1496  +    /// progess.
         1497  +    /// </summary>
         1498  +    /// <param name="backup">The backup object to destroy.</param>
         1499  +    internal override void FinishBackup(
         1500  +        SQLiteBackup backup
         1501  +        )
         1502  +    {
         1503  +        if (backup == null)
         1504  +            throw new ArgumentNullException("backup");
         1505  +
         1506  +        SQLiteBackupHandle handle = backup._sqlite_backup;
         1507  +
         1508  +        if (handle == null)
         1509  +            throw new InvalidOperationException(
         1510  +                "Backup object has an invalid handle.");
         1511  +
         1512  +        int n = UnsafeNativeMethods.sqlite3_backup_finish(handle);
         1513  +
         1514  +        if (n > 0 && n != backup._stepResult)
         1515  +            throw new SQLiteException(n, SQLiteLastError());
         1516  +    }
         1517  +
         1518  +    ///////////////////////////////////////////////////////////////////////////////////////////////
  1372   1519   
  1373   1520       /// <summary>
  1374   1521       /// Determines if the SQLite core library has been initialized for the
  1375   1522       /// current process.
  1376   1523       /// </summary>
  1377   1524       /// <returns>
  1378   1525       /// A boolean indicating whether or not the SQLite core library has been

Added System.Data.SQLite/SQLiteBackup.cs

            1  +/********************************************************
            2  + * ADO.NET 2.0 Data Provider for SQLite Version 3.X
            3  + * Written by Robert Simpson (robert@blackcastlesoft.com)
            4  + * 
            5  + * Released to the public domain, use at your own risk!
            6  + ********************************************************/
            7  +
            8  +namespace System.Data.SQLite
            9  +{
           10  +    using System;
           11  +
           12  +    /// <summary>
           13  +    /// Represents a single SQL backup in SQLite.
           14  +    /// </summary>
           15  +    internal sealed class SQLiteBackup : IDisposable
           16  +    {
           17  +        /// <summary>
           18  +        /// The underlying SQLite object this backup is bound to.
           19  +        /// </summary>
           20  +        internal SQLiteBase _sql;
           21  +
           22  +        /// <summary>
           23  +        /// The actual backup handle.
           24  +        /// </summary>
           25  +        internal SQLiteBackupHandle _sqlite_backup;
           26  +
           27  +        /// <summary>
           28  +        /// The destination database for the backup.
           29  +        /// </summary>
           30  +        internal IntPtr _destDb;
           31  +
           32  +        /// <summary>
           33  +        /// The destination database name for the backup.
           34  +        /// </summary>
           35  +        internal byte[] _zDestName;
           36  +
           37  +        /// <summary>
           38  +        /// The source database for the backup.
           39  +        /// </summary>
           40  +        internal IntPtr _sourceDb;
           41  +
           42  +        /// <summary>
           43  +        /// The source database name for the backup.
           44  +        /// </summary>
           45  +        internal byte[] _zSourceName;
           46  +
           47  +        /// <summary>
           48  +        /// The last result from the StepBackup method of the SQLite3 class.
           49  +        /// This is used to determine if the call to the FinishBackup method of
           50  +        /// the SQLite3 class should throw an exception when it receives a non-Ok
           51  +        /// return code from the core SQLite library.
           52  +        /// </summary>
           53  +        internal int _stepResult;
           54  +
           55  +        /// <summary>
           56  +        /// Initializes the backup.
           57  +        /// </summary>
           58  +        /// <param name="sqlbase">The base SQLite object.</param>
           59  +        /// <param name="backup">The backup handle.</param>
           60  +        /// <param name="destDb">The destination database for the backup.</param>
           61  +        /// <param name="zDestName">The destination database name for the backup.</param>
           62  +        /// <param name="sourceDb">The source database for the backup.</param>
           63  +        /// <param name="zSourceName">The source database name for the backup.</param>
           64  +        internal SQLiteBackup(
           65  +            SQLiteBase sqlbase,
           66  +            SQLiteBackupHandle backup,
           67  +            IntPtr destDb,
           68  +            byte[] zDestName,
           69  +            IntPtr sourceDb,
           70  +            byte[] zSourceName
           71  +            )
           72  +        {
           73  +            _sql = sqlbase;
           74  +            _sqlite_backup = backup;
           75  +            _destDb = destDb;
           76  +            _zDestName = zDestName;
           77  +            _sourceDb = sourceDb;
           78  +            _zSourceName = zSourceName;
           79  +        }
           80  +
           81  +        ///////////////////////////////////////////////////////////////////////////////////////////////
           82  +
           83  +        #region IDisposable Members
           84  +        /// <summary>
           85  +        /// Disposes and finalizes the backup.
           86  +        /// </summary>
           87  +        public void Dispose()
           88  +        {
           89  +            Dispose(true);
           90  +            GC.SuppressFinalize(this);
           91  +        }
           92  +        #endregion
           93  +
           94  +        ///////////////////////////////////////////////////////////////////////////////////////////////
           95  +
           96  +        #region IDisposable "Pattern" Members
           97  +        private bool disposed;
           98  +        private void CheckDisposed() /* throw */
           99  +        {
          100  +#if THROW_ON_DISPOSED
          101  +        if (disposed)
          102  +            throw new ObjectDisposedException(typeof(SQLiteBackup).Name);
          103  +#endif
          104  +        }
          105  +
          106  +        ///////////////////////////////////////////////////////////////////////////////////////////////
          107  +
          108  +        private void Dispose(bool disposing)
          109  +        {
          110  +            if (!disposed)
          111  +            {
          112  +                if (disposing)
          113  +                {
          114  +                    ////////////////////////////////////
          115  +                    // dispose managed resources here...
          116  +                    ////////////////////////////////////
          117  +
          118  +                    if (_sqlite_backup != null)
          119  +                    {
          120  +                        _sqlite_backup.Dispose();
          121  +                        _sqlite_backup = null;
          122  +                    }
          123  +
          124  +                    _zSourceName = null;
          125  +                    _sourceDb = IntPtr.Zero;
          126  +                    _zDestName = null;
          127  +                    _destDb = IntPtr.Zero;
          128  +                    _sql = null;
          129  +                }
          130  +
          131  +                //////////////////////////////////////
          132  +                // release unmanaged resources here...
          133  +                //////////////////////////////////////
          134  +
          135  +                disposed = true;
          136  +            }
          137  +        }
          138  +        #endregion
          139  +
          140  +        ///////////////////////////////////////////////////////////////////////////////////////////////
          141  +
          142  +        #region Destructor
          143  +        ~SQLiteBackup()
          144  +        {
          145  +            Dispose(false);
          146  +        }
          147  +        #endregion
          148  +    }
          149  +}

Changes to System.Data.SQLite/SQLiteBase.cs

   219    219       internal abstract bool AutoCommit
   220    220       {
   221    221         get;
   222    222       }
   223    223   
   224    224       internal abstract int FileControl(string zDbName, int op, IntPtr pArg);
   225    225   
          226  +    /// <summary>
          227  +    /// Creates a new SQLite backup object based on the provided destination
          228  +    /// database connection.  The source database connection is the one
          229  +    /// associated with this object.  The source and destination database
          230  +    /// connections cannot be the same.
          231  +    /// </summary>
          232  +    /// <param name="destCnn">The destination database connection handle.</param>
          233  +    /// <param name="destName">The destination database name.</param>
          234  +    /// <param name="sourceName">The source database name.</param>
          235  +    /// <returns>The newly created backup object.</returns>
          236  +    internal abstract SQLiteBackup InitializeBackup(
          237  +        SQLiteConnectionHandle destCnn, string destName,
          238  +        string sourceName);
          239  +
          240  +    /// <summary>
          241  +    /// Copies up to N pages from the source database to the destination
          242  +    /// database associated with the specified backup object.
          243  +    /// </summary>
          244  +    /// <param name="backup">The backup object to use.</param>
          245  +    /// <param name="nPage">
          246  +    /// The number of pages to copy, negative to copy all remaining pages.
          247  +    /// </param>
          248  +    /// <returns>
          249  +    /// True if there are more pages to be copied, false otherwise.
          250  +    /// </returns>
          251  +    internal abstract bool StepBackup(SQLiteBackup backup, int nPage);
          252  +
          253  +    /// <summary>
          254  +    /// Returns the number of pages remaining to be copied from the source
          255  +    /// database to the destination database associated with the specified
          256  +    /// backup object.
          257  +    /// </summary>
          258  +    /// <param name="backup">The backup object to check.</param>
          259  +    /// <returns>The number of pages remaining to be copied.</returns>
          260  +    internal abstract int RemainingBackup(SQLiteBackup backup);
          261  +
          262  +    /// <summary>
          263  +    /// Returns the total number of pages in the source database associated
          264  +    /// with the specified backup object.
          265  +    /// </summary>
          266  +    /// <param name="backup">The backup object to check.</param>
          267  +    /// <returns>The total number of pages in the source database.</returns>
          268  +    internal abstract int PageCountBackup(SQLiteBackup backup);
          269  +
          270  +    /// <summary>
          271  +    /// Destroys the backup object, rolling back any backup that may be in
          272  +    /// progess.
          273  +    /// </summary>
          274  +    /// <param name="backup">The backup object to destroy.</param>
          275  +    internal abstract void FinishBackup(SQLiteBackup backup);
          276  +
   226    277       ///////////////////////////////////////////////////////////////////////////////////////////////
   227    278   
   228    279       #region IDisposable Members
   229    280       public void Dispose()
   230    281       {
   231    282           Dispose(true);
   232    283           GC.SuppressFinalize(this);
................................................................................
   288    339   #if !SQLITE_STANDARD
   289    340         int len;
   290    341         return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg_interop(db, out len), len);
   291    342   #else
   292    343         return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg(db), -1);
   293    344   #endif
   294    345       }
          346  +
          347  +    internal static void FinishBackup(SQLiteBackupHandle backup)
          348  +    {
          349  +        lock (_lock)
          350  +        {
          351  +            int n = UnsafeNativeMethods.sqlite3_backup_finish(backup);
          352  +            if (n > 0) throw new SQLiteException(n, null);
          353  +        }
          354  +    }
   295    355   
   296    356       internal static void FinalizeStatement(SQLiteStatementHandle stmt)
   297    357       {
   298    358         lock (_lock)
   299    359         {
   300    360   #if !SQLITE_STANDARD
   301    361           int n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt);

Changes to System.Data.SQLite/System.Data.SQLite.Files.targets

    13     13     ******************************************************************************
    14     14     -->
    15     15   
    16     16     <ItemGroup>
    17     17       <Compile Include="AssemblyInfo.cs" />
    18     18       <Compile Include="SQLite3.cs" />
    19     19       <Compile Include="SQLite3_UTF16.cs" />
           20  +    <Compile Include="SQLiteBackup.cs" />
    20     21       <Compile Include="SQLiteBase.cs" />
    21     22       <Compile Include="SQLiteCommand.cs">
    22     23         <SubType>Component</SubType>
    23     24       </Compile>
    24     25       <Compile Include="SQLiteCommandBuilder.cs">
    25     26         <SubType>Component</SubType>
    26     27       </Compile>

Changes to System.Data.SQLite/UnsafeNativeMethods.cs

  1231   1231   #if !PLATFORM_COMPACTFRAMEWORK
  1232   1232       [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
  1233   1233   #else
  1234   1234       [DllImport(SQLITE_DLL)]
  1235   1235   #endif
  1236   1236       internal static extern int sqlite3_file_control(IntPtr db, byte[] zDbName, int op, IntPtr pArg);
  1237   1237   
         1238  +#if !PLATFORM_COMPACTFRAMEWORK
         1239  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         1240  +#else
         1241  +    [DllImport(SQLITE_DLL)]
         1242  +#endif
         1243  +    internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[] zDestName, IntPtr sourceDb, byte[] zSourceName);
         1244  +
         1245  +#if !PLATFORM_COMPACTFRAMEWORK
         1246  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         1247  +#else
         1248  +    [DllImport(SQLITE_DLL)]
         1249  +#endif
         1250  +    internal static extern int sqlite3_backup_step(IntPtr backup, int nPage);
         1251  +
         1252  +#if !PLATFORM_COMPACTFRAMEWORK
         1253  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         1254  +#else
         1255  +    [DllImport(SQLITE_DLL)]
         1256  +#endif
         1257  +    internal static extern int sqlite3_backup_finish(IntPtr backup);
         1258  +
         1259  +#if !PLATFORM_COMPACTFRAMEWORK
         1260  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         1261  +#else
         1262  +    [DllImport(SQLITE_DLL)]
         1263  +#endif
         1264  +    internal static extern int sqlite3_backup_remaining(IntPtr backup);
         1265  +
         1266  +#if !PLATFORM_COMPACTFRAMEWORK
         1267  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         1268  +#else
         1269  +    [DllImport(SQLITE_DLL)]
         1270  +#endif
         1271  +    internal static extern int sqlite3_backup_pagecount(IntPtr backup);
  1238   1272       #endregion
  1239   1273     }
  1240   1274   
  1241   1275   #if PLATFORM_COMPACTFRAMEWORK
  1242   1276     internal abstract class CriticalHandle : IDisposable
  1243   1277     {
  1244   1278       private bool _isClosed;
................................................................................
  1457   1491       }
  1458   1492   
  1459   1493       public override bool IsInvalid
  1460   1494       {
  1461   1495         get { return (handle == IntPtr.Zero); }
  1462   1496       }
  1463   1497     }
         1498  +
         1499  +  // Provides finalization support for unmanaged SQLite backup objects.
         1500  +  internal class SQLiteBackupHandle : CriticalHandle
         1501  +  {
         1502  +      public static implicit operator IntPtr(SQLiteBackupHandle backup)
         1503  +      {
         1504  +          return (backup != null) ? backup.handle : IntPtr.Zero;
         1505  +      }
         1506  +
         1507  +      public static implicit operator SQLiteBackupHandle(IntPtr backup)
         1508  +      {
         1509  +          return new SQLiteBackupHandle(backup);
         1510  +      }
         1511  +
         1512  +      private SQLiteBackupHandle(IntPtr backup)
         1513  +          : this()
         1514  +      {
         1515  +          SetHandle(backup);
         1516  +      }
         1517  +
         1518  +      internal SQLiteBackupHandle()
         1519  +          : base(IntPtr.Zero)
         1520  +      {
         1521  +      }
         1522  +
         1523  +      protected override bool ReleaseHandle()
         1524  +      {
         1525  +          try
         1526  +          {
         1527  +              SQLiteBase.FinishBackup(this);
         1528  +
         1529  +#if DEBUG && !NET_COMPACT_20
         1530  +              try
         1531  +              {
         1532  +                  Trace.WriteLine(String.Format(
         1533  +                      "FinishBackup: {0}", handle));
         1534  +              }
         1535  +              catch
         1536  +              {
         1537  +              }
         1538  +#endif
         1539  +
         1540  +#if DEBUG
         1541  +              return true;
         1542  +#endif
         1543  +          }
         1544  +#if DEBUG && !NET_COMPACT_20
         1545  +          catch (SQLiteException e)
         1546  +#else
         1547  +          catch (SQLiteException)
         1548  +#endif
         1549  +          {
         1550  +#if DEBUG && !NET_COMPACT_20
         1551  +              try
         1552  +              {
         1553  +                  Trace.WriteLine(String.Format(
         1554  +                      "FinishBackup: {0}, exception: {1}",
         1555  +                      handle, e));
         1556  +              }
         1557  +              catch
         1558  +              {
         1559  +              }
         1560  +#endif
         1561  +          }
         1562  +#if DEBUG
         1563  +          return false;
         1564  +#else
         1565  +          return true;
         1566  +#endif
         1567  +      }
         1568  +
         1569  +      public override bool IsInvalid
         1570  +      {
         1571  +          get { return (handle == IntPtr.Zero); }
         1572  +      }
         1573  +  }
  1464   1574   }