2002-11-01 Tim Coleman (tim@timcoleman.com) :
authorTim Coleman <tim@mono-cvs.ximian.com>
Sat, 2 Nov 2002 05:06:21 +0000 (05:06 -0000)
committerTim Coleman <tim@mono-cvs.ximian.com>
Sat, 2 Nov 2002 05:06:21 +0000 (05:06 -0000)
        * System.Data.Common/DbEnumerator.cs :
                Throw correct exception on Reset ()
                Add ColumnSize to schema table.
        * System.Data.SqlClient/SqlDataReader.cs :
                Add ColumnSize to schema table.
        * System.Data.SqlClient/SqlCommand.cs :
                Change the way that preparing is handled.
                Now uses sp_prepare on the server instead of temp
                stored procedures because it's the Right Thing[tm] to do.
        * System.Data.SqlClient/SqlConnection.cs :
                Store data readers here rather than in command
        * System.Data.SqlClient/SqlDataReader.cs :
                More implementation, including binary types
        * System.Data.SqlClient/SqlParameter.cs :
                Lowercase type name

svn path=/trunk/mcs/; revision=8746

mcs/class/System.Data/ChangeLog
mcs/class/System.Data/System.Data.Common/DbEnumerator.cs
mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs
mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs
mcs/class/System.Data/System.Data.SqlClient/SqlDataReader.cs
mcs/class/System.Data/System.Data.SqlClient/SqlParameter.cs

index db32a03a402eed481d8c1a7cf8822cd0326c0afb..0358156d90df64da8c6f4fa48f55a4e6761b3455 100644 (file)
@@ -1,7 +1,19 @@
 2002-11-01  Tim Coleman (tim@timcoleman.com) :
        * System.Data.Common/DbEnumerator.cs :
+               Throw correct exception on Reset ()
+               Add ColumnSize to schema table.
        * System.Data.SqlClient/SqlDataReader.cs :
                Add ColumnSize to schema table.
+       * System.Data.SqlClient/SqlCommand.cs :
+               Change the way that preparing is handled.
+               Now uses sp_prepare on the server instead of temp
+               stored procedures because it's the Right Thing[tm] to do.
+       * System.Data.SqlClient/SqlConnection.cs :
+               Store data readers here rather than in command
+       * System.Data.SqlClient/SqlDataReader.cs :
+               More implementation, including binary types
+       * System.Data.SqlClient/SqlParameter.cs :
+               Lowercase type name
 
 2002-10-31  Tim Coleman (tim@timcoleman.com)
         * System.Data.Common/DbDataAdapter.cs :
