System.Data.SQLite
Check-in [ad79758d0c]
Not logged in

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

Overview
Comment:Defer disposing of the connection created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ad79758d0cf4e60f5c58f60207e353bb71707a31
User & Date: mistachkin 2014-11-07 23:48:19
References
2014-11-07
23:51 Closed ticket [daeaf3150a]: SQLiteCommand.Execute SQLiteExecuteType.Reader support plus 4 other changes artifact: 98ef69fbee user: mistachkin
Context
2014-11-14
00:19
Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe]. check-in: 3bd76a0c9d user: mistachkin tags: trunk
2014-11-08
06:41
Do not emit ORDER BY, LIMIT, and OFFSET clauses for the left SELECT statement when a compound operator is involved. Candidate fix for [0a32885109]. check-in: 1f05aa9dd6 user: mistachkin tags: tkt-0a32885109
2014-11-07
23:48
Defer disposing of the connection created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a]. check-in: ad79758d0c user: mistachkin tags: trunk
22:00
Change support email address in the design-time components. Pursuant to [17405b6e06]. check-in: 9c43b0b95c 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.95.0 - November XX, 2014 <font color="red">(release scheduled)</font></b></p>
    47     47       <ul>
    48     48         <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_7_1.html">SQLite 3.8.7.1</a>.</li>
    49     49         <li>Make sure SQL statements generated for DbUpdateCommandTree objects are properly delimited.</li>
    50     50         <li>Various minor performance enhancements to the SQLiteDataReader class. Pursuant to <a href="http://system.data.sqlite.org/index.html/info/e122d26e70">[e122d26e70]</a>.</li>
           51  +      <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for <a href="http://system.data.sqlite.org/index.html/info/daeaf3150a">[daeaf3150a]</a>.</li>
    51     52         <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for <a href="http://system.data.sqlite.org/index.html/info/c5cc2fb334">[c5cc2fb334]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    52     53       </ul>
    53     54       <p><b>1.0.94.0 - September 9, 2014</b></p>
    54     55       <ul>
    55     56         <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>
    56     57         <li>Updated to <a href="http://www.nuget.org/packages/EntityFramework/6.1.1">Entity Framework 6.1.1</a>.</li>
    57     58         <li>Refactor and simplify NuGet packages in order to support per-solution SQLite interop assembly files.&nbsp;<b>** Potentially Incompatible Change **</b></li>

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

   702    702           return Execute(
   703    703               commandText, executeType, CommandBehavior.Default,
   704    704               connectionString, args);
   705    705       }
   706    706   
   707    707       /// <summary>
   708    708       /// This method creates a new connection, executes the query using the given
   709         -    /// execution type and command behavior, closes the connection, and returns
   710         -    /// the results.  If the connection string is null, a temporary in-memory
   711         -    /// database connection will be used.
          709  +    /// execution type and command behavior, closes the connection unless a data
          710  +    /// reader is created, and returns the results.  If the connection string is
          711  +    /// null, a temporary in-memory database connection will be used.
   712    712       /// </summary>
   713    713       /// <param name="commandText">
   714    714       /// The text of the command to be executed.
   715    715       /// </param>
   716    716       /// <param name="executeType">
   717    717       /// The execution type for the command.  This is used to determine which method
   718    718       /// of the command object to call, which then determines the type of results
