System.Data.SQLite
Check-in [1808779aa2]
Not logged in

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

Overview
Comment:Add SQLiteSourceId property to the SQLiteConnection class to return the SQLite core library source identifier. Enhance and revising Trace output (in DEBUG only) to be more accurate and to report resource cleanup exceptions. More work on unit testing infrastructure and the test case for ticket [e30b820248]. The SQLite3 class should always attempt to dispose the contained SQLiteConnectionHandle, even when called via the finalizer.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tkt-e30b820248
Files: files | file ages | folders
SHA1: 1808779aa2a28a3b0072e1aa7739ba0d4b776436
User & Date: mistachkin 2011-11-15 04:01:14
Context
2011-11-15
05:02
Plug memory leak in the sqlite3_close_interop function when a statement cannot be finalized. Have the vendor-specific initialization file for Eagle automatically set the TestPath of the interpreter. Closed-Leaf check-in: 10d400ebd0 user: mistachkin tags: tkt-e30b820248
04:01
Add SQLiteSourceId property to the SQLiteConnection class to return the SQLite core library source identifier. Enhance and revising Trace output (in DEBUG only) to be more accurate and to report resource cleanup exceptions. More work on unit testing infrastructure and the test case for ticket [e30b820248]. The SQLite3 class should always attempt to dispose the contained SQLiteConnectionHandle, even when called via the finalizer. check-in: 1808779aa2 user: mistachkin tags: tkt-e30b820248
2011-11-14
05:40
Allow the SQLiteLog class to be used without having an open connection (i.e. and skip going through the SQLite core library to do it). check-in: 374756ec29 user: mistachkin tags: tkt-e30b820248
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

    46     46       internal SQLite3(SQLiteDateFormats fmt, DateTimeKind kind)
    47     47         : base(fmt, kind)
    48     48       {
    49     49       }
    50     50   
    51     51       protected override void Dispose(bool bDisposing)
    52     52       {
    53         -      if (bDisposing)
    54         -        Close();
           53  +      Close();
    55     54       }
    56     55   
    57     56       // It isn't necessary to cleanup any functions we've registered.  If the connection
    58     57       // goes to the pool and is resurrected later, re-registered functions will overwrite the
    59     58       // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
    60     59       // resources belonging to the previously-registered functions.
    61     60       internal override void Close()
................................................................................
    90     89       internal static string SQLiteVersion
    91     90       {
    92     91         get
    93     92         {
    94     93           return UTF8ToString(UnsafeNativeMethods.sqlite3_libversion(), -1);
    95     94         }
    96     95       }
           96  +
           97  +    internal static string SQLiteSourceId
           98  +    {
           99  +      get
          100  +      {
          101  +        return UTF8ToString(UnsafeNativeMethods.sqlite3_sourceid(), -1);
          102  +      }
          103  +    }
    97    104   
    98    105       internal override bool AutoCommit
    99    106       {
   100    107         get
   101    108         {
   102    109           return IsAutocommit(_sql);
   103    110         }

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

   217    217       /// </summary>
   218    218       internal SQLiteStatement BuildNextCommand()
   219    219       {
   220    220         SQLiteStatement stmt = null;
   221    221   
   222    222         try
   223    223         {
   224         -        if (_statementList == null)
   225         -          _remainingText = _commandText;
   226         -
   227         -        stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText);
   228         -        if (stmt != null)
          224  +        if ((_cnn != null) && (_cnn._sql != null))
   229    225           {
   230         -          stmt._command = this;
   231    226             if (_statementList == null)
   232         -            _statementList = new List<SQLiteStatement>();
          227  +            _remainingText = _commandText;
   233    228   
   234         -          _statementList.Add(stmt);
          229  +          stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText);
   235    230   
   236         -          _parameterCollection.MapParameters(stmt);
   237         -          stmt.BindParameters();
   238         -        }        
          231  +          if (stmt != null)
          232  +          {
          233  +            stmt._command = this;
          234  +
          235  +            if (_statementList == null)
          236  +              _statementList = new List<SQLiteStatement>();
          237  +
          238  +            _statementList.Add(stmt);
          239  +
          240  +            _parameterCollection.MapParameters(stmt);
          241  +            stmt.BindParameters();
          242  +          }
          243  +        }
   239    244           return stmt;
   240    245         }
   241    246         catch (Exception)
   242    247         {
   243    248           if (stmt != null)
   244    249           {
   245         -          if (_statementList.Contains(stmt))
          250  +          if ((_statementList != null) && _statementList.Contains(stmt))
   246    251               _statementList.Remove(stmt);
   247    252   
   248    253             stmt.Dispose();
   249    254           }
   250    255   
   251    256           // If we threw an error compiling the statement, we cannot continue on so set the remaining text to null.
   252    257           _remainingText = null;

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

   483    483             _sql = null;
   484    484             _enlistment = null;
   485    485           }
   486    486   #endif
   487    487           if (_sql != null)
   488    488           {
   489    489             _sql.Close();
          490  +          _sql = null;
   490    491           }
   491         -        _sql = null;
   492    492           _transactionLevel = 0;
   493    493         }
   494    494         OnStateChange(ConnectionState.Closed);
   495    495       }
   496    496   
   497    497       /// <summary>
   498    498       /// Clears the connection pool associated with the connection.  Any other active connections using the same database file
