SqliteCommand reorganization and other changes.
authorJoshua Tauberer <joshua@mono-cvs.ximian.com>
Mon, 2 Jan 2006 19:20:56 +0000 (19:20 -0000)
committerJoshua Tauberer <joshua@mono-cvs.ximian.com>
Mon, 2 Jan 2006 19:20:56 +0000 (19:20 -0000)
svn path=/trunk/mcs/; revision=54967

mcs/class/Mono.Data.SqliteClient/ChangeLog
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient.dll.sources
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/ChangeLog
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/Sqlite.cs
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteCommand.cs
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteConnection.cs
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteDataReader.cs
mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteExceptions.cs [new file with mode: 0644]
mcs/class/Mono.Data.SqliteClient/Test/ChangeLog
mcs/class/Mono.Data.SqliteClient/Test/SqliteTest.cs
mcs/class/Mono.Data.SqliteClient/sources.list

index 6e259ed041cdf7c680815de393256d68324f8988..ea37cb76590c096c1f7dfa7e8e3f2f726c76d20e 100644 (file)
@@ -1,3 +1,8 @@
+2006-01-02  Joshua Tauberer  <tauberer@for.net
+
+       * Added Mono.Data.SqliteClient/SqliteExceptions.cs to
+         the sources files.
+
 2005-10-06  Chris Lahey  <clahey@localhost.localdomain>
 
        * Makefile (LIB_MCS_FLAGS): Requires Mono.Posix.dll.
index 1ccbe7fe54c8bf5c73a27011110e834407e39fb4..d1010f6623467755a896ee3106fced8f72cb9311 100644 (file)
@@ -13,3 +13,4 @@ Mono.Data.SqliteClient/SqliteRowUpdatedEventHandler.cs
 Mono.Data.SqliteClient/SqliteRowUpdatingEventArgs.cs
 Mono.Data.SqliteClient/SqliteRowUpdatingEventHandler.cs
 Mono.Data.SqliteClient/SqliteTransaction.cs
+Mono.Data.SqliteClient/SqliteExceptions.cs
index 635c7a003bdb77d9e73f1f4bcea150dace674b42..dbc6823feab0670870cae0537f7ad006fd786169 100644 (file)
@@ -1,3 +1,24 @@
+2006-01-02  Joshua Tauberer  <tauberer@for.net>
+
+       * SqliteCommand.cs: It was revealed that preparing
+         statements ahead of time was not possible as table
+         schema changes lead to errors.  This needed serious
+         reorganization.  SQL syntax errors and BUSY errors
+         are now raised in nice exceptions.
+       * SqliteDataReader.cs:
+               * Use object[] in place of ArrayList for each row.
+               * For Sqlite3, which remembers whether a value was
+                 an integer, text, real, or blob, actually return
+                 longs, strings, doubles, and byte[]s.
+               * GetDataTypeName() works where possible (Sqlite3).
+               * INT/INTEGER columns are now returned as ints,
+                 rather than longs, for Sqlite3.  Similarly for
+                 DATE and DATETIME columns, now returning DateTimes.
+       * SqliteConnection.cs.cs: BeginTransaction(IsolationLevel)
+         throws InvalidOperationException per the MS docs.
+       * SqliteExceptions.cs: New file with SqliteSyntaxException,
+         SqliteExecutionException, and its subclass SqliteBusyException.
+
 2005-12-27  Jonathan Pryor  <jonpryor@vt.edu>
 
        * SqliteCommand.cs: Use non-obsolete UnixMarshal members.
index cbbd104c076143cf608b7a46baec2eb37b6cbf18..9f8e47cab5d6a11c08a8c47a5bf637455e816392 100644 (file)
@@ -185,6 +185,9 @@ namespace Mono.Data.SqliteClient
                [DllImport ("sqlite3")]
                internal static extern double sqlite3_column_double (IntPtr pVm, int col);
                
+               [DllImport ("sqlite3")]
+               internal static extern IntPtr sqlite3_column_decltype (IntPtr pVm, int col);
+
                [DllImport ("sqlite3")]
                internal static extern int sqlite3_bind_parameter_count (IntPtr pStmt);
 
index b19c1b864469f8ba126814386e5f6c9fd9a6f5af..5787a04cd4701c0f95b0763e7078e431600021ee 100644 (file)
@@ -9,6 +9,7 @@
 //             Chris Turchin <chris@turchin.net>
 //             Jeroen Zwartepoorte <jeroen@xs4all.nl>
 //             Thomas Zoechling <thomas.zoechling@gmx.at>
+//             Joshua Tauberer <tauberer@for.net>
 //
 // Copyright (C) 2002  Vladimir Vukicevic
 //
@@ -57,7 +58,8 @@ namespace Mono.Data.SqliteClient
                private UpdateRowSource upd_row_source;
                private SqliteParameterCollection sql_params;
                private bool prepared = false;
-               private ArrayList pStmts;
+
+               static Regex v2Parameters = new Regex(@"(('[^']*?\:[^']*')*[^':]*?)*(?<param>:\w+)+([^':]*?('[^']*?\:[^']*'))*", RegexOptions.ExplicitCapture);
 
                #endregion
 
@@ -195,6 +197,152 @@ namespace Mono.Data.SqliteClient
                        return m.Value;
                }
                
