[hyena/sqlite] [Hyena.Data.Sqlite] Better error checking in binding



commit 7fc7ae3b3006500d581234a9d3495b450fc2773c
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Wed Nov 10 18:15:03 2010 -0600

    [Hyena.Data.Sqlite] Better error checking in binding

 .../Hyena.Data.Sqlite/HyenaSqliteCommand.cs        |    2 +-
 Hyena.Data.Sqlite/Hyena.Data.Sqlite/Sqlite.cs      |   99 +++++++-
 .../Hyena.Data.Sqlite/Tests/SqliteTests.cs         |  270 ++++++++++++--------
 3 files changed, 251 insertions(+), 120 deletions(-)
---
diff --git a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/HyenaSqliteCommand.cs b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
index 44ffbdb..a096834 100644
--- a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
+++ b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
@@ -91,7 +91,7 @@ namespace Hyena.Data.Sqlite
             result = null;
             int execution_ms = 0;
 
-            bool dispose_command = true;
+            bool dispose_command = ReaderDisposes;
             var sql_command = connection.CreateStatement (CurrentSqlText);
             sql_command.ReaderDisposes = ReaderDisposes;
             hconnection.OnExecuting (sql_command);
diff --git a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Sqlite.cs b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Sqlite.cs
index da0e920..f0f88a6 100644
--- a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Sqlite.cs
+++ b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Sqlite.cs
@@ -12,6 +12,8 @@ namespace Hyena.Data.Sqlite
         IntPtr ptr;
         internal IntPtr Ptr { get { return ptr; } }
 
+        internal List<Statement> Statements = new List<Statement> ();
+
         public string DbPath { get; private set; }
 
         public long LastInsertRowId {
@@ -24,11 +26,24 @@ namespace Hyena.Data.Sqlite
             CheckError (Native.sqlite3_open (Encoding.UTF8.GetBytes (dbPath), out ptr));
             if (ptr == IntPtr.Zero)
                 throw new Exception ("Unable to open connection");
+
+            Native.sqlite3_extended_result_codes (ptr, 1);
         }
 
         public void Dispose ()
         {
             if (ptr != IntPtr.Zero) {
+                lock (Statements) {
+                    var stmts = Statements.ToArray ();
+
+                    if (stmts.Length > 0)
+                        Hyena.Log.DebugFormat ("Connection disposing of {0} remaining statements", stmts.Length);
+
+                    foreach (var stmt in stmts) {
+                        stmt.Dispose ();
+                    }
+                }
+
                 CheckError (Native.sqlite3_close (ptr));
                 ptr = IntPtr.Zero;
             }
@@ -44,12 +59,17 @@ namespace Hyena.Data.Sqlite
             CheckError (errorCode, "");
         }
 
-        internal void CheckError (int errorCode, string msg)
+        internal void CheckError (int errorCode, string sql)
         {
             if (errorCode == 0 || errorCode == 100 || errorCode == 101)
                 return;
 
-            throw new Exception (msg + Native.sqlite3_errmsg16 (Ptr).PtrToString ());
+            string errmsg = Native.sqlite3_errmsg16 (Ptr).PtrToString ();
+            if (sql != null) {
+                errmsg = String.Format ("{0} (SQL: {1})", errmsg, sql);
+            }
+
+            throw new SqliteException (errorCode, errmsg);
         }
 
         public Statement CreateStatement (string sql)
@@ -117,6 +137,16 @@ namespace Hyena.Data.Sqlite
         }
     }
 
