System.Data.SQLite
Check-in [3b020c8c80]
Not logged in

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

Overview
Comment:Change the base type for the SQLiteConnectionFlags enumeration to long integer. Improve exception handling in all native callbacks implemented in the SQLiteConnection class. Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3b020c8c802fea88260e0c8598ad0b432253b91f
User & Date: mistachkin 2015-06-24 20:02:06
Context
2015-06-24
22:43
Make sure that manually calling the Cancel method still causes an Interrupt exception to be thrown. Update master release archive manifest. check-in: dae0c2e6fc user: mistachkin tags: trunk
20:02
Change the base type for the SQLiteConnectionFlags enumeration to long integer. Improve exception handling in all native callbacks implemented in the SQLiteConnection class. Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries. check-in: 3b020c8c80 user: mistachkin tags: trunk
00:13
Minor enhancement to the batch build tool. check-in: 2cccb64c09 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/version.html.

    44     44       <div id="mainBody">
    45     45       <h1 class="heading">Version History</h1>
    46     46       <p><b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b></p>
    47     47       <ul>
    48     48         <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_8_11.html">SQLite 3.8.11</a>.</li>
    49     49         <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    50     50         <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for <a href="https://system.data.sqlite.org/index.html/info/9d353b0bd8">[9d353b0bd8]</a>.</li>
           51  +      <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
           52  +      <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
           53  +      <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
    51     54         <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
    52     55         <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
    53     56         <li>Enable integration with the <a href="http://www.hwaci.com/sw/sqlite/zipvfs.html">ZipVFS</a> extension.</li>
    54     57       </ul>
    55     58       <p><b>1.0.97.0 - May 26, 2015</b></p>
    56     59       <ul>
    57     60         <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_10_2.html">SQLite 3.8.10.2</a>.</li>

Changes to System.Data.SQLite/SQLite3.cs.

   817    817           finally /* NOTE: Thread.Abort() protection. */
   818    818           {
   819    819             n = UnsafeNativeMethods.sqlite3_step(stmt._sqlite_stmt);
   820    820           }
   821    821   
   822    822           if (n == SQLiteErrorCode.Row) return true;
   823    823           if (n == SQLiteErrorCode.Done) return false;
          824  +        if (n == SQLiteErrorCode.Interrupt) return false;
   824    825   
   825    826           if (n != SQLiteErrorCode.Ok)
   826    827           {
   827    828             SQLiteErrorCode r;
   828    829   
   829    830             // An error occurred, attempt to reset the statement.  If the reset worked because the
   830    831             // schema has changed, re-try the step again.  If it errored our because the database
................................................................................
   953    954           // Recreate a dummy statement
   954    955           string str = null;
   955    956           using (SQLiteStatement tmp = Prepare(null, stmt._sqlStatement, null, (uint)(stmt._command._commandTimeout * 1000), ref str))
   956    957           {
   957    958             // Finalize the existing statement
   958    959             stmt._sqlite_stmt.Dispose();
   959    960             // Reassign a new statement pointer to the old statement and clear the temporary one
   960         -          stmt._sqlite_stmt = tmp._sqlite_stmt;
   961         -          tmp._sqlite_stmt = null;
          961  +          if (tmp != null)
          962  +          {
          963  +            stmt._sqlite_stmt = tmp._sqlite_stmt;
          964  +            tmp._sqlite_stmt = null;
          965  +          }
   962    966   
   963    967             // Reapply parameters
   964    968             stmt.BindParameters();
   965    969           }
   966    970           return SQLiteErrorCode.Unknown; // Reset was OK, with schema change
   967    971         }
   968    972         else if (n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy)
................................................................................
  1126   1130             {
  1127   1131               SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
  1128   1132                 SQLiteConnectionEventType.NewCriticalHandle, null, null,
  1129   1133                 null, null, statementHandle, strSql, new object[] { cnn,
  1130   1134                 strSql, previous, timeoutMS }));
  1131   1135             }
  1132   1136   
  1133         -          if (n == SQLiteErrorCode.Schema)
         1137  +          if (n == SQLiteErrorCode.Interrupt)
         1138  +            break;
         1139  +          else if (n == SQLiteErrorCode.Schema)
  1134   1140               retries++;
  1135   1141             else if (n == SQLiteErrorCode.Error)
  1136   1142             {
  1137   1143               if (String.Compare(GetLastError(), "near \"TYPES\": syntax error", StringComparison.OrdinalIgnoreCase) == 0)
  1138   1144               {
  1139   1145                 int pos = strSql.IndexOf(';');
  1140   1146                 if (pos == -1) pos = strSql.Length - 1;
................................................................................
  1197   1203               {
  1198   1204                 // Otherwise sleep for a random amount of time up to 150ms
  1199   1205                 System.Threading.Thread.Sleep(rnd.Next(1, 150));
  1200   1206               }
  1201   1207             }
  1202   1208           }
  1203   1209   
         1210  +        if (n == SQLiteErrorCode.Interrupt) return null;
  1204   1211           if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
  1205   1212   
  1206   1213           strRemain = UTF8ToString(ptr, len);
  1207   1214   
  1208   1215           if (statementHandle != null) cmd = new SQLiteStatement(this, flags, statementHandle, strSql.Substring(0, strSql.Length - strRemain.Length), previous);
  1209   1216   
  1210   1217           return cmd;
................................................................................
  2404   2411   
  2405   2412       internal override void ChangePassword(byte[] newPasswordBytes)
  2406   2413       {
  2407   2414         SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);
  2408   2415         if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
  2409   2416       }
  2410   2417   #endif
         2418  +
         2419  +    internal override void SetProgressHook(int nOps, SQLiteProgressCallback func)
         2420  +    {
         2421  +        UnsafeNativeMethods.sqlite3_progress_handler(_sql, nOps, func, IntPtr.Zero);
         2422  +    }
  2411   2423   
  2412   2424       internal override void SetAuthorizerHook(SQLiteAuthorizerCallback func)
  2413   2425       {
  2414   2426         UnsafeNativeMethods.sqlite3_set_authorizer(_sql, func, IntPtr.Zero);
  2415   2427       }
  2416   2428   
  2417   2429       internal override void SetUpdateHook(SQLiteUpdateCallback func)

Changes to System.Data.SQLite/SQLiteBase.cs.

   383    383       internal abstract void LogMessage(SQLiteErrorCode iErrCode, string zMessage);
   384    384   
   385    385   #if INTEROP_CODEC || INTEROP_INCLUDE_SEE
   386    386       internal abstract void SetPassword(byte[] passwordBytes);
   387    387       internal abstract void ChangePassword(byte[] newPasswordBytes);
   388    388   #endif
   389    389   
          390  +    internal abstract void SetProgressHook(int nOps, SQLiteProgressCallback func);
   390    391       internal abstract void SetAuthorizerHook(SQLiteAuthorizerCallback func);
   391    392       internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
   392    393       internal abstract void SetCommitHook(SQLiteCommitCallback func);
   393    394       internal abstract void SetTraceCallback(SQLiteTraceCallback func);
   394    395       internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
   395    396       internal abstract SQLiteErrorCode SetLogCallback(SQLiteLogCallback func);
   396    397   