+               private void BindParameters3 (IntPtr pStmt)
+               {
+                       if (sql_params == null) return;
+                       if (sql_params.Count == 0) return;
+                       
+                       int pcount = Sqlite.sqlite3_bind_parameter_count (pStmt);
+
+                       for (int i = 1; i <= pcount; i++) 
+                       {
+                               String name = Sqlite.sqlite3_bind_parameter_name (pStmt, i);
+                               SqliteParameter param = sql_params[name];
+                               Type ptype = param.Value.GetType ();
+                               
+                               SqliteError err;
+                               
+                               if (ptype.Equals (typeof (String))) 
+                               {
+                                       String s = (String)param.Value;
+                                       err = Sqlite.sqlite3_bind_text (pStmt, i, s, s.Length, (IntPtr)(-1));
+                               } 
+                               else if (ptype.Equals (typeof (DBNull))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_null (pStmt, i);
+                               }
+                               else if (ptype.Equals (typeof (Boolean))) 
+                               {
+                                       bool b = (bool)param.Value;
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, b ? 1 : 0);
+                               } else if (ptype.Equals (typeof (Byte))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (Byte)param.Value);
+                               }
+                               else if (ptype.Equals (typeof (Char))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (Char)param.Value);
+                               } 
+                               else if (ptype.Equals (typeof (Int16))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (Int16)param.Value);
+                               } 
+                               else if (ptype.Equals (typeof (Int32))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (Int32)param.Value);
+                               }
+                               else if (ptype.Equals (typeof (SByte))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (SByte)param.Value);
+                               } 
+                               else if (ptype.Equals (typeof (UInt16))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int (pStmt, i, (UInt16)param.Value);
+                               }
+                               else if (ptype.Equals (typeof (DateTime))) 
+                               {
+                                       DateTime dt = (DateTime)param.Value;
+                                       err = Sqlite.sqlite3_bind_int64 (pStmt, i, dt.ToFileTime ());
+                               } 
+                               else if (ptype.Equals (typeof (Double))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_double (pStmt, i, (Double)param.Value);
+                               }
+                               else if (ptype.Equals (typeof (Single))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_double (pStmt, i, (Single)param.Value);
+                               } 
+                               else if (ptype.Equals (typeof (UInt32))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int64 (pStmt, i, (UInt32)param.Value);
+                               }
+                               else if (ptype.Equals (typeof (Int64))) 
+                               {
+                                       err = Sqlite.sqlite3_bind_int64 (pStmt, i, (Int64)param.Value);
+                               } 
+                               else 
+                               {
+                                       throw new ApplicationException("Unkown Parameter Type");
+                               }
+                               if (err != SqliteError.OK) 
+                               {
+                                       throw new ApplicationException ("Sqlite error in bind " + err);
+                               }
+                       }
+               }
+
+               private void GetNextStatement (IntPtr pzStart, out IntPtr pzTail, out IntPtr pStmt)
+               {
+                       if (parent_conn.Version == 3)
+                       {
+                               SqliteError err = Sqlite.sqlite3_prepare (parent_conn.Handle, pzStart, -1, out pStmt, out pzTail);
+                               if (err != SqliteError.OK)
+                                       throw new SqliteSyntaxException (GetError3());
+                       }
+                       else
+                       {
+                               IntPtr errMsg;
+                               SqliteError err = Sqlite.sqlite_compile (parent_conn.Handle, pzStart, out pzTail, out pStmt, out errMsg);
+                               
+                               if (err != SqliteError.OK) 
+                               {
+                                       string msg = "unknown error";
+                                       if (errMsg != IntPtr.Zero) 
+                                       {
+                                               msg = Marshal.PtrToStringAnsi (errMsg);
+                                               Sqlite.sqliteFree (errMsg);
+                                       }
+                                       throw new SqliteSyntaxException (msg);
+                               }
+                       }
+               }
+               
+               // Executes a statement and ignores its result.
+               private void ExecuteStatement (IntPtr pStmt) {
+                       int cols;
+                       IntPtr pazValue, pazColName;
+                       ExecuteStatement (pStmt, out cols, out pazValue, out pazColName);
+               }
+
+               // Executes a statement and returns whether there is more data available.
+               internal bool ExecuteStatement (IntPtr pStmt, out int cols, out IntPtr pazValue, out IntPtr pazColName) {
+                       SqliteError err;
+                       
+                       if (parent_conn.Version == 3) 
+                       {
+                               err = Sqlite.sqlite3_step (pStmt);
+                               if (err == SqliteError.ERROR)
+                                       throw new SqliteExecutionException (GetError3());
+                               pazValue = IntPtr.Zero; pazColName = IntPtr.Zero; // not used for v=3
+                               cols = Sqlite.sqlite3_column_count (pStmt);
+                       }
+                       else 
+                       {
+                               err = Sqlite.sqlite_step (pStmt, out cols, out pazValue, out pazColName);
+                               if (err == SqliteError.ERROR)
+                                       throw new SqliteExecutionException ();
+                       }
+                       
+                       if (err == SqliteError.BUSY)
+                               throw new SqliteBusyException();
+                       
+                       if (err == SqliteError.MISUSE)
+                               throw new SqliteExecutionException();
+                               
+                       // err is either ROW or DONE.
+                       return err == SqliteError.ROW;
+               }
+               
                #endregion
 
                #region Public Methods
@@ -215,142 +363,28 @@ namespace Mono.Data.SqliteClient
                        //The above statement is true for the commented regEx, but I changed it to use the :-prefix, because now (12.05.2005 sqlite3) 
                        //sqlite is using : as Standard Parameterprefix
                        
-                       Regex r = new Regex(@"(('[^']*?\:[^']*')*[^':]*?)*(?<param>:\w+)+([^':]*?('[^']*?\:[^']*'))*",RegexOptions.ExplicitCapture);
                        MatchEvaluator me = new MatchEvaluator(ReplaceParams);
