2007-10-08 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcCommand.cs
index 66ace155acead152072e249b4427cb64d0ab4d5f..fd273afb5b695d99bf8ac092008699ddef023bef 100644 (file)
@@ -7,6 +7,30 @@
 // Copyright (C) Brian Ritchie, 2002
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
 using System.ComponentModel;
 using System.Data;
 using System.Data.Common;
@@ -15,40 +39,51 @@ using System.Runtime.InteropServices;
 
 namespace System.Data.Odbc
 {
-       /// <summary>
+        /// <summary>
        /// Represents an SQL statement or stored procedure to execute against a data source.
        /// </summary>
+       [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
+        [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
+#if NET_2_0
+       [DefaultEvent ("RecordsAffected")]
+       public sealed class OdbcCommand : DbCommand, ICloneable
+#else
        public sealed class OdbcCommand : Component, ICloneable, IDbCommand
+#endif //NET_2_0
        {
                #region Fields
 
                string commandText;
                int timeout;
                CommandType commandType;
+               UpdateRowSource updateRowSource = UpdateRowSource.Both;
+
                OdbcConnection connection;
-               OdbcParameterCollection parameters;
                OdbcTransaction transaction;
+               OdbcParameterCollection _parameters;
+
                bool designTimeVisible;
                bool prepared=false;
-               OdbcDataReader dataReader;
-               CommandBehavior behavior;
-               internal IntPtr hstmt;
+               IntPtr hstmt = IntPtr.Zero;
+
+               bool disposed = false;
                
                #endregion // Fields
 
                #region Constructors
 
                public OdbcCommand ()
-               {
-                       commandText = String.Empty;
-                       timeout = 30; // default timeout 
-                       commandType = CommandType.Text;
-                       connection = null;
-                       parameters = new OdbcParameterCollection ();
-                       transaction = null;
+               {
+                       this.CommandText = String.Empty;
+                       this.CommandTimeout = 30; // default timeout 
+                       this.CommandType = CommandType.Text;
+                       Connection = null;
+                       _parameters = new OdbcParameterCollection ();
+                       Transaction = null;
                        designTimeVisible = false;
-                       dataReader = null;
-                       behavior = CommandBehavior.Default;
+#if ONLY_1_1
+                       updateRowSource = UpdateRowSource.Both;
+#endif // ONLY_1_1
                }
 
                public OdbcCommand (string cmdText) : this ()
@@ -63,10 +98,10 @@ namespace System.Data.Odbc
                }
 
                public OdbcCommand (string cmdText,
-                                    OdbcConnection connection,
-                                    OdbcTransaction transaction) : this (cmdText, connection)
+                                   OdbcConnection connection,
+                                   OdbcTransaction transaction) : this (cmdText, connection)
                {
-                       this.transaction = transaction;
+                       this.Transaction = transaction;
                }
 
                #endregion // Constructors
@@ -77,36 +112,57 @@ namespace System.Data.Odbc
                {
                        get { return hstmt; }
                }
+               
 
-               public string CommandText 
+                [OdbcCategory ("Data")]
+                [DefaultValue ("")]
+                [OdbcDescriptionAttribute ("Command text to execute")]
+                [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+                [RefreshPropertiesAttribute (RefreshProperties.All)]
+               public 
+#if NET_2_0
+               override
+#endif
+               string CommandText 
                {
-                       get {
-                               return commandText;
-                       }
+                       get { return commandText; }
                        set { 
                                prepared=false;
                                commandText = value;
                        }
                }
 
-               public int CommandTimeout {
-                       get {
-                               return timeout;
-                       }
-                       set {
-                               timeout = value;
-                       }
+               [OdbcDescriptionAttribute ("Time to wait for command to execute")]
+#if NET_1_0 || ONLY_1_1
+                [DefaultValue (30)]
+#endif
+               public 
+#if NET_2_0
+               override
+#endif
+               int CommandTimeout {
+                       get { return timeout; }
+                       set { timeout = value; }
                }
 
-               public CommandType CommandType { 
-                       get {
-                               return commandType;
-                       }
-                       set {
-                               commandType = value;
-                       }
+               [OdbcCategory ("Data")]
+                [DefaultValue ("Text")]
+                [OdbcDescriptionAttribute ("How to interpret the CommandText")]
+                [RefreshPropertiesAttribute (RefreshProperties.All)]
+               public
+#if NET_2_0
+               override
+#endif
+               CommandType CommandType { 
+                       get { return commandType; }
+                       set { commandType = value; }
                }
 
+#if ONLY_1_1
+               [OdbcCategory ("Behavior")]
+                [OdbcDescriptionAttribute ("Connection used by the command")]
+                [DefaultValue (null)]
+                [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
                public OdbcConnection Connection { 
                        get {
                                return connection;
@@ -115,8 +171,30 @@ namespace System.Data.Odbc
                                connection = value;
                        }
                }
-
-               public bool DesignTimeVisible { 
+#endif // ONLY_1_1
+
+#if NET_2_0
+                [DefaultValue (null)]
+                [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+                public new OdbcConnection Connection
+                {
+                        get { return DbConnection as OdbcConnection; }
+                        set { DbConnection = value; }
+                }
+                
+#endif // NET_2_0
+
+               [BrowsableAttribute (false)]
+                [DesignOnlyAttribute (true)]
+                [DefaultValue (true)]
+#if NET_2_0
+                [EditorBrowsable (EditorBrowsableState.Never)]
+#endif
+               public 
+#if NET_2_0
+               override
+#endif
+               bool DesignTimeVisible { 
                        get {
                                return designTimeVisible;
                        }
@@ -125,16 +203,33 @@ namespace System.Data.Odbc
                        }
                }
 
-               public OdbcParameterCollection Parameters {
+
+               [OdbcCategory ("Data")]
+                [OdbcDescriptionAttribute ("The parameters collection")]
+                [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
+               public
+#if NET_2_0
+                new
+#endif // NET_2_0
+                OdbcParameterCollection Parameters {
                        get {
-                               return parameters;
-                       }
-                       set {
-                               parameters = value;
+#if ONLY_1_1
+                               return _parameters;
+                               #else
+                                return base.Parameters as OdbcParameterCollection;
+#endif // ONLY_1_1
+
                        }
                }
-
-               public OdbcTransaction Transaction {
+               
+               [BrowsableAttribute (false)]
+                [OdbcDescriptionAttribute ("The transaction used by the command")]
+                [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
+               public
+#if NET_2_0
+                new
+#endif // NET_2_0
+                OdbcTransaction Transaction {
                        get {
                                return transaction;
                        }
@@ -143,17 +238,35 @@ namespace System.Data.Odbc
                        }
                }
 
-               public UpdateRowSource UpdatedRowSource { 
-                       [MonoTODO]
-                       get {
-                               throw new NotImplementedException ();
-                       }
-                       [MonoTODO]
-                       set {
-                               throw new NotImplementedException ();
-                       }
+               [OdbcCategory ("Behavior")]
+                [DefaultValue (UpdateRowSource.Both)]
+                [OdbcDescriptionAttribute ("When used by a DataAdapter.Update, how command results are applied to the current DataRow")]
+               public 
+#if NET_2_0
+               override
+#endif
+               UpdateRowSource UpdatedRowSource { 
+                               get {
+                                       return updateRowSource;
+                               }
+                               set {
+                                       updateRowSource = value;
+                               }
                }
 
+#if NET_2_0
+                protected override DbConnection DbConnection 
+                {
+                        get { return connection; }
+                        set { 
+                                connection = (OdbcConnection) value; 
+                        }                        
+                }
+
+#endif // NET_2_0
+
+#if ONLY_1_1
+
                IDbConnection IDbCommand.Connection {
                        get {
                                return Connection;
@@ -168,125 +281,276 @@ namespace System.Data.Odbc
                                return Parameters;
                        }
                }
-
+               #else
+                protected override DbParameterCollection DbParameterCollection
+                {
+                        get { return _parameters as DbParameterCollection;}
+                }
+                
+#endif // NET_2_0
+
+#if ONLY_1_1
                IDbTransaction IDbCommand.Transaction  {
                        get {
                                return (IDbTransaction) Transaction;
                        }
                        set {
-                               throw new NotImplementedException ();
+                               if (value is OdbcTransaction)
+                                {
+                                        Transaction = (OdbcTransaction)value;
+                                }
+                                else
+                                {
+                                        throw new ArgumentException ();
+                                }
+                       }
+               }
+               #else
+               protected override DbTransaction DbTransaction 
+                {
+                       get { return transaction; }
+                       set {
+                                transaction = (OdbcTransaction)value;
                        }
                }
+#endif // ONLY_1_1
+
+
 
                #endregion // Properties
 
                #region Methods
 
-               public void Cancel () 
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                void Cancel () 
                {
                        if (hstmt!=IntPtr.Zero)
                        {
-                               OdbcReturn ret=libodbc.SQLCancel(hstmt);
-                               libodbchelper.DisplayError("SQLCancel",ret);
+                               OdbcReturn Ret=libodbc.SQLCancel(hstmt);
+                               if ((Ret!=OdbcReturn.Success) && (Ret!=OdbcReturn.SuccessWithInfo)) 
+                                       throw new OdbcException(new OdbcError("SQLCancel",OdbcHandleType.Stmt,hstmt));
                        }
                        else
                                throw new InvalidOperationException();
                }
 
-               public OdbcParameter CreateParameter ()
+#if ONLY_1_1
+               IDbDataParameter IDbCommand.CreateParameter ()
                {
-                       return new OdbcParameter ();
+                       return CreateParameter ();
                }
 
-               IDbDataParameter IDbCommand.CreateParameter ()
+#else
+                protected override DbParameter CreateDbParameter ()
+                {
+                        return CreateParameter ();
+                }
+                
+#endif // ONLY_1_1
+
+               public new OdbcParameter CreateParameter ()
                {
-                       return CreateParameter ();
+                       return new OdbcParameter ();
                }
-               
-               [MonoTODO]
+
                protected override void Dispose (bool disposing)
                {
+                       if (disposed)
+                               return;
+                       
+                       FreeStatement (); // free handles
+                       Connection = null;
+                       Transaction = null;
+                       disposed = true;
+               }
+
+               private IntPtr ReAllocStatment ()
+               {
+                       OdbcReturn ret;
+
+                       if (hstmt != IntPtr.Zero)
+                               FreeStatement ();
+
+                       ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLAllocHandle",OdbcHandleType.Dbc,Connection.hDbc));
+                       disposed = false;
+                       return hstmt;
+               }
+
+               private void FreeStatement ()
+               {
+                       if (hstmt == IntPtr.Zero)
+                               return;
+                       
+                       // free previously allocated handle.
+                       OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLCloseCursor",OdbcHandleType.Stmt,hstmt));
+                       
+                       ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,hstmt));
+                       hstmt = IntPtr.Zero;
                }
                
                private void ExecSQL(string sql)
                {
                        OdbcReturn ret;
-       
+                       if (! prepared && Parameters.Count <= 0) {
+
+                               ReAllocStatment ();
+                               
+                               ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS);
+                               if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo) &&
+                                   (ret != OdbcReturn.NoData))
+                                       throw new OdbcException(new OdbcError("SQLExecDirect",OdbcHandleType.Stmt,hstmt));
+                               return;
+                       }
+
                        if (!prepared)
-                       {
                                Prepare();
-                               if (Parameters.Count>0)
-                                       Parameters.Bind(hstmt);
-                       }
-                       
-                       if (prepared)
-                       {
-                               ret=libodbc.SQLExecute(hstmt);
-                               libodbchelper.DisplayError("SQLExecute",ret);
-                       }
-                       else
-                       {
-                               ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
-                               libodbchelper.DisplayError("SQLAllocHandle(hstmt)",ret);
-                               ret=libodbc.SQLExecDirect(hstmt, sql, sql.Length);
-                               libodbchelper.DisplayError("SQLExecDirect",ret);
-                       }
+
+                       BindParameters ();
+                       ret=libodbc.SQLExecute(hstmt);
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLExecute",OdbcHandleType.Stmt,hstmt));
                }
 
-               public int ExecuteNonQuery ()
+               internal void FreeIfNotPrepared ()
                {
-                       if (connection == null)
-                               throw new InvalidOperationException ();
-                       if (connection.State == ConnectionState.Closed)
-                               throw new InvalidOperationException ();
+                       if (! prepared)
+                               FreeStatement ();
+               }
+
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                int ExecuteNonQuery ()
+               {
+                       return ExecuteNonQuery (true);
+               }
+
+               private int ExecuteNonQuery (bool freeHandle) 
+               {
+                       int records = 0;
+                       if (Connection == null)
+                               throw new InvalidOperationException ("No open connection");
+                       if (Connection.State == ConnectionState.Closed)
+                               throw new InvalidOperationException ("Connection state is closed");
                        // FIXME: a third check is mentioned in .NET docs
-                       if (connection.DataReader != null)
-                               throw new InvalidOperationException ();
 
                        ExecSQL(CommandText);
 
-                       if (!prepared)
-                               libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);
-                       return 0;
+                       // .NET documentation says that except for INSERT, UPDATE and
+                        // DELETE  where the return value is the number of rows affected
+                        // for the rest of the commands the return value is -1.
+                        if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) ||
+                           (CommandText.ToUpper().IndexOf("INSERT")!=-1) ||
+                           (CommandText.ToUpper().IndexOf("DELETE")!=-1)) {
+                                                                                                    
+                               int numrows = 0;
+                               OdbcReturn ret = libodbc.SQLRowCount(hstmt,ref numrows);
+                               records = numrows;
+                        }
+                        else
+                               records = -1;
+
+                       if (freeHandle && !prepared)
+                               FreeStatement ();
+                       
+                       return records;
                }
 
-               public void Prepare()
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                void Prepare()
                {
-                       OdbcReturn ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
-                       libodbchelper.DisplayError("SQLAlloc(Prepare)",ret);
+                       ReAllocStatment ();
+                       
+                       OdbcReturn ret;
                        ret=libodbc.SQLPrepare(hstmt, CommandText, CommandText.Length);
-                       libodbchelper.DisplayError("SQLPrepare",ret);
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLPrepare",OdbcHandleType.Stmt,hstmt));
                        prepared=true;
                }
 
-               public OdbcDataReader ExecuteReader ()
+               private void BindParameters ()
+               {
+                       int i=1;
+                       foreach (OdbcParameter p in Parameters)
+                       {
+                               p.Bind(hstmt, i);
+                               p.CopyValue ();
+                               i++;
+                       }
+               }
+
+
+               public
+#if NET_2_0
+                new
+#endif // NET_2_0
+                OdbcDataReader ExecuteReader ()
                {
                        return ExecuteReader (CommandBehavior.Default);
                }
 
+#if ONLY_1_1
                IDataReader IDbCommand.ExecuteReader ()
                {
                        return ExecuteReader ();
                }
-
-               public OdbcDataReader ExecuteReader (CommandBehavior behavior)
+               #else
+                protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
+                {
+                        return ExecuteReader (behavior);
+                }
+                
+#endif // ONLY_1_1
+
+               public
+#if NET_2_0
+                new
+#endif // NET_2_0
+                OdbcDataReader ExecuteReader (CommandBehavior behavior)
                {
-                       ExecuteNonQuery();
-                       dataReader=new OdbcDataReader(this);
+                       int recordsAffected = ExecuteNonQuery(false);
+                       OdbcDataReader dataReader=new OdbcDataReader(this, behavior, recordsAffected);
                        return dataReader;
                }
 
-               IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
+#if ONLY_1_1
+                IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
                {
                        return ExecuteReader (behavior);
                }
-               
-               public object ExecuteScalar ()
+#endif // ONLY_1_1
+
+               public 
+#if NET_2_0
+               override
+#endif
+               object ExecuteScalar ()
                {
-                                       throw new NotImplementedException ();
-//                     if (connection.DataReader != null)
-//                             throw new InvalidOperationException ();
-//                     
+                       object val = null;
+                       OdbcDataReader reader=ExecuteReader();
+                       try
+                       {
+                               if (reader.Read ())
+                                       val=reader[0];
+                       }
+                       finally
+                       {
+                               reader.Close();
+                       }
+                       return val;
                }
 
                [MonoTODO]
@@ -297,7 +561,7 @@ namespace System.Data.Odbc
 
                public void ResetCommandTimeout ()
                {
-                       timeout = 30;
+                       CommandTimeout = 30;
                }
 
                #endregion