................................................................................
  1075   1075       /// <summary>
  1076   1076       /// Returns the version of the underlying SQLite database engine
  1077   1077       /// </summary>
  1078   1078       public static string SQLiteVersion
  1079   1079       {
  1080   1080         get { return SQLite3.SQLiteVersion; }
  1081   1081       }
         1082  +
         1083  +    /// <summary>
         1084  +    /// This method returns the string whose value is the same as the
         1085  +    /// SQLITE_SOURCE_ID C preprocessor macro used when compiling the
         1086  +    /// SQLite core library.
         1087  +    /// </summary>
         1088  +    public static string SQLiteSourceId
         1089  +    {
         1090  +      get { return SQLite3.SQLiteSourceId; }
         1091  +    }
  1082   1092   
  1083   1093       /// <summary>
  1084   1094       /// Returns the state of the connection.
  1085   1095       /// </summary>
  1086   1096   #if !PLATFORM_COMPACTFRAMEWORK
  1087   1097       [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  1088   1098   #endif

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

   353    353       [DllImport(SQLITE_DLL)]
   354    354   #endif
   355    355       internal static extern IntPtr sqlite3_libversion();
   356    356   
   357    357   #if !PLATFORM_COMPACTFRAMEWORK
   358    358       [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
   359    359   #else
          360  +    [DllImport(SQLITE_DLL)]
          361  +#endif
          362  +    internal static extern IntPtr sqlite3_sourceid();
          363  +
          364  +#if !PLATFORM_COMPACTFRAMEWORK
          365  +    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
          366  +#else
   360    367       [DllImport(SQLITE_DLL)]
   361    368   #endif
   362    369       internal static extern void sqlite3_interrupt(IntPtr db);
   363    370   
   364    371   #if !PLATFORM_COMPACTFRAMEWORK
   365    372       [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
   366    373   #else
................................................................................
   872    879       {
   873    880       }
   874    881   
   875    882       protected override bool ReleaseHandle()
   876    883       {
   877    884         try
   878    885         {
          886  +        SQLiteBase.CloseConnection(this);
          887  +
   879    888   #if DEBUG
   880         -        Trace.WriteLine(String.Format("CloseConnection: {0}", handle));
          889  +        try
          890  +        {
          891  +          Trace.WriteLine(String.Format(
          892  +              "CloseConnection: {0}", handle));
          893  +        }
          894  +        catch
          895  +        {
          896  +        }
   881    897   #endif
   882    898   
   883         -        SQLiteBase.CloseConnection(this);
   884    899   #if DEBUG
   885    900           return true;
   886    901   #endif
   887    902         }
          903  +#if DEBUG
          904  +      catch (SQLiteException e)
          905  +#else
   888    906         catch (SQLiteException)
          907  +#endif
   889    908         {
          909  +#if DEBUG
          910  +        try
          911  +        {
          912  +          Trace.WriteLine(String.Format(
          913  +              "CloseConnection: {0}, exception: {1}",
          914  +              handle, e));
          915  +        }
          916  +        catch
          917  +        {
          918  +        }
          919  +#endif
   890    920         }
   891    921   #if DEBUG
   892    922         return false;
   893    923   #else
   894    924         return true;
   895    925   #endif
   896    926       }
................................................................................
   925    955       {
   926    956       }
   927    957   
   928    958       protected override bool ReleaseHandle()
   929    959       {
   930    960         try
   931    961         {
          962  +        SQLiteBase.FinalizeStatement(this);
          963  +
   932    964   #if DEBUG
   933         -        Trace.WriteLine(String.Format("FinalizeStatement: {0}", handle));
          965  +        try
          966  +        {
          967  +          Trace.WriteLine(String.Format(
          968  +              "FinalizeStatement: {0}", handle));
          969  +        }
          970  +        catch
          971  +        {
          972  +        }
   934    973   #endif
   935    974   
   936         -        SQLiteBase.FinalizeStatement(this);
   937    975   #if DEBUG
   938    976           return true;
   939    977   #endif
   940    978         }
          979  +#if DEBUG
          980  +      catch (SQLiteException e)
          981  +#else
   941    982         catch (SQLiteException)
          983  +#endif
   942    984         {
          985  +#if DEBUG
          986  +        try
          987  +        {
          988  +          Trace.WriteLine(String.Format(
          989  +              "FinalizeStatement: {0}, exception: {1}",
          990  +              handle, e));
          991  +        }
          992  +        catch
          993  +        {
          994  +        }
          995  +#endif
   943    996         }
   944    997   #if DEBUG
   945    998         return false;
   946    999   #else
   947   1000         return true;
   948   1001   #endif
   949   1002       }

Changes to Tests/common.eagle.

   300    300       }
   301    301   
   302    302       proc checkForSQLite { channel } {
   303    303         tputs $channel "---- checking for core SQLite library... "
   304    304   
   305    305         if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \
   306    306                 SQLiteVersion} version] == 0} then {
          307  +        #
          308  +        # NOTE: Attempt to query the Fossil source identifier for the SQLite
          309  +        #       core library.
          310  +        #
          311  +        if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \
          312  +                SQLiteSourceId} sourceId]} then {
          313  +          #
          314  +          # NOTE: We failed to query the Fossil source identifier.
          315  +          #
          316  +          set sourceId unknown
          317  +        }
          318  +
   307    319           #
   308    320           # NOTE: Yes, the SQLite core library appears to be available.
   309    321           #
   310    322           addConstraint SQLite
   311    323   
   312         -        tputs $channel [appendArgs "yes (" $version ")\n"]
          324  +        tputs $channel [appendArgs "yes (" $version " " $sourceId ")\n"]
   313    325         } else {
   314    326           tputs $channel no\n
   315    327         }
   316    328       }
   317    329   
   318    330       proc getDateTimeFormat {} {
   319    331         #
................................................................................
   398    410         #       the temporary directory.  Each database used by a test should be
   399    411         #       cleaned up by that test using the "cleanupDb" procedure, below.
   400    412         #
   401    413         set fileName [file join [getTemporaryPath] [file tail $fileName]]
   402    414   
   403    415         #
   404    416         # NOTE: By default, delete any pre-existing database with the same file
   405         -      #       name.
          417  +      #       name if it currently exists.
   406    418         #
   407         -      if {$delete} then {
   408         -        catch {file delete $fileName}
          419  +      if {$delete && [file exists $fileName]} then {
          420  +        if {[catch {file delete $fileName} error]} then {
          421  +          #
          422  +          # NOTE: We somehow failed to delete the file, report why.
          423  +          #
          424  +          tputs $::test_channel [appendArgs \
          425  +              "==== WARNING: failed to delete database file \"" $fileName \
          426  +              "\" during setup, error: " \n\t $error \n]
          427  +        }
   409    428         }
   410    429   
   411    430         #
   412    431         # NOTE: Refer to the specified variable (e.g. "db") in the context of the
   413    432         #       caller.  The handle to the opened database will be stored there.
   414    433         #
   415    434         upvar 1 $varName db
................................................................................
   466    485         #
   467    486         upvar 1 $varName db
   468    487   
   469    488         #
   470    489         # NOTE: Close the connection to the database now.  This should allow us to
   471    490         #       delete the underlying database file.
   472    491         #
   473         -      catch {sql close $db}
          492  +      if {[info exists db] && [catch {sql close $db} error]} then {
          493  +        #
          494  +        # NOTE: We somehow failed to close the database, report why.
          495  +        #
          496  +        tputs $::test_channel [appendArgs \
          497  +            "==== WARNING: failed to close database \"" $db "\", error: " \
          498  +            \n\t $error \n]
          499  +      }
   474    500   
   475    501         #
   476         -      # NOTE: Delete the test database file now.  For now, all test database
   477         -      #       files are stored in the temporary directory.
          502  +      # NOTE: Attempt to delete the test database file now.  For now, all test
          503  +      #       database files are stored in the temporary directory.
   478    504         #
   479         -      catch {file delete [file join [getTemporaryPath] [file tail $fileName]]}
          505  +      set fileName [file join [getTemporaryPath] [file tail $fileName]]
          506  +
          507  +      if {[catch {file delete $fileName} error]} then {
          508  +        #
          509  +        # NOTE: We somehow failed to delete the file, report why.
          510  +        #
          511  +        tputs $::test_channel [appendArgs \
          512  +            "==== WARNING: failed to delete database file \"" $fileName \
          513  +            "\" during cleanup, error: " \n\t $error \n]
          514  +      }
   480    515       }
   481    516   
   482         -    proc reportSQLiteResources { channel } {
   483         -      tputs $channel "---- current memory in use by SQLite... "
          517  +    proc reportSQLiteResources { channel {quiet false} } {
          518  +      #
          519  +      # NOTE: Skip all output if we are running in "quiet" mode.
          520  +      #
          521  +      if {!$quiet} then {
          522  +        tputs $channel "---- current memory in use by SQLite... "
          523  +      }
   484    524   
   485    525         if {[catch {object invoke -flags +NonPublic \
   486    526                 System.Data.SQLite.UnsafeNativeMethods \
   487    527                 sqlite3_memory_used} memory] == 0} then {
   488         -        tputs $channel [appendArgs $memory " bytes\n"]
          528  +        if {!$quiet} then {
          529  +          tputs $channel [appendArgs $memory " bytes\n"]
          530  +        }
   489    531         } else {
   490    532           #
   491    533           # NOTE: Maybe the SQLite native library is unavailable?
   492    534           #
   493         -        tputs $channel unknown\n
          535  +        set memory unknown
          536  +
          537  +        if {!$quiet} then {
          538  +          tputs $channel [appendArgs $memory \n]
          539  +        }
   494    540         }
   495    541   
   496         -      tputs $channel "---- maximum memory in use by SQLite... "
          542  +      set result $memory; # NOTE: Return memory in-use to caller.
          543  +
          544  +      if {!$quiet} then {
          545  +        tputs $channel "---- maximum memory in use by SQLite... "
          546  +      }
   497    547   
   498    548         if {[catch {object invoke -flags +NonPublic \
   499    549                 System.Data.SQLite.UnsafeNativeMethods \
   500    550                 sqlite3_memory_highwater 0} memory] == 0} then {
   501         -        tputs $channel [appendArgs $memory " bytes\n"]
          551  +        if {!$quiet} then {
          552  +          tputs $channel [appendArgs $memory " bytes\n"]
          553  +        }
   502    554         } else {
   503    555           #
   504    556           # NOTE: Maybe the SQLite native library is unavailable?
   505    557           #
   506         -        tputs $channel unknown\n
          558  +        set memory unknown
          559  +
          560  +        if {!$quiet} then {
          561  +          tputs $channel [appendArgs $memory \n]
          562  +        }
   507    563         }
          564  +
          565  +      return $result
   508    566       }
   509    567   
   510    568       proc runSQLiteTestPrologue {} {
   511    569         #
   512    570         # NOTE: Skip running our custom prologue if the main one has been skipped.
   513    571         #
   514    572         if {![info exists ::no(prologue.eagle)]} then {
................................................................................
   517    575           #       copying, and loading) if we are so instructed.
   518    576           #
   519    577           if {![info exists ::no(sqliteFiles)]} then {
   520    578             #
   521    579             # NOTE: Skip trying to delete any files if we are so instructed.
   522    580             #
   523    581             if {![info exists ::no(deleteSqliteFiles)]} then {
          582  +            tryDeleteAssembly sqlite3.dll
   524    583               tryDeleteAssembly SQLite.Interop.dll
   525    584               tryDeleteAssembly System.Data.SQLite.dll
   526    585               tryDeleteAssembly System.Data.SQLite.Linq.dll
   527    586             }
   528    587   
   529    588             #
   530    589             # NOTE: Skip trying to copy any files if we are so instructed.
   531    590             #
   532    591             if {![info exists ::no(copySqliteFiles)]} then {
          592  +            tryCopyAssembly sqlite3.dll
   533    593               tryCopyAssembly SQLite.Interop.dll
   534    594               tryCopyAssembly System.Data.SQLite.dll
   535    595               tryCopyAssembly System.Data.SQLite.Linq.dll
   536    596             }
   537    597   
   538    598             #
   539    599             # NOTE: Skip trying to load any files if we are so instructed.

Changes to Tests/tkt-e30b820248.eagle.

    16     16   ###############################################################################
    17     17   
    18     18   package require System.Data.SQLite.Test
    19     19   runSQLiteTestPrologue
    20     20   
    21     21   ###############################################################################
    22     22   
    23         -runTest {test tkt-e30b820248-1.1 {statement disposal ordering} -setup {
    24         -  setupDb [set fileName tkt-e30b820248-1.1.db]
           23  +set memory_used [reportSQLiteResources $test_channel true]
           24  +
           25  +###############################################################################
           26  +
           27  +runTest {test tkt-e30b820248-1.1 {disposal ordering} -setup {
           28  +  set fileName tkt-e30b820248-1.1.db
    25     29   } -body {
    26     30     set id [object invoke Interpreter.GetActive NextId]
    27     31     set dataSource [file join [getTemporaryPath] $fileName]
    28     32     set name [file rootname [file tail $fileName]]
    29     33   
    30     34     set sql { \
    31     35       CREATE TABLE t1 (id1 INTEGER); \
    32     36       INSERT INTO t1 (id1) VALUES (1); \
    33     37       INSERT INTO t1 (id1) VALUES (2); \
    34     38       INSERT INTO t1 (id1) VALUES (?); \
    35     39       INSERT INTO t1 (id1) VALUES (?); \
    36     40       INSERT INTO t1 (id1) VALUES (?); \
           41  +    SELECT * FROM t1 ORDER BY id1; \
    37     42     }
    38     43   
    39     44     unset -nocomplain results errors
    40     45   
    41     46     set code [compileCSharpWith [subst {
    42     47       using System.Data.SQLite;
    43     48       using System.Diagnostics;
           49  +    using System.IO;
    44     50   
    45     51       namespace _Dynamic${id}
    46     52       {
    47     53         public class Test${id}
    48     54         {
    49     55           public static void Main()
    50     56           {
    51     57             using (TraceListener listener = new TextWriterTraceListener(
    52         -              "${test_log}", "${name}"))
           58  +              new FileStream("${test_log}", FileMode.Append,
           59  +                  FileAccess.Write, FileShare.ReadWrite), "${name}"))
    53     60             {
    54     61               Trace.Listeners.Add(listener);
    55     62               Trace.WriteLine("---- START TRACE \\"${name}\\"");
    56     63   
    57     64               using (SQLiteConnection connection = new SQLiteConnection(
    58     65                   "Data Source=${dataSource};"))
    59     66               {
................................................................................
    91     98       }
    92     99     }] results errors System.Data.SQLite.dll]
    93    100   
    94    101     list $code $results \
    95    102         [expr {[info exists errors] ? $errors : ""}] \
    96    103         [expr {$code eq "Ok" ? [catch {
    97    104           object invoke _Dynamic${id}.Test${id} Main
    98         -      } result] : [set result ""]}] $result
          105  +      } result] : [set result ""]}] $result \
          106  +      [reportSQLiteResources $test_channel true]
    99    107   } -cleanup {
   100    108     cleanupDb $fileName
   101    109   
   102    110     unset -nocomplain result code results errors sql dataSource id db fileName
   103    111   } -constraints \
   104    112   {eagle logFile monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \
   105         --match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\
   106         -\{\}$}}
          113  +-match regexp -result [appendArgs "^Ok\
          114  +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]}
          115  +
          116  +###############################################################################
          117  +
          118  +for {set i 2} {$i < 4} {incr i} {
          119  +  set memory_used [reportSQLiteResources $test_channel true]
          120  +
          121  +  #############################################################################
          122  +
          123  +  runTest {test [appendArgs tkt-e30b820248-1. $i] {disposal ordering} -setup {
          124  +    set fileName [appendArgs tkt-e30b820248-1. $i .db]
          125  +  } -body {
          126  +    set id [object invoke Interpreter.GetActive NextId]
          127  +    set dataSource [file join [getTemporaryPath] $fileName]
          128  +    set name [file rootname [file tail $fileName]]
          129  +
          130  +    set sql { \
          131  +      CREATE TABLE t1 (id1 INTEGER); \
          132  +      INSERT INTO t1 (id1) VALUES (1); \
          133  +      INSERT INTO t1 (id1) VALUES (2); \
          134  +      INSERT INTO t1 (id1) VALUES (3); \
          135  +      INSERT INTO t1 (id1) VALUES (4); \
          136  +      INSERT INTO t1 (id1) VALUES (5); \
          137  +      SELECT * FROM t1 ORDER BY id1; \
          138  +    }
          139  +
          140  +    unset -nocomplain results errors
          141  +
          142  +    set code [compileCSharpWith [subst {
          143  +      [expr {$i == 3 ? "using System;" : ""}]
          144  +      using System.Data.SQLite;
          145  +      using System.Diagnostics;
          146  +      using System.IO;
          147  +
          148  +      namespace _Dynamic${id}
          149  +      {
          150  +        public class Test${id}
          151  +        {
          152  +          #region Private Static Data
          153  +          private static SQLiteConnection connection;
          154  +          #endregion
          155  +
          156  +          /////////////////////////////////////////////////////////////////////
          157  +
          158  +          #region Public Static Methods
          159  +          public static void OpenConnection()
          160  +          {
          161  +            connection = new SQLiteConnection("Data Source=${dataSource};");
          162  +            connection.Open();
          163  +            connection.LogMessage(0, "Connection opened.");
          164  +          }
          165  +
          166  +          /////////////////////////////////////////////////////////////////////
          167  +
          168  +          public static SQLiteCommand CreateCommand(string sql)
          169  +          {
          170  +            SQLiteCommand command = connection.CreateCommand();
          171  +            command.CommandText = sql;
          172  +            connection.LogMessage(0, "Command created.");
          173  +            return command;
          174  +          }
          175  +
          176  +          /////////////////////////////////////////////////////////////////////
          177  +
          178  +          public static SQLiteDataReader ExecuteReader(SQLiteCommand command)
          179  +          {
          180  +            SQLiteDataReader dataReader = command.ExecuteReader();
          181  +            connection.LogMessage(0, "Command executed.");
          182  +            return dataReader;
          183  +          }
          184  +
          185  +          /////////////////////////////////////////////////////////////////////
          186  +
          187  +          public static SQLiteDataReader ExecuteReader(string sql)
          188  +          {
          189  +            SQLiteCommand command = CreateCommand(sql);
          190  +            SQLiteDataReader dataReader = command.ExecuteReader();
          191  +            connection.LogMessage(0, "Command executed.");
          192  +            return dataReader;
          193  +          }
          194  +
          195  +          /////////////////////////////////////////////////////////////////////
          196  +
          197  +          public static void CloseConnection()
          198  +          {
          199  +            connection.LogMessage(0, "Closing connection...");
          200  +            connection.Close();
          201  +          }
          202  +          #endregion
          203  +
          204  +          /////////////////////////////////////////////////////////////////////
          205  +
          206  +          public static void Main()
          207  +          {
          208  +            using (TraceListener listener = new TextWriterTraceListener(
          209  +                new FileStream("${test_log}", FileMode.Append,
          210  +                    FileAccess.Write, FileShare.ReadWrite), "${name}"))
          211  +            {
          212  +              Trace.Listeners.Add(listener);
          213  +              Trace.WriteLine("---- START TRACE \\"${name}\\"");
          214  +
          215  +              OpenConnection();
          216  +              SQLiteDataReader dataReader = ExecuteReader("${sql}");
          217  +
          218  +              [expr {$i == 3 ? {
          219  +                GC.Collect();
          220  +                GC.WaitForPendingFinalizers();
          221  +                GC.Collect();
          222  +              } : ""}]
          223  +
          224  +              dataReader.Close();
          225  +
          226  +              [expr {$i == 3 ? {
          227  +                GC.Collect();
          228  +                GC.WaitForPendingFinalizers();
          229  +                GC.Collect();
          230  +              } : ""}]
          231  +
          232  +              CloseConnection();
          233  +
          234  +              Trace.WriteLine("---- END TRACE \\"${name}\\"");
          235  +              Trace.Listeners.Remove(listener);
          236  +            }
          237  +          }
          238  +        }
          239  +      }
          240  +    }] results errors System.Data.SQLite.dll]
          241  +
          242  +    list $code $results \
          243  +        [expr {[info exists errors] ? $errors : ""}] \
          244  +        [expr {$code eq "Ok" ? [catch {
          245  +          object invoke _Dynamic${id}.Test${id} Main
          246  +        } result] : [set result ""]}] $result \
          247  +        [reportSQLiteResources $test_channel true]
          248  +  } -cleanup {
          249  +    cleanupDb $fileName
          250  +
          251  +    unset -nocomplain result code results errors sql dataSource id db fileName
          252  +  } -constraints {eagle logFile monoBug28 command.sql compile.DATA SQLite\
          253  +System.Data.SQLite} -match regexp -result [appendArgs "^Ok\
          254  +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]}
          255  +}
          256  +
          257  +###############################################################################
          258  +
          259  +unset -nocomplain i
          260  +
          261  +###############################################################################
          262  +
          263  +unset -nocomplain memory_used
   107    264   
   108    265   ###############################################################################
   109    266   
   110    267   runSQLiteTestEpilogue
   111    268   runTestEpilogue