-                       processedText = r.Replace(sql, me);
+                       processedText = v2Parameters.Replace(sql, me);
                        return processedText;
                }
                
                public void Prepare ()
                {
-                       pStmts = new ArrayList();
-                       string sqlcmds = sql;
+                       // There isn't much we can do here.  If a table schema
+                       // changes after preparing a statement, Sqlite bails,
+                       // so we can only compile statements right before we
+                       // want to run them.
                        
+                       if (prepared) return;           
+               
                        if (Parameters.Count > 0 && parent_conn.Version == 2)
                        {
-                               sqlcmds = ProcessParameters();
+                               sql = ProcessParameters();
                        }
                        
-                       SqliteError err = SqliteError.OK;
-                       IntPtr psql = UnixMarshal.StringToHeap(sqlcmds);
-                       IntPtr pzTail = psql;
-                       try {
-                               do { // sql may contain multiple sql commands, loop until they're all processed
-                                       IntPtr pStmt = IntPtr.Zero;
-                                       if (parent_conn.Version == 3)
-                                       {
-                                               err = Sqlite.sqlite3_prepare (parent_conn.Handle, pzTail, sql.Length, out pStmt, out pzTail);
-                                               if (err != SqliteError.OK) {
-                                                       string msg = Marshal.PtrToStringAnsi (Sqlite.sqlite3_errmsg (parent_conn.Handle));
-                                                       throw new ApplicationException (msg);
-                                               }
-                                       }
-                                       else
-                                       {
-                                               IntPtr errMsg;
-                                               err = Sqlite.sqlite_compile (parent_conn.Handle, pzTail, out pzTail, out pStmt, out errMsg);
-                                               
-                                               if (err != SqliteError.OK) 
-                                               {
-                                                       string msg = "unknown error";
-                                                       if (errMsg != IntPtr.Zero) 
-                                                       {
-                                                               msg = Marshal.PtrToStringAnsi (errMsg);
-                                                               Sqlite.sqliteFree (errMsg);
-                                                       }
-                                                       throw new ApplicationException ("Sqlite error: " + msg);
-                                               }
-                                       }
-                                               
-                                       pStmts.Add(pStmt);
-                                       
-                                       if (parent_conn.Version == 3) 
-                                       {
-                                               int pcount = Sqlite.sqlite3_bind_parameter_count (pStmt);
-                                               if (sql_params == null) pcount = 0;
-               
-                                               for (int i = 1; i <= pcount; i++) 
-                                               {
-                                                       String name = Sqlite.sqlite3_bind_parameter_name (pStmt, i);
-                                                       SqliteParameter param = sql_params[name];
-                                                       Type ptype = param.Value.GetType ();
-                                                       
-                                                       if (ptype.Equals (typeof (String))) 
-                                                       {
-                                                               String s = (String)param.Value;
-                                                               err = Sqlite.sqlite3_bind_text (pStmt, i, s, s.Length, (IntPtr)(-1));
-                                                       } 
-                                                       else if (ptype.Equals (typeof (DBNull))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_null (pStmt, i);
-                                                       }
-                                                       else if (ptype.Equals (typeof (Boolean))) 
-                                                       {
-                                                               bool b = (bool)param.Value;
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, b ? 1 : 0);
-                                                       } else if (ptype.Equals (typeof (Byte))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (Byte)param.Value);
-                                                       }
-                                                       else if (ptype.Equals (typeof (Char))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (Char)param.Value);
-                                                       } 
-                                                       else if (ptype.Equals (typeof (Int16))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (Int16)param.Value);
-                                                       } 
-                                                       else if (ptype.Equals (typeof (Int32))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (Int32)param.Value);
-                                                       }
-                                                       else if (ptype.Equals (typeof (SByte))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (SByte)param.Value);
-                                                       } 
-                                                       else if (ptype.Equals (typeof (UInt16))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int (pStmt, i, (UInt16)param.Value);
-                                                       }
-                                                       else if (ptype.Equals (typeof (DateTime))) 
-                                                       {
-                                                               DateTime dt = (DateTime)param.Value;
-                                                               err = Sqlite.sqlite3_bind_int64 (pStmt, i, dt.ToFileTime ());
-                                                       } 
-                                                       else if (ptype.Equals (typeof (Double))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_double (pStmt, i, (Double)param.Value);
-                                                       }
-                                                       else if (ptype.Equals (typeof (Single))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_double (pStmt, i, (Single)param.Value);
-                                                       } 
-                                                       else if (ptype.Equals (typeof (UInt32))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int64 (pStmt, i, (UInt32)param.Value);
-                                                       }
-                                                       else if (ptype.Equals (typeof (Int64))) 
-                                                       {
-                                                               err = Sqlite.sqlite3_bind_int64 (pStmt, i, (Int64)param.Value);
-                                                       } 
-                                                       else 
-                                                       {
-                                                               throw new ApplicationException("Unkown Parameter Type");
-                                                       }
-                                                       if (err != SqliteError.OK) 
-                                                       {
-                                                               throw new ApplicationException ("Sqlite error in bind " + err);
-                                                       }
-                                               }
-                                       }
-                               } while ((int)pzTail - (int)psql < sql.Length);
-                       } finally {
-                               UnixMarshal.FreeHeap(psql);
-                       }
-                       prepared=true;
+                       prepared = true;
                }
                