................................................................................
   862    863       Default = 0x06,
   863    864     }
   864    865   
   865    866     /// <summary>
   866    867     /// The extra behavioral flags that can be applied to a connection.
   867    868     /// </summary>
   868    869     [Flags()]
   869         -  public enum SQLiteConnectionFlags
          870  +  public enum SQLiteConnectionFlags : long
   870    871     {
   871    872         /// <summary>
   872    873         /// No extra flags.
   873    874         /// </summary>
   874    875         None = 0x0,
   875    876   
   876    877         /// <summary>
................................................................................
  1078   1079         /// <summary>
  1079   1080         /// When binding parameter values with the <see cref="DateTime" />
  1080   1081         /// type, take their <see cref="DateTimeKind" /> into account as
  1081   1082         /// well as that of the associated <see cref="SQLiteConnection" />.
  1082   1083         /// </summary>
  1083   1084         BindDateTimeWithKind = 0x10000000,
  1084   1085   
         1086  +      /// <summary>
         1087  +      /// If an exception is caught when raising the
         1088  +      /// <see cref="SQLiteConnection.Commit" /> event, the transaction
         1089  +      /// should be rolled back.  If this is not specified, the transaction
         1090  +      /// will continue the commit process instead.
         1091  +      /// </summary>
         1092  +      RollbackOnException = 0x20000000,
         1093  +
         1094  +      /// <summary>
         1095  +      /// If an exception is caught when raising the
         1096  +      /// <see cref="SQLiteConnection.Authorize" /> event, the action should
         1097  +      /// should be denied.  If this is not specified, the action will be
         1098  +      /// allowed instead.
         1099  +      /// </summary>
         1100  +      DenyOnException = 0x40000000,
         1101  +
         1102  +      /// <summary>
         1103  +      /// If an exception is caught when raising the
         1104  +      /// <see cref="SQLiteConnection.Progress" /> event, the operation
         1105  +      /// should be interrupted.  If this is not specified, the operation
         1106  +      /// will simply continue.
         1107  +      /// </summary>
         1108  +      InterruptOnException = 0x80000000,
         1109  +
  1085   1110         /// <summary>
  1086   1111         /// When binding parameter values or returning column values, always
  1087   1112         /// treat them as though they were plain text (i.e. no numeric,
  1088   1113         /// date/time, or other conversions should be attempted).
  1089   1114         /// </summary>
  1090   1115         BindAndGetAllAsText = BindAllAsText | GetAllAsText,
  1091   1116   

Changes to System.Data.SQLite/SQLiteConnection.cs.

   405    405     /// The maximum number of retries when preparing SQL to be executed.  This
   406    406     /// normally only applies to preparation errors resulting from the database
   407    407     /// schema being changed.
   408    408     /// </description>
   409    409     /// <description>N</description>
   410    410     /// <description>3</description>
   411    411     /// </item>
          412  +  /// <item>
          413  +  /// <description>ProgressOps</description>
          414  +  /// <description>
          415  +  /// The approximate number of virtual machine instructions between progress
          416  +  /// events.  In order for progress events to actually fire, the event handler
          417  +  /// must be added to the <see cref="Progress" /> event as well.
          418  +  /// </description>
          419  +  /// <description>N</description>
          420  +  /// <description>0</description>
          421  +  /// </item>
   412    422     /// </list>
   413    423     /// </remarks>
   414    424     public sealed partial class SQLiteConnection : DbConnection, ICloneable, IDisposable
   415    425     {
   416    426       #region Private Constants
   417    427       /// <summary>
   418    428       /// The "invalid value" for the <see cref="DbType" /> enumeration used
................................................................................
   463    473       private const bool DefaultPooling = false; // TODO: Maybe promote this to static property?
   464    474       private const bool DefaultLegacyFormat = false;
   465    475       private const bool DefaultForeignKeys = false;
   466    476       private const bool DefaultEnlist = true;
   467    477       private const bool DefaultSetDefaults = true;
   468    478       internal const int DefaultPrepareRetries = 3;
   469    479       private const string DefaultVfsName = null;
          480  +    private const int DefaultProgressOps = 0;
   470    481   
   471    482   #if INTEROP_INCLUDE_ZIPVFS
   472    483       private const string ZipVfs_Automatic = "automatic";
   473    484       private const string ZipVfs_V2 = "v2";
   474    485       private const string ZipVfs_V3 = "v3";
   475    486   
   476    487       private const string DefaultZipVfsVersion = null;
................................................................................
   633    644       /// <summary>
   634    645       /// The maximum number of retries when preparing SQL to be executed.  This
   635    646       /// normally only applies to preparation errors resulting from the database
   636    647       /// schema being changed.
   637    648       /// </summary>
   638    649       internal int _prepareRetries = DefaultPrepareRetries;
   639    650   
          651  +    /// <summary>
          652  +    /// The approximate number of virtual machine instructions between progress
          653  +    /// events.  In order for progress events to actually fire, the event handler
          654  +    /// must be added to the <see cref="SQLiteConnection.Progress" /> event as
          655  +    /// well.  This value will only be used when opening the database.
          656  +    /// </summary>
          657  +    private int _progressOps = DefaultProgressOps;
          658  +
   640    659       /// <summary>
   641    660       /// Non-zero if the built-in (i.e. framework provided) connection string
   642    661       /// parser should be used when opening the connection.
   643    662       /// </summary>
   644    663       private bool _parseViaFramework;
   645    664   
   646    665       internal bool _binaryGuid;
   647    666   
   648    667       internal int _version;
   649    668   
          669  +    private event SQLiteProgressEventHandler _progressHandler;
   650    670       private event SQLiteAuthorizerEventHandler _authorizerHandler;
   651    671       private event SQLiteUpdateEventHandler _updateHandler;
   652    672       private event SQLiteCommitHandler _commitHandler;
   653    673       private event SQLiteTraceEventHandler _traceHandler;
   654    674       private event EventHandler _rollbackHandler;
   655    675   
          676  +    private SQLiteProgressCallback _progressCallback;
   656    677       private SQLiteAuthorizerCallback _authorizerCallback;
   657    678       private SQLiteUpdateCallback _updateCallback;
   658    679       private SQLiteCommitCallback _commitCallback;
   659    680       private SQLiteTraceCallback _traceCallback;
   660    681       private SQLiteRollbackCallback _rollbackCallback;
   661    682       #endregion
   662    683   
................................................................................
  2698   2719         {
  2699   2720           bool usePooling = SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", GetDefaultPooling().ToString()));
  2700   2721           int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", DefaultMaxPoolSize.ToString()), CultureInfo.InvariantCulture);
  2701   2722   
  2702   2723           _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", DefaultConnectionTimeout.ToString()), CultureInfo.InvariantCulture);
  2703   2724           _busyTimeout = Convert.ToInt32(FindKey(opts, "BusyTimeout", DefaultBusyTimeout.ToString()), CultureInfo.InvariantCulture);
  2704   2725           _prepareRetries = Convert.ToInt32(FindKey(opts, "PrepareRetries", DefaultPrepareRetries.ToString()), CultureInfo.InvariantCulture);
         2726  +        _progressOps = Convert.ToInt32(FindKey(opts, "ProgressOps", DefaultProgressOps.ToString()), CultureInfo.InvariantCulture);
  2705   2727   
  2706   2728           enumValue = TryParseEnum(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", DefaultIsolationLevel.ToString()), true);
  2707   2729           _defaultIsolation = (enumValue is IsolationLevel) ? (IsolationLevel)enumValue : DefaultIsolationLevel;
  2708   2730           _defaultIsolation = GetEffectiveIsolationLevel(_defaultIsolation);
  2709   2731   
  2710   2732           if (_defaultIsolation != ImmediateIsolationLevel && _defaultIsolation != DeferredIsolationLevel)
  2711   2733             throw new NotSupportedException("Invalid Default IsolationLevel specified");
................................................................................
  2864   2886                     {
  2865   2887                         cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA foreign_keys={0}", boolValue ? "ON" : "OFF");
  2866   2888                         cmd.ExecuteNonQuery();
  2867   2889                     }
  2868   2890                 }
  2869   2891             }
  2870   2892   
         2893  +          if (_progressHandler != null)
         2894  +              _sql.SetProgressHook(_progressOps, _progressCallback);
         2895  +
  2871   2896             if (_authorizerHandler != null)
  2872   2897                 _sql.SetAuthorizerHook(_authorizerCallback);
  2873   2898   
  2874   2899             if (_commitHandler != null)
  2875   2900               _sql.SetCommitHook(_commitCallback);
  2876   2901   
  2877   2902             if (_updateHandler != null)
................................................................................
  2948   2973       /// schema being changed.
  2949   2974       /// </summary>
  2950   2975       public int PrepareRetries
  2951   2976       {
  2952   2977           get { CheckDisposed(); return _prepareRetries; }
  2953   2978           set { CheckDisposed(); _prepareRetries = value; }
  2954   2979       }
         2980  +
         2981  +    /// <summary>
         2982  +    /// The approximate number of virtual machine instructions between progress
         2983  +    /// events.  In order for progress events to actually fire, the event handler
         2984  +    /// must be added to the <see cref="SQLiteConnection.Progress" /> event as
         2985  +    /// well.  This value will only be used when the underlying native progress
         2986  +    /// callback needs to be changed.
         2987  +    /// </summary>
         2988  +    public int ProgressOps
         2989  +    {
         2990  +        get { CheckDisposed(); return _progressOps; }
         2991  +        set { CheckDisposed(); _progressOps = value; }
         2992  +    }
  2955   2993   
  2956   2994       /// <summary>
  2957   2995       /// Non-zero if the built-in (i.e. framework provided) connection string
  2958   2996       /// parser should be used when opening the connection.
  2959   2997       /// </summary>
  2960   2998       public bool ParseViaFramework
  2961   2999       {
................................................................................
  4893   4931         }
  4894   4932   
  4895   4933         tbl.EndLoadData();
  4896   4934         tbl.AcceptChanges();
  4897   4935   
  4898   4936         return tbl;
  4899   4937       }
         4938  +
         4939  +    /// <summary>
         4940  +    /// This event is raised periodically during long running queries.  Changing
         4941  +    /// the value of the <see cref="ProgressEventArgs.ReturnCode" /> property will
         4942  +    /// determine if the operation in progress will continue or be interrupted.
         4943  +    /// For the entire duration of the event, the associated connection and
         4944  +    /// statement objects must not be modified, either directly or indirectly, by
         4945  +    /// the called code.
         4946  +    /// </summary>
         4947  +    public event SQLiteProgressEventHandler Progress
         4948  +    {
         4949  +        add
         4950  +        {
         4951  +            CheckDisposed();
         4952  +
         4953  +            if (_progressHandler == null)
         4954  +            {
         4955  +                _progressCallback = new SQLiteProgressCallback(ProgressCallback);
         4956  +                if (_sql != null) _sql.SetProgressHook(_progressOps, _progressCallback);
         4957  +            }
         4958  +            _progressHandler += value;
         4959  +        }
         4960  +        remove
         4961  +        {
         4962  +            CheckDisposed();
         4963  +
         4964  +            _progressHandler -= value;
         4965  +            if (_progressHandler == null)
         4966  +            {
         4967  +                if (_sql != null) _sql.SetProgressHook(0, null);
         4968  +                _progressCallback = null;
         4969  +            }
         4970  +        }
         4971  +    }
  4900   4972   
  4901   4973       /// <summary>
  4902   4974       /// This event is raised whenever SQLite encounters an action covered by the
  4903   4975       /// authorizer during query preparation.  Changing the value of the
  4904   4976       /// <see cref="AuthorizerEventArgs.ReturnCode" /> property will determine if
  4905   4977       /// the specific action will be allowed, ignored, or denied.  For the entire
  4906   4978       /// duration of the event, the associated connection and statement objects
................................................................................
  4957   5029           if (_updateHandler == null)
  4958   5030           {
  4959   5031             if (_sql != null) _sql.SetUpdateHook(null);
  4960   5032             _updateCallback = null;
  4961   5033           }
  4962   5034         }
  4963   5035       }
         5036  +
         5037  +    private SQLiteProgressReturnCode ProgressCallback(
         5038  +        IntPtr pUserData /* NOT USED: Always IntPtr.Zero. */
         5039  +        )
         5040  +    {
         5041  +        try
         5042  +        {
         5043  +            ProgressEventArgs eventArgs = new ProgressEventArgs(
         5044  +                pUserData, SQLiteProgressReturnCode.Continue);
         5045  +
         5046  +            if (_progressHandler != null)
         5047  +                _progressHandler(this, eventArgs);
         5048  +
         5049  +            return eventArgs.ReturnCode;
         5050  +        }
         5051  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5052  +        {
         5053  +            try
         5054  +            {
         5055  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5056  +                        SQLiteConnectionFlags.LogCallbackException)
         5057  +                {
         5058  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5059  +                        String.Format(CultureInfo.CurrentCulture,
         5060  +                        "Caught exception in \"Progress\" method: {1}",
         5061  +                        e)); /* throw */
         5062  +                }
         5063  +            }
         5064  +            catch
         5065  +            {
         5066  +                // do nothing.
         5067  +            }
         5068  +        }
         5069  +
         5070  +        //
         5071  +        // NOTE: Should throwing an exception interrupt the operation?
         5072  +        //
         5073  +        if ((_flags & SQLiteConnectionFlags.InterruptOnException) ==
         5074  +                SQLiteConnectionFlags.InterruptOnException)
         5075  +        {
         5076  +            return SQLiteProgressReturnCode.Interrupt;
         5077  +        }
         5078  +        else
         5079  +        {
         5080  +            return SQLiteProgressReturnCode.Continue;
         5081  +        }
         5082  +    }
  4964   5083   
  4965   5084       private SQLiteAuthorizerReturnCode AuthorizerCallback(
  4966         -        IntPtr pUserData,
         5085  +        IntPtr pUserData, /* NOT USED: Always IntPtr.Zero. */
  4967   5086           SQLiteAuthorizerActionCode actionCode,
  4968   5087           IntPtr pArgument1,
  4969   5088           IntPtr pArgument2,
  4970   5089           IntPtr pDatabase,
  4971   5090           IntPtr pAuthContext)
  4972   5091       {
  4973         -        AuthorizerEventArgs eventArgs = new AuthorizerEventArgs(pUserData, actionCode,
  4974         -            SQLiteBase.UTF8ToString(pArgument1, -1), SQLiteBase.UTF8ToString(pArgument2, -1),
  4975         -            SQLiteBase.UTF8ToString(pDatabase, -1), SQLiteBase.UTF8ToString(pAuthContext, -1),
  4976         -            SQLiteAuthorizerReturnCode.Ok);
         5092  +        try
         5093  +        {
         5094  +            AuthorizerEventArgs eventArgs = new AuthorizerEventArgs(pUserData, actionCode,
         5095  +                SQLiteBase.UTF8ToString(pArgument1, -1), SQLiteBase.UTF8ToString(pArgument2, -1),
         5096  +                SQLiteBase.UTF8ToString(pDatabase, -1), SQLiteBase.UTF8ToString(pAuthContext, -1),
         5097  +                SQLiteAuthorizerReturnCode.Ok);
  4977   5098   
  4978         -        if (_authorizerHandler != null)
  4979         -            _authorizerHandler(this, eventArgs);
         5099  +            if (_authorizerHandler != null)
         5100  +                _authorizerHandler(this, eventArgs);
  4980   5101   
  4981         -        return eventArgs.ReturnCode;
         5102  +            return eventArgs.ReturnCode;
         5103  +        }
         5104  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5105  +        {
         5106  +            try
         5107  +            {
         5108  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5109  +                        SQLiteConnectionFlags.LogCallbackException)
         5110  +                {
         5111  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5112  +                        String.Format(CultureInfo.CurrentCulture,
         5113  +                        "Caught exception in \"Authorize\" method: {1}",
         5114  +                        e)); /* throw */
         5115  +                }
         5116  +            }
         5117  +            catch
         5118  +            {
         5119  +                // do nothing.
         5120  +            }
         5121  +        }
         5122  +
         5123  +        //
         5124  +        // NOTE: Should throwing an exception deny the action?
         5125  +        //
         5126  +        if ((_flags & SQLiteConnectionFlags.DenyOnException) ==
         5127  +                SQLiteConnectionFlags.DenyOnException)
         5128  +        {
         5129  +            return SQLiteAuthorizerReturnCode.Deny;
         5130  +        }
         5131  +        else
         5132  +        {
         5133  +            return SQLiteAuthorizerReturnCode.Ok;
         5134  +        }
  4982   5135       }
  4983   5136   
  4984         -    private void UpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid)
         5137  +    private void UpdateCallback(
         5138  +        IntPtr puser, /* NOT USED */
         5139  +        int type,
         5140  +        IntPtr database,
         5141  +        IntPtr table,
         5142  +        Int64 rowid
         5143  +        )
  4985   5144       {
  4986         -      _updateHandler(this, new UpdateEventArgs(
  4987         -        SQLiteBase.UTF8ToString(database, -1),
  4988         -        SQLiteBase.UTF8ToString(table, -1),
  4989         -        (UpdateEventType)type,
  4990         -        rowid));
         5145  +        try
         5146  +        {
         5147  +            _updateHandler(this, new UpdateEventArgs(
         5148  +              SQLiteBase.UTF8ToString(database, -1),
         5149  +              SQLiteBase.UTF8ToString(table, -1),
         5150  +              (UpdateEventType)type,
         5151  +              rowid));
         5152  +        }
         5153  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5154  +        {
         5155  +            try
         5156  +            {
         5157  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5158  +                        SQLiteConnectionFlags.LogCallbackException)
         5159  +                {
         5160  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5161  +                        String.Format(CultureInfo.CurrentCulture,
         5162  +                        "Caught exception in \"Update\" method: {1}",
         5163  +                        e)); /* throw */
         5164  +                }
         5165  +            }
         5166  +            catch
         5167  +            {
         5168  +                // do nothing.
         5169  +            }
         5170  +        }
  4991   5171       }
  4992   5172   
  4993   5173       /// <summary>
  4994   5174       /// This event is raised whenever SQLite is committing a transaction.
  4995   5175       /// Return non-zero to trigger a rollback.
  4996   5176       /// </summary>
  4997   5177       public event SQLiteCommitHandler Commit
