2002-11-01 Tim Coleman (tim@timcoleman.com) :
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlDataReader.cs
1 //
2 // System.Data.SqlClient.SqlDataReader.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //
9 // (C) Ximian, Inc 2002
10 // (C) Daniel Morgan 2002
11 // Copyright (C) Tim Coleman, 2002
12 //
13
14 using Mono.Data.TdsClient.Internal;
15 using System;
16 using System.Collections;
17 using System.ComponentModel;
18 using System.Data;
19 using System.Data.Common;
20
21 namespace System.Data.SqlClient {
22         public sealed class SqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
23         {
24                 #region Fields
25
26                 int fieldCount;
27                 bool hasRows;
28                 bool isClosed;
29                 int recordsAffected;
30                 bool moreResults;
31
32                 int resultsRead;
33                 int rowsRead;
34
35                 SqlCommand command;
36                 DataTable schemaTable;
37
38                 ArrayList dataTypeNames;
39                 ArrayList dataTypes;
40
41                 #endregion // Fields
42
43                 #region Constructors
44
45                 internal SqlDataReader (SqlCommand command)
46                 {
47                         schemaTable = ConstructSchemaTable ();
48                         this.resultsRead = 0;
49                         this.command = command;
50                         this.fieldCount = 0;
51                         this.isClosed = false;
52
53                         NextResult ();
54                 }
55
56                 #endregion
57
58                 #region Properties
59
60                 public int Depth {
61                         get { return 0; }
62                 }
63
64                 public int FieldCount {
65                         get { return fieldCount; }
66                 }
67
68                 public bool HasRows {
69                         get { return hasRows; }
70                 }
71
72                 public bool IsClosed {
73                         get { return isClosed; }
74                 }
75
76                 public object this [int i] {
77                         get { return GetValue (i); }
78                 }
79
80                 public object this [string name] {
81                         get { return GetValue (GetOrdinal (name)); }
82                 }
83                 
84                 public int RecordsAffected {
85                         get { return recordsAffected; }
86                 }
87
88                 #endregion // Properties
89
90                 #region Methods
91
92                 public void Close()
93                 {
94                         isClosed = true;
95                         command.CloseDataReader (moreResults);
96                 }
97
98                 private static DataTable ConstructSchemaTable ()
99                 {
100                         Type booleanType = Type.GetType ("System.Boolean");
101                         Type stringType = Type.GetType ("System.String");
102                         Type intType = Type.GetType ("System.Int32");
103                         Type typeType = Type.GetType ("System.Type");
104                         Type shortType = Type.GetType ("System.Int16");
105
106                         DataTable schemaTable = new DataTable ("SchemaTable");
107                         schemaTable.Columns.Add ("ColumnName", stringType);
108                         schemaTable.Columns.Add ("ColumnOrdinal", intType);
109                         schemaTable.Columns.Add ("ColumnSize", intType);
110                         schemaTable.Columns.Add ("NumericPrecision", shortType);
111                         schemaTable.Columns.Add ("NumericScale", shortType);
112                         schemaTable.Columns.Add ("IsUnique", booleanType);
113                         schemaTable.Columns.Add ("IsKey", booleanType);
114                         schemaTable.Columns.Add ("BaseServerName", stringType);
115                         schemaTable.Columns.Add ("BaseCatalogName", stringType);
116                         schemaTable.Columns.Add ("BaseColumnName", stringType);
117                         schemaTable.Columns.Add ("BaseSchemaName", stringType);
118                         schemaTable.Columns.Add ("BaseTableName", stringType);
119                         schemaTable.Columns.Add ("DataType", typeType);
120                         schemaTable.Columns.Add ("AllowDBNull", booleanType);
121                         schemaTable.Columns.Add ("ProviderType", intType);
122                         schemaTable.Columns.Add ("IsAliased", booleanType);
123                         schemaTable.Columns.Add ("IsExpression", booleanType);
124                         schemaTable.Columns.Add ("IsIdentity", booleanType);
125                         schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
126                         schemaTable.Columns.Add ("IsRowVersion", booleanType);
127                         schemaTable.Columns.Add ("IsHidden", booleanType);
128                         schemaTable.Columns.Add ("IsLong", booleanType);
129                         schemaTable.Columns.Add ("IsReadOnly", booleanType);
130
131                         return schemaTable;
132                 }
133
134                 public bool GetBoolean (int i)
135                 {
136                         object value = GetValue (i);
137                         if (!(value is bool))
138                                 throw new InvalidCastException ();
139                         return (bool) value;
140                 }
141
142                 public byte GetByte (int i)
143                 {
144                         object value = GetValue (i);
145                         if (!(value is byte))
146                                 throw new InvalidCastException ();
147                         return (byte) value;
148                 }
149
150                 [MonoTODO]
151                 public long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
152                 {
153                         object value = GetValue (i);
154                         if (!(value is byte []))
155                                 throw new InvalidCastException ();
156                         Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
157                         return ((byte []) value).Length - dataIndex;
158                 }
159
160                 [MonoTODO]
161                 public char GetChar (int i)
162                 {
163                         throw new NotImplementedException ();
164                 }
165
166                 [MonoTODO]
167                 public long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
168                 {
169                         object value = GetValue (i);
170                         if (!(value is char []))
171                                 throw new InvalidCastException ();
172                         Array.Copy ((char []) value, (int) dataIndex, buffer, bufferIndex, length);
173                         return ((char []) value).Length - dataIndex;
174                 }
175
176                 [MonoTODO]
177                 public IDataReader GetData (int i)
178                 {
179                         throw new NotImplementedException ();
180                 }
181
182                 public string GetDataTypeName (int i)
183                 {
184                         return (string) dataTypeNames [i];
185                 }
186
187                 public DateTime GetDateTime (int i)
188                 {
189                         object value = GetValue (i);
190                         if (!(value is DateTime))
191                                 throw new InvalidCastException ();
192                         return (DateTime) value;
193                 }
194
195                 public decimal GetDecimal (int i)
196                 {
197                         object value = GetValue (i);
198                         if (!(value is decimal))
199                                 throw new InvalidCastException ();
200                         return (decimal) value;
201                 }
202                 public double GetDouble (int i)
203                 {
204                         object value = GetValue (i);
205                         if (!(value is double))
206                                 throw new InvalidCastException ();
207                         return (double) value;
208                 }
209
210                 public Type GetFieldType (int i)
211                 {
212                         return (Type) schemaTable.Rows[i]["DataType"];
213                 }
214
215                 public float GetFloat (int i)
216                 {
217                         object value = GetValue (i);
218                         if (!(value is float))
219                                 throw new InvalidCastException ();
220                         return (float) value;
221                 }
222
223                 [MonoTODO]
224                 public Guid GetGuid (int i)
225                 {
226                         throw new NotImplementedException ();
227                 }
228
229                 public short GetInt16 (int i)
230                 {
231                         object value = GetValue (i);
232                         if (!(value is short))
233                                 throw new InvalidCastException ();
234                         return (short) value;
235                 }
236
237                 public int GetInt32 (int i)
238                 {
239                         object value = GetValue (i);
240                         if (!(value is int))
241                                 throw new InvalidCastException ();
242                         return (int) value;
243                 }
244
245                 public long GetInt64 (int i)
246                 {
247                         object value = GetValue (i);
248                         if (!(value is long))
249                                 throw new InvalidCastException ();
250                         return (long) value;
251                 }
252
253                 public string GetName (int i)
254                 {
255                         return (string) schemaTable.Rows[i]["ColumnName"];
256                 }
257
258                 [MonoTODO]
259                 public int GetOrdinal (string name)
260                 {
261                         foreach (DataRow schemaRow in schemaTable.Rows)
262                                 if (((string) schemaRow ["ColumnName"]).Equals (name))
263                                         return (int) schemaRow ["ColumnOrdinal"];
264                         foreach (DataRow schemaRow in schemaTable.Rows)
265                                 if (String.Compare (((string) schemaRow ["ColumnName"]), name, true) == 0)
266                                         return (int) schemaRow ["ColumnOrdinal"];
267                         throw new IndexOutOfRangeException ();
268                 }
269
270                 public DataTable GetSchemaTable ()
271                 {
272                         if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
273                                 return schemaTable;
274
275                         if (!moreResults)
276                                 return null;
277
278                         fieldCount = 0;
279
280                         dataTypeNames = new ArrayList ();
281                         dataTypes = new ArrayList ();
282
283                         foreach (TdsSchemaInfo schema in command.Tds.Schema) {
284                                 DataRow row = schemaTable.NewRow ();
285
286
287                                 switch (schema.ColumnType) {
288                                         case TdsColumnType.Image :
289                                                 dataTypeNames.Add ("image");
290                                                 row ["ProviderType"] = (int) SqlDbType.Image;
291                                                 row ["DataType"] = typeof (byte[]);
292                                                 break;
293                                         case TdsColumnType.Text :
294                                                 dataTypes.Add (typeof (string));
295                                                 dataTypeNames.Add ("text");
296                                                 row ["ProviderType"] = (int) SqlDbType.Text;
297                                                 row ["DataType"] = typeof (string);
298                                                 break;
299                                         case TdsColumnType.UniqueIdentifier :
300                                                 dataTypeNames.Add ("uniqueidentifier");
301                                                 row ["ProviderType"] = (int) SqlDbType.UniqueIdentifier;
302                                                 row ["DataType"] = typeof (Guid);
303                                                 break;
304                                         case TdsColumnType.VarBinary :
305                                         case TdsColumnType.BigVarBinary :
306                                                 dataTypeNames.Add ("varbinary");
307                                                 row ["ProviderType"] = (int) SqlDbType.VarBinary;
308                                                 row ["DataType"] = typeof (byte[]);
309                                                 break;
310                                         case TdsColumnType.IntN :
311                                         case TdsColumnType.Int4 :
312                                                 dataTypeNames.Add ("int");
313                                                 row ["ProviderType"] = (int) SqlDbType.Int;
314                                                 row ["DataType"] = typeof (int);
315                                                 break;
316                                         case TdsColumnType.VarChar :
317                                         case TdsColumnType.BigVarChar :
318                                                 dataTypeNames.Add ("varchar");
319                                                 row ["ProviderType"] = (int) SqlDbType.VarChar;
320                                                 row ["DataType"] = typeof (string);
321                                                 break;
322                                         case TdsColumnType.Binary :
323                                         case TdsColumnType.BigBinary :
324                                                 dataTypeNames.Add ("binary");
325                                                 row ["ProviderType"] = (int) SqlDbType.Binary;
326                                                 row ["DataType"] = typeof (byte[]);
327                                                 break;
328                                         case TdsColumnType.Char :
329                                         case TdsColumnType.BigChar :
330                                                 dataTypeNames.Add ("char");
331                                                 row ["ProviderType"] = (int) SqlDbType.Char;
332                                                 row ["DataType"] = typeof (string);
333                                                 break;
334                                         case TdsColumnType.Int1 :
335                                                 dataTypeNames.Add ("tinyint");
336                                                 row ["ProviderType"] = (int) SqlDbType.TinyInt;
337                                                 row ["DataType"] = typeof (byte);
338                                                 break;
339                                         case TdsColumnType.Bit :
340                                         case TdsColumnType.BitN :
341                                                 dataTypeNames.Add ("bit");
342                                                 row ["ProviderType"] = (int) SqlDbType.Bit;
343                                                 row ["DataType"] = typeof (bool);
344                                                 break;
345                                         case TdsColumnType.Int2 :
346                                                 dataTypeNames.Add ("smallint");
347                                                 row ["ProviderType"] = (int) SqlDbType.SmallInt;
348                                                 row ["DataType"] = typeof (short);
349                                                 break;
350                                         case TdsColumnType.DateTime4 :
351                                         case TdsColumnType.DateTime :
352                                         case TdsColumnType.DateTimeN :
353                                                 dataTypeNames.Add ("datetime");
354                                                 row ["ProviderType"] = (int) SqlDbType.DateTime;
355                                                 row ["DataType"] = typeof (DateTime);
356                                                 break;
357                                         case TdsColumnType.Real :
358                                                 dataTypeNames.Add ("real");
359                                                 row ["ProviderType"] = (int) SqlDbType.Real;
360                                                 row ["DataType"] = typeof (float);
361                                                 break;
362                                         case TdsColumnType.Money :
363                                         case TdsColumnType.MoneyN :
364                                         case TdsColumnType.Money4 :
365                                                 dataTypeNames.Add ("money");
366                                                 row ["ProviderType"] = (int) SqlDbType.Money;
367                                                 row ["DataType"] = typeof (decimal);
368                                                 break;
369                                         case TdsColumnType.Float8 :
370                                         case TdsColumnType.FloatN :
371                                                 dataTypeNames.Add ("float");
372                                                 row ["ProviderType"] = (int) SqlDbType.Float;
373                                                 row ["DataType"] = typeof (double);
374                                                 break;
375                                         case TdsColumnType.NText :
376                                                 dataTypeNames.Add ("ntext");
377                                                 row ["ProviderType"] = (int) SqlDbType.NText;
378                                                 row ["DataType"] = typeof (string);
379                                                 break;
380                                         case TdsColumnType.NVarChar :
381                                                 dataTypeNames.Add ("nvarchar");
382                                                 row ["ProviderType"] = (int) SqlDbType.NVarChar;
383                                                 row ["DataType"] = typeof (string);
384                                                 break;
385                                         case TdsColumnType.Decimal :
386                                         case TdsColumnType.Numeric :
387                                                 dataTypeNames.Add ("decimal");
388                                                 row ["ProviderType"] = (int) SqlDbType.Decimal;
389                                                 row ["DataType"] = typeof (decimal);
390                                                 break;
391                                         case TdsColumnType.NChar :
392                                                 dataTypeNames.Add ("nchar");
393                                                 row ["ProviderType"] = (int) SqlDbType.Char;
394                                                 row ["DataType"] = typeof (string);
395                                                 break;
396                                         case TdsColumnType.SmallMoney :
397                                                 dataTypeNames.Add ("smallmoney");
398                                                 row ["ProviderType"] = (int) SqlDbType.SmallMoney;
399                                                 row ["DataType"] = typeof (decimal);
400                                                 break;
401                                         default :
402                                                 dataTypeNames.Add ("variant");
403                                                 row ["ProviderType"] = (int) SqlDbType.Variant;
404                                                 row ["DataType"] = typeof (object);
405                                                 break;
406                                 }
407
408                                 row ["ColumnOrdinal"] = schema.ColumnOrdinal;
409                                 row ["ColumnSize"] = schema.ColumnSize;
410                                 row ["AllowDBNull"] = schema.AllowDBNull;
411                                 row ["IsReadOnly"] = schema.IsReadOnly;
412                                 row ["IsIdentity"] = schema.IsIdentity;
413                                 row ["IsKey"] = schema.IsKey;
414
415                                 // FIXME: Base Column Name and Column Name are not necessarily the same
416                                 if (schema.ColumnName == null)
417                                         row ["BaseColumnName"] = DBNull.Value;
418                                 else
419                                         row ["BaseColumnName"] = schema.ColumnName;
420
421                                 if (schema.ColumnName == null)
422                                         row ["ColumnName"] = DBNull.Value;
423                                 else
424                                         row ["ColumnName"] = schema.ColumnName;
425
426                                 if (schema.TableName == null)
427                                         row ["BaseTableName"] = DBNull.Value;
428                                 else
429                                         row ["BaseTableName"] = schema.TableName;
430
431                                 if (schema.NumericScale == 0)
432                                         row ["NumericPrecision"] = DBNull.Value;
433                                 else
434                                         row ["NumericPrecision"] = schema.NumericPrecision;
435
436                                 if (schema.NumericScale == 0)
437                                         row ["NumericScale"] = DBNull.Value;
438                                 else
439                                         row ["NumericScale"] = schema.NumericScale;
440
441                                 schemaTable.Rows.Add (row);
442
443                                 fieldCount += 1;
444                         }
445                         return schemaTable;
446                 }               
447
448                 public string GetString (int i)
449                 {
450                         object value = GetValue (i);
451                         if (!(value is string))
452                                 throw new InvalidCastException ();
453                         return (string) value;
454                 }
455
456                 public object GetValue (int i)
457                 {
458                         return command.Tds.ColumnValues[i];
459                 }
460
461                 public int GetValues (object[] values)
462                 {
463                         int len = values.Length;
464                         command.Tds.ColumnValues.CopyTo (0, values, 0, len);
465                         return (len > FieldCount ? len : FieldCount);
466                 }
467
468                 [MonoTODO]
469                 void IDisposable.Dispose ()
470                 {
471                         throw new NotImplementedException ();
472                 }
473
474                 IEnumerator IEnumerable.GetEnumerator ()
475                 {
476                         return new DbEnumerator (this);
477                 }
478
479                 public bool IsDBNull (int i)
480                 {
481                         return GetValue (i) == null;
482                 }
483
484                 public bool NextResult ()
485                 {
486                         if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
487                                 return false;
488                         if (command.CommandType == CommandType.StoredProcedure && command.Tds.DoneProc)
489                                 return false;
490
491                         schemaTable.Rows.Clear ();
492
493                         moreResults = command.Tds.NextResult ();
494                         command.Connection.CheckForErrors ();
495                         rowsRead = 0;
496                         resultsRead += 1;
497                         return moreResults;
498                 }
499
500                 public bool Read ()
501                 {
502                         if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && rowsRead > 0)
503                                 return false;
504                         if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
505                                 return false;
506                         if (!moreResults)
507                                 return false;
508
509                         bool result = command.Tds.NextRow ();
510                         command.Connection.CheckForErrors ();
511
512                         rowsRead += 1;
513
514                         return result;
515                 }
516
517                 #endregion // Methods
518         }
519 }