-               
                IDbDataParameter IDbCommand.CreateParameter()
                {
                        return CreateParameter ();
@@ -364,7 +398,7 @@ namespace Mono.Data.SqliteClient
                public int ExecuteNonQuery ()
                {
                        int rows_affected;
-                       SqliteDataReader r = ExecuteReader (CommandBehavior.Default, false, out rows_affected);
+                       ExecuteReader (CommandBehavior.Default, false, out rows_affected);
                        return rows_affected;
                }
                
@@ -402,75 +436,63 @@ namespace Mono.Data.SqliteClient
                
                public SqliteDataReader ExecuteReader (CommandBehavior behavior, bool want_results, out int rows_affected)
                {
-                       SqliteDataReader reader = null;
-                       SqliteError err = SqliteError.OK;
-                       IntPtr errMsg = IntPtr.Zero; 
+                       // The SQL string may contain multiple sql commands, so the main
+                       // thing to do is have Sqlite iterate through the commands.
+                       // If want_results, only the last command is returned as a
+                       // DataReader.  Otherwise, no command is returned as a
+                       // DataReader.
+               
+                       IntPtr psql = UnixMarshal.StringToHeap(sql);
+                       IntPtr pzTail = psql;
+                       IntPtr errMsgPtr;
+                       
                        parent_conn.StartExec ();
-                 
-                       try 
-                       {
-                               if (!prepared)
-                               {
-                                       Prepare ();
-                               }
-                               for (int i = 0; i < pStmts.Count; i++) {
-                                       IntPtr pStmt = (IntPtr)pStmts[i];
+                       
+                       rows_affected = 0;
+                       
+                       try {
+                               while (true) {
+                                       IntPtr pStmt;
+                                        
+                                       GetNextStatement(pzTail, out pzTail, out pStmt);
                                        
-                                       // If want_results, return the results of the last statement
-                                       // via the SqliteDataReader, and execute but ignore the results
-                                       // of the other statements.
-                                       if (i == pStmts.Count-1 && want_results) 
-                                       {
-                                               reader = new SqliteDataReader (this, pStmt, parent_conn.Version);
-                                               break;
-                                       } 
+                                       if (pStmt == IntPtr.Zero)
+                                               throw new Exception();
                                        
-                                       // Execute but ignore the results of these statements.
-                                       if (parent_conn.Version == 3) 
-                                       {
-                                               err = Sqlite.sqlite3_step (pStmt);
-                                       }
-                                       else 
-                                       {
-                                               int cols;
-                                               IntPtr pazValue = IntPtr.Zero;
-                                               IntPtr pazColName = IntPtr.Zero;
-                                               err = Sqlite.sqlite_step (pStmt, out cols, out pazValue, out pazColName);
-                                       }
-                                       // On error, misuse, or busy, don't bother with the rest of the statements.
-                                       if (err != SqliteError.ROW && err != SqliteError.DONE) break;
-                               }
-                       }
-                       finally 
-                       {       
-                               foreach (IntPtr pStmt in pStmts) {
-                                       if (parent_conn.Version == 3) 
-                                       {
-                                               err = Sqlite.sqlite3_finalize (pStmt);
-                                       }
-                                       else
-                                       {
-                                               err = Sqlite.sqlite_finalize (pStmt, out errMsg);
+                                       // pzTail is positioned after the last byte in the
+                                       // statement, which will be the NULL character if
+                                       // this was the last statement.
+                                       bool last = Marshal.ReadByte(pzTail) == 0;
+
+                                       try {
+                                               if (parent_conn.Version == 3)
+                                                       BindParameters3 (pStmt);
+                                               
+                                               if (last && want_results)
+                                                       return new SqliteDataReader (this, pStmt, parent_conn.Version);
+
+                                               ExecuteStatement(pStmt);
+                                               
+                                               if (last) // rows_affected is only used if !want_results
+                                                       rows_affected = NumChanges ();
+                                               
+                                       } finally {
+                                               if (parent_conn.Version == 3) 
+                                                       Sqlite.sqlite3_finalize (pStmt);
+                                               else
+                                                       Sqlite.sqlite_finalize (pStmt, out errMsgPtr);
                                        }
+                                       
+                                       if (last) break;
                                }
+
+                               return null;
+                       } finally {
                                parent_conn.EndExec ();
-                               prepared = false;
-                       }
-                       
-                       if (err != SqliteError.OK &&
-                           err != SqliteError.DONE &&
-                           err != SqliteError.ROW) 
-                       {
-                               if (errMsg != IntPtr.Zero) 
-                               {
-                                       // TODO: Get the message text
-                               }
-                               throw new ApplicationException ("Sqlite error");
+                               UnixMarshal.FreeHeap (psql);
                        }
-                       rows_affected = NumChanges ();
-                       return reader;
                }
-               
+
                public int LastInsertRowID () 
                {
                        if (parent_conn.Version == 3)
@@ -478,6 +500,10 @@ namespace Mono.Data.SqliteClient
                        else
                                return Sqlite.sqlite_last_insert_rowid(parent_conn.Handle);
                }
+               
+               private string GetError3() {
+                       return Marshal.PtrToStringAnsi (Sqlite.sqlite3_errmsg (parent_conn.Handle));
+               }
        #endregion
        }
 }
index 17591953873873091caa096d66ee171d169fbf96..d2d9bd69ff97dea993ddec2b1017ffa54aae453a 100644 (file)
@@ -184,7 +184,7 @@ namespace Mono.Data.SqliteClient
                public IDbTransaction BeginTransaction ()
                {
                        if (state != ConnectionState.Open)
-                               throw new InvalidOperationException("Invalid operation: The connection is close");
+                               throw new InvalidOperationException("Invalid operation: The connection is closed");
                        
                        SqliteTransaction t = new SqliteTransaction();
                        t.Connection = this;
@@ -196,7 +196,7 @@ namespace Mono.Data.SqliteClient
                
                public IDbTransaction BeginTransaction (IsolationLevel il)
                {
-                       return null;
+                       throw new InvalidOperationException();
                }
                
                public void Close ()
@@ -240,7 +240,6 @@ namespace Mono.Data.SqliteClient
                        }
                        
                        IntPtr errmsg = IntPtr.Zero;
-                       Exception dll_error = null;
 
                        if (Version == 2){
                                try {
@@ -250,8 +249,7 @@ namespace Mono.Data.SqliteClient
                                                Sqlite.sqliteFree (errmsg);
                                                throw new ApplicationException (msg);
                                        }
-                               } catch (DllNotFoundException dll){
-                                       dll_error = dll;
+                               } catch (DllNotFoundException dll) {
                                        db_version = 3;
                                }
                        }
index 85bc38dd6d7facfcdb35a4d544003b7394b7a270..d8380d3406af60d1ffcae775ba1db2f56c2e0232 100644 (file)
@@ -6,6 +6,7 @@
 //
 // Author(s): Vladimir Vukicevic  <vladimir@pobox.com>
 //            Everaldo Canuto  <everaldo_canuto@yahoo.com.br>
+//                       Joshua Tauberer <tauberer@for.net>
 //
 // Copyright (C) 2002  Vladimir Vukicevic
 //
@@ -44,12 +45,13 @@ namespace Mono.Data.SqliteClient
                
                private SqliteCommand command;
                private ArrayList rows;
-               private ArrayList columns;
+               private string[] columns;
                private Hashtable column_names;
                private int current_row;
                private bool closed;
                private bool reading;
                private int records_affected;
+               private string[] decltypes;
                
                #endregion
 
@@ -59,12 +61,11 @@ namespace Mono.Data.SqliteClient
                {
                        command = cmd;
                        rows = new ArrayList ();
-                       columns = new ArrayList ();
                        column_names = new Hashtable ();
                        closed = false;
                        current_row = -1;
                        reading = true;
-                       ReadpVm (pVm, version);
+                       ReadpVm (pVm, version, cmd);
                        ReadingDone ();
                }
                
@@ -77,15 +78,15 @@ namespace Mono.Data.SqliteClient
                }
                
                public int FieldCount {
-                       get { return columns.Count; }
+                       get { return columns.Length; }
                }
                
                public object this[string name] {
-                       get { return ((ArrayList) rows[current_row])[(int) column_names[name]]; }
+                       get { return ((object[]) rows[current_row])[(int) column_names[name]]; }
                }
                
                public object this[int i] {
-                       get { return ((ArrayList) rows[current_row])[i]; }
+                       get { return ((object[]) rows[current_row])[i]; }
                }
                
                public bool IsClosed {
@@ -100,75 +101,96 @@ namespace Mono.Data.SqliteClient
 
                #region Internal Methods
                
-               internal void ReadpVm (IntPtr pVm, int version)
+               internal void ReadpVm (IntPtr pVm, int version, SqliteCommand cmd)
                {
-                       int pN = 0;
-                       IntPtr pazValue = IntPtr.Zero;
-                       IntPtr pazColName = IntPtr.Zero;
-                       SqliteError res;
+                       int pN;
+                       IntPtr pazValue;
+                       IntPtr pazColName;
+                       bool first = true;
+                       
+                       int[] declmode = null;
 
                        while (true) {
-                               if (version == 3) {
-                                       res = Sqlite.sqlite3_step (pVm);
-                                       pN = Sqlite.sqlite3_column_count (pVm);
-                               } else
-                                       res = Sqlite.sqlite_step (pVm, out pN, out pazValue, out pazColName);
-                               if (res == SqliteError.ERROR) {         
-                                       throw new ApplicationException ("Sqlite error");
-                               }
-                               if (res == SqliteError.DONE) {
-                                       break;
-                               }
-                               // We have some data; lets read it
-                               if (column_names.Count == 0) {
+                               bool hasdata = cmd.ExecuteStatement(pVm, out pN, out pazValue, out pazColName);
+                       
+                               // For the first row, get the column information
+                               if (first) {
+                                       first = false;
+                                       
+                                       if (version == 3) {
+                                               // A decltype might be null if the type is unknown to sqlite.
+                                               decltypes = new string[pN];
+                                               declmode = new int[pN]; // 1 == integer, 2 == datetime
+                                               for (int i = 0; i < pN; i++) {
+                                                       IntPtr decl = Sqlite.sqlite3_column_decltype (pVm, i);
+                                                       if (decl != IntPtr.Zero) {
+                                                               decltypes[i] = Marshal.PtrToStringAnsi (decl).ToLower();
+                                                               if (decltypes[i] == "int" || decltypes[i] == "integer")
+                                                                       declmode[i] = 1;
+                                                               else if (decltypes[i] == "date" || decltypes[i] == "datetime")
+                                                                       declmode[i] = 2;
+                                                       }
+                                               }
+                                       }
+                                       
+                                       columns = new string[pN];       
                                        for (int i = 0; i < pN; i++) {
-                                               string colName = "";
+                                               string colName;
                                                if (version == 2) {
                                                        IntPtr fieldPtr = (IntPtr)Marshal.ReadInt32 (pazColName, i*IntPtr.Size);
                                                        colName = Marshal.PtrToStringAnsi (fieldPtr);
                                                } else {
                                                        colName = Marshal.PtrToStringAnsi (Sqlite.sqlite3_column_name (pVm, i));
                                                }
-                                               columns.Add (colName);
+                                               columns[i] = colName;
                                                column_names [colName] = i;
                                        }
                                }
-                               ArrayList data_row = new ArrayList (pN);
+
+                               if (!hasdata) break;
+                               
+                               object[] data_row = new object [pN];
                                for (int i = 0; i < pN; i++) {
-                                       string colData = "";
                                        if (version == 2) {
                                                IntPtr fieldPtr = (IntPtr)Marshal.ReadInt32 (pazValue, i*IntPtr.Size);
-                                               colData = Marshal.PtrToStringAnsi (fieldPtr);
-                                               data_row.Add (Marshal.PtrToStringAnsi (fieldPtr));
+                                               data_row[i] = Marshal.PtrToStringAnsi (fieldPtr);
                                        } else {
                                                switch (Sqlite.sqlite3_column_type (pVm, i)) {
                                                        case 1:
-                                                               Int64 sqliteint64 = Sqlite.sqlite3_column_int64 (pVm, i);
-                                                               data_row.Add (sqliteint64.ToString ());
+                                                               // If the column was declared as an 'int' or 'integer', let's play
+                                                               // nice and return an int (version 3 only).
+                                                               if (declmode[i] == 1)
+                                                                       data_row[i] = (int)Sqlite.sqlite3_column_int64 (pVm, i);
+                                                               else
+                                                                       data_row[i] = Sqlite.sqlite3_column_int64 (pVm, i);
                                                                break;
                                                        case 2:
-                                                               double sqlitedouble = Sqlite.sqlite3_column_double (pVm, i);
-                                                               data_row.Add (sqlitedouble.ToString ());
+                                                               data_row[i] = Sqlite.sqlite3_column_double (pVm, i);
                                                                break;
                                                        case 3:
-                                                               colData = Marshal.PtrToStringAnsi (Sqlite.sqlite3_column_text (pVm, i));
-                                                               data_row.Add (colData);
+                                                               data_row[i] = Marshal.PtrToStringAnsi (Sqlite.sqlite3_column_text (pVm, i));
+                                                               
+                                                               // If the column was declared as a 'date' or 'datetime', let's play
+                                                               // nice and return a DateTime (version 3 only).
+                                                               if (declmode[i] == 2)
+                                                                       data_row[i] = DateTime.Parse((string)data_row[i]);
                                                                break;
                                                        case 4:
                                                                int blobbytes = Sqlite.sqlite3_column_bytes (pVm, i);
                                                                IntPtr blobptr = Sqlite.sqlite3_column_blob (pVm, i);
                                                                byte[] blob = new byte[blobbytes];
                                                                Marshal.Copy (blobptr, blob, 0, blobbytes);
-                                                               data_row.Add (blob);
+                                                               data_row[i] = blob;
                                                                break;
                                                        case 5:
-                                                               data_row.Add (null);
+                                                               data_row[i] = null;
                                                                break;
                                                        default:
                                                                throw new ApplicationException ("FATAL: Unknown sqlite3_column_type");
                                                }
-                                       }       
+                                       }
                                }
+                               
                                rows.Add (data_row);
                        }
                }
@@ -278,12 +300,12 @@ namespace Mono.Data.SqliteClient
                
                public bool GetBoolean (int i)
                {
-                       return Convert.ToBoolean ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToBoolean (((object[]) rows[current_row])[i]);
                }
                
                public byte GetByte (int i)
                {
-                       return Convert.ToByte ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToByte (((object[]) rows[current_row])[i]);
                }
                
                public long GetBytes (int i, long fieldOffset, byte[] buffer, int bufferOffset, int length)
@@ -293,7 +315,7 @@ namespace Mono.Data.SqliteClient
                
                public char GetChar (int i)
                {
-                       return Convert.ToChar ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToChar (((object[]) rows[current_row])[i]);
                }
                
                public long GetChars (int i, long fieldOffset, char[] buffer, int bufferOffset, int length)
@@ -308,32 +330,34 @@ namespace Mono.Data.SqliteClient
                
                public string GetDataTypeName (int i)
                {
+                       if (decltypes != null && decltypes[i] != null)
+                               return decltypes[i];
                        return "text"; // SQL Lite data type
                }
                
                public DateTime GetDateTime (int i)
                {
-                       return Convert.ToDateTime ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToDateTime (((object[]) rows[current_row])[i]);
                }
                
                public decimal GetDecimal (int i)
                {
-                       return Convert.ToDecimal ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToDecimal (((object[]) rows[current_row])[i]);
                }
                
                public double GetDouble (int i)
                {
-                       return Convert.ToDouble ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToDouble (((object[]) rows[current_row])[i]);
                }
                
                public Type GetFieldType (int i)
                {
-                       return System.Type.GetType ("System.String"); // .NET data type
+                       return ((object[]) rows[current_row])[i].GetType();
                }
                
                public float GetFloat (int i)
                {
-                       return Convert.ToSingle ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToSingle (((object[]) rows[current_row])[i]);
                }
                
                public Guid GetGuid (int i)
@@ -343,22 +367,22 @@ namespace Mono.Data.SqliteClient
                
                public short GetInt16 (int i)
                {
-                       return Convert.ToInt16 ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToInt16 (((object[]) rows[current_row])[i]);
                }
                
                public int GetInt32 (int i)
                {
-                       return Convert.ToInt32 ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToInt32 (((object[]) rows[current_row])[i]);
                }
                
                public long GetInt64 (int i)
                {
-                       return Convert.ToInt64 ((string) ((ArrayList) rows[current_row])[i]);
+                       return Convert.ToInt64 (((object[]) rows[current_row])[i]);
                }
                
                public string GetName (int i)
                {
-                       return (string) columns[i];
+                       return columns[i];
                }
                
                public int GetOrdinal (string name)
@@ -368,20 +392,20 @@ namespace Mono.Data.SqliteClient
                
                public string GetString (int i)
                {
-                       return ((string) ((ArrayList) rows[current_row])[i]);
+                       return (((object[]) rows[current_row])[i]).ToString();
                }
                
                public object GetValue (int i)
                {
-                       return ((ArrayList) rows[current_row])[i];
+                       return ((object[]) rows[current_row])[i];
                }
                
                public int GetValues (object[] values)
                {
-                       int num_to_fill = System.Math.Min (values.Length, columns.Count);
+                       int num_to_fill = System.Math.Min (values.Length, columns.Length);
                        for (int i = 0; i < num_to_fill; i++) {
-                               if (((ArrayList) rows[current_row])[i] != null) {
-                                       values[i] = ((ArrayList) rows[current_row])[i];
+                               if (((object[]) rows[current_row])[i] != null) {
+                                       values[i] = ((object[]) rows[current_row])[i];
                                } else {
                                        values[i] = DBNull.Value;
                                }
@@ -391,7 +415,7 @@ namespace Mono.Data.SqliteClient
                
                public bool IsDBNull (int i)
                {
-                       return (((ArrayList) rows[current_row])[i] == null);
+                       return (((object[]) rows[current_row])[i] == null);
                }
                        
                #endregion
diff --git a/mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteExceptions.cs b/mcs/class/Mono.Data.SqliteClient/Mono.Data.SqliteClient/SqliteExceptions.cs
new file mode 100644 (file)
index 0000000..f0c36eb
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.Data;
+
+namespace Mono.Data.SqliteClient 
+{
+       // This exception is raised whenever a statement cannot be compiled.
+       public class SqliteSyntaxException : ApplicationException
+       {
+               public SqliteSyntaxException() : this("An error occurred compiling the Sqlite command.")
+               {
+               }
+               
+               public SqliteSyntaxException(string message) : base(message)
+               {
+               }
+
+               public SqliteSyntaxException(string message, Exception cause) : base(message, cause)
+               {
+               }
+       }
+
+       // This exception is raised whenever the execution
+       // of a statement fails.
+       public class SqliteExecutionException : ApplicationException
+       {
+               public SqliteExecutionException() : this("An error occurred executing the Sqlite command.")
+               {
+               }
+               
+               public SqliteExecutionException(string message) : base(message)
+               {
+               }
+
+               public SqliteExecutionException(string message, Exception cause) : base(message, cause)
+               {
+               }
+       }
+
+       // This exception is raised whenever Sqlite says it
+       // cannot run a command because something is busy.
+       public class SqliteBusyException : SqliteExecutionException
+       {
+               public SqliteBusyException() : this("The database is locked.")
+               {
+               }
+               
+               public SqliteBusyException(string message) : base(message)
+               {
+               }
+
+               public SqliteBusyException(string message, Exception cause) : base(message, cause)
+               {
+               }
+       }
+
+}
\ No newline at end of file
index 9d279a93a10017bbe1dcc8c6b8e2d977a4b5eed0..e41b3bb2124af74982c860b65b9895daca3e63b8 100644 (file)
@@ -1,3 +1,7 @@
+2006-01-02  Joshua Tauberer <tauberer@for.net>
+
+       * SqliteTest.cs: Cleaned up and added new stuff.
+
 2005-05-20  Sureshkumar T  <tsureshkumar@novell.com>
 
        * test.sql: script to create a test db.
index e397579d06c533bcfb109e8074049b5f68d9a333..90aca7c683dd43fd5e5e739d39fc33ce12b3545d 100644 (file)
@@ -28,11 +28,19 @@ namespace Test.Mono.Data.SqliteClient
                [STAThread]\r
                static void Main(string[] args)\r
                {\r
-                       Console.WriteLine("If this test works, you should get:");\r
-                       Console.WriteLine("Data 1: 5");\r
-                       Console.WriteLine("Data 2: Mono");\r
-\r
-                       Console.WriteLine("create SqliteConnection...");\r
+                       Test(false);\r
+                       Console.WriteLine();\r
+                       Test(true);\r
+               }\r
+               \r
+               static void Test(bool v3) {\r
+                       if (!v3)\r
+                               Console.WriteLine("Testing Version 2");\r
+                       else\r
+                               Console.WriteLine("Testing Version 3");\r
+                               \r
+                       System.IO.File.Delete("SqliteTest.db");\r
+               \r
                        SqliteConnection dbcon = new SqliteConnection();\r
                        \r
                        // the connection string is a URL that points\r
@@ -42,43 +50,47 @@ namespace Test.Mono.Data.SqliteClient
                        // "URI=file:some/path"\r
                        string connectionString =\r
                                "URI=file:SqliteTest.db";\r
-                       Console.WriteLine("setting ConnectionString using: " + \r
-                               connectionString);\r
+                       if (v3)\r
+                               connectionString += ",Version=3";\r
                        dbcon.ConnectionString = connectionString;\r
                                \r
-                       Console.WriteLine("open the connection...");\r
                        dbcon.Open();\r
 \r
-                       Console.WriteLine("create SqliteCommand to CREATE TABLE MONO_TEST");\r
                        SqliteCommand dbcmd = new SqliteCommand();\r
                        dbcmd.Connection = dbcon;\r
                        \r
                        dbcmd.CommandText = \r
                                "CREATE TABLE MONO_TEST ( " +\r
                                "NID INT, " +\r
-                               "NDESC TEXT )";\r
-                       Console.WriteLine("execute command...");\r
-                       dbcmd.ExecuteNonQuery();\r
+                               "NDESC TEXT, " +\r
+                               "NTIME DATETIME); " +\r
+                               "INSERT INTO MONO_TEST  " +\r
+                               "(NID, NDESC, NTIME )"+\r
+                               "VALUES(1,'One', '2006-01-01')";\r
+                       Console.WriteLine("Create & insert modified rows = 1: " + dbcmd.ExecuteNonQuery());\r
 \r
-                       Console.WriteLine("set and execute command to INSERT INTO MONO_TEST");\r
                        dbcmd.CommandText =\r
                                "INSERT INTO MONO_TEST  " +\r
-                               "(NID, NDESC )"+\r
-                               "VALUES(5,'Mono')";\r
-                       dbcmd.ExecuteNonQuery();\r
+                               "(NID, NDESC, NTIME )"+\r
+                               "VALUES(2,'Two', '2006-01-02')";\r
+                       Console.WriteLine("Insert modified rows and ID = 1, 2: " + dbcmd.ExecuteNonQuery() + " , " + dbcmd.LastInsertRowID());\r
 \r
-                       Console.WriteLine("set command to SELECT FROM MONO_TEST");\r
                        dbcmd.CommandText =\r
                                "SELECT * FROM MONO_TEST";\r
                        SqliteDataReader reader;\r
-                       Console.WriteLine("execute reader...");\r
                        reader = dbcmd.ExecuteReader();\r
 \r
                        Console.WriteLine("read and display data...");\r
-                       while(reader.Read()) {\r
-                               Console.WriteLine("Data 1: " + reader[0].ToString());\r
-                               Console.WriteLine("Data 2: " + reader[1].ToString());\r
-                       }\r
+                       while(reader.Read())\r
+                               for (int i = 0; i < reader.FieldCount; i++)\r
+                                       Console.WriteLine(" Col {0}: {1} (type: {2}, data type: {3})",\r
+                                               i, reader[i].ToString(), reader[i].GetType().FullName, reader.GetDataTypeName(i));\r
+\r
+                       dbcmd.CommandText = "SELECT NDESC FROM MONO_TEST WHERE NID=2";\r
+                       Console.WriteLine("read and display a scalar = 'Two': " + dbcmd.ExecuteScalar());\r
+\r
+                       dbcmd.CommandText = "SELECT count(*) FROM MONO_TEST";\r
+                       Console.WriteLine("read and display a non-column scalar = 2: " + dbcmd.ExecuteScalar());\r
 \r
                        Console.WriteLine("read and display data using DataAdapter...");\r
                        SqliteDataAdapter adapter = new SqliteDataAdapter("SELECT * FROM MONO_TEST", connectionString);\r
@@ -87,20 +99,31 @@ namespace Test.Mono.Data.SqliteClient
                        foreach(DataTable myTable in dataset.Tables){\r
                                foreach(DataRow myRow in myTable.Rows){\r
                                        foreach (DataColumn myColumn in myTable.Columns){\r
-                                               Console.WriteLine(myRow[myColumn]);\r
+                                               Console.WriteLine(" " + myRow[myColumn]);\r
                                        }\r
                                }\r
                        }\r
 \r
-                       \r
-                       Console.WriteLine("clean up...");\r
+                       try {\r
+                               dbcmd.CommandText = "SELECT NDESC INVALID SYNTAX FROM MONO_TEST WHERE NID=2";\r
+                               dbcmd.ExecuteNonQuery();\r
+                               Console.WriteLine("Should not reach here.");\r
+                       } catch (Exception e) {\r
+                               Console.WriteLine("Testing a syntax error: " + e.GetType().Name + ": " + e.Message);\r
+                       }\r
+\r
+                       /*try {\r
+                               dbcmd.CommandText = "SELECT 0/0 FROM MONO_TEST WHERE NID=2";\r
+                               Console.WriteLine("Should not reach here: " + dbcmd.ExecuteScalar());\r
+                       } catch (Exception e) {\r
+                               Console.WriteLine("Testing an execution error: " + e.GetType().Name + ": " + e.Message);\r
+                       }*/\r
+\r
                        dataset.Dispose();\r
                        adapter.Dispose();\r
                        reader.Close();\r
                        dbcmd.Dispose();\r
                        dbcon.Close();\r
-\r
-                       Console.WriteLine("Done.");\r
                }\r
        }\r
 }\r
index b489208abb04cdfbffb0ef3856c777ed104c7dab..5d3c92ddeb74ce6d89fc1c8b6663c568882d349f 100644 (file)
@@ -9,4 +9,5 @@ Mono.Data.SqliteClient/SqliteRowUpdatedEventArgs.cs
 Mono.Data.SqliteClient/SqliteRowUpdatedEventHandler.cs
 Mono.Data.SqliteClient/SqliteRowUpdatingEventArgs.cs
 Mono.Data.SqliteClient/SqliteRowUpdatingEventHandler.cs
-Mono.Data.SqliteClient/SqliteTransaction.cs
\ No newline at end of file
+Mono.Data.SqliteClient/SqliteTransaction.cs
+Mono.Data.SqliteClient/SqliteExceptions.cs