[bcl] Remove ONLY_1_1 defines from class libs
[mono-project.git] / mcs / class / System.Data / System.Data.Odbc / OdbcDataReader.cs
blob301d082677aacbd09a6c05ef1905d0105805bfb4
1 //
2 // System.Data.Odbc.OdbcDataReader
3 //
4 // Author:
5 // Brian Ritchie (brianlritchie@hotmail.com)
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Sureshkumar T <tsureshkumar@novell.com> (2004)
8 //
9 // Copyright (C) Brian Ritchie, 2002
10 // Copyright (C) Daniel Morgan, 2002
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.ComponentModel;
38 using System.Data;
39 using System.Data.Common;
40 using System.Globalization;
41 using System.Text;
43 namespace System.Data.Odbc
45 public sealed class OdbcDataReader : DbDataReader
47 #region Fields
49 private OdbcCommand command;
50 private bool open;
51 private int currentRow;
52 private OdbcColumn[] cols;
53 private IntPtr hstmt;
54 private int _recordsAffected = -1;
55 bool disposed;
56 private DataTable _dataTableSchema;
57 private CommandBehavior behavior;
59 #endregion
61 #region Constructors
63 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
65 this.command = command;
66 this.CommandBehavior = behavior;
67 open = true;
68 currentRow = -1;
69 hstmt = command.hStmt;
70 // Init columns array;
71 short colcount = 0;
72 libodbc.SQLNumResultCols (hstmt, ref colcount);
73 cols = new OdbcColumn [colcount];
74 GetColumns ();
77 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
78 int recordAffected) : this (command, behavior)
80 _recordsAffected = recordAffected;
84 #endregion
86 #region Properties
88 private CommandBehavior CommandBehavior {
89 get { return behavior; }
90 set { behavior = value; }
93 public
94 override
95 int Depth {
96 get {
97 return 0; // no nested selects supported
101 public
102 override
103 int FieldCount {
104 get {
105 if (IsClosed)
106 throw new InvalidOperationException ("The reader is closed.");
107 return cols.Length;
111 public
112 override
113 bool IsClosed {
114 get {
115 return !open;
119 public
120 override
121 object this [string value] {
122 get {
123 int pos = GetOrdinal (value);
124 return this [pos];
128 public
129 override
130 object this [int i] {
131 get {
132 return GetValue (i);
136 public
137 override
138 int RecordsAffected {
139 get {
140 return _recordsAffected;
144 [MonoTODO]
145 public
146 override
147 bool HasRows {
148 get { throw new NotImplementedException(); }
151 private OdbcConnection Connection {
152 get {
153 if (command != null)
154 return command.Connection;
155 return null;
159 #endregion
161 #region Methods
163 private int ColIndex (string colname)
165 int i = 0;
166 foreach (OdbcColumn col in cols) {
167 if (col != null) {
168 if (col.ColumnName == colname)
169 return i;
170 if (String.Compare (col.ColumnName, colname, true) == 0)
171 return i;
173 i++;
175 return -1;
178 // Dynamically load column descriptions as needed.
179 private OdbcColumn GetColumn (int ordinal)
181 if (cols [ordinal] == null) {
182 short bufsize = 255;
183 byte [] colname_buffer = new byte [bufsize];
184 string colname;
185 short colname_size = 0;
186 uint ColSize = 0;
187 short DecDigits = 0, Nullable = 0, dt = 0;
188 OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1),
189 colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize,
190 ref DecDigits, ref Nullable);
191 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
192 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
193 colname = RemoveTrailingNullChar (Encoding.Unicode.GetString (colname_buffer));
194 OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
195 c.AllowDBNull = (Nullable != 0);
196 c.Digits = DecDigits;
197 if (c.IsVariableSizeType)
198 c.MaxLength = (int) ColSize;
199 cols [ordinal] = c;
201 return cols [ordinal];
204 // Load all column descriptions
205 private void GetColumns ()
207 for(int i = 0; i < cols.Length; i++)
208 GetColumn (i);
211 public
212 override
213 void Close ()
215 // FIXME : have to implement output parameter binding
216 open = false;
217 currentRow = -1;
219 this.command.FreeIfNotPrepared ();
221 if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
222 this.command.Connection.Close ();
226 public
227 override
228 bool GetBoolean (int i)
230 return (bool) GetValue (i);
233 public
234 override
235 byte GetByte (int i)
237 return Convert.ToByte (GetValue (i));
240 public
241 override
242 long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
244 if (IsClosed)
245 throw new InvalidOperationException ("Reader is not open.");
246 if (currentRow == -1)
247 throw new InvalidOperationException ("No data available.");
249 OdbcReturn ret = OdbcReturn.Error;
250 bool copyBuffer = false;
251 int returnVal = 0, outsize = 0;
252 byte [] tbuff = new byte [length+1];
254 if (buffer == null)
255 length = 0;
256 ret=libodbc.SQLGetData (hstmt, (ushort) (i + 1), SQL_C_TYPE.BINARY, tbuff, length,
257 ref outsize);
259 if (ret == OdbcReturn.NoData)
260 return 0;
262 if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
263 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
265 OdbcException odbcException = null;
266 if ( (ret == OdbcReturn.SuccessWithInfo))
267 odbcException = Connection.CreateOdbcException (
268 OdbcHandleType.Stmt, hstmt);
270 if (buffer == null)
271 return outsize; //if buffer is null,return length of the field
273 if (ret == OdbcReturn.SuccessWithInfo) {
274 if (outsize == (int) OdbcLengthIndicator.NoTotal)
275 copyBuffer = true;
276 else if (outsize == (int) OdbcLengthIndicator.NullData) {
277 copyBuffer = false;
278 returnVal = -1;
279 } else {
280 string sqlstate = odbcException.Errors [0].SQLState;
281 //SQLState: String Data, Right truncated
282 if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC)
283 throw odbcException;
284 copyBuffer = true;
286 } else {
287 copyBuffer = outsize == -1 ? false : true;
288 returnVal = outsize;
291 if (copyBuffer) {
292 if (outsize == (int) OdbcLengthIndicator.NoTotal) {
293 int j = 0;
294 while (tbuff [j] != libodbc.C_NULL) {
295 buffer [bufferIndex + j] = tbuff [j];
296 j++;
298 returnVal = j;
299 } else {
300 int read_bytes = Math.Min (outsize, length);
301 for (int j = 0; j < read_bytes; j++)
302 buffer [bufferIndex + j] = tbuff [j];
303 returnVal = read_bytes;
306 return returnVal;
309 [MonoTODO]
310 public
311 override
312 char GetChar (int i)
314 throw new NotImplementedException ();
317 [MonoTODO]
318 public
319 override
320 long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
322 if (IsClosed)
323 throw new InvalidOperationException ("The reader is closed.");
324 if (currentRow == -1)
325 throw new InvalidOperationException ("No data available.");
326 if (i < 0 || i >= FieldCount)
327 throw new IndexOutOfRangeException ();
328 throw new NotImplementedException ();
331 [MonoTODO]
332 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
334 IDataReader GetData (int i)
336 throw new NotImplementedException ();
339 public
340 override
341 string GetDataTypeName (int i)
343 if (IsClosed)
344 throw new InvalidOperationException ("The reader is closed.");
345 if (i < 0 || i >= FieldCount)
346 throw new IndexOutOfRangeException ();
347 return GetColumnAttributeStr (i + 1, FieldIdentifier.TypeName);
350 public DateTime GetDate (int i)
352 return GetDateTime (i);
355 public
356 override
357 DateTime GetDateTime (int i)
359 return (DateTime) GetValue (i);
362 public
363 override
364 decimal GetDecimal (int i)
366 return (decimal) GetValue (i);
369 public
370 override
371 double GetDouble (int i)
373 return (double) GetValue (i);
376 public
377 override
378 Type GetFieldType (int i)
380 if (IsClosed)
381 throw new InvalidOperationException ("The reader is closed.");
382 return GetColumn (i).DataType;
385 public
386 override
387 float GetFloat (int i)
389 return (float) GetValue (i);
392 [MonoTODO]
393 public
394 override
395 Guid GetGuid (int i)
397 throw new NotImplementedException ();
400 public
401 override
402 short GetInt16 (int i)
404 return (short) GetValue (i);
407 public
408 override
409 int GetInt32 (int i)
411 return (int) GetValue (i);
414 public
415 override
416 long GetInt64 (int i)
418 return (long) GetValue (i);
421 public
422 override
423 string GetName (int i)
425 if (IsClosed)
426 throw new InvalidOperationException ("The reader is closed.");
427 return GetColumn (i).ColumnName;
430 public
431 override
432 int GetOrdinal (string value)
434 if (IsClosed)
435 throw new InvalidOperationException ("The reader is closed.");
436 if (value == null)
437 throw new ArgumentNullException ("fieldName");
439 int i = ColIndex (value);
440 if (i == -1)
441 throw new IndexOutOfRangeException ();
442 return i;
445 [MonoTODO]
446 public
447 override
448 DataTable GetSchemaTable ()
450 if (IsClosed)
451 throw new InvalidOperationException ("The reader is closed.");
453 // FIXME :
454 // * Map OdbcType to System.Type and assign to DataType.
455 // This will eliminate the need for IsStringType in
456 // OdbcColumn
458 if (_dataTableSchema != null)
459 return _dataTableSchema;
461 DataTable dataTableSchema = null;
462 // Only Results from SQL SELECT Queries
463 // get a DataTable for schema of the result
464 // otherwise, DataTable is null reference
465 if (cols.Length > 0) {
466 dataTableSchema = new DataTable ();
468 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
469 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
470 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
471 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
472 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
473 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
474 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
475 DataColumn dc = dataTableSchema.Columns["IsKey"];
476 dc.AllowDBNull = true; // IsKey can have a DBNull
477 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
478 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
479 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
480 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
481 dataTableSchema.Columns.Add ("DataType", typeof(Type));
482 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
483 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
484 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
485 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
486 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
487 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
488 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
489 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
490 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
491 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
493 DataRow schemaRow;
495 for (int i = 0; i < cols.Length; i += 1 ) {
496 OdbcColumn col=GetColumn(i);
498 schemaRow = dataTableSchema.NewRow ();
499 dataTableSchema.Rows.Add (schemaRow);
501 schemaRow ["ColumnName"] = col.ColumnName;
502 schemaRow ["ColumnOrdinal"] = i;
503 schemaRow ["ColumnSize"] = col.MaxLength;
504 schemaRow ["NumericPrecision"] = GetColumnAttribute (i+1, FieldIdentifier.Precision);
505 schemaRow ["NumericScale"] = GetColumnAttribute (i+1, FieldIdentifier.Scale);
506 schemaRow ["BaseTableName"] = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
507 schemaRow ["BaseSchemaName"] = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
508 schemaRow ["BaseCatalogName"] = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
509 schemaRow ["BaseColumnName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
510 schemaRow ["DataType"] = col.DataType;
511 schemaRow ["IsUnique"] = false;
512 schemaRow ["IsKey"] = DBNull.Value;
513 schemaRow ["AllowDBNull"] = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
514 schemaRow ["ProviderType"] = (int) col.OdbcType;
515 schemaRow ["IsAutoIncrement"] = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
516 schemaRow ["IsExpression"] = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
517 schemaRow ["IsAliased"] = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
518 schemaRow ["IsReadOnly"] = ((bool) schemaRow ["IsExpression"]
519 || GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
521 // FIXME: all of these
522 schemaRow ["IsIdentity"] = false;
523 schemaRow ["IsRowVersion"] = false;
524 schemaRow ["IsHidden"] = false;
525 schemaRow ["IsLong"] = false;
527 // FIXME: according to Brian,
528 // this does not work on MS .NET
529 // however, we need it for Mono
530 // for now
531 // schemaRow.AcceptChanges();
534 // set primary keys
535 DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
536 "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
538 string lastTableName = String.Empty,
539 lastSchemaName = String.Empty,
540 lastCatalogName = String.Empty;
541 string [] keys = null; // assumed to be sorted.
542 foreach (DataRow row in rows) {
543 string tableName = (string) row ["BaseTableName"];
544 string schemaName = (string) row ["BaseSchemaName"];
545 string catalogName = (string) row ["BaseCatalogName"];
547 if (tableName != lastTableName || schemaName != lastSchemaName
548 || catalogName != lastCatalogName)
549 keys = GetPrimaryKeys (catalogName, schemaName, tableName);
551 if (keys != null &&
552 Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
553 row ["IsKey"] = true;
554 row ["IsUnique"] = true;
555 row ["AllowDBNull"] = false;
556 GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
558 lastTableName = tableName;
559 lastSchemaName = schemaName;
560 lastCatalogName = catalogName;
562 dataTableSchema.AcceptChanges ();
564 return (_dataTableSchema = dataTableSchema);
567 public
568 override
569 string GetString (int i)
571 object ret = GetValue (i);
573 if (ret != null && ret.GetType () != typeof (string))
574 return Convert.ToString (ret);
575 else
576 return (string) GetValue (i);
579 [MonoTODO]
580 public TimeSpan GetTime (int i)
582 throw new NotImplementedException ();
585 public
586 override
587 object GetValue (int i)
589 if (IsClosed)
590 throw new InvalidOperationException ("The reader is closed.");
591 if (currentRow == -1)
592 throw new InvalidOperationException ("No data available.");
593 if (i > cols.Length-1 || i < 0)
594 throw new IndexOutOfRangeException ();
596 OdbcReturn ret;
597 int outsize = 0, bufsize;
598 byte[] buffer;
599 OdbcColumn col = GetColumn (i);
600 object DataValue = null;
601 ushort ColIndex = Convert.ToUInt16 (i + 1);
603 // Check cached values
604 if (col.Value == null) {
605 // odbc help file
606 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
607 switch (col.OdbcType) {
608 case OdbcType.Bit:
609 short bit_data = 0;
610 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
611 if (outsize != (int) OdbcLengthIndicator.NullData)
612 DataValue = bit_data == 0 ? "False" : "True";
613 break;
614 case OdbcType.Numeric:
615 case OdbcType.Decimal:
616 bufsize = 50;
617 buffer = new byte [bufsize]; // According to sqlext.h, use SQL_CHAR for decimal.
618 // FIXME : use Numeric.
619 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
620 if (outsize!=-1) {
621 byte [] temp = new byte [outsize];
622 for (int j = 0; j < outsize; j++)
623 temp [j] = buffer [j];
624 DataValue = Decimal.Parse (Encoding.Default.GetString (temp),
625 CultureInfo.InvariantCulture);
627 break;
628 case OdbcType.TinyInt:
629 short short_data = 0;
630 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
631 DataValue = Convert.ToByte (short_data);
632 break;
633 case OdbcType.Int:
634 int int_data = 0;
635 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
636 DataValue = int_data;
637 break;
639 case OdbcType.SmallInt:
640 short sint_data = 0;
641 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
642 DataValue = sint_data;
643 break;
645 case OdbcType.BigInt:
646 long long_data = 0;
647 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
648 DataValue = long_data;
649 break;
650 case OdbcType.NChar:
651 bufsize = 255;
652 buffer = new byte [bufsize];
653 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.WCHAR, buffer, bufsize, ref outsize);
654 if (outsize != (int) OdbcLengthIndicator.NullData)
655 if (!(ret == OdbcReturn.SuccessWithInfo
656 && outsize == (int) OdbcLengthIndicator.NoTotal))
657 DataValue = Encoding.Unicode.GetString (buffer, 0, outsize);
658 break;
659 case OdbcType.NText:
660 case OdbcType.NVarChar:
661 bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
662 buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
663 StringBuilder sb = new StringBuilder ();
664 char[] charBuffer = new char[bufsize];
665 Decoder unicodeDecoder = Encoding.Unicode.GetDecoder ();
666 do {
667 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
668 if (ret == OdbcReturn.Error)
669 break;
670 // Fix for strance ODBC drivers (like psqlODBC)
671 if (ret == OdbcReturn.Success && outsize==-1)
672 ret = OdbcReturn.NoData;
674 if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
675 if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
676 outsize = bufsize;
677 int charCount = unicodeDecoder.GetChars (buffer, 0, outsize, charBuffer, 0);
678 string strValue = new String (charBuffer, 0, charCount);
679 sb.Append (RemoveTrailingNullChar (strValue));
681 } while (ret != OdbcReturn.NoData);
682 DataValue = sb.ToString ();
683 charBuffer = null;
684 break;
685 case OdbcType.Text:
686 case OdbcType.VarChar:
687 bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
688 buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
689 StringBuilder sb1 = new StringBuilder ();
690 charBuffer = new char[bufsize];
691 Decoder defaultDecoder = Encoding.Default.GetDecoder();
692 do {
693 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
694 if (ret == OdbcReturn.Error)
695 break;
696 // Fix for strance ODBC drivers (like psqlODBC)
697 if (ret == OdbcReturn.Success && outsize==-1)
698 ret = OdbcReturn.NoData;
699 if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
700 if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
701 outsize = bufsize - 1;
702 int charCount = defaultDecoder.GetChars(buffer, 0, outsize, charBuffer, 0);
703 sb1.Append(charBuffer, 0, charCount);
705 } while (ret != OdbcReturn.NoData);
706 DataValue = sb1.ToString ();
707 break;
708 case OdbcType.Real:
709 float float_data = 0;
710 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
711 DataValue = float_data;
712 break;
713 case OdbcType.Double:
714 double double_data = 0;
715 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
716 DataValue = double_data;
717 break;
718 case OdbcType.Timestamp:
719 case OdbcType.DateTime:
720 case OdbcType.Date:
721 case OdbcType.Time:
722 OdbcTimestamp ts_data = new OdbcTimestamp();
723 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
724 if (outsize != -1) {// This means SQL_NULL_DATA
725 if (col.OdbcType == OdbcType.Time) {
726 // libodbc returns value in first three fields for OdbcType.Time
727 DataValue = new System.TimeSpan (ts_data.year, ts_data.month, ts_data.day);
728 } else {
729 DataValue = new DateTime(ts_data.year, ts_data.month,
730 ts_data.day, ts_data.hour, ts_data.minute,
731 ts_data.second);
732 if (ts_data.fraction != 0)
733 DataValue = ((DateTime) DataValue).AddTicks ((long)ts_data.fraction / 100);
736 break;
737 case OdbcType.VarBinary :
738 case OdbcType.Image :
739 bufsize = (col.MaxLength < 255 && col.MaxLength > 0 ? col.MaxLength : 255);
740 buffer= new byte [bufsize];
741 ArrayList al = new ArrayList ();
742 //get the size of data to be returned.
743 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, 0, ref outsize);
744 if (outsize != (int) OdbcLengthIndicator.NullData) {
745 do {
746 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
747 if (ret == OdbcReturn.Error)
748 break;
749 if (ret != OdbcReturn.NoData && outsize != -1) {
750 if (outsize < bufsize) {
751 byte [] tmparr = new byte [outsize];
752 Array.Copy (buffer, 0, tmparr, 0, outsize);
753 al.AddRange (tmparr);
754 } else
755 al.AddRange (buffer);
756 } else {
757 break;
759 } while (ret != OdbcReturn.NoData);
761 DataValue = al.ToArray (typeof (byte));
762 break;
763 case OdbcType.Binary :
764 bufsize = col.MaxLength;
765 buffer = new byte [bufsize];
766 GetBytes (i, 0, buffer, 0, bufsize);
767 ret = OdbcReturn.Success;
768 DataValue = buffer;
769 break;
770 default:
771 bufsize = 255;
772 buffer = new byte[bufsize];
773 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
774 if (outsize != (int) OdbcLengthIndicator.NullData)
775 if (! (ret == OdbcReturn.SuccessWithInfo
776 && outsize == (int) OdbcLengthIndicator.NoTotal))
777 DataValue = Encoding.Default.GetString (buffer, 0, outsize);
778 break;
781 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData))
782 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
784 if (outsize == -1) // This means SQL_NULL_DATA
785 col.Value = DBNull.Value;
786 else
787 col.Value = DataValue;
789 return col.Value;
792 public
793 override
794 int GetValues (object [] values)
796 int numValues = 0;
798 if (IsClosed)
799 throw new InvalidOperationException ("The reader is closed.");
800 if (currentRow == -1)
801 throw new InvalidOperationException ("No data available.");
803 // copy values
804 for (int i = 0; i < values.Length; i++) {
805 if (i < FieldCount) {
806 values [i] = GetValue (i);
807 } else {
808 values [i] = null;
812 // get number of object instances in array
813 if (values.Length < FieldCount)
814 numValues = values.Length;
815 else if (values.Length == FieldCount)
816 numValues = FieldCount;
817 else
818 numValues = FieldCount;
820 return numValues;
824 public override IEnumerator GetEnumerator ()
826 return new DbEnumerator (this);
829 protected override
830 void Dispose (bool disposing)
832 if (disposed)
833 return;
835 if (disposing) {
836 // dispose managed resources
837 Close ();
840 command = null;
841 cols = null;
842 _dataTableSchema = null;
843 disposed = true;
846 public
847 override
848 bool IsDBNull (int i)
850 return (GetValue (i) is DBNull);
853 /// <remarks>
854 /// Move to the next result set.
855 /// </remarks>
856 public
857 override
858 bool NextResult ()
860 OdbcReturn ret = OdbcReturn.Success;
861 ret = libodbc.SQLMoreResults (hstmt);
862 if (ret == OdbcReturn.Success) {
863 short colcount = 0;
864 libodbc.SQLNumResultCols (hstmt, ref colcount);
865 cols = new OdbcColumn [colcount];
866 _dataTableSchema = null; // force fresh creation
867 GetColumns ();
869 return (ret == OdbcReturn.Success);
872 /// <remarks>
873 /// Load the next row in the current result set.
874 /// </remarks>
875 private bool NextRow ()
877 OdbcReturn ret = libodbc.SQLFetch (hstmt);
878 if (ret != OdbcReturn.Success)
879 currentRow = -1;
880 else
881 currentRow++;
883 // Clear cached values from last record
884 foreach (OdbcColumn col in cols) {
885 if (col != null)
886 col.Value = null;
888 return (ret == OdbcReturn.Success);
891 private int GetColumnAttribute (int column, FieldIdentifier fieldId)
893 OdbcReturn ret = OdbcReturn.Error;
894 byte [] buffer = new byte [255];
895 short outsize = 0;
896 int val = 0;
897 ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
898 buffer, (short)buffer.Length,
899 ref outsize, ref val);
900 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
901 throw Connection.CreateOdbcException (
902 OdbcHandleType.Stmt, hstmt);
903 return val;
906 private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
908 OdbcReturn ret = OdbcReturn.Error;
909 byte [] buffer = new byte [255];
910 short outsize = 0;
911 int val = 0;
912 ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
913 buffer, (short)buffer.Length,
914 ref outsize, ref val);
915 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
916 throw Connection.CreateOdbcException (
917 OdbcHandleType.Stmt, hstmt);
918 string value = string.Empty;
919 if (outsize > 0)
920 value = Encoding.Unicode.GetString (buffer, 0, outsize);
921 return value;
924 private string [] GetPrimaryKeys (string catalog, string schema, string table)
926 if (cols.Length <= 0)
927 return new string [0];
929 ArrayList keys = null;
930 try {
931 keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
932 } catch (OdbcException) {
933 try {
934 keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
935 } catch (OdbcException) {
938 if (keys == null)
939 return null;
940 keys.Sort ();
941 return (string []) keys.ToArray (typeof (string));
944 private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
946 ArrayList keys = new ArrayList ();
947 IntPtr handle = IntPtr.Zero;
948 OdbcReturn ret;
949 try {
950 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
951 command.Connection.hDbc, ref handle);
952 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
953 throw Connection.CreateOdbcException (
954 OdbcHandleType.Dbc, Connection.hDbc);
956 ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,
957 schema, -3, table, -3);
958 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
959 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
961 int length = 0;
962 byte [] primaryKey = new byte [255];
964 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
965 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
966 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
968 while (true) {
969 ret = libodbc.SQLFetch (handle);
970 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
971 break;
972 string pkey = Encoding.Default.GetString (primaryKey, 0, length);
973 keys.Add (pkey);
975 } finally {
976 if (handle != IntPtr.Zero) {
977 ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
978 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
979 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
981 ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
982 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
983 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
986 return keys;
989 private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
991 ArrayList keys = new ArrayList ();
992 IntPtr handle = IntPtr.Zero;
993 OdbcReturn ret;
994 try {
995 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
996 command.Connection.hDbc, ref handle);
997 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
998 throw Connection.CreateOdbcException (
999 OdbcHandleType.Dbc, Connection.hDbc);
1001 ret = libodbc.SQLStatistics (handle, catalog, -3,
1002 schema, -3,
1003 table, -3,
1004 libodbc.SQL_INDEX_UNIQUE,
1005 libodbc.SQL_QUICK);
1006 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1007 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1009 // NON_UNIQUE
1010 int nonUniqueLength = 0;
1011 short nonUnique = libodbc.SQL_FALSE;
1012 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref nonUnique, sizeof (short), ref nonUniqueLength);
1013 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1014 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1016 // COLUMN_NAME
1017 int length = 0;
1018 byte [] colName = new byte [255];
1019 ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
1020 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1021 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1023 while (true) {
1024 ret = libodbc.SQLFetch (handle);
1025 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1026 break;
1027 if (nonUnique == libodbc.SQL_TRUE) {
1028 string pkey = Encoding.Default.GetString (colName, 0, length);
1029 keys.Add (pkey);
1030 break;
1033 } finally {
1034 if (handle != IntPtr.Zero) {
1035 ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
1036 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1037 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1039 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, handle);
1040 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1041 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1044 return keys;
1047 public
1048 override
1049 bool Read ()
1051 return NextRow ();
1054 static string RemoveTrailingNullChar (string value)
1056 return value.TrimEnd ('\0');
1059 #endregion