Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -48,12 +48,11 @@ { } protected override void Dispose(bool bDisposing) { - if (bDisposing) - Close(); + Close(); } // It isn't necessary to cleanup any functions we've registered. If the connection // goes to the pool and is resurrected later, re-registered functions will overwrite the // previous functions. The SQLiteFunctionCookieHandle will take care of freeing unmanaged @@ -92,10 +91,18 @@ get { return UTF8ToString(UnsafeNativeMethods.sqlite3_libversion(), -1); } } + + internal static string SQLiteSourceId + { + get + { + return UTF8ToString(UnsafeNativeMethods.sqlite3_sourceid(), -1); + } + } internal override bool AutoCommit { get { Index: System.Data.SQLite/SQLiteCommand.cs ================================================================== --- System.Data.SQLite/SQLiteCommand.cs +++ System.Data.SQLite/SQLiteCommand.cs @@ -219,32 +219,37 @@ { SQLiteStatement stmt = null; try { - if (_statementList == null) - _remainingText = _commandText; - - stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText); - if (stmt != null) - { - stmt._command = this; - if (_statementList == null) - _statementList = new List(); - - _statementList.Add(stmt); - - _parameterCollection.MapParameters(stmt); - stmt.BindParameters(); - } + if ((_cnn != null) && (_cnn._sql != null)) + { + if (_statementList == null) + _remainingText = _commandText; + + stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText); + + if (stmt != null) + { + stmt._command = this; + + if (_statementList == null) + _statementList = new List(); + + _statementList.Add(stmt); + + _parameterCollection.MapParameters(stmt); + stmt.BindParameters(); + } + } return stmt; } catch (Exception) { if (stmt != null) { - if (_statementList.Contains(stmt)) + if ((_statementList != null) && _statementList.Contains(stmt)) _statementList.Remove(stmt); stmt.Dispose(); } Index: System.Data.SQLite/SQLiteConnection.cs ================================================================== --- System.Data.SQLite/SQLiteConnection.cs +++ System.Data.SQLite/SQLiteConnection.cs @@ -485,12 +485,12 @@ } #endif if (_sql != null) { _sql.Close(); + _sql = null; } - _sql = null; _transactionLevel = 0; } OnStateChange(ConnectionState.Closed); } @@ -1077,10 +1077,20 @@ /// public static string SQLiteVersion { get { return SQLite3.SQLiteVersion; } } + + /// + /// This method returns the string whose value is the same as the + /// SQLITE_SOURCE_ID C preprocessor macro used when compiling the + /// SQLite core library. + /// + public static string SQLiteSourceId + { + get { return SQLite3.SQLiteSourceId; } + } /// /// Returns the state of the connection. /// #if !PLATFORM_COMPACTFRAMEWORK Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -355,10 +355,17 @@ internal static extern IntPtr sqlite3_libversion(); #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else + [DllImport(SQLITE_DLL)] +#endif + internal static extern IntPtr sqlite3_sourceid(); + +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else [DllImport(SQLITE_DLL)] #endif internal static extern void sqlite3_interrupt(IntPtr db); #if !PLATFORM_COMPACTFRAMEWORK @@ -874,21 +881,44 @@ protected override bool ReleaseHandle() { try { -#if DEBUG - Trace.WriteLine(String.Format("CloseConnection: {0}", handle)); -#endif - SQLiteBase.CloseConnection(this); + +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "CloseConnection: {0}", handle)); + } + catch + { + } +#endif + #if DEBUG return true; #endif } +#if DEBUG + catch (SQLiteException e) +#else catch (SQLiteException) +#endif { +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "CloseConnection: {0}, exception: {1}", + handle, e)); + } + catch + { + } +#endif } #if DEBUG return false; #else return true; @@ -927,21 +957,44 @@ protected override bool ReleaseHandle() { try { + SQLiteBase.FinalizeStatement(this); + #if DEBUG - Trace.WriteLine(String.Format("FinalizeStatement: {0}", handle)); + try + { + Trace.WriteLine(String.Format( + "FinalizeStatement: {0}", handle)); + } + catch + { + } #endif - SQLiteBase.FinalizeStatement(this); #if DEBUG return true; #endif } +#if DEBUG + catch (SQLiteException e) +#else catch (SQLiteException) +#endif { +#if DEBUG + try + { + Trace.WriteLine(String.Format( + "FinalizeStatement: {0}, exception: {1}", + handle, e)); + } + catch + { + } +#endif } #if DEBUG return false; #else return true; Index: Tests/common.eagle ================================================================== --- Tests/common.eagle +++ Tests/common.eagle @@ -302,16 +302,28 @@ proc checkForSQLite { channel } { tputs $channel "---- checking for core SQLite library... " if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \ SQLiteVersion} version] == 0} then { + # + # NOTE: Attempt to query the Fossil source identifier for the SQLite + # core library. + # + if {[catch {object invoke -flags +NonPublic System.Data.SQLite.SQLite3 \ + SQLiteSourceId} sourceId]} then { + # + # NOTE: We failed to query the Fossil source identifier. + # + set sourceId unknown + } + # # NOTE: Yes, the SQLite core library appears to be available. # addConstraint SQLite - tputs $channel [appendArgs "yes (" $version ")\n"] + tputs $channel [appendArgs "yes (" $version " " $sourceId ")\n"] } else { tputs $channel no\n } } @@ -400,14 +412,21 @@ # set fileName [file join [getTemporaryPath] [file tail $fileName]] # # NOTE: By default, delete any pre-existing database with the same file - # name. + # name if it currently exists. # - if {$delete} then { - catch {file delete $fileName} + if {$delete && [file exists $fileName]} then { + if {[catch {file delete $fileName} error]} then { + # + # NOTE: We somehow failed to delete the file, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to delete database file \"" $fileName \ + "\" during setup, error: " \n\t $error \n] + } } # # NOTE: Refer to the specified variable (e.g. "db") in the context of the # caller. The handle to the opened database will be stored there. @@ -468,45 +487,84 @@ # # NOTE: Close the connection to the database now. This should allow us to # delete the underlying database file. # - catch {sql close $db} + if {[info exists db] && [catch {sql close $db} error]} then { + # + # NOTE: We somehow failed to close the database, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to close database \"" $db "\", error: " \ + \n\t $error \n] + } # - # NOTE: Delete the test database file now. For now, all test database - # files are stored in the temporary directory. + # NOTE: Attempt to delete the test database file now. For now, all test + # database files are stored in the temporary directory. # - catch {file delete [file join [getTemporaryPath] [file tail $fileName]]} + set fileName [file join [getTemporaryPath] [file tail $fileName]] + + if {[catch {file delete $fileName} error]} then { + # + # NOTE: We somehow failed to delete the file, report why. + # + tputs $::test_channel [appendArgs \ + "==== WARNING: failed to delete database file \"" $fileName \ + "\" during cleanup, error: " \n\t $error \n] + } } - proc reportSQLiteResources { channel } { - tputs $channel "---- current memory in use by SQLite... " + proc reportSQLiteResources { channel {quiet false} } { + # + # NOTE: Skip all output if we are running in "quiet" mode. + # + if {!$quiet} then { + tputs $channel "---- current memory in use by SQLite... " + } if {[catch {object invoke -flags +NonPublic \ System.Data.SQLite.UnsafeNativeMethods \ sqlite3_memory_used} memory] == 0} then { - tputs $channel [appendArgs $memory " bytes\n"] + if {!$quiet} then { + tputs $channel [appendArgs $memory " bytes\n"] + } } else { # # NOTE: Maybe the SQLite native library is unavailable? # - tputs $channel unknown\n + set memory unknown + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } } - tputs $channel "---- maximum memory in use by SQLite... " + set result $memory; # NOTE: Return memory in-use to caller. + + if {!$quiet} then { + tputs $channel "---- maximum memory in use by SQLite... " + } if {[catch {object invoke -flags +NonPublic \ System.Data.SQLite.UnsafeNativeMethods \ sqlite3_memory_highwater 0} memory] == 0} then { - tputs $channel [appendArgs $memory " bytes\n"] + if {!$quiet} then { + tputs $channel [appendArgs $memory " bytes\n"] + } } else { # # NOTE: Maybe the SQLite native library is unavailable? # - tputs $channel unknown\n + set memory unknown + + if {!$quiet} then { + tputs $channel [appendArgs $memory \n] + } } + + return $result } proc runSQLiteTestPrologue {} { # # NOTE: Skip running our custom prologue if the main one has been skipped. @@ -519,19 +577,21 @@ if {![info exists ::no(sqliteFiles)]} then { # # NOTE: Skip trying to delete any files if we are so instructed. # if {![info exists ::no(deleteSqliteFiles)]} then { + tryDeleteAssembly sqlite3.dll tryDeleteAssembly SQLite.Interop.dll tryDeleteAssembly System.Data.SQLite.dll tryDeleteAssembly System.Data.SQLite.Linq.dll } # # NOTE: Skip trying to copy any files if we are so instructed. # if {![info exists ::no(copySqliteFiles)]} then { + tryCopyAssembly sqlite3.dll tryCopyAssembly SQLite.Interop.dll tryCopyAssembly System.Data.SQLite.dll tryCopyAssembly System.Data.SQLite.Linq.dll } Index: Tests/tkt-e30b820248.eagle ================================================================== --- Tests/tkt-e30b820248.eagle +++ Tests/tkt-e30b820248.eagle @@ -18,12 +18,16 @@ package require System.Data.SQLite.Test runSQLiteTestPrologue ############################################################################### -runTest {test tkt-e30b820248-1.1 {statement disposal ordering} -setup { - setupDb [set fileName tkt-e30b820248-1.1.db] +set memory_used [reportSQLiteResources $test_channel true] + +############################################################################### + +runTest {test tkt-e30b820248-1.1 {disposal ordering} -setup { + set fileName tkt-e30b820248-1.1.db } -body { set id [object invoke Interpreter.GetActive NextId] set dataSource [file join [getTemporaryPath] $fileName] set name [file rootname [file tail $fileName]] @@ -32,26 +36,29 @@ INSERT INTO t1 (id1) VALUES (1); \ INSERT INTO t1 (id1) VALUES (2); \ INSERT INTO t1 (id1) VALUES (?); \ INSERT INTO t1 (id1) VALUES (?); \ INSERT INTO t1 (id1) VALUES (?); \ + SELECT * FROM t1 ORDER BY id1; \ } unset -nocomplain results errors set code [compileCSharpWith [subst { using System.Data.SQLite; using System.Diagnostics; + using System.IO; namespace _Dynamic${id} { public class Test${id} { public static void Main() { using (TraceListener listener = new TextWriterTraceListener( - "${test_log}", "${name}")) + new FileStream("${test_log}", FileMode.Append, + FileAccess.Write, FileShare.ReadWrite), "${name}")) { Trace.Listeners.Add(listener); Trace.WriteLine("---- START TRACE \\"${name}\\""); using (SQLiteConnection connection = new SQLiteConnection( @@ -93,19 +100,169 @@ list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main - } result] : [set result ""]}] $result + } result] : [set result ""]}] $result \ + [reportSQLiteResources $test_channel true] } -cleanup { cleanupDb $fileName unset -nocomplain result code results errors sql dataSource id db fileName } -constraints \ {eagle logFile monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ --match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ -\{\}$}} +-match regexp -result [appendArgs "^Ok\ +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]} + +############################################################################### + +for {set i 2} {$i < 4} {incr i} { + set memory_used [reportSQLiteResources $test_channel true] + + ############################################################################# + + runTest {test [appendArgs tkt-e30b820248-1. $i] {disposal ordering} -setup { + set fileName [appendArgs tkt-e30b820248-1. $i .db] + } -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getTemporaryPath] $fileName] + set name [file rootname [file tail $fileName]] + + set sql { \ + CREATE TABLE t1 (id1 INTEGER); \ + INSERT INTO t1 (id1) VALUES (1); \ + INSERT INTO t1 (id1) VALUES (2); \ + INSERT INTO t1 (id1) VALUES (3); \ + INSERT INTO t1 (id1) VALUES (4); \ + INSERT INTO t1 (id1) VALUES (5); \ + SELECT * FROM t1 ORDER BY id1; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + [expr {$i == 3 ? "using System;" : ""}] + using System.Data.SQLite; + using System.Diagnostics; + using System.IO; + + namespace _Dynamic${id} + { + public class Test${id} + { + #region Private Static Data + private static SQLiteConnection connection; + #endregion + + ///////////////////////////////////////////////////////////////////// + + #region Public Static Methods + public static void OpenConnection() + { + connection = new SQLiteConnection("Data Source=${dataSource};"); + connection.Open(); + connection.LogMessage(0, "Connection opened."); + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteCommand CreateCommand(string sql) + { + SQLiteCommand command = connection.CreateCommand(); + command.CommandText = sql; + connection.LogMessage(0, "Command created."); + return command; + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteDataReader ExecuteReader(SQLiteCommand command) + { + SQLiteDataReader dataReader = command.ExecuteReader(); + connection.LogMessage(0, "Command executed."); + return dataReader; + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteDataReader ExecuteReader(string sql) + { + SQLiteCommand command = CreateCommand(sql); + SQLiteDataReader dataReader = command.ExecuteReader(); + connection.LogMessage(0, "Command executed."); + return dataReader; + } + + ///////////////////////////////////////////////////////////////////// + + public static void CloseConnection() + { + connection.LogMessage(0, "Closing connection..."); + connection.Close(); + } + #endregion + + ///////////////////////////////////////////////////////////////////// + + public static void Main() + { + using (TraceListener listener = new TextWriterTraceListener( + new FileStream("${test_log}", FileMode.Append, + FileAccess.Write, FileShare.ReadWrite), "${name}")) + { + Trace.Listeners.Add(listener); + Trace.WriteLine("---- START TRACE \\"${name}\\""); + + OpenConnection(); + SQLiteDataReader dataReader = ExecuteReader("${sql}"); + + [expr {$i == 3 ? { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } : ""}] + + dataReader.Close(); + + [expr {$i == 3 ? { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } : ""}] + + CloseConnection(); + + Trace.WriteLine("---- END TRACE \\"${name}\\""); + Trace.Listeners.Remove(listener); + } + } + } + } + }] results errors System.Data.SQLite.dll] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result \ + [reportSQLiteResources $test_channel true] + } -cleanup { + cleanupDb $fileName + + unset -nocomplain result code results errors sql dataSource id db fileName + } -constraints {eagle logFile monoBug28 command.sql compile.DATA SQLite\ +System.Data.SQLite} -match regexp -result [appendArgs "^Ok\ +System#CodeDom#Compiler#CompilerResults#\\d+ \\{\\} 0 \\{\\} " $memory_used \$]} +} + +############################################################################### + +unset -nocomplain i + +############################################################################### + +unset -nocomplain memory_used ############################################################################### runSQLiteTestEpilogue runTestEpilogue