// ByteFX.Data data access components for .Net // Copyright (C) 2002-2003 ByteFX, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Data; using System.ComponentModel; using System.Collections; namespace ByteFX.Data.MySQLClient { #if WINDOWS [System.Drawing.ToolboxBitmap( typeof(MySQLCommand), "Designers.command.bmp")] #endif public sealed class MySQLCommand : Component, IDbCommand, ICloneable { MySQLConnection m_connection; MySQLTransaction m_txn; string m_sCmdText; int m_UpdateCount; UpdateRowSource m_updatedRowSource = UpdateRowSource.Both; MySQLParameterCollection m_parameters = new MySQLParameterCollection(); // Implement the default constructor here. public MySQLCommand() { } // Implement other constructors here. public MySQLCommand(string cmdText) { m_sCmdText = cmdText; } public MySQLCommand(System.ComponentModel.IContainer container) { /// /// Required for Windows.Forms Class Composition Designer support /// container.Add(this); } public MySQLCommand(string cmdText, MySQLConnection connection) { m_sCmdText = cmdText; m_connection = connection; } public new void Dispose() { base.Dispose(); } public MySQLCommand(string cmdText, MySQLConnection connection, MySQLTransaction txn) { m_sCmdText = cmdText; m_connection = connection; m_txn = txn; } /**** * IMPLEMENT THE REQUIRED PROPERTIES. ****/ public string CommandText { get { return m_sCmdText; } set { m_sCmdText = value; } } public int CommandTimeout { /* * The sample does not support a command time-out. As a result, * for the get, zero is returned because zero indicates an indefinite * time-out period. For the set, throw an exception. */ get { return 0; } set { if (value != 0) throw new NotSupportedException(); } } public CommandType CommandType { /* * The sample only supports CommandType.Text. */ get { return CommandType.Text; } set { if (value != CommandType.Text) throw new NotSupportedException(); } } public IDbConnection Connection { /* * The user should be able to set or change the connection at * any time. */ get { return m_connection; } set { /* * The connection is associated with the transaction * so set the transaction object to return a null reference if the connection * is reset. */ if (m_connection != value) this.Transaction = null; m_connection = (MySQLConnection)value; } } public MySQLParameterCollection Parameters { get { return m_parameters; } } IDataParameterCollection IDbCommand.Parameters { get { return m_parameters; } } public IDbTransaction Transaction { /* * Set the transaction. Consider additional steps to ensure that the transaction * is compatible with the connection, because the two are usually linked. */ get { return m_txn; } set { m_txn = (MySQLTransaction)value; } } public UpdateRowSource UpdatedRowSource { get { return m_updatedRowSource; } set { m_updatedRowSource = value; } } /**** * IMPLEMENT THE REQUIRED METHODS. ****/ public void Cancel() { // The sample does not support canceling a command // once it has been initiated. throw new NotSupportedException(); } public IDbDataParameter CreateParameter() { return new MySQLParameter(); } /// /// Convert the SQL command into a series of ASCII bytes streaming /// each of the parameters into the proper place /// /// Source SQL command with parameter markers /// Byte array with all parameters included private ArrayList ConvertSQLToBytes(string sql) { ArrayList byteArrays = new ArrayList(); System.IO.MemoryStream ms = new System.IO.MemoryStream(); if (sql[ sql.Length-1 ] != ';') sql += ';'; byte[] bytes = System.Text.Encoding.ASCII.GetBytes(sql); byte left_byte = 0; int parm_start=-1, parm_end = -1; for (int x=0; x < bytes.Length; x++) { byte b = bytes[x]; // if we see a quote marker, then check to see if we are opening // or closing a quote if (b == '\'' || b == '\"') { if (b == left_byte) { left_byte = 0; } else { if (left_byte == 0) left_byte = b; } ms.WriteByte(b); } // if we find a ; not part of a quoted string, then take the parsed portion // as a sql command and add it to the array else if (b == ';' && left_byte == 0) { byte[] sqlBytes = ms.ToArray(); byteArrays.Add( sqlBytes ); ms = new System.IO.MemoryStream(); } // if we see the marker for a parameter, then save its position and // look for the end else if (b == '@' && left_byte == 0) { parm_start = x; left_byte = b; } // if we see a space and we are tracking a parameter, then end the parameter and have // that parameter serialize itself to the memory streams else if ((b == ' ' || b == ',' || b == ';' || b == ')') && left_byte == '@') { parm_end = x-1; string parm_name = sql.Substring(parm_start, parm_end-parm_start+1); MySQLParameter p = (m_parameters[parm_name] as MySQLParameter); p.SerializeToBytes(ms); ms.WriteByte(b); if (b == ';') { byte[] sqlBytes = ms.ToArray(); byteArrays.Add( sqlBytes ); ms = new System.IO.MemoryStream(); } left_byte = 0; } // we want to write out the bytes in all cases except when we are parsing out a parameter else if (left_byte != '@') ms.WriteByte( b ); } // if we have any left, then add it at the end if (ms.Length > 0) { byte[] newbytes = ms.ToArray(); byteArrays.Add( newbytes ); } /* string s = new string('c', 0); byte[] bites = (byte[])byteArrays[0]; for (int zt=0; zt < bites.Length; zt++) s += Convert.ToChar(bites[zt]); System.Windows.Forms.MessageBox.Show(s); */ return byteArrays; } /// /// Executes a single non-select SQL statement. Examples of this are update, /// insert, etc. /// /// Number of rows affected public int ExecuteNonQuery() { /* * ExecuteNonQuery is intended for commands that do * not return results, instead returning only the number * of records affected. */ // There must be a valid and open connection. if (m_connection == null || m_connection.State != ConnectionState.Open) throw new InvalidOperationException("Connection must valid and open"); ArrayList list = ConvertSQLToBytes( m_sCmdText ); m_UpdateCount = 0; // Execute the command. Driver d = m_connection.Driver; try { for (int x=0; x < list.Count; x++) { d.SendQuery( (byte[])list[x] ); if (d.LastResult == 0) m_UpdateCount += d.ReadLength(); } } catch (Exception ex) { throw ex; } return m_UpdateCount; } IDataReader IDbCommand.ExecuteReader () { return ExecuteReader (); } IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior) { return ExecuteReader (behavior); } public MySQLDataReader ExecuteReader() { return ExecuteReader(CommandBehavior.Default); } public MySQLDataReader ExecuteReader(CommandBehavior behavior) { /* * ExecuteReader should retrieve results from the data source * and return a DataReader that allows the user to process * the results. */ // There must be a valid and open connection. if (m_connection == null || m_connection.State != ConnectionState.Open) throw new InvalidOperationException("Connection must valid and open"); if (0 != (behavior & CommandBehavior.CloseConnection)) { } if (0 != (behavior & CommandBehavior.Default)) { } if (0 != (behavior & CommandBehavior.KeyInfo)) { } if (0 != (behavior & CommandBehavior.SchemaOnly)) { } if (0 != (behavior & CommandBehavior.SequentialAccess)) { } if (0 != (behavior & CommandBehavior.SingleResult)) { } if (0 != (behavior & CommandBehavior.SingleRow)) { } /* * ExecuteReader should retrieve results from the data source * and return a DataReader that allows the user to process * the results. */ ArrayList cmds = ConvertSQLToBytes( m_sCmdText ); m_UpdateCount = 0; MySQLDataReader reader = new MySQLDataReader(m_connection, behavior == CommandBehavior.SequentialAccess); // Execute the command. Driver d = m_connection.Driver; try { for (int x=0; x < cmds.Count; x++) { /* string st = new string('c',0); for (int z=0; z < ((byte[])cmds[x]).Length; z++) { st += Convert.ToChar( ((byte[])cmds[x])[z] ); } System.Windows.Forms.MessageBox.Show(st); */ /* System.IO.FileStream fs = new System.IO.FileStream("c:\\cmd.sql", System.IO.FileMode.OpenOrCreate); byte[] bites = (byte[])(cmds[0]); fs.Write(bites, 0, bites.Length); fs.Close(); */ d.SendQuery( (byte[])cmds[x] ); if (d.LastResult == 0) m_UpdateCount += d.ReadLength(); else reader.LoadResults(); } } catch (Exception ex) { throw ex; } return reader; //TODO implement rest of command behaviors on ExecuteReader /* * The only CommandBehavior option supported by this * sample is the automatic closing of the connection * when the user is done with the reader. */ // if (behavior == CommandBehavior.CloseConnection) // return new TemplateDataReader(resultset, m_connection); // else // return new TemplateDataReader(resultset); } /// /// ExecuteScalar executes a single SQL command that will return /// a single row with a single column, or if more rows/columns are /// returned it will return the first column of the first row. /// /// public object ExecuteScalar() { // There must be a valid and open connection. if (m_connection == null || m_connection.State != ConnectionState.Open) throw new InvalidOperationException("Connection must valid and open"); MySQLDataReader reader = new MySQLDataReader(m_connection, false); ArrayList cmds = ConvertSQLToBytes( m_sCmdText ); m_UpdateCount = 0; // Execute the command. Driver d = m_connection.Driver; try { for (int x=0; x < cmds.Count; x++) { d.SendQuery( (byte[])cmds[x] ); if (d.LastResult == 0) m_UpdateCount += d.ReadLength(); else reader.LoadResults(); } } catch (Exception ex) { throw ex; } if (! reader.Read()) return null; return reader.GetValue(0); } public void Prepare() { } #region ICloneable public object Clone() { MySQLCommand clone = new MySQLCommand(m_sCmdText, m_connection, m_txn); foreach (MySQLParameter p in m_parameters) { clone.Parameters.Add(p.Clone()); } return clone; } #endregion } }