................................................................................
  5046   5226           {
  5047   5227             if (_sql != null) _sql.SetTraceCallback(null);
  5048   5228               _traceCallback = null;
  5049   5229           }
  5050   5230         }
  5051   5231       }
  5052   5232   
  5053         -    private void TraceCallback(IntPtr puser, IntPtr statement)
         5233  +    private void TraceCallback(
         5234  +        IntPtr puser, /* NOT USED */
         5235  +        IntPtr statement
         5236  +        )
  5054   5237       {
  5055         -      _traceHandler(this, new TraceEventArgs(
  5056         -        SQLiteBase.UTF8ToString(statement, -1)));
         5238  +        try
         5239  +        {
         5240  +            if (_traceHandler != null)
         5241  +                _traceHandler(this, new TraceEventArgs(
         5242  +                  SQLiteBase.UTF8ToString(statement, -1)));
         5243  +        }
         5244  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5245  +        {
         5246  +            try
         5247  +            {
         5248  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5249  +                        SQLiteConnectionFlags.LogCallbackException)
         5250  +                {
         5251  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5252  +                        String.Format(CultureInfo.CurrentCulture,
         5253  +                        "Caught exception in \"Trace\" method: {1}",
         5254  +                        e)); /* throw */
         5255  +                }
         5256  +            }
         5257  +            catch
         5258  +            {
         5259  +                // do nothing.
         5260  +            }
         5261  +        }
  5057   5262       }
  5058   5263   
  5059   5264       /// <summary>
  5060   5265       /// This event is raised whenever SQLite is rolling back a transaction.
  5061   5266       /// </summary>
  5062   5267       public event EventHandler RollBack
  5063   5268       {
................................................................................
  5081   5286           {
  5082   5287             if (_sql != null) _sql.SetRollbackHook(null);
  5083   5288             _rollbackCallback = null;
  5084   5289           }
  5085   5290         }
  5086   5291       }
  5087   5292   
  5088         -
  5089         -    private int CommitCallback(IntPtr parg)
         5293  +    private int CommitCallback(
         5294  +        IntPtr parg /* NOT USED */
         5295  +        )
  5090   5296       {
  5091         -      CommitEventArgs e = new CommitEventArgs();
  5092         -      _commitHandler(this, e);
  5093         -      return (e.AbortTransaction == true) ? 1 : 0;
         5297  +        try
         5298  +        {
         5299  +            CommitEventArgs e = new CommitEventArgs();
         5300  +
         5301  +            if (_commitHandler != null)
         5302  +                _commitHandler(this, e);
         5303  +
         5304  +            return (e.AbortTransaction == true) ? 1 : 0;
         5305  +        }
         5306  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5307  +        {
         5308  +            try
         5309  +            {
         5310  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5311  +                        SQLiteConnectionFlags.LogCallbackException)
         5312  +                {
         5313  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5314  +                        String.Format(CultureInfo.CurrentCulture,
         5315  +                        "Caught exception in \"Commit\" method: {1}",
         5316  +                        e)); /* throw */
         5317  +                }
         5318  +            }
         5319  +            catch
         5320  +            {
         5321  +                // do nothing.
         5322  +            }
         5323  +        }
         5324  +
         5325  +        //
         5326  +        // NOTE: Should throwing an exception rollback the transaction?
         5327  +        //
         5328  +        if ((_flags & SQLiteConnectionFlags.RollbackOnException) ==
         5329  +                SQLiteConnectionFlags.RollbackOnException)
         5330  +        {
         5331  +            return 1; // rollback
         5332  +        }
         5333  +        else
         5334  +        {
         5335  +            return 0; // commit
         5336  +        }
  5094   5337       }
  5095   5338   
  5096         -    private void RollbackCallback(IntPtr parg)
         5339  +    private void RollbackCallback(
         5340  +        IntPtr parg /* NOT USED */
         5341  +        )
  5097   5342       {
  5098         -      _rollbackHandler(this, EventArgs.Empty);
         5343  +        try
         5344  +        {
         5345  +            if (_rollbackHandler != null)
         5346  +                _rollbackHandler(this, EventArgs.Empty);
         5347  +        }
         5348  +        catch (Exception e) /* NOTE: Must catch ALL. */
         5349  +        {
         5350  +            try
         5351  +            {
         5352  +                if ((_flags & SQLiteConnectionFlags.LogCallbackException) ==
         5353  +                        SQLiteConnectionFlags.LogCallbackException)
         5354  +                {
         5355  +                    SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
         5356  +                        String.Format(CultureInfo.CurrentCulture,
         5357  +                        "Caught exception in \"Rollback\" method: {1}",
         5358  +                        e)); /* throw */
         5359  +                }
         5360  +            }
         5361  +            catch
         5362  +            {
         5363  +                // do nothing.
         5364  +            }
         5365  +        }
  5099   5366       }
  5100         -
  5101   5367     }
  5102   5368   
  5103   5369     /// <summary>
  5104   5370     /// The I/O file cache flushing behavior for the connection
  5105   5371     /// </summary>
  5106   5372     public enum SynchronizationModes
  5107   5373     {
................................................................................
  5115   5381       Full = 1,
  5116   5382       /// <summary>
  5117   5383       /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing
  5118   5384       /// </summary>
  5119   5385       Off = 2,
  5120   5386     }
  5121   5387   
         5388  +#if !PLATFORM_COMPACTFRAMEWORK
         5389  +  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         5390  +#endif
         5391  +  internal delegate SQLiteProgressReturnCode SQLiteProgressCallback(IntPtr pUserData);
         5392  +
  5122   5393   #if !PLATFORM_COMPACTFRAMEWORK
  5123   5394     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  5124   5395   #endif
  5125   5396     internal delegate SQLiteAuthorizerReturnCode SQLiteAuthorizerCallback(
  5126   5397       IntPtr pUserData,
  5127   5398       SQLiteAuthorizerActionCode actionCode,
  5128   5399       IntPtr pArgument1,
................................................................................
  5147   5418     internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement);
  5148   5419   
  5149   5420   #if !PLATFORM_COMPACTFRAMEWORK
  5150   5421     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  5151   5422   #endif
  5152   5423     internal delegate void SQLiteRollbackCallback(IntPtr puser);
  5153   5424   
         5425  +  /// <summary>
         5426  +  /// Raised each time the number of virtual machine instructions is
         5427  +  /// approximately equal to the value of the
         5428  +  /// <see cref="SQLiteConnection.ProgressOps" /> property.
         5429  +  /// </summary>
         5430  +  /// <param name="sender">The connection performing the operation.</param>
         5431  +  /// <param name="e">A <see cref="ProgressEventArgs" /> that contains the
         5432  +  /// event data.</param>
         5433  +  public delegate void SQLiteProgressEventHandler(object sender, ProgressEventArgs e);
         5434  +
  5154   5435     /// <summary>
  5155   5436     /// Raised when authorization is required to perform an action contained
  5156   5437     /// within a SQL query.
  5157   5438     /// </summary>
  5158   5439     /// <param name="sender">The connection performing the action.</param>
  5159   5440     /// <param name="e">A <see cref="AuthorizerEventArgs" /> that contains the
  5160   5441     /// event data.</param>
