* RegexTest.cs: Add some simple tests for debugging/zen
[mono.git] / mcs / class / ByteFX.Data / mysqlclient / command.cs
index 63fe2ca75ad9ed039d45d3ac34b905cef660a23e..1d1c7c446375d9b77aea6e0a0811e18084045db5 100755 (executable)
@@ -20,86 +20,130 @@ using System.Data;
 using System.ComponentModel;\r
 using System.Collections;\r
 \r
-namespace ByteFX.Data.MySQLClient\r
+namespace ByteFX.Data.MySqlClient\r
 {\r
+       /// <summary>\r
+       /// Represents a SQL statement to execute against a MySQL database. This class cannot be inherited.\r
+       /// </summary>\r
+       /// <include file='docs/MySqlCommand.xml' path='MyDocs/MyMembers[@name="Class"]/*'/>\r
 #if WINDOWS\r
-       [System.Drawing.ToolboxBitmap( typeof(MySQLCommand), "Designers.command.bmp")]\r
+       [System.Drawing.ToolboxBitmap( typeof(MySqlCommand), "MySqlClient.resources.command.bmp")]\r
 #endif\r
-       public sealed class MySQLCommand : Component, IDbCommand, ICloneable\r
+       [System.ComponentModel.DesignerCategory("Code")]\r
+       public sealed class MySqlCommand : Component, IDbCommand, ICloneable\r
        {\r
-               MySQLConnection                         m_connection;\r
-               MySQLTransaction                        m_txn;\r
-               string                                          m_sCmdText;\r
-               int                                                     m_UpdateCount;\r
-               UpdateRowSource                         m_updatedRowSource = UpdateRowSource.Both;\r
-               MySQLParameterCollection        m_parameters = new MySQLParameterCollection();\r
-\r
-               // Implement the default constructor here.\r
-               public MySQLCommand()\r
+               MySqlConnection                         connection;\r
+               MySqlTransaction                        curTransaction;\r
+               string                                          cmdText;\r
+               int                                                     updateCount;\r
+               UpdateRowSource                         updatedRowSource = UpdateRowSource.Both;\r
+               MySqlParameterCollection        parameters = new MySqlParameterCollection();\r
+               private ArrayList                       arraySql = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+               /// </summary>\r
+               public MySqlCommand()\r
                {\r
                }\r
 \r
-               // Implement other constructors here.\r
-               public MySQLCommand(string cmdText)\r
+               /// <summary>\r
+               /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+               /// </summary>\r
+               public MySqlCommand(string cmdText)\r
                {\r
-                       m_sCmdText = cmdText;\r
+                       this.cmdText = cmdText;\r
                }\r
 \r
-               public MySQLCommand(System.ComponentModel.IContainer container)\r
+               /// <summary>\r
+               /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+               /// </summary>\r
+               public MySqlCommand(System.ComponentModel.IContainer container)\r
                {\r
-                       /// <summary>\r
-                       /// Required for Windows.Forms Class Composition Designer support\r
-                       /// </summary>\r
+                       // Required for Windows.Forms Class Composition Designer support\r
                        container.Add(this);\r
                }\r
 \r
-               public MySQLCommand(string cmdText, MySQLConnection connection)\r
+               /// <summary>\r
+               /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+               /// </summary>\r
+               public MySqlCommand(string cmdText, MySqlConnection connection)\r
                {\r
-                       m_sCmdText    = cmdText;\r
-                       m_connection  = connection;\r
+                       this.cmdText    = cmdText;\r
+                       this.connection  = connection;\r
                }\r
 \r
+               /// <summary>\r
+               /// Disposes of this instance of MySqlCommand\r
+               /// </summary>\r
                public new void Dispose() \r
                {\r
                        base.Dispose();\r
                }\r
 \r
-               public MySQLCommand(string cmdText, MySQLConnection connection, MySQLTransaction txn)\r
+               /// <summary>\r
+               /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+               /// </summary>\r
+               public MySqlCommand(string cmdText, MySqlConnection connection, MySqlTransaction txn)\r
                {\r
-                       m_sCmdText              = cmdText;\r
-                       m_connection    = connection;\r
-                       m_txn                   = txn;\r
+                       this.cmdText    = cmdText;\r
+                       this.connection = connection;\r
+                       curTransaction  = txn;\r
                } \r
 \r
-               /****\r
-               * IMPLEMENT THE REQUIRED PROPERTIES.\r
-               ****/\r
+               #region Properties\r
+\r
+               /// <summary>\r
+               /// Gets or sets the SQL statement to execute at the data source.\r
+               /// </summary>\r
+               [Category("Data")]\r
+               [Description("Command text to execute")]\r
+#if WINDOWS\r
+               [Editor("ByteFX.Data.Common.Design.SqlCommandTextEditor,MySqlClient.Design", typeof(System.Drawing.Design.UITypeEditor))]\r
+#endif\r
                public string CommandText\r
                {\r
-                       get { return m_sCmdText;  }\r
-                       set  { m_sCmdText = value;  }\r
+                       get { return cmdText;  }\r
+                       set  { cmdText = value;  }\r
                }\r
 \r
+               internal int UpdateCount \r
+               {\r
+                       get { return updateCount; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.\r
+               /// </summary>\r
+               [Category("Misc")]\r
+               [Description("Time to wait for command to execute")]\r
                public int CommandTimeout\r
                {\r
-                       /*\r
-                       * The sample does not support a command time-out. As a result,\r
-                       * for the get, zero is returned because zero indicates an indefinite\r
-                       * time-out period. For the set, throw an exception.\r
-                       */\r
+                       // TODO: support this\r
                        get  { return 0; }\r
                        set  { if (value != 0) throw new NotSupportedException(); }\r
                }\r
 \r
+               /// <summary>\r
+               /// Gets or sets a value indicating how the CommandText property is to be interpreted.  Only\r
+               /// type Text is currently supported.\r
+               /// </summary>\r
+               [Category("Data")]\r
                public CommandType CommandType\r
                {\r
-                       /*\r
-                       * The sample only supports CommandType.Text.\r
-                       */\r
                        get { return CommandType.Text; }\r
-                       set { if (value != CommandType.Text) throw new NotSupportedException(); }\r
+                       set \r
+                       { \r
+                               if (value != CommandType.Text) \r
+                                       throw new NotSupportedException("This version of the MySql provider only supports Text command types"); \r
+                       }\r
                }\r
 \r
+               /// <summary>\r
+               /// Gets or sets the MySqlConnection used by this instance of the MySqlCommand.\r
+               /// </summary>\r
+               [Category("Behavior")]\r
+               [Description("Connection used by the command")]\r
                public IDbConnection Connection\r
                {\r
                        /*\r
@@ -108,7 +152,7 @@ namespace ByteFX.Data.MySQLClient
                        */\r
                        get \r
                        { \r
-                               return m_connection;  \r
+                               return connection;  \r
                        }\r
                        set\r
                        {\r
@@ -117,23 +161,33 @@ namespace ByteFX.Data.MySQLClient
                                * so set the transaction object to return a null reference if the connection \r
                                * is reset.\r
                                */\r
-                               if (m_connection != value)\r
+                               if (connection != value)\r
                                this.Transaction = null;\r
 \r
-                               m_connection = (MySQLConnection)value;\r
+                               connection = (MySqlConnection)value;\r
                        }\r
                }\r
 \r
-               public MySQLParameterCollection Parameters\r
+               /// <summary>\r
+               /// Gets the MySqlParameterCollection.\r
+               /// </summary>\r
+               [Category("Data")]\r
+               [Description("The parameters collection")]\r
+               [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]\r
+               public MySqlParameterCollection Parameters\r
                {\r
-                       get  { return m_parameters; }\r
+                       get  { return parameters; }\r
                }\r
 \r
                IDataParameterCollection IDbCommand.Parameters\r
                {\r
-                       get  { return m_parameters; }\r
+                       get  { return parameters; }\r
                }\r
 \r
+               /// <summary>\r
+               ///     Gets or sets the MySqlTransaction within which the MySqlCommand executes.\r
+               /// </summary>\r
+               [Browsable(false)]\r
                public IDbTransaction Transaction\r
                {\r
                        /*\r
@@ -142,172 +196,200 @@ namespace ByteFX.Data.MySQLClient
                        */\r
                        get \r
                        { \r
-                               return m_txn; \r
+                               return curTransaction; \r
                        }\r
                        set \r
                        { \r
-                               m_txn = (MySQLTransaction)value; \r
+                               curTransaction = (MySqlTransaction)value; \r
                        }\r
                }\r
 \r
+               /// <summary>\r
+               /// Gets or sets how command results are applied to the DataRow when used by the Update method of the DbDataAdapter.\r
+               /// </summary>\r
+               [Category("Behavior")]\r
                public UpdateRowSource UpdatedRowSource\r
                {\r
                        get \r
                        { \r
-                               return m_updatedRowSource;  \r
+                               return updatedRowSource;  \r
                        }\r
                        set \r
                        { \r
-                               m_updatedRowSource = value; \r
+                               updatedRowSource = value; \r
                        }\r
                }\r
+               #endregion\r
 \r
-               /****\r
-                       * IMPLEMENT THE REQUIRED METHODS.\r
-                       ****/\r
+               #region Methods\r
+               /// <summary>\r
+               /// Attempts to cancel the execution of a MySqlCommand.  This operation is not supported.\r
+               /// </summary>\r
+               /// <exception cref="NotSupportedException">This operation is not supported.</exception>\r
                public void Cancel()\r
                {\r
-                       // The sample does not support canceling a command\r
-                       // once it has been initiated.\r
                        throw new NotSupportedException();\r
                }\r
 \r
-               public IDbDataParameter CreateParameter()\r
+               /// <summary>\r
+               /// Creates a new instance of a MySqlParameter object.\r
+               /// </summary>\r
+               /// <returns></returns>\r
+               public MySqlParameter CreateParameter()\r
+               {\r
+                       return new MySqlParameter();\r
+               }\r
+\r
+               IDbDataParameter IDbCommand.CreateParameter()\r
                {\r
-                       return new MySQLParameter();\r
+                       return CreateParameter();\r
                }\r
 \r
-               /// <summary>\r
-               /// Convert the SQL command into a series of ASCII bytes streaming\r
-               /// each of the parameters into the proper place\r
-               /// </summary>\r
-               /// <param name="sql">Source SQL command with parameter markers</param>\r
-               /// <returns>Byte array with all parameters included</returns>\r
-               private ArrayList ConvertSQLToBytes(string sql)\r
+               private ArrayList SplitSql(string sql)\r
                {\r
-                       ArrayList       byteArrays = new ArrayList();\r
-                       System.IO.MemoryStream ms = new System.IO.MemoryStream();\r
-                       \r
-                       if (sql[ sql.Length-1 ] != ';')\r
-                               sql += ';';\r
-                       byte[] bytes = System.Text.Encoding.ASCII.GetBytes(sql);\r
+                       ArrayList commands = new ArrayList();\r
+                       System.IO.MemoryStream ms = new System.IO.MemoryStream(sql.Length);\r
+\r
+                       // first we tack on a semi-colon, if not already there, to make our\r
+                       // sql processing code easier.  Then we ask our encoder to give us\r
+                       // the bytes for this sql string\r
+                       byte[] bytes = connection.Encoding.GetBytes(sql + ";");\r
 \r
                        byte left_byte = 0;\r
-                       int  parm_start=-1, parm_end = -1;\r
+                       bool escaped = false;\r
+                       int  parm_start=-1;\r
                        for (int x=0; x < bytes.Length; x++)\r
                        {\r
                                byte b = bytes[x];\r
+\r
                                // if we see a quote marker, then check to see if we are opening\r
                                // or closing a quote\r
-                               if (b == '\'' || b == '\"')\r
+                               if ((b == '\'' || b == '\"') && ! escaped )\r
                                {\r
-                                       if (b == left_byte)\r
-                                       {\r
-                                               left_byte = 0;\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               if (left_byte == 0)\r
-                                                       left_byte = b;\r
-                                       }\r
-                                       ms.WriteByte(b);\r
+                                       if (b == left_byte) left_byte = 0;\r
+                                       else if (left_byte == 0) left_byte = b;\r
                                }\r
 \r
-                                       // if we find a ; not part of a quoted string, then take the parsed portion\r
-                                       // as a sql command and add it to the array\r
-                               else if (b == ';' && left_byte == 0)\r
+                               else if (b == '\\') \r
                                {\r
-                                       byte[] sqlBytes = ms.ToArray();\r
-                                       byteArrays.Add( sqlBytes );\r
-                                       ms = new System.IO.MemoryStream();\r
+                                       escaped = !escaped;\r
                                }\r
 \r
                                        // if we see the marker for a parameter, then save its position and\r
                                        // look for the end\r
-                               else if (b == '@' && left_byte == 0) \r
-                               {\r
+                               else if (b == '@' && left_byte == 0 && ! escaped && parm_start==-1) \r
                                        parm_start = x;\r
-                                       left_byte = b;\r
-                               }\r
 \r
                                        // if we see a space and we are tracking a parameter, then end the parameter and have\r
-                                       // that parameter serialize itself to the memory streams\r
-                               else if ((b == ' ' || b == ',' || b == ';' || b == ')') && left_byte == '@')\r
+                                       // that parameter serialize itself to the memory stream\r
+                               else if (parm_start > -1 && (b != '@') && (b != '$') && (b != '_') && (b != '.') && ! Char.IsLetterOrDigit((char)b))\r
                                {\r
-                                       parm_end = x-1;\r
-                                       string parm_name = sql.Substring(parm_start, parm_end-parm_start+1);\r
-                                       MySQLParameter p = (m_parameters[parm_name] as MySQLParameter);\r
-                                       p.SerializeToBytes(ms);\r
-                                       ms.WriteByte(b);\r
+                                       string parm_name = sql.Substring(parm_start, x-parm_start); \r
 \r
-                                       if (b == ';') \r
+                                       if (parameters.Contains( parm_name ))\r
                                        {\r
-                                               byte[] sqlBytes = ms.ToArray();\r
-                                               byteArrays.Add( sqlBytes );\r
-                                               ms = new System.IO.MemoryStream();\r
+                                               MySqlParameter p = (parameters[parm_name] as MySqlParameter);\r
+                                               p.SerializeToBytes(ms, connection );\r
                                        }\r
+                                       else\r
+                                       {\r
+                                               // otherwise assume system param. just write it out\r
+                                               byte[] buf = connection.Encoding.GetBytes(parm_name);\r
+                                               ms.Write(buf, 0, buf.Length); \r
+                                       }\r
+                                       parm_start=-1;\r
+                               }\r
 \r
-                                       left_byte = 0;\r
+                               // if we are not in a string and we are not escaped and we are on a semi-colon,\r
+                               // then write out what we have as a command\r
+                               if (left_byte == 0 && ! escaped && b == ';' && ms.Length > 0)\r
+                               {\r
+                                       bool goodcmd = false;\r
+                                       byte[] byteArray = ms.ToArray();\r
+                                       foreach (byte cmdByte in byteArray)\r
+                                               if (cmdByte != ' ') { goodcmd = true; break; }\r
+\r
+                                       if (goodcmd)\r
+                                               commands.Add( byteArray );\r
+                                       ms.SetLength(0);\r
                                }\r
+                               else if (parm_start == -1)\r
+                                       ms.WriteByte(b);\r
+\r
 \r
-                                       // we want to write out the bytes in all cases except when we are parsing out a parameter\r
-                               else if (left_byte != '@')\r
-                                       ms.WriteByte( b );\r
+                               // we want to write out the bytes in all cases except when we are parsing out a parameter\r
+                               if (escaped && b != '\\') escaped = false;\r
                        }\r
 \r
-                       // if we have any left, then add it at the end\r
-                       if (ms.Length > 0) \r
+                       return commands;\r
+               }\r
+\r
+               private void ReadOffResultSet()\r
+               {\r
+                       Driver driver = connection.InternalConnection.Driver;\r
+\r
+                       // first read off the schema\r
+                       Packet packet = driver.ReadPacket();\r
+                       while (! packet.IsLastPacket())\r
+                               packet = driver.ReadPacket();\r
+\r
+                       // now read off the data\r
+                       packet = driver.ReadPacket();\r
+                       while (! packet.IsLastPacket())\r
+                               packet = driver.ReadPacket();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal function to execute the next command in an array of commands\r
+               /// </summary>\r
+               internal CommandResult ExecuteBatch( bool stopAtResultSet )\r
+               {\r
+                       Driver driver = connection.InternalConnection.Driver;\r
+\r
+                       while (arraySql.Count > 0)\r
                        {\r
-                               byte[] newbytes = ms.ToArray();\r
-                               byteArrays.Add( newbytes );\r
-                       }\r
+                               byte[] sql = (byte[])arraySql[0];\r
+                               arraySql.RemoveAt(0);\r
 \r
-/*                     string s = new string('c', 0);\r
-                       byte[] bites = (byte[])byteArrays[0];\r
-                       for (int zt=0; zt < bites.Length; zt++)\r
-                               s += Convert.ToChar(bites[zt]);\r
-                       System.Windows.Forms.MessageBox.Show(s);\r
-*/\r
-                       return byteArrays;\r
+                               CommandResult result = driver.Send( DBCmd.QUERY, sql );\r
+                               \r
+                               if (result.IsResultSet)\r
+                               {\r
+                                       if (stopAtResultSet) return result;\r
+                                       result.Clear();\r
+                                       continue;\r
+                               }\r
+\r
+                               // at this point, we know it is a zero field count\r
+                               if (updateCount == -1) updateCount = 0;\r
+                               updateCount += result.RowsAffected;\r
+                       }\r
+                       return null;\r
                }\r
 \r
                /// <summary>\r
-               /// Executes a single non-select SQL statement.  Examples of this are update,\r
-               /// insert, etc.\r
+               /// Executes a SQL statement against the connection and returns the number of rows affected.\r
                /// </summary>\r
                /// <returns>Number of rows affected</returns>\r
                public int ExecuteNonQuery()\r
                {\r
-                       /*\r
-                       * ExecuteNonQuery is intended for commands that do\r
-                       * not return results, instead returning only the number\r
-                       * of records affected.\r
-                       */\r
-\r
                        // There must be a valid and open connection.\r
-                       if (m_connection == null || m_connection.State != ConnectionState.Open)\r
-                       throw new InvalidOperationException("Connection must valid and open");\r
+                       if (connection == null || connection.State != ConnectionState.Open)\r
+                               throw new InvalidOperationException("Connection must be valid and open");\r
 \r
-                       ArrayList list = ConvertSQLToBytes( m_sCmdText );\r
-                       m_UpdateCount = 0;\r
+                       // Data readers have to be closed first\r
+                       if (connection.Reader != null)\r
+                               throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first.");\r
 \r
-                       // Execute the command.\r
-                       Driver d = m_connection.Driver;\r
-                       try \r
-                       {\r
-                               for (int x=0; x < list.Count; x++)\r
-                               {\r
-                                       d.SendQuery( (byte[])list[x] ); \r
-                                       if (d.LastResult == 0)\r
-                                               m_UpdateCount += d.ReadLength();\r
-                               }\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw ex;\r
-                       }\r
+                       // execute any commands left in the queue from before.\r
+                       //ExecuteBatch(false);\r
+                       \r
+                       arraySql = SplitSql( cmdText );\r
+                       updateCount = 0;\r
+\r
+                       ExecuteBatch(false);\r
 \r
-                       return m_UpdateCount;\r
+                       return (int)updateCount;\r
                }\r
 \r
                IDataReader IDbCommand.ExecuteReader ()\r
@@ -320,30 +402,31 @@ namespace ByteFX.Data.MySQLClient
                        return ExecuteReader (behavior);\r
                }\r
 \r
-               public MySQLDataReader ExecuteReader()\r
+               /// <summary>\r
+               /// Overloaded. Sends the CommandText to the Connection and builds a MySqlDataReader.\r
+               /// </summary>\r
+               /// <returns></returns>\r
+               public MySqlDataReader ExecuteReader()\r
                {\r
                        return ExecuteReader(CommandBehavior.Default);\r
                }\r
 \r
-               public MySQLDataReader ExecuteReader(CommandBehavior behavior)\r
-               {\r
-                       /*\r
-                       * ExecuteReader should retrieve results from the data source\r
-                       * and return a DataReader that allows the user to process \r
-                       * the results.\r
-                       */\r
 \r
+               /// <summary>\r
+               /// Overloaded. Sends the CommandText to the Connection and builds a MySqlDataReader.\r
+               /// </summary>\r
+               /// <returns></returns>\r
+               public MySqlDataReader ExecuteReader(CommandBehavior behavior)\r
+               {\r
                        // There must be a valid and open connection.\r
-                       if (m_connection == null || m_connection.State != ConnectionState.Open)\r
-                               throw new InvalidOperationException("Connection must valid and open");\r
+                       if (connection == null || connection.State != ConnectionState.Open)\r
+                               throw new InvalidOperationException("Connection must be valid and open");\r
 \r
-                       if (0 != (behavior & CommandBehavior.CloseConnection))\r
-                       {\r
-                       }\r
+                       // make sure all readers on this connection are closed\r
+                       if (connection.Reader != null)\r
+                               throw new InvalidOperationException("There is already an open DataReader associated with this Connection which must be closed first.");\r
 \r
-                       if (0 != (behavior & CommandBehavior.Default))\r
-                       {\r
-                       }\r
+                       string sql = cmdText;\r
 \r
                        if (0 != (behavior & CommandBehavior.KeyInfo))\r
                        {\r
@@ -363,113 +446,68 @@ namespace ByteFX.Data.MySQLClient
 \r
                        if (0 != (behavior & CommandBehavior.SingleRow))\r
                        {\r
+                               sql = String.Format("SET SQL_SELECT_LIMIT=1;{0};SET sql_select_limit=-1;", cmdText);\r
                        }\r
 \r
+                       arraySql = SplitSql( sql );\r
 \r
-                       /*\r
-                       * ExecuteReader should retrieve results from the data source\r
-                       * and return a DataReader that allows the user to process \r
-                       * the results.\r
-                       */\r
-                       ArrayList cmds = ConvertSQLToBytes( m_sCmdText );\r
-                       m_UpdateCount = 0;\r
-\r
-                       MySQLDataReader reader = new MySQLDataReader(m_connection, behavior == CommandBehavior.SequentialAccess);\r
-\r
-                       // Execute the command.\r
-                       Driver d = m_connection.Driver;\r
-                       try \r
-                       {\r
-                               for (int x=0; x < cmds.Count; x++)\r
-                               {\r
-/*                                     string st = new string('c',0);\r
-                                       for (int z=0; z < ((byte[])cmds[x]).Length; z++)\r
-                                       {\r
-                                               st += Convert.ToChar( ((byte[])cmds[x])[z] );\r
-                                       }\r
-                                       System.Windows.Forms.MessageBox.Show(st); */\r
-\r
-/*                                     System.IO.FileStream fs = new System.IO.FileStream("c:\\cmd.sql",  System.IO.FileMode.OpenOrCreate);\r
-                                       byte[] bites = (byte[])(cmds[0]);\r
-                                       fs.Write(bites, 0, bites.Length);\r
-                                       fs.Close();\r
-*/\r
-                                       d.SendQuery( (byte[])cmds[x] );\r
-                                       if (d.LastResult == 0)\r
-                                               m_UpdateCount += d.ReadLength();\r
-                                       else\r
-                                               reader.LoadResults();\r
-                               }\r
-                       }\r
-                       catch (Exception ex) \r
-                       {\r
-                               throw ex;\r
-                       }\r
+                       updateCount = -1;\r
+                       MySqlDataReader reader = new MySqlDataReader(this, behavior);\r
 \r
+                       // move to the first resultset\r
+                       reader.NextResult();\r
+                       connection.Reader = reader;\r
                        return reader;\r
-\r
-                       //TODO implement rest of command behaviors on ExecuteReader\r
-                       /*\r
-                       * The only CommandBehavior option supported by this\r
-                       * sample is the automatic closing of the connection\r
-                       * when the user is done with the reader.\r
-                       */\r
-               //      if (behavior == CommandBehavior.CloseConnection)\r
-               //        return new TemplateDataReader(resultset, m_connection);\r
-               //      else\r
-               //        return new TemplateDataReader(resultset);\r
                }\r
 \r
                /// <summary>\r
-               /// ExecuteScalar executes a single SQL command that will return\r
-               /// a single row with a single column, or if more rows/columns are\r
-               /// returned it will return the first column of the first row.\r
+               /// Executes the query, and returns the first column of the first row in the \r
+               /// result set returned by the query. Extra columns or rows are ignored.\r
                /// </summary>\r
                /// <returns></returns>\r
                public object ExecuteScalar()\r
                {\r
                        // There must be a valid and open connection.\r
-                       if (m_connection == null || m_connection.State != ConnectionState.Open)\r
-                               throw new InvalidOperationException("Connection must valid and open");\r
-\r
-                       MySQLDataReader reader = new MySQLDataReader(m_connection, false);\r
-                       ArrayList cmds = ConvertSQLToBytes( m_sCmdText );\r
-                       m_UpdateCount = 0;\r
-\r
-                       // Execute the command.\r
-                       Driver d = m_connection.Driver;\r
-\r
-                       try \r
-                       {\r
-                               for (int x=0; x < cmds.Count; x++)\r
-                               {\r
-                                       d.SendQuery( (byte[])cmds[x] );\r
-                                       if (d.LastResult == 0)\r
-                                               m_UpdateCount += d.ReadLength();\r
-                                       else\r
-                                               reader.LoadResults();\r
-                               }\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw ex;\r
-                       }\r
-\r
-                       if (! reader.Read()) return null;\r
-                       return reader.GetValue(0);\r
+                       if (connection == null || connection.State != ConnectionState.Open)\r
+                               throw new InvalidOperationException("Connection must be valid and open");\r
+\r
+                       // Data readers have to be closed first\r
+                       if (connection.Reader != null)\r
+                               throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first.");\r
+\r
+                       arraySql = SplitSql( cmdText );\r
+\r
+                       updateCount = -1;\r
+                       MySqlDataReader reader = new MySqlDataReader(this, 0);\r
+                       reader.NextResult();\r
+                       object val = null;\r
+                       if (reader.Read())\r
+                               val = reader.GetValue(0);\r
+                       reader.Close();\r
+                       return val;\r
                }\r
 \r
+               /// <summary>\r
+               /// Creates a prepared version of the command on an instance of MySQL Server. This\r
+               /// is currently not supported.\r
+               /// </summary>\r
                public void Prepare()\r
                {\r
                }\r
+               #endregion\r
 \r
                #region ICloneable\r
+               /// <summary>\r
+               /// Creates a clone of this MySqlCommand object.  CommandText, Connection, and Transaction properties\r
+               /// are included as well as the entire parameter list.\r
+               /// </summary>\r
+               /// <returns>The cloned MySqlCommand object</returns>\r
                public object Clone() \r
                {\r
-                       MySQLCommand clone = new MySQLCommand(m_sCmdText, m_connection, m_txn);\r
-                       foreach (MySQLParameter p in m_parameters) \r
+                       MySqlCommand clone = new MySqlCommand(cmdText, connection, curTransaction);\r
+                       foreach (MySqlParameter p in parameters) \r
                        {\r
-                               clone.Parameters.Add(p.Clone());\r
+                               clone.Parameters.Add((p as ICloneable).Clone());\r
                        }\r
                        return clone;\r
                }\r