2 // Mono.Data.MySql.MySqlCommand.cs
5 // Daniel Morgan (danmorg@sc.rr.com)
7 // (C) Daniel Morgan, 2002
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Collections
;
33 using System
.ComponentModel
;
35 using System
.Data
.Common
;
36 using System
.Runtime
.InteropServices
;
40 namespace Mono
.Data
.MySql
{
41 public sealed class MySqlCommand
: Component
, IDbCommand
, ICloneable
{
45 private string sql
= "";
46 private int timeout
= 30;
47 // default is 30 seconds
48 // for command execution
50 private MySqlConnection conn
= null;
51 private MySqlTransaction trans
= null;
52 private CommandType cmdType
= CommandType
.Text
;
53 private bool designTime
= false;
54 private MySqlParameterCollection parmCollection
= new
55 MySqlParameterCollection();
57 // MySqlDataReader state data for ExecuteReader()
58 //private MySqlDataReader dataReader = null;
59 private string[] commands
= null;
60 private int currentQuery
= -1;
61 private CommandBehavior cmdBehavior
= CommandBehavior
.Default
;
63 private bool disposed
= false;
65 private const char bindChar
= ':';
71 public MySqlCommand() {
75 public MySqlCommand (string cmdText
) {
79 public MySqlCommand (string cmdText
, MySqlConnection connection
) {
84 public MySqlCommand (string cmdText
, MySqlConnection connection
,
85 MySqlTransaction transaction
) {
91 #endregion // Constructors
96 public void Cancel () {
97 // FIXME: use non-blocking Exec for this
98 throw new NotImplementedException ();
101 // FIXME: is this the correct way to return a stronger type?
103 IDbDataParameter IDbCommand
.CreateParameter () {
104 return CreateParameter ();
108 public MySqlParameter
CreateParameter () {
109 return new MySqlParameter ();
112 public int ExecuteNonQuery () {
113 int rowsAffected
= -1;
115 IntPtr res
= ExecuteSQL (sql
);
117 if(res
.Equals(IntPtr
.Zero
)) {
118 // no result set returned, get records affected
119 rowsAffected
= (int) MySql
.AffectedRows(conn
.NativeMySqlInitStruct
);
122 MySql
.FreeResult(res
);
125 // >= 0 of the number of rows affected by
126 // INSERT, UPDATE, DELETE
132 IDataReader IDbCommand
.ExecuteReader () {
133 return ExecuteReader ();
137 public MySqlDataReader
ExecuteReader () {
138 return ExecuteReader(CommandBehavior
.Default
);
142 IDataReader IDbCommand
.ExecuteReader (
143 CommandBehavior behavior
) {
145 return ExecuteReader (behavior
);
149 public MySqlDataReader
ExecuteReader (CommandBehavior behavior
) {
151 MySqlDataReader reader
= null;
155 commands
= sql
.Split(new Char
[] {';'}
);
156 reader
= new MySqlDataReader(this, behavior
);
162 // called by an MySqlDataReader's NextResult()
163 internal IntPtr
NextResult (out bool result
) {
165 IntPtr mysqlResult
= IntPtr
.Zero
;
169 if(currentQuery
< commands
.Length
) {
172 // don't execute empty queries
173 while((query
= commands
[currentQuery
]).Equals("")) {
175 if(currentQuery
>= commands
.Length
)
178 mysqlResult
= ExecuteSQL (query
);
179 result
= true; // has result
185 public object ExecuteScalar () {
189 IntPtr res
= ExecuteSQL (sql
);
191 int numRows
= MySql
.NumRows(res
);
192 int numFields
= MySql
.NumFields(res
);
194 MySqlMarshalledField fd
;
195 fd
= (MySqlMarshalledField
) Marshal
.PtrToStructure(MySql
.FetchField(res
),
196 typeof(MySqlMarshalledField
));
197 string fieldName
= fd
.Name
;
198 int fieldType
= fd
.FieldType
;
199 MySqlEnumFieldTypes mysqlFieldType
= (MySqlEnumFieldTypes
) fieldType
;
200 DbType fieldDbType
= MySqlHelper
.MySqlTypeToDbType(mysqlFieldType
);
203 row
= MySql
.FetchRow(res
);
204 if(row
== IntPtr
.Zero
) {
209 // only get first column/first row
210 string objValue
= GetColumnData(row
, 0);
211 obj
= MySqlHelper
.ConvertDbTypeToSystem (mysqlFieldType
, fieldDbType
, objValue
);
214 MySql
.FreeResult(res
);
220 // command: string in - SQL command
221 // IntPtr (MySqlResult) return - the result
222 // Use of this function needs to check to see if
223 // if the return equal to IntPtr.Zero
224 // Example: IntPtr res = ExecuteSQL ("SELECT * FROM DB");
225 // if (res == IntPtr.Zero) { // do something }
227 internal IntPtr
ExecuteSQL (string command
) {
231 throw new InvalidOperationException(
232 "Connection is null");
234 if (conn
.State
!= ConnectionState
.Open
)
235 throw new InvalidOperationException(
236 "ConnectionState is not Open");
238 if (sql
.Equals (String
.Empty
))
239 throw new InvalidOperationException(
240 "CommandText is Empty");
242 string query
= TweakQuery(sql
);
244 int rcq
= MySql
.Query(conn
.NativeMySqlInitStruct
, query
);
248 "Could not execute command [" +
250 "] on server because: " +
251 MySql
.Error(conn
.NativeMySqlInitStruct
);
252 throw new MySqlException(msg
);
254 IntPtr result
= MySql
.StoreResult(conn
.NativeMySqlInitStruct
);
258 string TweakQuery(string query
) {
259 string statement
= "";
262 case CommandType
.Text
:
263 statement
= ReplaceParameterPlaceholders (query
);
265 case CommandType
.StoredProcedure
:
266 string sParmList
= GetStoredProcParmList ();
267 statement
= "SELECT " + query
+ "(" + sParmList
+ ")";
269 case CommandType
.TableDirect
:
271 "SELECT * FROM " + query
;
277 string GetStoredProcParmList () {
278 StringBuilder s
= new StringBuilder();
281 for(int p
= 0; p
< parmCollection
.Count
; p
++) {
282 MySqlParameter prm
= parmCollection
[p
];
283 if(prm
.Direction
== ParameterDirection
.Input
) {
284 string strObj
= MySqlHelper
.
285 ObjectToString(prm
.DbType
,
296 // TODO: this only supports input parameters,
297 // need support for output, input/output,
298 // and return parameters
299 // As far as I know, MySQL does not support
300 // parameters so the parameters support in this
301 // provider is just a search and replace.
302 string ReplaceParameterPlaceholders (string query
) {
304 string resultSql
= "";
306 StringBuilder result
= new StringBuilder();
307 char[] chars
= sql
.ToCharArray();
308 bool bStringConstFound
= false;
310 for(int i
= 0; i
< chars
.Length
; i
++) {
311 if(chars
[i
] == '\'') {
312 if(bStringConstFound
== true)
313 bStringConstFound
= false;
315 bStringConstFound
= true;
317 result
.Append(chars
[i
]);
319 else if(chars
[i
] == bindChar
&&
320 bStringConstFound
== false) {
322 StringBuilder parm
= new StringBuilder();
324 while(i
<= chars
.Length
) {
326 if(i
== chars
.Length
)
331 if(Char
.IsLetterOrDigit(ch
)) {
335 string p
= parm
.ToString();
336 bool found
= BindReplace(result
, p
);
341 // *** Error Handling
342 Console
.WriteLine("Error: parameter not found: " + p
);
351 result
.Append(chars
[i
]);
354 resultSql
= result
.ToString();
358 bool BindReplace (StringBuilder result
, string p
) {
362 if(parmCollection
.Contains(p
) == true) {
364 MySqlParameter prm
= parmCollection
[p
];
366 // convert object to string and place
368 if(prm
.Direction
== ParameterDirection
.Input
) {
369 string strObj
= MySqlHelper
.
370 ObjectToString(prm
.DbType
,
372 result
.Append(strObj
);
375 result
.Append(bindChar
+ p
);
383 public XmlReader
ExecuteXmlReader () {
384 //MySqlDataReader dataReader = ExecuteReader ();
385 //MySqlXmlTextReader textReader = new MySqlXmlTextReader (dataReader);
386 //XmlReader xmlReader = new XmlTextReader (textReader);
388 throw new NotImplementedException ();
392 public void Prepare () {
393 // FIXME: parameters have to be implemented for this
394 throw new NotImplementedException ();
397 object ICloneable
.Clone() {
398 throw new NotImplementedException ();
401 // Used to marshal a field value from the database result set.
402 // The indexed column data on the current result set row.
403 // res = the result set from a MySql.Query().
404 // index = the column index.
405 internal string GetColumnData(IntPtr res
, int index
) {
406 IntPtr str
= Marshal
.ReadIntPtr(res
, index
*IntPtr
.Size
);
407 if (str
== IntPtr
.Zero
)
409 string s
= Marshal
.PtrToStringAnsi(str
);
413 #endregion // Methods
417 public string CommandText
{
427 public int CommandTimeout
{
433 // FIXME: if value < 0, throw
436 // throw ArgumentException;
441 public CommandType CommandType
{
451 IDbConnection IDbCommand
.Connection
{
457 // FIXME: throw an InvalidOperationException
458 // if the change was during a
459 // transaction in progress
462 Connection
= (MySqlConnection
) value;
464 // Connection = value;
466 // FIXME: set Transaction property to null
470 public MySqlConnection Connection
{
472 // conn defaults to null
477 // FIXME: throw an InvalidOperationException
478 // if the change was during
479 // a transaction in progress
481 // FIXME: set Transaction property to null
485 public bool DesignTimeVisible
{
495 IDataParameterCollection IDbCommand
.Parameters
{
501 public MySqlParameterCollection Parameters
{
503 return parmCollection
;
507 IDbTransaction IDbCommand
.Transaction
{
513 // FIXME: error handling - do not allow
514 // setting of transaction if transaction
516 Transaction
= (MySqlTransaction
) value;
521 public MySqlTransaction Transaction
{
527 // FIXME: error handling
533 public UpdateRowSource UpdatedRowSource
{
534 // FIXME: do this once DbDataAdaptor
535 // and DataRow are done
537 throw new NotImplementedException ();
540 throw new NotImplementedException ();
544 #endregion // Properties
546 #region Inner Classes
548 #endregion // Inner Classes
552 protected override void Dispose(bool disposing
) {
556 // release any managed resources
558 // release any unmanaged resources
561 this.disposed
= true;
564 base.Dispose(disposing
);
573 #endregion //Destructors