1 // ByteFX.Data data access components for .Net
\r
2 // Copyright (C) 2002-2003 ByteFX, Inc.
\r
4 // This library is free software; you can redistribute it and/or
\r
5 // modify it under the terms of the GNU Lesser General Public
\r
6 // License as published by the Free Software Foundation; either
\r
7 // version 2.1 of the License, or (at your option) any later version.
\r
9 // This library is distributed in the hope that it will be useful,
\r
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
12 // Lesser General Public License for more details.
\r
14 // You should have received a copy of the GNU Lesser General Public
\r
15 // License along with this library; if not, write to the Free Software
\r
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
20 using System.ComponentModel;
\r
21 using System.Collections;
\r
23 namespace ByteFX.Data.MySQLClient
\r
26 [System.Drawing.ToolboxBitmap( typeof(MySQLCommand), "Designers.command.bmp")]
\r
28 public sealed class MySQLCommand : Component, IDbCommand, ICloneable
\r
30 MySQLConnection m_connection;
\r
31 MySQLTransaction m_txn;
\r
34 UpdateRowSource m_updatedRowSource = UpdateRowSource.Both;
\r
35 MySQLParameterCollection m_parameters = new MySQLParameterCollection();
\r
37 // Implement the default constructor here.
\r
38 public MySQLCommand()
\r
42 // Implement other constructors here.
\r
43 public MySQLCommand(string cmdText)
\r
45 m_sCmdText = cmdText;
\r
48 public MySQLCommand(System.ComponentModel.IContainer container)
\r
51 /// Required for Windows.Forms Class Composition Designer support
\r
53 container.Add(this);
\r
56 public MySQLCommand(string cmdText, MySQLConnection connection)
\r
58 m_sCmdText = cmdText;
\r
59 m_connection = connection;
\r
62 public new void Dispose()
\r
67 public MySQLCommand(string cmdText, MySQLConnection connection, MySQLTransaction txn)
\r
69 m_sCmdText = cmdText;
\r
70 m_connection = connection;
\r
75 * IMPLEMENT THE REQUIRED PROPERTIES.
\r
77 public string CommandText
\r
79 get { return m_sCmdText; }
\r
80 set { m_sCmdText = value; }
\r
83 public int CommandTimeout
\r
86 * The sample does not support a command time-out. As a result,
\r
87 * for the get, zero is returned because zero indicates an indefinite
\r
88 * time-out period. For the set, throw an exception.
\r
91 set { if (value != 0) throw new NotSupportedException(); }
\r
94 public CommandType CommandType
\r
97 * The sample only supports CommandType.Text.
\r
99 get { return CommandType.Text; }
\r
100 set { if (value != CommandType.Text) throw new NotSupportedException(); }
\r
103 public IDbConnection Connection
\r
106 * The user should be able to set or change the connection at
\r
111 return m_connection;
\r
116 * The connection is associated with the transaction
\r
117 * so set the transaction object to return a null reference if the connection
\r
120 if (m_connection != value)
\r
121 this.Transaction = null;
\r
123 m_connection = (MySQLConnection)value;
\r
127 public MySQLParameterCollection Parameters
\r
129 get { return m_parameters; }
\r
132 IDataParameterCollection IDbCommand.Parameters
\r
134 get { return m_parameters; }
\r
137 public IDbTransaction Transaction
\r
140 * Set the transaction. Consider additional steps to ensure that the transaction
\r
141 * is compatible with the connection, because the two are usually linked.
\r
149 m_txn = (MySQLTransaction)value;
\r
153 public UpdateRowSource UpdatedRowSource
\r
157 return m_updatedRowSource;
\r
161 m_updatedRowSource = value;
\r
166 * IMPLEMENT THE REQUIRED METHODS.
\r
168 public void Cancel()
\r
170 // The sample does not support canceling a command
\r
171 // once it has been initiated.
\r
172 throw new NotSupportedException();
\r
175 public IDbDataParameter CreateParameter()
\r
177 return new MySQLParameter();
\r
181 /// Convert the SQL command into a series of ASCII bytes streaming
\r
182 /// each of the parameters into the proper place
\r
184 /// <param name="sql">Source SQL command with parameter markers</param>
\r
185 /// <returns>Byte array with all parameters included</returns>
\r
186 private ArrayList ConvertSQLToBytes(string sql)
\r
188 ArrayList byteArrays = new ArrayList();
\r
189 System.IO.MemoryStream ms = new System.IO.MemoryStream();
\r
191 if (sql[ sql.Length-1 ] != ';')
\r
193 byte[] bytes = System.Text.Encoding.ASCII.GetBytes(sql);
\r
195 byte left_byte = 0;
\r
196 int parm_start=-1, parm_end = -1;
\r
197 for (int x=0; x < bytes.Length; x++)
\r
200 // if we see a quote marker, then check to see if we are opening
\r
201 // or closing a quote
\r
202 if (b == '\'' || b == '\"')
\r
204 if (b == left_byte)
\r
210 if (left_byte == 0)
\r
216 // if we find a ; not part of a quoted string, then take the parsed portion
\r
217 // as a sql command and add it to the array
\r
218 else if (b == ';' && left_byte == 0)
\r
220 byte[] sqlBytes = ms.ToArray();
\r
221 byteArrays.Add( sqlBytes );
\r
222 ms = new System.IO.MemoryStream();
\r
225 // if we see the marker for a parameter, then save its position and
\r
226 // look for the end
\r
227 else if (b == '@' && left_byte == 0)
\r
233 // if we see a space and we are tracking a parameter, then end the parameter and have
\r
234 // that parameter serialize itself to the memory streams
\r
235 else if ((b == ' ' || b == ',' || b == ';' || b == ')') && left_byte == '@')
\r
238 string parm_name = sql.Substring(parm_start, parm_end-parm_start+1);
\r
239 MySQLParameter p = (m_parameters[parm_name] as MySQLParameter);
\r
240 p.SerializeToBytes(ms);
\r
245 byte[] sqlBytes = ms.ToArray();
\r
246 byteArrays.Add( sqlBytes );
\r
247 ms = new System.IO.MemoryStream();
\r
253 // we want to write out the bytes in all cases except when we are parsing out a parameter
\r
254 else if (left_byte != '@')
\r
258 // if we have any left, then add it at the end
\r
259 if (ms.Length > 0)
\r
261 byte[] newbytes = ms.ToArray();
\r
262 byteArrays.Add( newbytes );
\r
265 /* string s = new string('c', 0);
\r
266 byte[] bites = (byte[])byteArrays[0];
\r
267 for (int zt=0; zt < bites.Length; zt++)
\r
268 s += Convert.ToChar(bites[zt]);
\r
269 System.Windows.Forms.MessageBox.Show(s);
\r
275 /// Executes a single non-select SQL statement. Examples of this are update,
\r
278 /// <returns>Number of rows affected</returns>
\r
279 public int ExecuteNonQuery()
\r
282 * ExecuteNonQuery is intended for commands that do
\r
283 * not return results, instead returning only the number
\r
284 * of records affected.
\r
287 // There must be a valid and open connection.
\r
288 if (m_connection == null || m_connection.State != ConnectionState.Open)
\r
289 throw new InvalidOperationException("Connection must valid and open");
\r
291 ArrayList list = ConvertSQLToBytes( m_sCmdText );
\r
294 // Execute the command.
\r
295 Driver d = m_connection.Driver;
\r
298 for (int x=0; x < list.Count; x++)
\r
300 d.SendQuery( (byte[])list[x] );
\r
301 if (d.LastResult == 0)
\r
302 m_UpdateCount += d.ReadLength();
\r
305 catch (Exception ex)
\r
310 return m_UpdateCount;
\r
313 IDataReader IDbCommand.ExecuteReader ()
\r
315 return ExecuteReader ();
\r
318 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
\r
320 return ExecuteReader (behavior);
\r
323 public MySQLDataReader ExecuteReader()
\r
325 return ExecuteReader(CommandBehavior.Default);
\r
328 public MySQLDataReader ExecuteReader(CommandBehavior behavior)
\r
331 * ExecuteReader should retrieve results from the data source
\r
332 * and return a DataReader that allows the user to process
\r
336 // There must be a valid and open connection.
\r
337 if (m_connection == null || m_connection.State != ConnectionState.Open)
\r
338 throw new InvalidOperationException("Connection must valid and open");
\r
340 if (0 != (behavior & CommandBehavior.CloseConnection))
\r
344 if (0 != (behavior & CommandBehavior.Default))
\r
348 if (0 != (behavior & CommandBehavior.KeyInfo))
\r
352 if (0 != (behavior & CommandBehavior.SchemaOnly))
\r
356 if (0 != (behavior & CommandBehavior.SequentialAccess))
\r
360 if (0 != (behavior & CommandBehavior.SingleResult))
\r
364 if (0 != (behavior & CommandBehavior.SingleRow))
\r
370 * ExecuteReader should retrieve results from the data source
\r
371 * and return a DataReader that allows the user to process
\r
374 ArrayList cmds = ConvertSQLToBytes( m_sCmdText );
\r
377 MySQLDataReader reader = new MySQLDataReader(m_connection, behavior == CommandBehavior.SequentialAccess);
\r
379 // Execute the command.
\r
380 Driver d = m_connection.Driver;
\r
383 for (int x=0; x < cmds.Count; x++)
\r
385 /* string st = new string('c',0);
\r
386 for (int z=0; z < ((byte[])cmds[x]).Length; z++)
\r
388 st += Convert.ToChar( ((byte[])cmds[x])[z] );
\r
390 System.Windows.Forms.MessageBox.Show(st); */
\r
392 /* System.IO.FileStream fs = new System.IO.FileStream("c:\\cmd.sql", System.IO.FileMode.OpenOrCreate);
\r
393 byte[] bites = (byte[])(cmds[0]);
\r
394 fs.Write(bites, 0, bites.Length);
\r
397 d.SendQuery( (byte[])cmds[x] );
\r
398 if (d.LastResult == 0)
\r
399 m_UpdateCount += d.ReadLength();
\r
401 reader.LoadResults();
\r
404 catch (Exception ex)
\r
411 //TODO implement rest of command behaviors on ExecuteReader
\r
413 * The only CommandBehavior option supported by this
\r
414 * sample is the automatic closing of the connection
\r
415 * when the user is done with the reader.
\r
417 // if (behavior == CommandBehavior.CloseConnection)
\r
418 // return new TemplateDataReader(resultset, m_connection);
\r
420 // return new TemplateDataReader(resultset);
\r
424 /// ExecuteScalar executes a single SQL command that will return
\r
425 /// a single row with a single column, or if more rows/columns are
\r
426 /// returned it will return the first column of the first row.
\r
428 /// <returns></returns>
\r
429 public object ExecuteScalar()
\r
431 // There must be a valid and open connection.
\r
432 if (m_connection == null || m_connection.State != ConnectionState.Open)
\r
433 throw new InvalidOperationException("Connection must valid and open");
\r
435 MySQLDataReader reader = new MySQLDataReader(m_connection, false);
\r
436 ArrayList cmds = ConvertSQLToBytes( m_sCmdText );
\r
439 // Execute the command.
\r
440 Driver d = m_connection.Driver;
\r
444 for (int x=0; x < cmds.Count; x++)
\r
446 d.SendQuery( (byte[])cmds[x] );
\r
447 if (d.LastResult == 0)
\r
448 m_UpdateCount += d.ReadLength();
\r
450 reader.LoadResults();
\r
453 catch (Exception ex)
\r
458 if (! reader.Read()) return null;
\r
459 return reader.GetValue(0);
\r
462 public void Prepare()
\r
467 public object Clone()
\r
469 MySQLCommand clone = new MySQLCommand(m_sCmdText, m_connection, m_txn);
\r
470 foreach (MySQLParameter p in m_parameters)
\r
472 clone.Parameters.Add(p.Clone());
\r