................................................................................
   737    737           string commandText,
   738    738           SQLiteExecuteType executeType,
   739    739           CommandBehavior commandBehavior,
   740    740           string connectionString,
   741    741           params object[] args
   742    742           )
   743    743       {
   744         -        if (connectionString == null)
   745         -            connectionString = DefaultConnectionString;
          744  +        SQLiteConnection connection = null;
   746    745   
   747         -        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
          746  +        try
   748    747           {
   749         -            connection.Open();
          748  +            if (connectionString == null)
          749  +                connectionString = DefaultConnectionString;
   750    750   
   751         -            using (SQLiteCommand command = connection.CreateCommand())
          751  +            using (connection = new SQLiteConnection(connectionString))
   752    752               {
   753         -                command.CommandText = commandText;
          753  +                connection.Open();
   754    754   
   755         -                if (args != null)
          755  +                using (SQLiteCommand command = connection.CreateCommand())
   756    756                   {
   757         -                    foreach (object arg in args)
          757  +                    command.CommandText = commandText;
          758  +
          759  +                    if (args != null)
   758    760                       {
   759         -                        if (arg is SQLiteParameter)
   760         -                            command.Parameters.Add((SQLiteParameter)arg);
   761         -                        else
   762         -                            command.Parameters.Add(new SQLiteParameter(DbType.Object, arg));
          761  +                        foreach (object arg in args)
          762  +                        {
          763  +                            if (arg is SQLiteParameter)
          764  +                                command.Parameters.Add((SQLiteParameter)arg);
          765  +                            else
          766  +                                command.Parameters.Add(new SQLiteParameter(DbType.Object, arg));
          767  +                        }
   763    768                       }
   764         -                }
   765         -
   766         -                switch (executeType)
   767         -                {
   768         -                    case SQLiteExecuteType.None:
   769         -                        {
   770         -                            //
   771         -                            // NOTE: Do nothing.
   772         -                            //
   773         -                            break;
   774         -                        }
   775         -                    case SQLiteExecuteType.NonQuery:
   776         -                        {
   777         -                            return command.ExecuteNonQuery(commandBehavior);
   778         -                        }
   779         -                    case SQLiteExecuteType.Scalar:
   780         -                        {
   781         -                            return command.ExecuteScalar(commandBehavior);
   782         -                        }
   783         -                    case SQLiteExecuteType.Reader:
   784         -                        {
   785         -                            return command.ExecuteReader(commandBehavior);
   786         -                        }
          769  +
          770  +                    switch (executeType)
          771  +                    {
          772  +                        case SQLiteExecuteType.None:
          773  +                            {
          774  +                                //
          775  +                                // NOTE: Do nothing.
          776  +                                //
          777  +                                break;
          778  +                            }
          779  +                        case SQLiteExecuteType.NonQuery:
          780  +                            {
          781  +                                return command.ExecuteNonQuery(commandBehavior);
          782  +                            }
          783  +                        case SQLiteExecuteType.Scalar:
          784  +                            {
          785  +                                return command.ExecuteScalar(commandBehavior);
          786  +                            }
          787  +                        case SQLiteExecuteType.Reader:
          788  +                            {
          789  +                                bool success = true;
          790  +
          791  +                                try
          792  +                                {
          793  +                                    //
          794  +                                    // NOTE: The CloseConnection flag is being added here.
          795  +                                    //       This should force the returned data reader to
          796  +                                    //       close the connection when it is disposed.  In
          797  +                                    //       order to prevent the containing using block
          798  +                                    //       from disposing the connection prematurely,
          799  +                                    //       the innermost finally block sets the internal
          800  +                                    //       no-disposal flag to true.  The outer finally
          801  +                                    //       block will reset the internal no-disposal flag
          802  +                                    //       to false so that the data reader will be able
          803  +                                    //       to (eventually) dispose of the connection.
          804  +                                    //
          805  +                                    return command.ExecuteReader(
          806  +                                        commandBehavior | CommandBehavior.CloseConnection);
          807  +                                }
          808  +                                catch
          809  +                                {
          810  +                                    success = false;
          811  +                                    throw;
          812  +                                }
          813  +                                finally
          814  +                                {
          815  +                                    //
          816  +                                    // NOTE: If an exception was not thrown, that can only
          817  +                                    //       mean the data reader was successfully created
          818  +                                    //       and now owns the connection.  Therefore, set
          819  +                                    //       the internal no-disposal flag (temporarily)
          820  +                                    //       in order to exit the containing using block
          821  +                                    //       without disposing it.
          822  +                                    //
          823  +                                    if (success)
          824  +                                        connection._noDispose = true;
          825  +                                }
          826  +                            }
          827  +                    }
   787    828                   }
   788    829               }
   789    830           }
          831  +        finally
          832  +        {
          833  +            //
          834  +            // NOTE: Now that the using block has been exited, reset the
          835  +            //       internal disposal flag for the connection.  This is
          836  +            //       always done if the connection was created because
          837  +            //       it will be harmless whether or not the data reader
          838  +            //       now owns it.
          839  +            //
          840  +            if (connection != null)
          841  +                connection._noDispose = false;
          842  +        }
   790    843   
   791    844           return null;
   792    845       }
   793    846   
   794    847       /// <summary>
   795    848       /// Overrides the default behavior to return a SQLiteDataReader specialization class
   796    849       /// </summary>

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

   320    320     /// <b>False</b> - Skip attempting to expand the data source file name to a fully qualified path before opening.
   321    321     /// </description>
   322    322     /// <description>N</description>
   323    323     /// <description>True</description>
   324    324     /// </item>
   325    325     /// </list>
   326    326     /// </remarks>
   327         -  public sealed partial class SQLiteConnection : DbConnection, ICloneable
          327  +  public sealed partial class SQLiteConnection : DbConnection, ICloneable, IDisposable
   328    328     {
   329    329       #region Private Constants
   330    330       /// <summary>
   331    331       /// The "invalid value" for the <see cref="DbType" /> enumeration used
   332    332       /// by the <see cref="DefaultDbType" /> property.  This constant is shared
   333    333       /// by this class and the SQLiteConnectionStringBuilder class.
   334    334       /// </summary>
................................................................................
   430    430       /// </summary>
   431    431       private string _connectionString;
   432    432       /// <summary>
   433    433       /// Nesting level of the transactions open on the connection
   434    434       /// </summary>
   435    435       internal int _transactionLevel;
   436    436   
          437  +    /// <summary>
          438  +    /// If this flag is non-zero, the <see cref="Dispose()" /> method will have
          439  +    /// no effect; however, the <see cref="Close" /> method will continue to
          440  +    /// behave as normal.
          441  +    /// </summary>
          442  +    internal bool _noDispose;
          443  +
   437    444       /// <summary>
   438    445       /// If set, then the connection is currently being disposed.
   439    446       /// </summary>
   440    447       private bool _disposing;
   441    448   
   442    449       /// <summary>
   443    450       /// The default isolation level for new transactions
................................................................................
   603    610       /// </param>
   604    611       /// <param name="parseViaFramework">
   605    612       /// Non-zero to parse the connection string using the built-in (i.e.
   606    613       /// framework provided) parser when opening the connection.
   607    614       /// </param>
   608    615       public SQLiteConnection(string connectionString, bool parseViaFramework)
   609    616       {
          617  +      _noDispose = false;
          618  +
   610    619   #if (SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK) && PRELOAD_NATIVE_LIBRARY
   611    620         UnsafeNativeMethods.Initialize();
   612    621   #endif
   613    622   
   614    623         SQLiteLog.Initialize();
   615    624   
   616    625   #if !PLATFORM_COMPACTFRAMEWORK && !INTEROP_LEGACY_CLOSE && SQLITE_STANDARD
................................................................................
  1240   1249                   dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
  1241   1250                   false);
  1242   1251           }
  1243   1252       }
  1244   1253   
  1245   1254       ///////////////////////////////////////////////////////////////////////////////////////////////
  1246   1255   
         1256  +    #region IDisposable Members
         1257  +    /// <summary>
         1258  +    /// Disposes and finalizes the connection, if applicable.
         1259  +    /// </summary>
         1260  +    public new void Dispose()
         1261  +    {
         1262  +        if (_noDispose)
         1263  +            return;
         1264  +
         1265  +        base.Dispose();
         1266  +    }
         1267  +    #endregion
         1268  +
         1269  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         1270  +
  1247   1271       #region IDisposable "Pattern" Members
  1248   1272       private bool disposed;
  1249   1273       private void CheckDisposed() /* throw */
  1250   1274       {
  1251   1275   #if THROW_ON_DISPOSED
  1252   1276           if (disposed)
  1253   1277               throw new ObjectDisposedException(typeof(SQLiteConnection).Name);
................................................................................
  1254   1278   #endif
  1255   1279       }
  1256   1280   
  1257   1281       ///////////////////////////////////////////////////////////////////////////////////////////////
  1258   1282   
  1259   1283       protected override void Dispose(bool disposing)
  1260   1284       {
         1285  +#if !NET_COMPACT_20 && TRACE_WARNING
         1286  +        if ((_flags & SQLiteConnectionFlags.TraceWarning) == SQLiteConnectionFlags.TraceWarning)
         1287  +        {
         1288  +            if (_noDispose)
         1289  +            {
         1290  +                System.Diagnostics.Trace.WriteLine(String.Format(CultureInfo.CurrentCulture,
         1291  +                    "WARNING: Disposing of connection \"{0}\" with the no-dispose flag set.",
         1292  +                    _connectionString));
         1293  +            }
         1294  +        }
         1295  +#endif
         1296  +
  1261   1297           _disposing = true;
  1262   1298   
  1263   1299           try
  1264   1300           {
  1265   1301               if (!disposed)
  1266   1302               {
  1267   1303                   //if (disposing)

Added Tests/tkt-daeaf3150a.eagle.

            1  +###############################################################################
            2  +#
            3  +# tkt-daeaf3150a.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 tkt-daeaf3150a-1.1 {static Execute connection disposal} -setup {
           24  +  unset -nocomplain result sql
           25  +} -body {
           26  +  set sql(1) { \
           27  +    CREATE TABLE t1(x); \
           28  +    INSERT INTO t1 (x) VALUES (NULL); \
           29  +    SELECT x FROM t1 ORDER BY x; \
           30  +  }
           31  +
           32  +  set sql(2) { \
           33  +    CREATE TABLE t1(x); \
           34  +    INSERT INTO t1 (x) VALUES (?); \
           35  +    SELECT x FROM t1 ORDER BY x; \
           36  +  }
           37  +
           38  +  set result(1) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           39  +      "this will not execute" None null]
           40  +
           41  +  set result(2) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           42  +      $sql(1) NonQuery null]
           43  +
           44  +  set result(3) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           45  +      $sql(1) Scalar null]
           46  +
           47  +  set result(4) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           48  +      $sql(1) Reader null]
           49  +
           50  +  object invoke $result(4) Read; object invoke $result(4) Close
           51  +
           52  +  set result(5) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           53  +      "this will not execute" None null 1]
           54  +
           55  +  set result(6) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           56  +      $sql(2) NonQuery null 1]
           57  +
           58  +  set result(7) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           59  +      $sql(2) Scalar null 1]
           60  +
           61  +  set result(8) [object invoke System.Data.SQLite.SQLiteCommand Execute \
           62  +      $sql(2) Reader null 1]
           63  +
           64  +  list $result(1) $result(2) $result(3) $result(4) $result(5) $result(6) \
           65  +      $result(7) $result(8)
           66  +} -cleanup {
           67  +  unset -nocomplain result sql
           68  +} -constraints {eagle monoBug28 SQLite System.Data.SQLite} -match regexp \
           69  +-result {^\{\} 1 System#DBNull#\d+ System#Data#SQLite#SQLiteDataReader#\d+ \{\}\
           70  +1 1 System#Data#SQLite#SQLiteDataReader#\d+$}}
           71  +
           72  +###############################################################################
           73  +
           74  +runSQLiteTestEpilogue
           75  +runTestEpilogue

Changes to readme.htm.

   211    211   <p>
   212    212       <b>1.0.95.0 - November XX, 2014 <font color="red">(release scheduled)</font></b>
   213    213   </p>
   214    214   <ul>
   215    215       <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_7.html">SQLite 3.8.7</a>.</li>
   216    216       <li>Make sure SQL statements generated for DbUpdateCommandTree objects are properly delimited.</li>
   217    217       <li>Various minor performance enhancements to the SQLiteDataReader class. Pursuant to [e122d26e70].</li>
          218  +    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
   218    219       <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
   219    220   </ul>
   220    221   <p>
   221    222       <b>1.0.94.0 - September 9, 2014</b>
   222    223   </p>
   223    224   <ul>
   224    225       <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>

Changes to www/news.wiki.

     5      5   <p>
     6      6       <b>1.0.95.0 - November XX, 2014 <font color="red">(release scheduled)</font></b>
     7      7   </p>
     8      8   <ul>
     9      9       <li>Updated to [http://www.sqlite.org/releaselog/3_8_7_1.html|SQLite 3.8.7.1].</li>
    10     10       <li>Make sure SQL statements generated for DbUpdateCommandTree objects are properly delimited.</li>
    11     11       <li>Various minor performance enhancements to the SQLiteDataReader class. Pursuant to [e122d26e70].</li>
           12  +    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
    12     13       <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity.  Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    13     14   </ul>
    14     15   <p>
    15     16       <b>1.0.94.0 - September 9, 2014</b>
    16     17   </p>
    17     18   <ul>
    18     19       <li>Updated to [http://www.sqlite.org/releaselog/3_8_6.html|SQLite 3.8.6].</li>