2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient / TdsCommand.cs
index f25c7c663583d9db9c56cbbd0969fc2580f111d0..4a25e4c58bdb1c84c471c9c295d28c04b43c060f 100644 (file)
 // Mono.Data.TdsClient.TdsCommand.cs
 //
 // Author:
+//   Rodrigo Moya (rodrigo@ximian.com)
+//   Daniel Morgan (danmorg@sc.rr.com)
 //   Tim Coleman (tim@timcoleman.com)
 //
-// Copyright (C) 2002 Tim Coleman
+// (C) Ximian, Inc 2002 http://www.ximian.com/
+// (C) Daniel Morgan, 2002
+// Copyright (C) Tim Coleman, 2002
 //
 
+//
+// 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 Mono.Data.Tds;
 using Mono.Data.Tds.Protocol;
 using System;
+using System.Collections;
+using System.Collections.Specialized;
 using System.ComponentModel;
 using System.Data;
+using System.Data.Common;
+using System.Runtime.InteropServices;
+using System.Text;
 
 namespace Mono.Data.TdsClient {
-        public class TdsCommand : Component, ICloneable, IDbCommand
+       public sealed class TdsCommand : Component, IDbCommand, ICloneable
        {
                #region Fields
 
-               string commandText;
+               bool disposed = false;
                int commandTimeout;
+               bool designTimeVisible;
+               string commandText;
                CommandType commandType;
                TdsConnection connection;
-               TdsParameterCollection parameters;
                TdsTransaction transaction;
+               UpdateRowSource updatedRowSource;
+               CommandBehavior behavior = CommandBehavior.Default;
+               TdsParameterCollection parameters;
 
                #endregion // Fields
 
                #region Constructors
 
-               public TdsCommand ()
+               public TdsCommand() 
                        : this (String.Empty, null, null)
                {
                }
 
-               public TdsCommand (string commandText)
+               public TdsCommand (string commandText) 
                        : this (commandText, null, null)
                {
+                       commandText = commandText;
                }
 
-               public TdsCommand (string commandText, TdsConnection connection)
+               public TdsCommand (string commandText, TdsConnection connection) 
                        : this (commandText, connection, null)
                {
+                       Connection = connection;
                }
 
-               public TdsCommand (string commandText, TdsConnection connection, TdsTransaction transaction)
+               public TdsCommand (string commandText, TdsConnection connection, TdsTransaction transaction) 
                {
                        this.commandText = commandText;
+                       this.connection = connection;
                        this.transaction = transaction;
                        this.commandType = CommandType.Text;
-                       this.connection = connection;
+                       this.updatedRowSource = UpdateRowSource.Both;
+
+                       this.designTimeVisible = false;
+                       this.commandTimeout = 30;
+                       parameters = new TdsParameterCollection (this);
                }
 
                #endregion // Constructors
 
                #region Properties
 
+               internal CommandBehavior CommandBehavior {
+                       get { return behavior; }
+               }
+
                public string CommandText {
                        get { return commandText; }
                        set { commandText = value; }
                }
 
                public int CommandTimeout {
-                       get { return commandTimeout; }
-                       set { commandTimeout = value; }
+                       get { return commandTimeout;  }
+                       set { 
+                               if (commandTimeout < 0)
+                                       throw new ArgumentException ("The property value assigned is less than 0.");
+                               commandTimeout = value; 
+                       }
                }
 
-               public CommandType CommandType {
+               public CommandType CommandType  {
                        get { return commandType; }
-                       set { commandType = value; }
+                       set { 
+                               if (value == CommandType.TableDirect)
+                                       throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono TdsClient Data Provider.");
+                               commandType = value; 
+                       }
                }
 
-               public TdsConnection Connection {       
+               public TdsConnection Connection {
                        get { return connection; }
                        set { 
                                if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
                                        throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
                                transaction = null;
-                               connection = value;
+                               connection = value; 
                        }
                }
 
+               public bool DesignTimeVisible {
+                       get { return designTimeVisible; } 
+                       set { designTimeVisible = value; }
+               }
+
+               public TdsParameterCollection Parameters {
+                       get { return parameters; }
+               }
+
+               internal ITds Tds {
+                       get { return Connection.Tds; }
+               }
+
                IDbConnection IDbCommand.Connection {
                        get { return Connection; }
                        set { 
-                               if (!(value is TdsConnection)) 
-                                       throw new ArgumentException ();
-                               Connection = (TdsConnection) value;     
+                               if (!(value is TdsConnection))
+                                       throw new InvalidCastException ("The value was not a valid TdsConnection.");
+                               Connection = (TdsConnection) value;
                        }
                }
 
-               IDataParameterCollection IDbCommand.Parameters {
+               IDataParameterCollection IDbCommand.Parameters  {
                        get { return Parameters; }
                }
 
                IDbTransaction IDbCommand.Transaction {
                        get { return Transaction; }
                        set { 
-                               if (!(value is TdsTransaction)) 
+                               if (!(value is TdsTransaction))
                                        throw new ArgumentException ();
                                Transaction = (TdsTransaction) value; 
                        }
                }
 
-               public TdsParameterCollection Parameters {
-                       get { return parameters; }
-               }
-
-               internal ITds Tds {
-                       get { return connection.Tds; }
-               }
-
                public TdsTransaction Transaction {
                        get { return transaction; }
                        set { transaction = value; }
+               }       
+
+               public UpdateRowSource UpdatedRowSource {
+                       get { return updatedRowSource; }
+                       set { updatedRowSource = value; }
                }
 
-               [MonoTODO]
-               public UpdateRowSource UpdatedRowSource {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+               #endregion // Fields
+
+               #region Methods
+
+               public void Cancel () 
+               {
+                       if (Connection == null || Connection.Tds == null)
+                               return;
+                       Connection.Tds.Cancel ();
                }
 
-               #endregion // Properties
+               internal void CloseDataReader (bool moreResults)
+               {
+                       GetOutputParameters ();
+                       Connection.DataReader = null;
 
-                #region Methods
+                       if ((behavior & CommandBehavior.CloseConnection) != 0)
+                               Connection.Close ();
+               }
 
-               [MonoTODO]
-               public void Cancel ()
+               public TdsParameter CreateParameter () 
                {
-                       throw new NotImplementedException ();
+                       return new TdsParameter ();
                }
 
-               [MonoTODO]
-               TdsParameter CreateParameter ()
+               internal void DeriveParameters ()
                {
-                       throw new NotImplementedException ();
+                       if (commandType != CommandType.StoredProcedure)
+                               throw new InvalidOperationException (String.Format ("TdsCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
+                       ValidateCommand ("DeriveParameters");
+
+                       TdsParameterCollection localParameters = new TdsParameterCollection (this);
+                       localParameters.Add ("@P1", TdsType.NVarChar, commandText.Length).Value = commandText;
+
+                       string sql = "sp_procedure_params_rowset";
+
+                       Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
+
+                       TdsDataReader reader = new TdsDataReader (this);
+                       parameters.Clear ();
+                       object[] dbValues = new object[reader.FieldCount];
+
+                       while (reader.Read ()) {
+                               reader.GetValues (dbValues);
+                               parameters.Add (new TdsParameter (dbValues));
+                       }
+                       reader.Close ();        
+               }
+
+               private void Execute (CommandBehavior behavior, bool wantResults)
+               {
+                       TdsMetaParameterCollection parms = Parameters.MetaParameters;
+                       bool schemaOnly = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
+                       bool keyInfo = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
+
+                       StringBuilder sql1 = new StringBuilder ();
+                       StringBuilder sql2 = new StringBuilder ();
+
+                       if (schemaOnly || keyInfo)
+                               sql1.Append ("SET FMTONLY OFF;");
+                       if (keyInfo) {
+                               sql1.Append ("SET NO_BROWSETABLE ON;");
+                               sql2.Append ("SET NO_BROWSETABLE OFF;");
+                       }
+                       if (schemaOnly) {
+                               sql1.Append ("SET FMTONLY ON;");
+                               sql2.Append ("SET FMTONLY OFF;");
+                       }
+
+                       switch (CommandType) {
+                       case CommandType.StoredProcedure:
+                               if (keyInfo || schemaOnly)
+                                       Connection.Tds.Execute (sql1.ToString ());
+                               Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
+                               if (keyInfo || schemaOnly)
+                                       Connection.Tds.Execute (sql2.ToString ());
+                               break;
+                       case CommandType.Text:
+                               string sql = String.Format ("{0}{1}{2}", sql1.ToString (), CommandText, sql2.ToString ());
+                               Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
+                               break;
+                       }
                }
 
                public int ExecuteNonQuery ()
                {
                        ValidateCommand ("ExecuteNonQuery");
-                       return connection.Tds.ExecuteNonQuery (FormatQuery (commandText, commandType));
+                       int result = 0;
+
+                       try {
+                               Execute (CommandBehavior.Default, false);
+                       }
+                       catch (TdsTimeoutException e) {
+                               throw TdsException.FromTdsInternalException ((TdsInternalException) e);
+                       }
+
+                       GetOutputParameters ();
+                       return result;
                }
 
                public TdsDataReader ExecuteReader ()
@@ -151,34 +287,57 @@ namespace Mono.Data.TdsClient {
                public TdsDataReader ExecuteReader (CommandBehavior behavior)
                {
                        ValidateCommand ("ExecuteReader");
-                       connection.DataReader = new TdsDataReader (this);
-                       return connection.DataReader;
+                       try {
+                               Execute (behavior, true);
+                       }
+                       catch (TdsTimeoutException e) {
+                               throw TdsException.FromTdsInternalException ((TdsInternalException) e);
+                       }
+                       Connection.DataReader = new TdsDataReader (this);
+                       return Connection.DataReader;
                }
 
-               [MonoTODO]
                public object ExecuteScalar ()
                {
-                       throw new NotImplementedException ();
+                       ValidateCommand ("ExecuteScalar");
+                       try {
+                               Execute (CommandBehavior.Default, true);
+                       }
+                       catch (TdsTimeoutException e) {
+                               throw TdsException.FromTdsInternalException ((TdsInternalException) e);
+                       }
+
+                       if (!Connection.Tds.NextResult () || !Connection.Tds.NextRow ())
+                               return null;
+
+                       object result = Connection.Tds.ColumnValues [0];
+                       CloseDataReader (true);
+                       return result;
                }
 
-               private static string FormatQuery (string commandText, CommandType commandType)
+               private void GetOutputParameters ()
                {
-                       switch (commandType) {
-                       case CommandType.Text :
-                               return commandText;
-                       case CommandType.TableDirect :
-                               return String.Format ("select * from {0}", commandText);
-                       case CommandType.StoredProcedure :
-                               return String.Format ("exec {0}", commandText);
+                       Connection.Tds.SkipToEnd ();
+
+                       IList list = Connection.Tds.ColumnValues;
+
+                       if (list != null && list.Count > 0) {
+                               int index = 0;
+                               foreach (TdsParameter parameter in parameters) {
+                                       if (parameter.Direction != ParameterDirection.Input) {
+                                               parameter.Value = list [index];
+                                               index += 1;
+                                       }
+                                       if (index >= list.Count)
+                                               break;
+                               }
                        }
-                       throw new InvalidOperationException ("Invalid command type");
                }
 
-               [MonoTODO]
-                object ICloneable.Clone()
-                {
-                        throw new NotImplementedException ();
-                }
+               object ICloneable.Clone ()
+               {
+                       return new TdsCommand (commandText, Connection);
+               }
 
                IDbDataParameter IDbCommand.CreateParameter ()
                {
@@ -195,26 +354,30 @@ namespace Mono.Data.TdsClient {
                        return ExecuteReader (behavior);
                }
 
-               [MonoTODO]
                public void Prepare ()
                {
-                       throw new NotImplementedException ();
+                       throw new NotSupportedException ("TdsClient does not support PREPARE.");
+               }
+
+               public void ResetCommandTimeout ()
+               {
+                       commandTimeout = 30;
                }
 
                private void ValidateCommand (string method)
                {
-                       if (connection == null)
+                       if (Connection == null)
                                throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
-                       if (connection.Transaction != null && transaction != connection.Transaction)
+                       if (Connection.Transaction != null && transaction != Connection.Transaction)
                                throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
-                       if (connection.State != ConnectionState.Open)
+                       if (Connection.State != ConnectionState.Open)
                                throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
                        if (commandText == String.Empty || commandText == null)
                                throw new InvalidOperationException ("The command text for this Command has not been set.");
-                       if (connection.DataReader != null)
+                       if (Connection.DataReader != null)
                                throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
                }
 
-                #endregion // Methods
+               #endregion // Methods
        }
 }