................................................................................
  5224   5505       string destinationName,
  5225   5506       int pages,
  5226   5507       int remainingPages,
  5227   5508       int totalPages,
  5228   5509       bool retry
  5229   5510     );
  5230   5511     #endregion
         5512  +
         5513  +  ///////////////////////////////////////////////////////////////////////////////////////////////
         5514  +
         5515  +  public class ProgressEventArgs : EventArgs
         5516  +  {
         5517  +      /// <summary>
         5518  +      /// The user-defined native data associated with this event.  Currently,
         5519  +      /// this will always contain the value of <see cref="IntPtr.Zero" />.
         5520  +      /// </summary>
         5521  +      public readonly IntPtr UserData;
         5522  +
         5523  +      /// <summary>
         5524  +      /// The return code for the current call into the progress callback.
         5525  +      /// </summary>
         5526  +      public SQLiteProgressReturnCode ReturnCode;
         5527  +
         5528  +      /// <summary>
         5529  +      /// Constructs an instance of this class with default property values.
         5530  +      /// </summary>
         5531  +      private ProgressEventArgs()
         5532  +      {
         5533  +          this.UserData = IntPtr.Zero;
         5534  +          this.ReturnCode = SQLiteProgressReturnCode.Continue;
         5535  +      }
         5536  +
         5537  +      /// <summary>
         5538  +      /// Constructs an instance of this class with specific property values.
         5539  +      /// </summary>
         5540  +      /// <param name="pUserData">
         5541  +      /// The user-defined native data associated with this event.
         5542  +      /// </param>
         5543  +      /// <param name="returnCode">
         5544  +      /// The progress return code.
         5545  +      /// </param>
         5546  +      internal ProgressEventArgs(
         5547  +          IntPtr pUserData,
         5548  +          SQLiteProgressReturnCode returnCode
         5549  +          )
         5550  +          : this()
         5551  +      {
         5552  +          this.UserData = pUserData;
         5553  +          this.ReturnCode = returnCode;
         5554  +      }
         5555  +  }
  5231   5556   
  5232   5557     ///////////////////////////////////////////////////////////////////////////////////////////////
  5233   5558   
  5234   5559     /// <summary>
  5235   5560     /// The data associated with a call into the authorizer.
  5236   5561     /// </summary>
  5237   5562     public class AuthorizerEventArgs : EventArgs