+    public class SqliteException : Exception
+    {
+        public int ErrorCode { get; private set; }
+
+        public SqliteException (int errorCode, string message) : base (String.Format ("Sqlite error {0}: {1}", errorCode, message))
+        {
+            ErrorCode = errorCode;
+        }
+    }
+
     public interface IDataReader : IDisposable
     {
         bool Read ();
@@ -131,14 +161,17 @@ namespace Hyena.Data.Sqlite
         Connection connection;
         bool bound;
         QueryReader reader;
+        bool disposed;
 
         internal IntPtr Ptr { get { return ptr; } }
         internal bool Bound { get { return bound; } }
         internal Connection Connection { get { return connection; } }
 
+        public bool IsDisposed { get { return disposed; } }
+
         public string CommandText { get; private set; }
         public int ParameterCount { get; private set; }
-        public bool ReaderDisposes { get; set; }
+        public bool ReaderDisposes { get; internal set; }
 
         internal event EventHandler Disposed;
 
@@ -148,7 +181,12 @@ namespace Hyena.Data.Sqlite
             this.connection = connection;
 
             IntPtr pzTail = IntPtr.Zero;
-            connection.CheckError (Native.sqlite3_prepare16_v2 (connection.Ptr, sql, sql.Length * 2, out ptr, out pzTail), sql);
+            CheckError (Native.sqlite3_prepare16_v2 (connection.Ptr, sql, -1, out ptr, out pzTail));
+            
+            lock (Connection.Statements) {
+                Connection.Statements.Add (this);
+            }
+
             if (pzTail != IntPtr.Zero && Marshal.ReadByte (pzTail) != 0) {
                 Dispose ();
                 throw new ArgumentException ("sql", String.Format ("This sqlite binding does not support multiple commands in one statement:\n  {0}", sql));
@@ -158,12 +196,31 @@ namespace Hyena.Data.Sqlite
             reader = new QueryReader () { Statement = this };
         }
 
+        internal void CheckDisposed ()
+        {
+            if (disposed)
+                throw new InvalidOperationException ("Statement is disposed");
+        }
+
+        private string ShortSql { get { return CommandText.Substring (0, Math.Min (CommandText.Length, 20)); } }
+
         public void Dispose ()
         {
+            if (disposed)
+                return;
+
+            disposed = true;
             if (ptr != IntPtr.Zero) {
-                connection.CheckError (Native.sqlite3_finalize (ptr));
+                // Don't check for error here, because if the most recent evaluation had an error finalize will return it too
+                // See http://sqlite.org/c3ref/finalize.html
+                Native.sqlite3_finalize (ptr);
+
                 ptr = IntPtr.Zero;
 
+                lock (Connection.Statements) {
+                    Connection.Statements.Remove (this);
+                }
+
                 var h = Disposed;
                 if (h != null) {
                     h (this, EventArgs.Empty);
@@ -178,10 +235,11 @@ namespace Hyena.Data.Sqlite
 
         public Statement Bind (params object [] vals)
         {
+            CheckDisposed ();
             if (vals == null || vals.Length != ParameterCount || ParameterCount == 0)
                 throw new ArgumentException ("vals", String.Format ("Statement has {0} parameters", ParameterCount));
 
-            connection.CheckError (Native.sqlite3_reset (ptr));
+            Reset ();
 
             for (int i = 1; i <= vals.Length; i++) {
                 int code = 0;
@@ -205,16 +263,27 @@ namespace Hyena.Data.Sqlite
                     code = Native.sqlite3_bind_text16 (Ptr, i, str, str.Length * 2, (IntPtr)(-1));
                 }
 
-                connection.CheckError (code);
+                CheckError (code);
             }
 
             bound = true;
             return this;
         }
 
+        internal void CheckError (int code)
+        {
+            connection.CheckError (code, CommandText);
+        }
+
+        private void Reset ()
+        {
+            CheckError (Native.sqlite3_reset (ptr));
+        }
+
         public IEnumerator<IDataReader> GetEnumerator ()
         {
-            connection.CheckError (Native.sqlite3_reset (ptr));
+            CheckDisposed ();
+            Reset ();
             while (reader.Read ()) {
                 yield return reader;
             }
@@ -227,17 +296,22 @@ namespace Hyena.Data.Sqlite
 
         public Statement Execute ()
         {
+            CheckDisposed ();
+            Reset ();
             reader.Read ();
             return this;
         }
 
         public object QueryScalar ()
         {
+            CheckDisposed ();
+            Reset ();
             return reader.Read () ? reader[0] : null;
         }
 
         public QueryReader Query ()
         {
+            CheckDisposed ();
             return reader;
         }
     }
@@ -260,6 +334,7 @@ namespace Hyena.Data.Sqlite
         public int FieldCount {
             get {
                 if (column_count == -1) {
+                    Statement.CheckDisposed ();
                     column_count = Native.sqlite3_column_count (Ptr);
                 }
                 return column_count;
@@ -268,6 +343,7 @@ namespace Hyena.Data.Sqlite
 
         public bool Read ()
         {
+            Statement.CheckDisposed ();
             if (Statement.ParameterCount > 0 && !Statement.Bound)
                 throw new InvalidOperationException ("Statement not bound");
 
@@ -275,13 +351,14 @@ namespace Hyena.Data.Sqlite
             if (code == ROW) {
                 return true;
             } else {
-                Statement.Connection.CheckError (code);
+                Statement.CheckError (code);
                 return false;
             }
         }
 
         public object this[int i] {
             get {
+                Statement.CheckDisposed ();
                 int type = Native.sqlite3_column_type (Ptr, i);
                 switch (type) {
                     case SQLITE_INTEGER:
@@ -302,6 +379,7 @@ namespace Hyena.Data.Sqlite
 
         public object this[string columnName] {
             get {
+                Statement.CheckDisposed ();
                 if (columns == null) {
                     columns = new Dictionary<string, int> ();
                     for (int i = 0; i < FieldCount; i++) {
@@ -359,6 +437,9 @@ namespace Hyena.Data.Sqlite
         [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
         internal static extern int sqlite3_create_collation16(IntPtr db, string strName, int eTextRep, IntPtr ctx, SqliteCollation fcompare);
 
+        [DllImport(SQLITE_DLL)]
+        internal static extern int sqlite3_extended_result_codes (IntPtr db, int onoff);
+
         // Statement functions
         [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
         internal static extern int sqlite3_prepare16_v2(IntPtr db, string pSql, int nBytes, out IntPtr stmt, out IntPtr ptrRemain);
diff --git a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Tests/SqliteTests.cs b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Tests/SqliteTests.cs
index eb9c5bd..f4b7b4b 100644
--- a/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Tests/SqliteTests.cs
+++ b/Hyena.Data.Sqlite/Hyena.Data.Sqlite/Tests/SqliteTests.cs
@@ -34,93 +34,106 @@ using System.Linq;
 using NUnit.Framework;
 using Hyena.Data.Sqlite;
 
-namespace Hyena.Data.Sqlite.Tests
+namespace Hyena.Data.Sqlite
 {
     [TestFixture]
     public class SqliteTests
     {
+        Connection con;
+        string dbfile = "hyena-data-sqlite-test.db";
+
+        [SetUp]
+        public void Setup ()
+        {
+            con = new Connection (dbfile);
+        }
+
+        [TearDown]
+        public void TearDown ()
+        {
+            Assert.AreEqual (0, con.Statements.Count);
+            con.Dispose ();
+            System.IO.File.Delete (dbfile);
+        }
+
         [Test]
         public void Test ()
         {
-            using (var con = new Connection (":memory:")) {
-                using (var stmt = con.CreateStatement ("SELECT 'foobar' as version")) {
-                    Assert.AreEqual ("foobar", stmt.First ()[0]);
-                    Assert.AreEqual ("foobar", stmt.First ()["version"]);
-                }
+            using (var stmt = con.CreateStatement ("SELECT 'foobar' as version")) {
+                Assert.AreEqual ("foobar", stmt.QueryScalar ());
+                Assert.AreEqual ("foobar", stmt.First ()[0]);
+                Assert.AreEqual ("foobar", stmt.First ()["version"]);
+            }
 
-                using (var stmt = con.CreateStatement ("SELECT 2 + 5 as res")) {
-                    Assert.AreEqual (7, stmt.First ()[0]);
-                    Assert.AreEqual (7, stmt.First ()["res"]);
+            using (var stmt = con.CreateStatement ("SELECT 2 + 5 as res")) {
+                Assert.AreEqual (7, stmt.QueryScalar ());
+                Assert.AreEqual (7, stmt.First ()[0]);
+                Assert.AreEqual (7, stmt.First ()["res"]);
 
-                    try {
-                        stmt.Bind ();
-                        Assert.Fail ("should not be able to bind parameterless statement");
-                    } catch {}
-                }
+                try {
+                    stmt.Bind ();
+                    Assert.Fail ("should not be able to bind parameterless statement");
+                } catch {}
             }
         }
 
         [Test]
         public void TestBinding ()
         {
-            using (var con = new Connection (":memory:")) {
-                using (var stmt = con.CreateStatement ("SELECT ? as version")) {
-                    try {
-                        stmt.First ();
-                        Assert.Fail ("unbound statement should have thrown an exception");
-                    } catch {}
-
-                    try {
-                        stmt.Bind (1, 2);
-                        Assert.Fail ("bound statement with the wrong number of parameters");
-                    } catch {}
-
-                    try {
-                        stmt.Bind ();
-                        Assert.Fail ("bound statement with the wrong number of parameters");
-                    } catch {}
-
-                    stmt.Bind (21);
-                    Assert.AreEqual (21, stmt.First ()[0]);
-                    Assert.AreEqual (21, stmt.First ()["version"]);
-
-                    stmt.Bind ("ffoooo");
-                    Assert.AreEqual ("ffoooo", stmt.First ()[0]);
-                    Assert.AreEqual ("ffoooo", stmt.First ()["version"]);
-                }
+            using (var stmt = con.CreateStatement ("SELECT ? as version")) {
+                try {
+                    stmt.First ();
+                    Assert.Fail ("unbound statement should have thrown an exception");
+                } catch {}
 
-                using (var stmt = con.CreateStatement ("SELECT ? as a, ? as b, ?")) {
-                    stmt.Bind (1, "two", 3.3);
-                    Assert.AreEqual (1, stmt.First ()[0]);
-                    Assert.AreEqual ("two", stmt.First ()["b"]);
-                    Assert.AreEqual (3.3, stmt.First ()[2]);
-                }
+                try {
+                    stmt.Bind (1, 2);
+                    Assert.Fail ("bound statement with the wrong number of parameters");
+                } catch {}
+
+                try {
+                    stmt.Bind ();
+                    Assert.Fail ("bound statement with the wrong number of parameters");
+                } catch {}
+
+                stmt.Bind (21);
+                Assert.AreEqual (21, stmt.First ()[0]);
+                Assert.AreEqual (21, stmt.First ()["version"]);
+
+                stmt.Bind ("ffoooo");
+                Assert.AreEqual ("ffoooo", stmt.First ()[0]);
+                Assert.AreEqual ("ffoooo", stmt.First ()["version"]);
+            }
+
+            using (var stmt = con.CreateStatement ("SELECT ? as a, ? as b, ?")) {
+                stmt.Bind (1, "two", 3.3);
+                Assert.AreEqual (1, stmt.QueryScalar ());
+                Assert.AreEqual ("two", stmt.First ()["b"]);
+                Assert.AreEqual (3.3, stmt.First ()[2]);
             }
         }
 
         [Test]
         public void CreateTable ()
         {
-            using (var con = new Connection (":memory:")) {
-                CreateUsers (con);
+            CreateUsers (con);
 
-                using (var stmt = con.CreateStatement ("SELECT COUNT(*) FROM Users")) {
-                    Assert.AreEqual (2, stmt.First ()[0]);
-                }
+            using (var stmt = con.CreateStatement ("SELECT COUNT(*) FROM Users")) {
+                Assert.AreEqual (2, stmt.QueryScalar ());
+            }
 
-                using (var stmt = con.CreateStatement ("SELECT ID, Name FROM Users ORDER BY NAME")) {
-                    var row1 = stmt.First ();
-                    Assert.AreEqual ("Aaron", row1["Name"]);
-                    Assert.AreEqual ("Aaron", row1[1]);
-                    Assert.AreEqual (2, row1["ID"]);
-                    Assert.AreEqual (2, row1[0]);
-
-                    var row2 = stmt.Skip (1).First ();
-                    Assert.AreEqual ("Gabriel", row2["Name"]);
-                    Assert.AreEqual ("Gabriel", row2[1]);
-                    Assert.AreEqual (1, row2["ID"]);
-                    Assert.AreEqual (1, row2[0]);
-                }
+            using (var stmt = con.CreateStatement ("SELECT ID, Name FROM Users ORDER BY NAME")) {
+                var row1 = stmt.First ();
+                Assert.AreEqual ("Aaron", row1["Name"]);
+                Assert.AreEqual ("Aaron", row1[1]);
+                Assert.AreEqual (2, row1["ID"]);
+                Assert.AreEqual (2, row1[0]);
+
+                var row2 = stmt.Skip (1).First ();
+                Assert.AreEqual ("Gabriel", row2["Name"]);
+                Assert.AreEqual ("Gabriel", row2[1]);
+                Assert.AreEqual (1, row2["ID"]);
+                Assert.AreEqual (1, row2[0]);
             }
         }
 
@@ -132,6 +145,10 @@ namespace Hyena.Data.Sqlite.Tests
 
             using (var stmt = con.CreateStatement ("CREATE TABLE Users (ID INTEGER PRIMARY KEY, Name TEXT)")) {
                 stmt.Execute ();
+                try {
+                    stmt.Execute ();
+                    Assert.Fail ("Shouldn't be able to create table; already exists");
+                } catch {}
             }
 
             using (var stmt = con.CreateStatement ("INSERT INTO Users (Name) VALUES (?)")) {
@@ -143,74 +160,107 @@ namespace Hyena.Data.Sqlite.Tests
         [Test]
         public void CheckInterleavedAccess ()
         {
-            using (var con = new Connection (":memory:")) {
-                CreateUsers (con);
+            CreateUsers (con);
 
-                var q1 = con.Query ("SELECT ID, Name FROM Users ORDER BY NAME");
-                var q2 = con.Query ("SELECT ID, Name FROM Users ORDER BY ID");
+            var q1 = con.Query ("SELECT ID, Name FROM Users ORDER BY NAME");
+            var q2 = con.Query ("SELECT ID, Name FROM Users ORDER BY ID");
 
-                Assert.IsTrue (q1.Read ());
-                Assert.IsTrue (q2.Read ());
-                Assert.AreEqual ("Aaron", q1["Name"]);
-                Assert.AreEqual ("Gabriel", q2["Name"]);
+            Assert.IsTrue (q1.Read ());
+            Assert.IsTrue (q2.Read ());
+            Assert.AreEqual ("Aaron", q1["Name"]);
+            Assert.AreEqual ("Gabriel", q2["Name"]);
 
-                Assert.IsTrue (q2.Read ());
-                Assert.AreEqual ("Aaron", q2["Name"]);
-                Assert.IsTrue (q1.Read ());
-                Assert.AreEqual ("Gabriel", q1["Name"]);
+            Assert.IsTrue (q2.Read ());
+            Assert.AreEqual ("Aaron", q2["Name"]);
+            Assert.IsTrue (q1.Read ());
+            Assert.AreEqual ("Gabriel", q1["Name"]);
 
-                q1.Dispose ();
-                q2.Dispose ();
-            }
+            q1.Dispose ();
+            q2.Dispose ();
+        }
+
+        [Test]
+        public void ConnectionDisposesStatements ()
+        {
+            var stmt = con.CreateStatement ("SELECT 1");
+            Assert.IsFalse (stmt.IsDisposed);
+            con.Dispose ();
+            Assert.IsTrue (stmt.IsDisposed);
         }
 
         [Test]
         public void MultipleCommands ()
         {
-            using (var con = new Connection (":memory:")) {
-                try {
-                    using (var stmt = con.CreateStatement ("CREATE TABLE Lusers (ID INTEGER PRIMARY KEY, Name TEXT); INSERT INTO Lusers (Name) VALUES ('Foo')")) {
-                        stmt.Execute ();
-                    }
-                    Assert.Fail ("Mutliple commands aren't supported in this sqlite binding");
-                } catch {}
+            try {
+                using (var stmt = con.CreateStatement ("CREATE TABLE Lusers (ID INTEGER PRIMARY KEY, Name TEXT); INSERT INTO Lusers (Name) VALUES ('Foo')")) {
+                    stmt.Execute ();
+                }
+                Assert.Fail ("Mutliple commands aren't supported in this sqlite binding");
+            } catch {}
+        }
+
+        [Test]
+        public void Query ()
+        {
+            using (var q = con.Query ("SELECT 7")) {
+                int rows = 0;
+                while (q.Read ()) {
+                    Assert.AreEqual (7, q[0]);
+                    rows++;
+                }
+                Assert.AreEqual (1, rows);
             }
         }
 
         [Test]
+        public void QueryScalar ()
+        {
+            Assert.AreEqual (7, con.QueryScalar ("SELECT 7"));
+        }
+
+        [Test]
+        public void Execute ()
+        {
+            try {
+                con.QueryScalar ("SELECT COUNT(*) FROM Users");
+                Assert.Fail ("Should have thrown an exception");
+            } catch {}
+            con.Execute ("CREATE TABLE Users (ID INTEGER PRIMARY KEY, Name TEXT)");
+            Assert.AreEqual (0, con.QueryScalar ("SELECT COUNT(*) FROM Users"));
+        }
+
+        [Test]
         public void Functions ()
         {
-            using (var con = new Connection (":memory:")) {
-                con.AddFunction<Md5Function> ();
+            con.AddFunction<Md5Function> ();
 
-                using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?)")) {
-                    Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.Bind (1, "testing").QueryScalar ());
-                    Assert.AreEqual (null, stmt.Bind (1, null).QueryScalar ());
-                }
+            using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?)")) {
+                Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.Bind (1, "testing").QueryScalar ());
+                Assert.AreEqual (null, stmt.Bind (1, null).QueryScalar ());
+            }
 
-                using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?)")) {
-                    Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.Bind (2, "test", "ing").QueryScalar ());
-                    Assert.AreEqual (null, stmt.Bind (2, null, null).QueryScalar ());
-                }
+            using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?)")) {
+                Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.Bind (2, "test", "ing").QueryScalar ());
+                Assert.AreEqual (null, stmt.Bind (2, null, null).QueryScalar ());
+            }
 
-                using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?, ?)")) {
-                    Assert.AreEqual (null, stmt.Bind (3, null, "", null).QueryScalar ());
-
-                    try {
-                        con.RemoveFunction<Md5Function> ();
-                        Assert.Fail ("Removed function while statement active");
-                    } catch (Exception e) {
-                        Assert.AreEqual ("Unable to delete/modify user-function due to active statements", e.Message);
-                    }
-                }
+            using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?, ?)")) {
+                Assert.AreEqual (null, stmt.Bind (3, null, "", null).QueryScalar ());
 
                 try {
-                    using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?, ?)")) {
-                        Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.QueryScalar ());
-                        Assert.Fail ("Function HYENA_MD5 should no longer exist");
-                    }
-                } catch {}
+                    con.RemoveFunction<Md5Function> ();
+                    Assert.Fail ("Removed function while statement active");
+                } catch (SqliteException e) {
+                    Assert.AreEqual (5, e.ErrorCode);
+                }
             }
+
+            try {
+                using (var stmt = con.CreateStatement ("SELECT HYENA_MD5(?, ?, ?, ?)")) {
+                    Assert.AreEqual ("ae2b1fca515949e5d54fb22b8ed95575", stmt.QueryScalar ());
+                    Assert.Fail ("Function HYENA_MD5 should no longer exist");
+                }
+            } catch {}
         }
     }
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]