2 // Mono.Data.MySql.MySqlDataReader.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
8 // (C) Ximian, Inc 2002
9 // (C) Daniel Morgan 2002
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
35 using System
.ComponentModel
;
37 using System
.Data
.Common
;
38 using System
.Runtime
.InteropServices
;
40 namespace Mono
.Data
.MySql
{
42 /// Provides a means of reading one or more forward-only streams
43 /// of result sets obtained by executing a command
44 /// at a SQL database.
46 public sealed class MySqlDataReader
: MarshalByRefObject
,
47 IEnumerable
, IDataReader
, IDisposable
, IDataRecord
52 private MySqlCommand cmd
;
55 private string[] fieldName
;
56 private MySqlEnumFieldTypes
[] fieldType
; // MySQL data type
57 private DbType
[] fieldDbType
; // DbType translated from MySQL type
58 private uint[] fieldLength
;
59 private uint[] fieldMaxLength
;
60 private uint[] fieldFlags
;
62 private object[] dataValue
;
64 private bool open
= false;
66 private int recordsAffected
= -1;
67 private int currentQuery
= 0;
69 private int currentRow
= -1;
70 private IntPtr res
= IntPtr
.Zero
;
71 private IntPtr row
= IntPtr
.Zero
;
73 private int numFields
;
76 private CommandBehavior cmdBehavior
;
78 private bool disposed
= false;
84 internal MySqlDataReader (MySqlCommand sqlCmd
, CommandBehavior behavior
) {
88 // cmd.OpenReader(this);
89 cmdBehavior
= behavior
;
94 #region Public Methods
100 // free MySqlDataReader resources in SqlCommand
101 // and allow SqlConnection to be used again
104 // TODO: get parameters from result
109 public DataTable
GetSchemaTable() {
111 DataTable dataTableSchema
= null;
112 // Only Results from SQL SELECT Queries
113 // get a DataTable for schema of the result
114 // otherwise, DataTable is null reference
117 dataTableSchema
= new DataTable ("SchemaTable");
119 dataTableSchema
.Columns
.Add ("ColumnName", typeof (string));
120 dataTableSchema
.Columns
.Add ("ColumnOrdinal", typeof (int));
121 dataTableSchema
.Columns
.Add ("ColumnSize", typeof (int));
122 dataTableSchema
.Columns
.Add ("NumericPrecision", typeof (int));
123 dataTableSchema
.Columns
.Add ("NumericScale", typeof (int));
124 dataTableSchema
.Columns
.Add ("IsUnique", typeof (bool));
125 dataTableSchema
.Columns
.Add ("IsKey", typeof (bool));
126 DataColumn dc
= dataTableSchema
.Columns
["IsKey"];
127 dc
.AllowDBNull
= true; // IsKey can have a DBNull
128 dataTableSchema
.Columns
.Add ("BaseCatalogName", typeof (string));
129 dataTableSchema
.Columns
.Add ("BaseColumnName", typeof (string));
130 dataTableSchema
.Columns
.Add ("BaseSchemaName", typeof (string));
131 dataTableSchema
.Columns
.Add ("BaseTableName", typeof (string));
132 dataTableSchema
.Columns
.Add ("DataType", typeof(Type
));
133 dataTableSchema
.Columns
.Add ("AllowDBNull", typeof (bool));
134 dataTableSchema
.Columns
.Add ("ProviderType", typeof (int));
135 dataTableSchema
.Columns
.Add ("IsAliased", typeof (bool));
136 dataTableSchema
.Columns
.Add ("IsExpression", typeof (bool));
137 dataTableSchema
.Columns
.Add ("IsIdentity", typeof (bool));
138 dataTableSchema
.Columns
.Add ("IsAutoIncrement", typeof (bool));
139 dataTableSchema
.Columns
.Add ("IsRowVersion", typeof (bool));
140 dataTableSchema
.Columns
.Add ("IsHidden", typeof (bool));
141 dataTableSchema
.Columns
.Add ("IsLong", typeof (bool));
142 dataTableSchema
.Columns
.Add ("IsReadOnly", typeof (bool));
148 for (int i
= 0; i
< numFields
; i
+= 1 ) {
150 schemaRow
= dataTableSchema
.NewRow ();
152 schemaRow
["ColumnName"] = fieldName
[i
];
153 schemaRow
["ColumnOrdinal"] = i
+ 1;
155 schemaRow
["ColumnSize"] = (int) fieldMaxLength
[i
];
156 schemaRow
["NumericPrecision"] = 0;
157 schemaRow
["NumericScale"] = 0;
159 if((cmdBehavior
& CommandBehavior
.KeyInfo
) == CommandBehavior
.KeyInfo
) {
160 // TODO: need to get KeyInfo
161 schemaRow
["IsUnique"] = false;
162 schemaRow
["IsKey"] = false;
165 schemaRow
["IsUnique"] = false;
166 schemaRow
["IsKey"] = DBNull
.Value
;
168 schemaRow
["BaseCatalogName"] = "";
170 schemaRow
["BaseColumnName"] = fieldName
[i
];
171 schemaRow
["BaseSchemaName"] = "";
172 schemaRow
["BaseTableName"] = "";
174 typ
= MySqlHelper
.DbTypeToSystemType (fieldDbType
[i
]);
175 schemaRow
["DataType"] = typ
;
177 schemaRow
["AllowDBNull"] = false;
179 schemaRow
["ProviderType"] = (int) fieldType
[i
];
180 schemaRow
["IsAliased"] = false;
181 schemaRow
["IsExpression"] = false;
182 schemaRow
["IsIdentity"] = false;
183 schemaRow
["IsAutoIncrement"] = false;
184 schemaRow
["IsRowVersion"] = false;
185 schemaRow
["IsHidden"] = false;
186 schemaRow
["IsLong"] = false;
187 schemaRow
["IsReadOnly"] = false;
189 dataTableSchema
.Rows
.Add (schemaRow
);
193 return dataTableSchema
;
196 private void ClearFields () {
202 fieldMaxLength
= null;
207 public bool NextResult () {
210 recordsAffected
= -1;
214 res
= cmd
.NextResult (out resultReturned
);
215 if (resultReturned
== false)
216 return false; // no result returned
218 if ((cmdBehavior
& CommandBehavior
.SingleResult
) == CommandBehavior
.SingleResult
)
219 if (currentQuery
> 0) {
220 if(res
== IntPtr
.Zero
)
221 recordsAffected
= (int) MySql
.AffectedRows(cmd
.Connection
.NativeMySqlInitStruct
);
223 return true; // result returned
226 if((cmdBehavior
& CommandBehavior
.SchemaOnly
) == CommandBehavior
.SchemaOnly
) {
228 return false; // no result returned
231 if(res
== IntPtr
.Zero
) {
232 // no result set returned
233 recordsAffected
= (int) MySql
.AffectedRows (cmd
.Connection
.NativeMySqlInitStruct
);
239 // get meta data about result set
240 numRows
= MySql
.NumRows(res
);
241 numFields
= MySql
.NumFields(res
);
242 // get meta data about each field
243 fieldName
= new string[numFields
];
244 fieldType
= new MySqlEnumFieldTypes
[numFields
];
245 fieldLength
= new uint[numFields
];
246 fieldMaxLength
= new uint[numFields
];
247 fieldFlags
= new uint[numFields
];
249 fieldDbType
= new DbType
[numFields
];
251 // marshal each meta data field
252 // into field* arrays
253 MySqlMarshalledField marshField
= null;
254 for (int i
= 0; i
< numFields
; i
++) {
256 marshField
= (MySqlMarshalledField
) Marshal
.PtrToStructure(MySql
.FetchField(res
),
257 typeof(MySqlMarshalledField
));
259 // copy memebers in marshalField to fields[i]
260 fieldName
[i
] = marshField
.Name
;
261 int myType
= marshField
.FieldType
;
262 fieldType
[i
] = (MySqlEnumFieldTypes
) myType
;
263 fieldLength
[i
] = marshField
.Length
;
264 fieldMaxLength
[i
] = marshField
.MaxLength
;
265 fieldFlags
[i
] = marshField
.Flags
;
267 fieldDbType
[i
] = MySqlHelper
.MySqlTypeToDbType((MySqlEnumFieldTypes
)fieldType
[i
]);
271 return true; // result returned
278 if(currentRow
< numRows
- 1) {
282 if(numFields
> 0 && currentRow
> 0)
283 if((cmdBehavior
& CommandBehavior
.SingleRow
) ==
284 CommandBehavior
.SingleRow
) {
286 currentRow
= numRows
- 1;
290 row
= MySql
.FetchRow (res
);
291 if (row
== IntPtr
.Zero
) {
292 MySql
.FreeResult (res
);
297 dataValue
= new object[numFields
];
298 for (int col
= 0; col
< numFields
; col
++) {
299 GetDataValue (row
, col
);
302 return true; // not EOF
307 void GetDataValue (IntPtr row
, int col
) {
308 // marshal column data value
309 string objValue
= cmd
.GetColumnData(row
, col
);
311 // tranlate from native MySql c type
312 // to a .NET type here
313 dataValue
[col
] = MySqlHelper
.ConvertDbTypeToSystem (fieldType
[col
],
314 fieldDbType
[col
], objValue
);
316 // TODO: for CommandBehavior.SequentialAccess -
317 // used for reading Large OBjects
318 //if((cmdBehavior & CommandBehavior.SequentialAccess) ==
319 // CommandBehavior.SequentialAccess) {
324 public byte GetByte (int i
) {
325 throw new NotImplementedException ();
328 // TODO: CommandBehavior.SequentialAccess
331 public long GetBytes (int i
, long fieldOffset
,
332 byte[] buffer
, int bufferOffset
,
335 throw new NotImplementedException ();
339 public char GetChar (int i
) {
340 throw new NotImplementedException ();
343 // TODO: CommandBehavior.SequentialAccess
346 public long GetChars (int i
, long fieldOffset
,
347 char[] buffer
, int bufferOffset
,
350 throw new NotImplementedException ();
354 public IDataReader
GetData (int i
) {
355 throw new NotImplementedException ();
358 public string GetDataTypeName (int i
) {
359 return MySqlHelper
.GetMySqlTypeName (fieldType
[i
]);
362 public DateTime
GetDateTime(int i
) {
363 return (DateTime
) dataValue
[i
];
366 public decimal GetDecimal(int i
) {
367 return (decimal) dataValue
[i
];
370 public double GetDouble(int i
) {
371 return (double) dataValue
[i
];
374 public Type
GetFieldType(int i
) {
375 return MySqlHelper
.DbTypeToSystemType (fieldDbType
[i
]);
378 public float GetFloat(int i
) {
379 return (float) dataValue
[i
];
382 public Guid
GetGuid(int i
) {
383 throw new NotImplementedException ();
386 public short GetInt16(int i
) {
387 return (short) dataValue
[i
];
390 public int GetInt32(int i
) {
391 return (int) dataValue
[i
];
394 public long GetInt64(int i
) {
395 return (long) dataValue
[i
];
398 public string GetName(int i
) {
402 public int GetOrdinal (string name
) {
406 for(i
= 0; i
< numFields
; i
++) {
407 if(fieldName
[i
].Equals (name
))
411 for(i
= 0; i
< numFields
; i
++) {
415 ta
= fieldName
[i
].ToUpper ();
423 throw new MissingFieldException ("Missing field: " + name
);
426 public string GetString (int i
) {
427 return (string) dataValue
[i
];
430 public object GetValue (int i
) {
434 public int GetValues(object[] values
)
436 Array
.Copy (dataValue
, values
, dataValue
.Length
);
437 return dataValue
.Length
;
440 public bool IsDBNull(int i
) {
441 if(dataValue
[i
] == DBNull
.Value
)
446 public bool GetBoolean(int i
) {
447 return (bool) dataValue
[i
];
450 IEnumerator IEnumerable
.GetEnumerator () {
451 return new DbEnumerator (this);
454 #endregion // Public Methods
458 private void Dispose(bool disposing
) {
461 // release any managed resources
467 fieldMaxLength
= null;
471 // release any unmanaged resources
473 // clear unmanaged MySQL result set
475 if(res
!= IntPtr
.Zero
) {
476 MySql
.FreeResult(res
);
481 this.disposed
= true;
485 void IDisposable
.Dispose() {
494 #endregion // Destructors
500 return 0; // always return zero, unless
501 // this provider will allow
506 public bool IsClosed
{
515 public int RecordsAffected
{
517 return recordsAffected
;
521 public int FieldCount
{
527 public object this[string name
] {
531 for(i
= 0; i
< numFields
; i
++) {
532 if(fieldName
[i
].Equals(name
))
536 for(i
= 0; i
< numFields
; i
++) {
540 ta
= fieldName
[i
].ToUpper();
548 throw new MissingFieldException("Missing field: " + name
);
552 public object this[int i
] {
558 #endregion // Properties