Changes to System.Data.SQLite/SQLiteConnectionStringBuilder.cs.

   289    289               return Convert.ToInt32(value, CultureInfo.CurrentCulture);
   290    290           }
   291    291           set
   292    292           {
   293    293               this["prepareretries"] = value;
   294    294           }
   295    295       }
          296  +
          297  +    /// <summary>
          298  +    /// Gets/sets the approximate number of virtual machine instructions between
          299  +    /// progress events.  In order for progress events to actually fire, the event
          300  +    /// handler must be added to the <see cref="SQLiteConnection.Progress" /> event
          301  +    /// as well.
          302  +    /// </summary>
          303  +    [DisplayName("Progress Ops")]
          304  +    [Browsable(true)]
          305  +    [DefaultValue(0)]
          306  +    public int ProgressOps
          307  +    {
          308  +        get
          309  +        {
          310  +            object value;
          311  +            TryGetValue("progressops", out value);
          312  +            return Convert.ToInt32(value, CultureInfo.CurrentCulture);
          313  +        }
          314  +        set
          315  +        {
          316  +            this["progressops"] = value;
          317  +        }
          318  +    }
   296    319   
   297    320       /// <summary>
   298    321       /// Determines whether or not the connection will automatically participate
   299    322       /// in the current distributed transaction (if one exists)
   300    323       /// </summary>
   301    324       [Browsable(true)]
   302    325       [DefaultValue(true)]