index 7afa09c82871304136b5afc2a8451d5958baec66..cd8c5a1e520c6a8bece37294a33dc7505549958c 100644 (file)
@@ -100,7 +100,7 @@ namespace System.Data.Common {
 
                public virtual void Reset ()
                {
-                       throw new InvalidOperationException ("This enumerator can only go forward.");   
+                       throw new NotSupportedException ();
                }
                
                #endregion // Methods
index 2050c37ea022775f4848f2e1446dc1ffab8b0ceb..791c417305c4d32bc399ff97d01c951cf35bf893 100644 (file)
@@ -13,6 +13,7 @@
 
 using Mono.Data.TdsClient.Internal;
 using System;
+using System.Collections;
 using System.Collections.Specialized;
 using System.ComponentModel;
 using System.Data;
@@ -33,11 +34,11 @@ namespace System.Data.SqlClient {
                CommandType commandType;
                SqlConnection connection;
                SqlTransaction transaction;
-
                SqlParameterCollection parameters = new SqlParameterCollection ();
-               private CommandBehavior behavior = CommandBehavior.Default;
 
-               NameValueCollection procedureCache = new NameValueCollection ();
+               CommandBehavior behavior = CommandBehavior.Default;
+               NameValueCollection preparedStatements = new NameValueCollection ();
+               bool isPrepared;
 
                #endregion // Fields
 
@@ -66,6 +67,7 @@ namespace System.Data.SqlClient {
                        this.connection = connection;
                        this.transaction = transaction;
                        this.commandType = CommandType.Text;
+
                        this.designTimeVisible = false;
                        this.commandTimeout = 30;
                }
@@ -94,7 +96,6 @@ namespace System.Data.SqlClient {
 
                public CommandType CommandType  {
                        get { return commandType; }
-                       [MonoTODO ("Validate")]
                        set { commandType = value; }
                }
 
@@ -158,6 +159,95 @@ namespace System.Data.SqlClient {
 
                #region Methods
 
+               private string BuildCommand ()
+               {
+                       isPrepared = true;
+
+                       string statementHandle = preparedStatements [commandText];
+                       if (statementHandle != null) {
+                               string proc = String.Format ("sp_execute {0}", statementHandle);        
+                               if (parameters.Count > 0)
+                                       proc += ",";
+                               return BuildProcedureCall (proc, parameters);
+                       }
+
+                       isPrepared = false;
+                       string sql;
+
+                       switch (commandType) {
+                       case CommandType.Text :
+                               sql = commandText;
+                               break;
+                       case CommandType.TableDirect :
+                               sql = String.Format ("SELECT * FROM {0}", commandText);
+                               break;
+                       case CommandType.StoredProcedure :
+                               return BuildProcedureCall (commandText, parameters);
+                       default :
+                               throw new InvalidOperationException ("The CommandType was invalid.");
+                       }
+
+                       if ((behavior & CommandBehavior.KeyInfo) > 0)
+                               sql += " FOR BROWSE";
+
+                       return sql;
+               }
+
+               private string BuildPrepare ()
+               {
+                       StringBuilder parms = new StringBuilder ();
+                       foreach (SqlParameter parameter in parameters) {
+                               if (parms.Length > 0)
+                                       parms.Append (", ");
+                               parms.Append (parameter.Prepare ());
+                               if (parameter.Direction == ParameterDirection.Output)
+                                       parms.Append (" output");
+                       }
+
+                       string declare = "declare @p1 int\nset @p1=NULL";
+                       string exec = String.Format ("exec sp_prepare @p1 output, N'{0}', N'{1}'", parms.ToString (), commandText);
+                       return String.Format ("{0}\n{1}", declare, exec);
+               }
+
+               private static string BuildProcedureCall (string commandText, SqlParameterCollection parameters)
+               {
+                       StringBuilder parms = new StringBuilder ();
+                       StringBuilder declarations = new StringBuilder ();
+                       StringBuilder outParms = new StringBuilder ();
+
+                       foreach (SqlParameter parameter in parameters) {
+                               switch (parameter.Direction) {
+                               case ParameterDirection.Input :
+                                       if (parms.Length > 0)
+                                               parms.Append (", ");
+                                       parms.Append (FormatParameter (parameter));
+                                       break;
+                               case ParameterDirection.Output :
+                                       if (parms.Length > 0)
+                                               parms.Append (", ");
+                                       parms.Append (parameter.ParameterName);
+                                       parms.Append (" output");
+
+                                       if (outParms.Length > 0) {
+                                               outParms.Append (", ");
+                                               declarations.Append (", ");
+                                       }
+                                       else {
+                                               outParms.Append ("select ");
+                                               declarations.Append ("declare ");
+                                       }
+
+                                       declarations.Append (parameter.Prepare ());
+                                       outParms.Append (parameter.ParameterName);
+                                       break;
+                               default :
+                                       throw new NotImplementedException ("Only support input and output parameters.");
+                               }
+                       }
+
+                       return String.Format ("{0}\nexec {1} {2}\n{3}", declarations.ToString (), commandText, parms.ToString (), outParms.ToString ());
+               }
+
                public void Cancel () 
                {
                        if (connection == null || connection.Tds == null)
@@ -168,20 +258,9 @@ namespace System.Data.SqlClient {
 
                internal void CloseDataReader (bool moreResults)
                {
-                       while (moreResults)
-                               moreResults = connection.Tds.NextResult ();
+                       GetOutputParameters ();
+                       connection.DataReader = null;
 
-                       if (connection.Tds.OutputParameters.Count > 0) {
-                               int index = 0;
-                               foreach (SqlParameter parameter in parameters) {
-                                       if (parameter.Direction != ParameterDirection.Input)
-                                               parameter.Value = connection.Tds.OutputParameters[index];
-                                       index += 1;
-                                       if (index >= connection.Tds.OutputParameters.Count)
-                                               break;
-                               }
-                       }
-                       connection.DataReaderOpen = false;
                        if ((behavior & CommandBehavior.CloseConnection) != 0)
                                connection.Close ();
                }
@@ -193,8 +272,11 @@ namespace System.Data.SqlClient {
 
                public int ExecuteNonQuery ()
                {
-                       int result = connection.Tds.ExecuteNonQuery (ValidateQuery ("ExecuteNonQuery"));
+                       ValidateCommand ("ExecuteNonQuery");
+                       Console.WriteLine (BuildCommand ());
+                       int result = connection.Tds.ExecuteNonQuery (BuildCommand ());
                        connection.CheckForErrors ();
+                       GetOutputParameters ();
                        return result;
                }
 
@@ -205,16 +287,20 @@ namespace System.Data.SqlClient {
 
                public SqlDataReader ExecuteReader (CommandBehavior behavior)
                {
+                       ValidateCommand ("ExecuteReader");
                        this.behavior = behavior;
-                       connection.Tds.ExecuteQuery (ValidateQuery ("ExecuteReader"));
+                       connection.Tds.ExecuteQuery (BuildCommand ());
                        connection.CheckForErrors ();
-                       connection.DataReaderOpen = true;
-                       return new SqlDataReader (this);
+                       connection.DataReader = new SqlDataReader (this);
+
+                       return connection.DataReader;
                }
 
                public object ExecuteScalar ()
                {
-                       connection.Tds.ExecuteQuery (ValidateQuery ("ExecuteScalar"));
+                       ValidateCommand ("ExecuteScalar");
+                       connection.Tds.ExecuteQuery (BuildCommand ());
+                       connection.CheckForErrors ();
 
                        bool moreResults = connection.Tds.NextResult ();
                        connection.CheckForErrors ();
@@ -235,9 +321,9 @@ namespace System.Data.SqlClient {
 
                public XmlReader ExecuteXmlReader ()
                {
-                       connection.Tds.ExecuteQuery (ValidateQuery ("ExecuteXmlReader"));
+                       ValidateCommand ("ExecuteXmlReader");
+                       connection.Tds.ExecuteQuery (BuildCommand ());
                        connection.CheckForErrors ();
-                       connection.DataReaderOpen = true;
 
                        SqlDataReader dataReader = new SqlDataReader (this);
                        SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
@@ -262,64 +348,41 @@ namespace System.Data.SqlClient {
                                case SqlDbType.SmallMoney :
                                case SqlDbType.TinyInt :
                                        return parameter.Value.ToString ();
+                               case SqlDbType.NVarChar :
+                               case SqlDbType.NChar :
+                                       return String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
                                default:
                                        return String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
                        }
                }
 
-               static string FormatQuery (string commandText, CommandType commandType, SqlParameterCollection parameters)
+               private void GetOutputParameters ()
                {
-                       StringBuilder result = new StringBuilder ();
-
-                       switch (commandType) {
-                       case CommandType.Text :
-                               return commandText;
-                       case CommandType.TableDirect :
-                               return String.Format ("SELECT * FROM {0}", commandText);
-                       case CommandType.StoredProcedure :
+                       IList list;
 
-                               StringBuilder parms = new StringBuilder ();
-                               StringBuilder declarations = new StringBuilder ();
+                       connection.Tds.SkipToEnd ();
 
+                       if (commandType == CommandType.StoredProcedure || isPrepared)
+                               list = connection.Tds.ColumnValues;
+                       else
+                               list = connection.Tds.OutputParameters;
+               
+                       if (list != null && list.Count > 0) {
+                               int index = 0;
                                foreach (SqlParameter parameter in parameters) {
-                                       switch (parameter.Direction) {
-                                       case ParameterDirection.Input :
-                                               if (parms.Length > 0)
-                                                       result.Append (",");
-                                               parms.Append (FormatParameter (parameter));
-                                               break;
-                                       case ParameterDirection.Output :
-                                               if (parms.Length > 0)
-                                                       parms.Append (",");
-                                               parms.Append (parameter.ParameterName);
-                                               parms.Append (" OUT");
-
-                                               if (declarations.Length == 0)
-                                                       declarations.Append ("DECLARE ");
-                                               else
-                                                       declarations.Append (",");
-
-                                               declarations.Append (parameter.Prepare ());
-                                               break;
-                                       default :
-                                               throw new NotImplementedException ("Only support input and output parameters.");
+                                       if (parameter.Direction != ParameterDirection.Input) {
+                                               parameter.Value = list [index];
+                                               index += 1;
                                        }
+                                       if (index >= list.Count)
+                                               break;
                                }
-                               result.Append (declarations.ToString ());
-                               result.Append (" EXEC ");
-                               result.Append (commandText);
-                               result.Append (" ");
-                               result.Append (parms);
-                               return result.ToString ();
-                       default:
-                               throw new InvalidOperationException ("The CommandType was not recognized.");
                        }
                }
 
-               [MonoTODO]
                object ICloneable.Clone ()
                {
-                       throw new NotImplementedException ();
+                       return new SqlCommand (commandText, connection);
                }
 
                IDbDataParameter IDbCommand.CreateParameter ()
@@ -344,30 +407,15 @@ namespace System.Data.SqlClient {
 
                public void Prepare ()
                {
-                       bool prependComma = false;
-                       Guid uniqueId = Guid.NewGuid ();
-                       string procedureName = String.Format ("#mono#{0}", uniqueId.ToString ("N"));
-                       StringBuilder procedureString = new StringBuilder ();
+                       ValidateCommand ("Prepare");
+       Console.WriteLine (BuildPrepare ());
+                       connection.Tds.ExecuteNonQuery (BuildPrepare ());
+                       connection.CheckForErrors ();
 
-                       procedureString.Append ("CREATE PROC ");
-                       procedureString.Append (procedureName);
-                       procedureString.Append (" (");
+                       if (connection.Tds.OutputParameters.Count == 0 || connection.Tds.OutputParameters[0] == null)
+                               throw new Exception ("Could not prepare the statement.");
 
-                       foreach (SqlParameter parameter in parameters) {
-                               if (prependComma)
-                                       procedureString.Append (", ");
-                               else
-                                       prependComma = true;
-                               procedureString.Append (parameter.Prepare ());
-                               if (parameter.Direction == ParameterDirection.Output)
-                                       procedureString.Append (" OUT");
-                       }
-                               
-                       procedureString.Append (") AS ");
-                       procedureString.Append (commandText);
-                       string cmdText = FormatQuery (procedureName, CommandType.StoredProcedure, parameters);
-                       connection.Tds.ExecuteNonQuery (procedureString.ToString ());
-                       procedureCache[commandText] = cmdText;
+                       preparedStatements [commandText] = ((int) connection.Tds.OutputParameters [0]).ToString ();
                }
 
                public void ResetCommandTimeout ()
@@ -375,25 +423,20 @@ namespace System.Data.SqlClient {
                        commandTimeout = 30;
                }
 
-               string ValidateQuery (string methodName)
+               private void ValidateCommand (string method)
                {
                        if (connection == null)
-                               throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", methodName));
+                               throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
                        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)
-                               throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", methodName));
+                               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.");
-
-                       string sql = procedureCache[commandText];
-                       if (sql == null)
-                               sql = FormatQuery (commandText, commandType, parameters);
-               
-                       if ((behavior & CommandBehavior.KeyInfo) != 0)
-                               sql += " FOR BROWSE";
-
-                       return sql;
+                       if (connection.DataReader != null)
+                               throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
+                       if (connection.XmlReader != null)
+                               throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
                }
 
                #endregion // Methods
index 957c5604956d44aa282e340b338dc132e590044e..1072a0e17dde12f82cf90b91b0fa055de841ad92 100644 (file)
@@ -21,6 +21,7 @@ using System.Data.Common;
 using System.EnterpriseServices;
 using System.Net;
 using System.Text;
+using System.Xml;
 
 namespace System.Data.SqlClient {
        public sealed class SqlConnection : Component, IDbConnection, ICloneable        
@@ -52,7 +53,9 @@ namespace System.Data.SqlClient {
 
                // The current state
                ConnectionState state = ConnectionState.Closed;
-               bool dataReaderOpen = false;
+
+               SqlDataReader dataReader = null;
+               XmlReader xmlReader = null;
 
 
                // The TDS object
@@ -89,10 +92,10 @@ namespace System.Data.SqlClient {
                public string Database  {
                        get { return tds.Database; }
                }
-
-               internal bool DataReaderOpen {
-                       get { return dataReaderOpen; }
-                       set { dataReaderOpen = value; }
+               
+               internal SqlDataReader DataReader {
+                       get { return dataReader; }
+                       set { dataReader = value; }
                }
 
                public string DataSource {
@@ -123,6 +126,11 @@ namespace System.Data.SqlClient {
                        get { return parms.Hostname; }
                }
 
+               internal XmlReader XmlReader {
+                       get { return xmlReader; }
+                       set { xmlReader = value; }
+               }
+
                #endregion // Properties
 
                #region Methods
index 050c621ca22960ebf316dfdb097db199af57f93b..36a9b04a46f2ba8afea928c0b184ca67041c01ac 100644 (file)
@@ -34,7 +34,6 @@ namespace System.Data.SqlClient {
 
                SqlCommand command;
                DataTable schemaTable;
-               FieldNameLookup lookup;
 
                ArrayList dataTypeNames;
                ArrayList dataTypes;
@@ -82,10 +81,6 @@ namespace System.Data.SqlClient {
                        get { return GetValue (GetOrdinal (name)); }
                }
                
-               internal FieldNameLookup Lookup {
-                       get { return lookup; }
-               }
-
                public int RecordsAffected {
                        get { return recordsAffected; }
                }
@@ -136,22 +131,30 @@ namespace System.Data.SqlClient {
                        return schemaTable;
                }
 
-               [MonoTODO]
                public bool GetBoolean (int i)
                {
-                       throw new NotImplementedException ();
+                       object value = GetValue (i);
+                       if (!(value is bool))
+                               throw new InvalidCastException ();
+                       return (bool) value;
                }
 
-               [MonoTODO]
                public byte GetByte (int i)
                {
-                       throw new NotImplementedException ();
+                       object value = GetValue (i);
+                       if (!(value is byte))
+                               throw new InvalidCastException ();
+                       return (byte) value;
                }
 
                [MonoTODO]
                public long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
                {
-                       throw new NotImplementedException ();
+                       object value = GetValue (i);
+                       if (!(value is byte []))
+                               throw new InvalidCastException ();
+                       Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
+                       return ((byte []) value).Length - dataIndex;
                }
 
                [MonoTODO]
@@ -163,7 +166,11 @@ namespace System.Data.SqlClient {
                [MonoTODO]
                public long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
                {
-                       throw new NotImplementedException ();
+                       object value = GetValue (i);
+                       if (!(value is char []))
+                               throw new InvalidCastException ();
+                       Array.Copy ((char []) value, (int) dataIndex, buffer, bufferIndex, length);
+                       return ((char []) value).Length - dataIndex;
                }
 
                [MonoTODO]
@@ -478,16 +485,13 @@ namespace System.Data.SqlClient {
                {
                        if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
                                return false;
+                       if (command.CommandType == CommandType.StoredProcedure && command.Tds.DoneProc)
+                               return false;
 
                        schemaTable.Rows.Clear ();
 
-                       if (lookup != null)
-                               lookup.Clear ();
-
                        moreResults = command.Tds.NextResult ();
                        command.Connection.CheckForErrors ();
-                       if (moreResults)
-                               lookup = new FieldNameLookup (GetSchemaTable ());
                        rowsRead = 0;
                        resultsRead += 1;
                        return moreResults;
index dd739cdf5d013a47111ef229ad8aa1bd440d76a2..c50e10f111e7a33b4d867b9417ee55d4b6c40e80 100644 (file)
@@ -198,7 +198,7 @@ namespace System.Data.SqlClient {
                        StringBuilder result = new StringBuilder ();
                        result.Append (parmName);
                        result.Append (" ");
-                       result.Append (dbtype.ToString ());
+                       result.Append (dbtype.ToString ().ToLower ());
 
                        switch (dbtype) {
                        case SqlDbType.Image :