Changes to System.Data.SQLite/SQLiteConvert.cs.

  2611   2611   
  2612   2612         /// <summary>
  2613   2613         /// A recursive query will be executed.  The action-specific arguments
  2614   2614         /// are two null values.
  2615   2615         /// </summary>
  2616   2616         Recursive = 33
  2617   2617     }
         2618  +
         2619  +  /// <summary>
         2620  +  /// The possible return codes for the progress callback.
         2621  +  /// </summary>
         2622  +  public enum SQLiteProgressReturnCode /* int */
         2623  +  {
         2624  +      /// <summary>
         2625  +      /// The operation should continue.
         2626  +      /// </summary>
         2627  +      Continue = 0,
         2628  +
         2629  +      /// <summary>
         2630  +      /// The operation should be interrupted.
         2631  +      /// </summary>
         2632  +      Interrupt = 1
         2633  +  }
  2618   2634   
  2619   2635     /// <summary>
  2620   2636     /// The return code for the current call into the authorizer.
  2621   2637     /// </summary>
  2622   2638     public enum SQLiteAuthorizerReturnCode
  2623   2639     {
  2624   2640         /// <summary>

Changes to System.Data.SQLite/UnsafeNativeMethods.cs.

  2122   2122       [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
  2123   2123   #else
  2124   2124       [DllImport(SQLITE_DLL)]
  2125   2125   #endif
  2126   2126       internal static extern void zipvfsInit_v3(int regDflt);
  2127   2127   #endif
  2128   2128   
         2129  +#if !PLATFORM_COMPACTFRAMEWORK
         2130  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
         2131  +#else
         2132  +    [DllImport(SQLITE_DLL)]
         2133  +#endif
         2134  +    internal static extern void sqlite3_progress_handler(IntPtr db, int ops, SQLiteProgressCallback func, IntPtr pvUser);
         2135  +
  2129   2136   #if !PLATFORM_COMPACTFRAMEWORK
  2130   2137       [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
  2131   2138   #else
  2132   2139       [DllImport(SQLITE_DLL)]
  2133   2140   #endif
  2134   2141       internal static extern IntPtr sqlite3_set_authorizer(IntPtr db, SQLiteAuthorizerCallback func, IntPtr pvUser);
  2135   2142   

Changes to Tests/basic.eagle.

  1050   1050                      BinaryGUID "Data Source" Uri FullUri "Default Timeout" \
  1051   1051                      Enlist FailIfMissing "Legacy Format" "Read Only" \
  1052   1052                      Password "Page Size" "Max Page Count" "Cache Size" \
  1053   1053                      DateTimeFormat DateTimeKind DateTimeFormatString \
  1054   1054                      BaseSchemaName "Journal Mode" "Default IsolationLevel" \
  1055   1055                      "Foreign Keys" Flags SetDefaults ToFullPath HexPassword \
  1056   1056                      DefaultDbType DefaultTypeName NoSharedFlags PrepareRetries \
  1057         -                   ZipVfsVersion VfsName BusyTimeout]
         1057  +                   ZipVfsVersion VfsName BusyTimeout ProgressOps]
  1058   1058   
  1059   1059       set values [list null 3 Normal True False \
  1060   1060                        True test.db test.db file:test.db 60 \
  1061   1061                        False True False True \
  1062   1062                        secret 4096 1024 8192 \
  1063   1063                        UnixEpoch Utc yyyy-MM-dd sqlite_schema \
  1064   1064                        Memory Serializable False \
  1065   1065                        Default False False 736563726574 String \
  1066         -                     TEXT True 20 v2 test 1000]
         1066  +                     TEXT True 20 v2 test 1000 2000]
  1067   1067   
  1068   1068       set propertyNames [list null Version SyncMode UseUTF16Encoding Pooling \
  1069   1069                               BinaryGUID DataSource Uri FullUri DefaultTimeout \
  1070   1070                               Enlist FailIfMissing LegacyFormat ReadOnly \
  1071   1071                               Password PageSize MaxPageCount CacheSize \
  1072   1072                               DateTimeFormat DateTimeKind DateTimeFormatString \
  1073   1073                               BaseSchemaName JournalMode DefaultIsolationLevel \
  1074   1074                               ForeignKeys Flags SetDefaults ToFullPath \
  1075   1075                               HexPassword DefaultDbType DefaultTypeName \
  1076   1076                               NoSharedFlags PrepareRetries ZipVfsVersion \
  1077         -                            VfsName BusyTimeout]
         1077  +                            VfsName BusyTimeout ProgressOps]
  1078   1078   
  1079   1079       foreach key $keys value $values propertyName $propertyNames {
  1080   1080         set code [catch {
  1081   1081           object invoke _Dynamic${id}.Test${id} GetConnectionString \
  1082   1082               $key $value $propertyName
  1083   1083         } result]
  1084   1084   
................................................................................
  1105   1105   BaseSchemaName=sqlite_schema\} 0 \{Memory, Journal Mode=Memory\} 0\
  1106   1106   \{Serializable, Default IsolationLevel=Serializable\} 0 \{False, Foreign\
  1107   1107   Keys=False\} 0 \{(?:Default|LogCallbackException),\
  1108   1108   Flags=(?:Default|LogCallbackException)\} 0 \{False, SetDefaults=False\} 0\
  1109   1109   \{False, ToFullPath=False\} 0 {736563726574, HexPassword=736563726574} 0\
  1110   1110   \{String, DefaultDbType=String\} 0 \{TEXT, DefaultTypeName=TEXT\} 0 \{True,\
  1111   1111   NoSharedFlags=True\} 0 \{20, PrepareRetries=20\} 0 \{v2, ZipVfsVersion=v2\} 0\
  1112         -\{test, VfsName=test\} 0 \{1000, BusyTimeout=1000\}$}}
         1112  +\{test, VfsName=test\} 0 \{1000, BusyTimeout=1000\} 0 \{2000,\
         1113  +ProgressOps=2000\}$}}
  1113   1114   
  1114   1115   ###############################################################################
  1115   1116   
  1116   1117   runTest {test data-1.18 {SQLiteConvert ToDateTime (Julian Day)} -body {
  1117   1118     set dateTime [object invoke -create System.Data.SQLite.SQLiteConvert \
  1118   1119         ToDateTime 2455928.0 Utc]
  1119   1120   

Added Tests/progress.eagle.

            1  +###############################################################################
            2  +#
            3  +# progress.eagle --
            4  +#
            5  +# Written by Joe Mistachkin.
            6  +# Released to the public domain, use at your own risk!
            7  +#
            8  +###############################################################################
            9  +
           10  +package require Eagle
           11  +package require Eagle.Library
           12  +package require Eagle.Test
           13  +
           14  +runTestPrologue
           15  +
           16  +###############################################################################
           17  +
           18  +package require System.Data.SQLite.Test
           19  +runSQLiteTestPrologue
           20  +
           21  +###############################################################################
           22  +
           23  +runTest {test progress-1.1 {no progress without ProgressOps} -setup {
           24  +  setupDb [set fileName progress-1.1.db]
           25  +} -body {
           26  +  set id [object invoke Interpreter.GetActive NextId]
           27  +  set dataSource [file join [getDatabaseDirectory] $fileName]
           28  +
           29  +  set sql { \
           30  +    CREATE TABLE t1(x INTEGER); \
           31  +    INSERT INTO t1 (x) VALUES(1); \
           32  +    INSERT INTO t1 (x) VALUES(2); \
           33  +    INSERT INTO t1 (x) VALUES(3); \
           34  +    INSERT INTO t1 (x) VALUES(4); \
           35  +    SELECT x FROM t1 ORDER BY x; \
           36  +  }
           37  +
           38  +  unset -nocomplain results errors
           39  +
           40  +  set code [compileCSharpWith [subst {
           41  +    using System.Data.SQLite;
           42  +
           43  +    namespace _Dynamic${id}
           44  +    {
           45  +      public static class Test${id}
           46  +      {
           47  +        private static int count = 0;
           48  +
           49  +        ///////////////////////////////////////////////////////////////////////
           50  +
           51  +        public static void MyProgressHandler(
           52  +          object sender,
           53  +          ProgressEventArgs e
           54  +          )
           55  +        {
           56  +          count++;
           57  +        }
           58  +
           59  +        ///////////////////////////////////////////////////////////////////////
           60  +
           61  +        public static int Main()
           62  +        {
           63  +          using (SQLiteConnection connection = new SQLiteConnection(
           64  +              "Data Source=${dataSource};[getFlagsProperty]"))
           65  +          {
           66  +            connection.Progress += MyProgressHandler;
           67  +            connection.Open();
           68  +
           69  +            using (SQLiteCommand command = new SQLiteCommand("${sql}",
           70  +                connection))
           71  +            {
           72  +              command.ExecuteNonQuery();
           73  +            }
           74  +          }
           75  +
           76  +          return count;
           77  +        }
           78  +      }
           79  +    }
           80  +  }] true true true results errors System.Data.SQLite.dll]
           81  +
           82  +  list $code $results \
           83  +      [expr {[info exists errors] ? $errors : ""}] \
           84  +      [expr {$code eq "Ok" ? [catch {
           85  +        object invoke _Dynamic${id}.Test${id} Main
           86  +      } result] : [set result ""]}] $result \
           87  +      [expr {[string is integer -strict $result] && $result == 0 ? 1 : 0}]
           88  +} -cleanup {
           89  +  cleanupDb $fileName
           90  +
           91  +  unset -nocomplain result results errors code sql dataSource id db fileName
           92  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
           93  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
           94  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 0 1$}}
           95  +
           96  +###############################################################################
           97  +
           98  +runTest {test progress-1.2 {simple progress counter} -setup {
           99  +  setupDb [set fileName progress-1.2.db]
          100  +} -body {
          101  +  set id [object invoke Interpreter.GetActive NextId]
          102  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          103  +
          104  +  set sql { \
          105  +    CREATE TABLE t1(x INTEGER); \
          106  +    INSERT INTO t1 (x) VALUES(1); \
          107  +    INSERT INTO t1 (x) VALUES(2); \
          108  +    INSERT INTO t1 (x) VALUES(3); \
          109  +    INSERT INTO t1 (x) VALUES(4); \
          110  +    SELECT x FROM t1 ORDER BY x; \
          111  +  }
          112  +
          113  +  unset -nocomplain results errors
          114  +
          115  +  set code [compileCSharpWith [subst {
          116  +    using System.Data.SQLite;
          117  +
          118  +    namespace _Dynamic${id}
          119  +    {
          120  +      public static class Test${id}
          121  +      {
          122  +        private static int count = 0;
          123  +
          124  +        ///////////////////////////////////////////////////////////////////////
          125  +
          126  +        public static void MyProgressHandler(
          127  +          object sender,
          128  +          ProgressEventArgs e
          129  +          )
          130  +        {
          131  +          count++;
          132  +        }
          133  +
          134  +        ///////////////////////////////////////////////////////////////////////
          135  +
          136  +        public static int Main()
          137  +        {
          138  +          using (SQLiteConnection connection = new SQLiteConnection(
          139  +              "Data Source=${dataSource};ProgressOps=1;[getFlagsProperty]"))
          140  +          {
          141  +            connection.Progress += MyProgressHandler;
          142  +            connection.Open();
          143  +
          144  +            using (SQLiteCommand command = new SQLiteCommand("${sql}",
          145  +                connection))
          146  +            {
          147  +              command.ExecuteNonQuery();
          148  +            }
          149  +          }
          150  +
          151  +          return count;
          152  +        }
          153  +      }
          154  +    }
          155  +  }] true true true results errors System.Data.SQLite.dll]
          156  +
          157  +  list $code $results \
          158  +      [expr {[info exists errors] ? $errors : ""}] \
          159  +      [expr {$code eq "Ok" ? [catch {
          160  +        object invoke _Dynamic${id}.Test${id} Main
          161  +      } result] : [set result ""]}] $result \
          162  +      [expr {[string is integer -strict $result] && $result > 0 ? 1 : 0}]
          163  +} -cleanup {
          164  +  cleanupDb $fileName
          165  +
          166  +  unset -nocomplain result results errors code sql dataSource id db fileName
          167  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          168  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          169  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+ 1$}}
          170  +
          171  +###############################################################################
          172  +
          173  +runTest {test progress-1.3 {progress with interrupt} -setup {
          174  +  setupDb [set fileName progress-1.3.db]
          175  +} -body {
          176  +  set id [object invoke Interpreter.GetActive NextId]
          177  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          178  +
          179  +  set sql { \
          180  +    CREATE TABLE t1(x INTEGER); \
          181  +    INSERT INTO t1 (x) VALUES(1); \
          182  +    INSERT INTO t1 (x) VALUES(2); \
          183  +    INSERT INTO t1 (x) VALUES(3); \
          184  +    INSERT INTO t1 (x) VALUES(4); \
          185  +    SELECT x FROM t1 ORDER BY x; \
          186  +  }
          187  +
          188  +  unset -nocomplain results errors
          189  +
          190  +  set code [compileCSharpWith [subst {
          191  +    using System.Data.SQLite;
          192  +
          193  +    namespace _Dynamic${id}
          194  +    {
          195  +      public static class Test${id}
          196  +      {
          197  +        private static int count = 0;
          198  +
          199  +        ///////////////////////////////////////////////////////////////////////
          200  +
          201  +        public static void MyProgressHandler(
          202  +          object sender,
          203  +          ProgressEventArgs e
          204  +          )
          205  +        {
          206  +          count++;
          207  +          e.ReturnCode = SQLiteProgressReturnCode.Interrupt;
          208  +        }
          209  +
          210  +        ///////////////////////////////////////////////////////////////////////
          211  +
          212  +        public static int Main()
          213  +        {
          214  +          using (SQLiteConnection connection = new SQLiteConnection(
          215  +              "Data Source=${dataSource};ProgressOps=1;[getFlagsProperty]"))
          216  +          {
          217  +            connection.Progress += MyProgressHandler;
          218  +            connection.Open();
          219  +
          220  +            using (SQLiteCommand command = new SQLiteCommand("${sql}",
          221  +                connection))
          222  +            {
          223  +              command.ExecuteNonQuery();
          224  +            }
          225  +          }
          226  +
          227  +          return count;
          228  +        }
          229  +      }
          230  +    }
          231  +  }] true true true results errors System.Data.SQLite.dll]
          232  +
          233  +  list $code $results \
          234  +      [expr {[info exists errors] ? $errors : ""}] \
          235  +      [expr {$code eq "Ok" ? [catch {
          236  +        object invoke _Dynamic${id}.Test${id} Main
          237  +      } result] : [set result ""]}] $result \
          238  +      [expr {[string is integer -strict $result] && $result > 0 ? 1 : 0}]
          239  +} -cleanup {
          240  +  cleanupDb $fileName
          241  +
          242  +  unset -nocomplain result results errors code sql dataSource id db fileName
          243  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          244  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          245  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+ 1$}}
          246  +
          247  +###############################################################################
          248  +
          249  +runTest {test progress-1.4 {progress with exception} -setup {
          250  +  setupDb [set fileName progress-1.4.db]
          251  +} -body {
          252  +  set id [object invoke Interpreter.GetActive NextId]
          253  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          254  +
          255  +  set sql { \
          256  +    CREATE TABLE t1(x INTEGER); \
          257  +    INSERT INTO t1 (x) VALUES(1); \
          258  +    INSERT INTO t1 (x) VALUES(2); \
          259  +    INSERT INTO t1 (x) VALUES(3); \
          260  +    INSERT INTO t1 (x) VALUES(4); \
          261  +    SELECT x FROM t1 ORDER BY x; \
          262  +  }
          263  +
          264  +  unset -nocomplain results errors
          265  +
          266  +  set code [compileCSharpWith [subst {
          267  +    using System;
          268  +    using System.Data.SQLite;
          269  +
          270  +    namespace _Dynamic${id}
          271  +    {
          272  +      public static class Test${id}
          273  +      {
          274  +        private static int count = 0;
          275  +
          276  +        ///////////////////////////////////////////////////////////////////////
          277  +
          278  +        public static void MyProgressHandler(
          279  +          object sender,
          280  +          ProgressEventArgs e
          281  +          )
          282  +        {
          283  +          count++;
          284  +          throw new Exception();
          285  +        }
          286  +
          287  +        ///////////////////////////////////////////////////////////////////////
          288  +
          289  +        public static int Main()
          290  +        {
          291  +          using (SQLiteConnection connection = new SQLiteConnection(
          292  +              "Data Source=${dataSource};ProgressOps=1;[getFlagsProperty]"))
          293  +          {
          294  +            connection.Progress += MyProgressHandler;
          295  +            connection.Open();
          296  +
          297  +            using (SQLiteCommand command = new SQLiteCommand("${sql}",
          298  +                connection))
          299  +            {
          300  +              command.ExecuteNonQuery();
          301  +            }
          302  +          }
          303  +
          304  +          return count;
          305  +        }
          306  +      }
          307  +    }
          308  +  }] true true true results errors System.Data.SQLite.dll]
          309  +
          310  +  list $code $results \
          311  +      [expr {[info exists errors] ? $errors : ""}] \
          312  +      [expr {$code eq "Ok" ? [catch {
          313  +        object invoke _Dynamic${id}.Test${id} Main
          314  +      } result] : [set result ""]}] $result \
          315  +      [expr {[string is integer -strict $result] && $result > 0 ? 1 : 0}]
          316  +} -cleanup {
          317  +  cleanupDb $fileName
          318  +
          319  +  unset -nocomplain result results errors code sql dataSource id db fileName
          320  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          321  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          322  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+ 1$}}
          323  +
          324  +###############################################################################
          325  +
          326  +runSQLiteTestEpilogue
          327  +runTestEpilogue

Changes to readme.htm.

   211    211   <p>
   212    212       <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
   213    213   </p>
   214    214   <ul>
   215    215       <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_8_11.html">SQLite 3.8.11</a>.</li>
   216    216       <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
   217    217       <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
          218  +    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
          219  +    <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
          220  +    <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
   218    221       <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
   219    222       <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
   220    223       <li>Enable integration with the <a href="http://www.hwaci.com/sw/sqlite/zipvfs.html">ZipVFS</a> extension.</li>
   221    224   </ul>
   222    225   <p>
   223    226       <b>1.0.97.0 - May 26, 2015</b>
   224    227   </p>

Changes to www/news.wiki.

     5      5   <p>
     6      6       <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
     7      7   </p>
     8      8   <ul>
     9      9       <li>Updated to [https://www.sqlite.org/draft/releaselog/3_8_11.html|SQLite 3.8.11].</li>
    10     10       <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    11     11       <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
           12  +    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
           13  +    <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
           14  +    <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
    12     15       <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
    13     16       <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
    14     17       <li>Enable integration with the [http://www.hwaci.com/sw/sqlite/zipvfs.html|ZipVFS] extension.</li>
    15     18   </ul>
    16     19   <p>
    17     20       <b>1.0.97.0 - May 26, 2015</b>
    18     21   </p>