**** Merged from MCS ****
[mono-project.git] / mcs / class / IBM.Data.DB2 / IBM.Data.DB2 / DB2DataReader.cs
blobe08986bc94681ce857f7013fa07bcf17e8e400e0
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 using System;
23 using System.Collections;
24 using System.Data;
25 using System.Runtime.InteropServices;
26 using System.Globalization;
27 using System.Text;
29 namespace IBM.Data.DB2
31 /// <summary>
32 /// Summary description for DB2ClientDataReader.
33 /// DB2ClientDataReader.
34 /// </summary>
35 public sealed class DB2DataReader : MarshalByRefObject, IDataReader
37 private struct ColumnInfo
39 public string Colname;
40 public int Sqltype;
43 private object[] _resultSet;
44 private ColumnInfo[] columnInfo;
45 private Hashtable columnsNames;
46 private const int internalBufferSize = 100;
47 private IntPtr internalBuffer;
48 internal DB2Connection db2Conn;
49 internal DB2Command db2Comm;
50 internal IntPtr hwndStmt;
51 private int recordsAffected;
52 private bool hasData = false;
53 private int fieldCount = -1;
54 private CommandBehavior behavior;
55 private bool hasRows;
56 private bool skipReadOnce;
59 #region Constructors and destructors
60 /// <summary>
61 ///
62 /// </summary>
63 /// <param name="con"></Connection object to Db2>
64 /// <param name="com"></Command object>
65 internal DB2DataReader(DB2Connection con, DB2Command com, CommandBehavior behavior)
67 db2Conn = con;
68 db2Comm = com;
69 this.behavior = behavior;
70 hwndStmt = com.statementHandle; //We have access to the results through the statement handle
72 _resultSet = null;
74 GetFieldCountAndAffectedRows();
75 internalBuffer = Marshal.AllocHGlobal(internalBufferSize);
77 isClosed = false;
80 #endregion
82 private void GetFieldCountAndAffectedRows()
84 short sqlRet;
85 recordsAffected = -1;
86 if((behavior & CommandBehavior.SchemaOnly) == 0)
88 //How many rows affected. numRows will be -1 if we aren't dealing with an Insert, Delete or Update, or if the statement did not execute successfully
89 sqlRet = DB2CLIWrapper.SQLRowCount(hwndStmt, out recordsAffected);
90 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecDirect error.", db2Conn);
92 short colCount;
93 sqlRet = DB2CLIWrapper.SQLNumResultCols(hwndStmt, out colCount);
94 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLNumResultCols", db2Conn);
95 fieldCount = colCount;
98 #region Properties
100 #region Depth property
102 ///Depth of nesting for the current row, need to figure out what this translates into
103 ///with DB2.
105 private int depth = 0;
106 public int Depth
110 if(isClosed)
112 throw new InvalidOperationException("Reader is closed");
114 return depth;
117 #endregion
119 #region IsClosed property
120 /// <summary>
121 /// True if the reader is closed.
122 /// </summary>
123 private bool isClosed = true;
124 public bool IsClosed
128 return isClosed;
131 #endregion
133 #region RecordsAffected property
135 /// Number of records affected by this operation. Will be zero until we close the
136 /// reader
137 ///
139 public int RecordsAffected
143 return recordsAffected;
146 #endregion
148 #endregion
150 #region Methods
154 #region Close method
156 public void Close()
158 Dispose();
161 public void Dispose()
163 Dispose(true);
164 GC.SuppressFinalize(this);
167 void Dispose(bool disposing)
169 if(!isClosed)
171 if(disposing)
173 short sqlRet;
176 sqlRet = DB2CLIWrapper.SQLMoreResults(this.hwndStmt);
177 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Db2ClientDataReader - SQLMoreResults", db2Conn);
178 } while(sqlRet != DB2Constants.SQL_NO_DATA_FOUND);
180 _resultSet = null;
181 hasData = false;
182 isClosed=true;
184 if(db2Comm != null)
186 db2Comm.DataReaderClosed();
187 db2Comm = null;
190 Marshal.FreeHGlobal(internalBuffer);
192 isClosed = true;
195 ~DB2DataReader()
197 Dispose(false);
200 #endregion
202 #region GetSchemaTable
204 public DataTable GetSchemaTable()
206 if(isClosed)
208 throw new InvalidOperationException("No data exists for the row/column.");
211 DataTable _schemaTable = BuildNewSchemaTable();
213 short sqlRet;
214 IntPtr ptrCharacterAttribute = IntPtr.Zero;
215 InitMem(256, ref ptrCharacterAttribute);
216 short buflen = 256;
217 short strlen = 256;
218 int numericattr = 0;
219 int colsize;
220 string colname;
221 int sqltype;
222 int precision;
223 int scale;
224 int nullable;
225 int updatable;
226 int isautoincrement;
227 string baseschemaname;
228 //string basecatalogname;
229 string basetablename;
230 string basecolumnname;
232 string previousTableName = null;
233 string previousSchemaName = null;
234 bool differentTablesUsed = false;
236 for (short i=1; i<=fieldCount; i++)
238 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_COLUMN_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
239 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
240 colname = Marshal.PtrToStringUni(ptrCharacterAttribute);
242 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_CONCISE_TYPE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
243 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
244 sqltype = numericattr;
246 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_OCTET_LENGTH, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
247 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
248 colsize = numericattr;
250 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_PRECISION, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
251 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
252 precision = numericattr;
254 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_SCALE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
255 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
256 scale = numericattr;
258 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_NULLABLE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
259 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
260 nullable = numericattr;
262 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_UPDATABLE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
263 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
264 updatable = numericattr;
266 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_AUTO_UNIQUE_VALUE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
267 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
268 isautoincrement = numericattr;
270 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_BASE_COLUMN_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
271 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
272 basecolumnname = Marshal.PtrToStringUni(ptrCharacterAttribute);
274 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_BASE_TABLE_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
275 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
276 basetablename = Marshal.PtrToStringUni(ptrCharacterAttribute);
278 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_SCHEMA_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr);
279 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn);
280 baseschemaname = Marshal.PtrToStringUni(ptrCharacterAttribute);
281 DataRow r = _schemaTable.NewRow();
283 r["ColumnName"] = colname;
284 r["ColumnOrdinal"] = i - 1;
285 r["ColumnSize"] = colsize;
286 r["NumericPrecision"] = precision;
287 r["NumericScale"] = scale;
288 r["DataType"] = GetManagedType((short)sqltype);
289 r["ProviderType"] = sqltype;
290 r["IsLong"] = IsLong((short)sqltype);
291 r["AllowDBNull"] = (nullable==0) ? false : true;
292 r["IsReadOnly"] = (basecolumnname == null) || (basecolumnname == "");
293 r["IsRowVersion"] = false;
294 r["IsUnique"] = false;
295 r["IsKeyColumn"] = false;
296 r["IsAutoIncrement"] = (isautoincrement==0) ? false : true;
297 r["BaseSchemaName"] = baseschemaname;
298 r["BaseCatalogName"] = "";
299 r["BaseTableName"] = basetablename;
300 r["BaseColumnName"] = basecolumnname;
302 _schemaTable.Rows.Add(r);
304 if(!differentTablesUsed)
306 if(((previousSchemaName == baseschemaname) && (previousTableName == basetablename)) ||
307 (previousTableName == null))
309 previousTableName = basetablename;
310 previousSchemaName = baseschemaname;
312 else
314 differentTablesUsed = true;
318 if(!differentTablesUsed &&
319 ((behavior & CommandBehavior.KeyInfo) != 0) &&
320 (db2Comm.Transaction == null) &&
321 (previousTableName != null) &&
322 (previousTableName != ""))
324 DB2Command schemaInfoCommand = db2Conn.CreateCommand();
325 schemaInfoCommand.CommandText =
326 "select concat(concat(INDSCHEMA,'.'),INDNAME), COLNAMES, UNIQUERULE from syscat.INDEXES " +
327 "where TABSCHEMA=? and TABNAME=? and uniquerule in ('P','U') order by UNIQUERULE";
328 schemaInfoCommand.Parameters.Add("TABSCHEMA", previousSchemaName);
329 schemaInfoCommand.Parameters.Add("TABNAME", previousTableName);
330 using(DB2DataReader reader = schemaInfoCommand.ExecuteReader())
332 bool keyColumnSet = false;
333 while(reader.Read())
335 string indexName = reader.GetString(0);
336 string[] indexColumns = reader.GetString(1).TrimStart('-', '+').Split('-', '+');
337 bool primary = reader.GetString(2) == "P";
339 bool allColumnsFound = true;
340 for(int i= 0; i < indexColumns.Length; i++)
342 int ordinal = FieldNameLookup(_schemaTable, indexColumns[i]);
343 if(ordinal < 0)
345 allColumnsFound = false;
346 break;
348 if(indexColumns.Length == 1)
349 _schemaTable.Rows[ordinal]["IsUnique"] = true;
351 if(allColumnsFound && !keyColumnSet)
353 for(int i= 0; i < indexColumns.Length; i++)
354 _schemaTable.Rows[FieldNameLookup(_schemaTable, indexColumns[i])]["IsKeyColumn"] = true;
355 keyColumnSet = true;
359 if(db2Conn.openConnection.MajorVersion >= 8)
363 schemaInfoCommand.CommandText =
364 "select COLNAME from SYSCAT.COLIDENTATTRIBUTES where TABSCHEMA=? and TABNAME=?";
365 using(DB2DataReader reader = schemaInfoCommand.ExecuteReader())
367 while(reader.Read())
369 string columnName = reader.GetString(0);
371 int ordinal = FieldNameLookup(_schemaTable, columnName);
372 if(ordinal >= 0)
373 _schemaTable.Rows[ordinal]["IsAutoIncrement"] = true;
377 catch{}
380 return _schemaTable;
382 #endregion
384 #region NextResult
386 public bool NextResult()
388 hasRows = false;
389 skipReadOnce = false;
390 hasData = false;
391 columnInfo = null;
392 _resultSet = null;
394 if((behavior & (CommandBehavior.SchemaOnly | CommandBehavior.SingleResult)) != 0)
395 return false;
397 short sqlRet = DB2CLIWrapper.SQLMoreResults(this.hwndStmt);
398 if(sqlRet == DB2Constants.SQL_NO_DATA_FOUND)
399 return false;
400 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Db2ClientDataReader - SQLMoreResults", db2Conn);
401 return true;
403 #endregion
405 #region Read
407 #if !NET_1_0
408 public bool HasRows
412 if(hasData)
413 return true;
415 hasRows = Read();
416 hasData = false;
417 skipReadOnce = true;
418 return hasRows;
421 #endif
423 public bool Read()
425 if (isClosed)
426 throw new InvalidOperationException("Reader is closed");
427 if((behavior & CommandBehavior.SchemaOnly) != 0)
428 return false;
430 if(skipReadOnce)
432 skipReadOnce = false;
433 hasData = hasRows;
434 return hasRows;
437 _resultSet = null;
438 hasData = false;
440 short sqlRet = DB2CLIWrapper.SQLFetch(hwndStmt);
441 if(sqlRet == DB2Constants.SQL_NO_DATA_FOUND)
442 return false;
443 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLFetch 1", db2Conn);
445 hasData = true;
446 return true;
448 #endregion
450 #region GetColumnInfo
451 private void GetColumnInfo()
453 if(isClosed)
454 throw new InvalidOperationException("Reader is closed");
455 if(fieldCount <= 0)
456 throw new InvalidOperationException("No Fields found"); // TODO: check error
457 if(columnInfo != null)
458 return;
460 columnInfo = new ColumnInfo[fieldCount];
461 columnsNames = new Hashtable(fieldCount);
463 StringBuilder sb = new StringBuilder(400);
464 for(int i = 0; i < columnInfo.Length; i++)
466 short sqlRet;
467 short strlen;
468 int numericAttribute;
470 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)(i + 1), (short)DB2Constants.SQL_DESC_BASE_COLUMN_NAME, sb, (short)sb.Capacity, out strlen, out numericAttribute);
471 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable");
472 columnInfo[i].Colname = sb.ToString();
473 columnsNames[columnInfo[i].Colname.ToUpper()] = i;
475 sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)(i + 1), (short)DB2Constants.SQL_DESC_CONCISE_TYPE, sb, (short)sb.Capacity, out strlen, out columnInfo[i].Sqltype);
476 DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable");
481 #endregion
483 #region Describe/Bind/Fetch functions
485 ///Broke these out so that we can use different paths for Immediate executions and Prepared executions
486 /// <summary>
487 /// Does the describe and bind steps for the query result set. Called for both immediate and prepared queries.
488 /// </summary>
490 /// <summary>
491 /// FetchResults does what it says.
492 /// </summary>
493 /// <param name="dbVals"></param>
494 /// <param name="sqlLen_or_IndPtr"></param>
495 /// <param name="_resultSet"></param>
496 private int FieldNameLookup(DataTable _schemaTable, string name)
498 for(int i = 0; i < _schemaTable.Rows.Count; i++)
500 if(CultureInfo.CurrentCulture.CompareInfo.Compare(name, (string)_schemaTable.Rows[i]["BaseColumnName"],
501 CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0)
503 return i;
506 return -1;
509 #endregion
511 #region IDataRecord Interface
512 ///Code for the IDataRecord interface
514 #region FieldCount
517 public int FieldCount
521 if (isClosed)
522 throw new InvalidOperationException("Reader is closed");
524 return fieldCount;
527 #endregion
529 #region Item accessors
530 public object this[string name]
534 int ordinal = GetOrdinal(name);
535 return this[ordinal];
538 public object this[int col]
542 if(columnInfo == null)
544 GetColumnInfo();
546 switch(columnInfo[col].Sqltype)
548 case DB2Constants.SQL_INTEGER:
549 return GetInt32Internal(col);
550 case DB2Constants.SQL_SMALLINT:
551 return GetInt16Internal(col);
552 case DB2Constants.SQL_BIGINT:
553 return GetInt64Internal(col);
554 case DB2Constants.SQL_DOUBLE:
555 return GetDoubleInternal(col);
556 case DB2Constants.SQL_REAL:
557 return GetFloatInternal(col);
558 case DB2Constants.SQL_DECIMAL:
559 return GetDecimalInternal(col);
560 case DB2Constants.SQL_DATETIME:
561 case DB2Constants.SQL_TYPE_TIMESTAMP:
562 return GetDateTimeInternal(col);
563 case DB2Constants.SQL_TYPE_DATE:
564 return GetDateInternal(col);
565 case DB2Constants.SQL_TYPE_TIME:
566 return GetTimeInternal(col);
567 case DB2Constants.SQL_TYPE_CLOB:
568 case DB2Constants.SQL_CHAR:
569 case DB2Constants.SQL_VARCHAR:
570 return GetStringInternal(col);
571 case DB2Constants.SQL_TYPE_BLOB:
572 case DB2Constants.SQL_TYPE_BINARY:
573 case DB2Constants.SQL_LONGVARBINARY:
574 case DB2Constants.SQL_VARBINARY:
575 return GetBlobDataInternal(col);
577 throw new NotImplementedException("Unknown SQL type " + columnInfo[col].Sqltype);
580 #endregion
582 #region GetBytes
584 /// GetBytes, return a stream of bytes
586 public long GetBytes(int col, long fieldOffset, byte[] buffer, int bufferOffset, int length)
588 // TODO: need better implementation for big BLOBs
590 byte[] sourceArray = (byte[])this[col];
591 #if NET_1_0
592 if(buffer == null)
594 Array.Copy(sourceArray, (int)fieldOffset, buffer, bufferOffset, length);
596 return sourceArray.Length;
597 #else
598 if(buffer == null)
600 Array.Copy(sourceArray, fieldOffset, buffer, bufferOffset, length);
602 return sourceArray.LongLength;
603 #endif
605 #endregion
607 #region GetChars
609 ///GetChars, returns char array
611 public long GetChars(int col, long fieldOffset, char[] buffer, int bufferOffset, int length)
613 // TODO: need better implementation for big CLOBs
615 string sourceString = GetString(col);
616 if(buffer == null)
618 sourceString.CopyTo((int)fieldOffset, buffer, bufferOffset, length);
620 return (long)sourceString.Length;
622 #endregion
624 #region GetBoolean method
626 public Boolean GetBoolean(int col)
628 return (Boolean)GetBooleanInternal(col);
630 internal object GetBooleanInternal(int col)
632 if((col < 0) || (col >= fieldCount))
634 throw new IndexOutOfRangeException("col");
636 if(!hasData)
638 throw new InvalidOperationException("No data");
640 if(_resultSet == null)
642 _resultSet = new object[fieldCount];
644 if(_resultSet[col] == null)
646 int len;
647 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_BIT, internalBuffer, internalBufferSize, out len);
648 if(len == DB2Constants.SQL_NULL_DATA)
650 _resultSet[col] = DBNull.Value;
652 else
654 _resultSet[col] = Marshal.ReadByte(internalBuffer) != 0;
657 return _resultSet[col];
659 #endregion
661 #region GetGuid
663 /// GetDateTime method
664 ///
665 public Guid GetGuid(int col)
667 return (Guid)GetGuidInternal(col);
669 internal object GetGuidInternal(int col)
671 if((col < 0) || (col >= fieldCount))
673 throw new IndexOutOfRangeException("col");
675 if(!hasData)
677 throw new InvalidOperationException("No data");
679 if(_resultSet == null)
681 _resultSet = new object[fieldCount];
683 if(_resultSet[col] == null)
685 int len;
686 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_GUID, internalBuffer, internalBufferSize, out len);
687 if(len == DB2Constants.SQL_NULL_DATA)
689 _resultSet[col] = DBNull.Value;
691 else
693 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(Guid));
696 return _resultSet[col];
699 #endregion
701 #region GetByte
703 ///GetByte
705 public Byte GetByte(int col)
707 return (Byte)GetByteInternal(col);
709 internal object GetByteInternal(int col)
711 if((col < 0) || (col >= fieldCount))
713 throw new IndexOutOfRangeException("col");
715 if(!hasData)
717 throw new InvalidOperationException("No data");
719 if(_resultSet == null)
721 _resultSet = new object[fieldCount];
723 if(_resultSet[col] == null)
725 int len;
726 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_UTINYINT, internalBuffer, 10, out len);
727 if(len == DB2Constants.SQL_NULL_DATA)
729 _resultSet[col] = DBNull.Value;
731 else
733 _resultSet[col] = Marshal.ReadByte(internalBuffer);
736 return _resultSet[col];
738 #endregion
740 #region GetChar
742 ///GetChar, return column as a char
744 public Char GetChar(int col)
746 return (Char)GetCharInternal(col);
748 internal object GetCharInternal(int col)
750 if((col < 0) || (col >= fieldCount))
752 throw new IndexOutOfRangeException("col");
754 if(!hasData)
756 throw new InvalidOperationException("No data");
758 if(_resultSet == null)
760 _resultSet = new object[fieldCount];
762 if(_resultSet[col] == null)
764 int len;
765 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_WCHAR, internalBuffer, 10, out len);
766 if(len == DB2Constants.SQL_NULL_DATA)
768 _resultSet[col] = DBNull.Value;
770 else
772 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(char));
775 return _resultSet[col];
777 #endregion
779 #region GetData
781 /// GetData method
782 ///
783 public IDataReader GetData(int col)
785 //Have to research this one, not quite sure what the docs mean
786 //DB2 does have some structured data types, is that what this is for?
787 throw new NotSupportedException();
789 #endregion
791 #region GetDataTypeName
793 ///GetDataTypeName return the type of data
795 public string GetDataTypeName(int col)
797 if(columnInfo == null)
799 GetColumnInfo();
801 switch(columnInfo[col].Sqltype)
803 case DB2Constants.SQL_INTEGER:
804 return "INTEGER";
805 case DB2Constants.SQL_SMALLINT:
806 return "SMALLINT";
807 case DB2Constants.SQL_BIGINT:
808 return "BIGINT";
809 case DB2Constants.SQL_DOUBLE:
810 return "DOUBLE";
811 case DB2Constants.SQL_REAL:
812 return "REAL";
813 case DB2Constants.SQL_DECIMAL:
814 return "DECIMAL";
815 case DB2Constants.SQL_DATETIME:
816 return "DATETIME";
817 case DB2Constants.SQL_TYPE_TIMESTAMP:
818 return "TIMESTAMP";
819 case DB2Constants.SQL_TYPE_DATE:
820 return "DATE";
821 case DB2Constants.SQL_TYPE_TIME:
822 return "TIME";
823 case DB2Constants.SQL_TYPE_CLOB:
824 return "CLOB";
825 case DB2Constants.SQL_CHAR:
826 return "CHAR";
827 case DB2Constants.SQL_VARCHAR:
828 return "VARCHAR";
829 case DB2Constants.SQL_TYPE_BLOB:
830 return "BLOB";
831 case DB2Constants.SQL_TYPE_BINARY:
832 return "BINARY";
833 case DB2Constants.SQL_LONGVARBINARY:
834 return "LONGVARBINARY";
835 case DB2Constants.SQL_VARBINARY:
836 return "VARBINARY";
838 throw new NotImplementedException("Unknown SQL type " + columnInfo[col].Sqltype);
840 #endregion
842 #region GetDateTime
844 /// GetDateTime method
845 ///
847 public DateTime GetDateTime(int col)
849 return (DateTime)GetDateTimeInternal(col);
851 internal object GetDateTimeInternal(int col)
853 if((col < 0) || (col >= fieldCount))
855 throw new IndexOutOfRangeException("col");
857 if(!hasData)
859 throw new InvalidOperationException("No data");
861 if(_resultSet == null)
863 _resultSet = new object[fieldCount];
865 if(_resultSet[col] == null)
867 int len;
868 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_TIMESTAMP, internalBuffer, internalBufferSize, out len);
869 if(len == DB2Constants.SQL_NULL_DATA)
871 _resultSet[col] = DBNull.Value;
873 else
875 DateTime ret = new DateTime(
876 Marshal.ReadInt16(internalBuffer, 0), // year
877 Marshal.ReadInt16(internalBuffer, 2), // month
878 Marshal.ReadInt16(internalBuffer, 4), // day
879 Marshal.ReadInt16(internalBuffer, 6), // hour
880 Marshal.ReadInt16(internalBuffer, 8), // minute
881 Marshal.ReadInt16(internalBuffer, 10));// second
882 _resultSet[col] = ret.AddTicks(Marshal.ReadInt32(internalBuffer, 12) / 100); // nanoseconds
885 return _resultSet[col];
887 #endregion
889 #region GetDate
891 /// GetDate method
892 ///
893 public DateTime GetDate(int col)
895 return (DateTime)GetDateInternal(col);
897 internal object GetDateInternal(int col)
899 if((col < 0) || (col >= fieldCount))
901 throw new IndexOutOfRangeException("col");
903 if(!hasData)
905 throw new InvalidOperationException("No data");
907 if(_resultSet == null)
909 _resultSet = new object[fieldCount];
911 if(_resultSet[col] == null)
913 int len;
914 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_DATE, internalBuffer, internalBufferSize, out len);
915 if(len == DB2Constants.SQL_NULL_DATA)
917 _resultSet[col] = DBNull.Value;
919 else
921 _resultSet[col] = new DateTime(
922 Marshal.ReadInt16(internalBuffer, 0), // year
923 Marshal.ReadInt16(internalBuffer, 2), // month
924 Marshal.ReadInt16(internalBuffer, 4)); // day
927 return _resultSet[col];
930 #endregion
932 #region GetTime
934 /// GetTime method
935 ///
936 public TimeSpan GetTimeSpan(int col)
938 return (TimeSpan)GetTimeInternal(col);
940 public TimeSpan GetTime(int col)
942 return (TimeSpan)GetTimeInternal(col);
944 internal object GetTimeInternal(int col)
946 if((col < 0) || (col >= fieldCount))
948 throw new IndexOutOfRangeException("col");
950 if(!hasData)
952 throw new InvalidOperationException("No data");
954 if(_resultSet == null)
956 _resultSet = new object[fieldCount];
958 if(_resultSet[col] == null)
960 int len;
961 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_TIME, internalBuffer, internalBufferSize, out len);
962 if(len == DB2Constants.SQL_NULL_DATA)
964 _resultSet[col] = DBNull.Value;
966 else
968 _resultSet[col] = new TimeSpan(
969 Marshal.ReadInt16(internalBuffer, 0), // Hour
970 Marshal.ReadInt16(internalBuffer, 2), // Minute
971 Marshal.ReadInt16(internalBuffer, 4)); // Second
974 return _resultSet[col];
977 #endregion
980 #region GetDecimal
982 ///GetDecimal method
985 public Decimal GetDecimal(int col)
987 return (Decimal)GetDecimalInternal(col);
989 internal object GetDecimalInternal(int col)
991 object tmp = GetStringInternal(col);
992 if(tmp is string)
994 _resultSet[col] = decimal.Parse(((string)_resultSet[col]).Replace(',','.'), // sometimes we get a '.' and sometimes we get a ','
995 System.Globalization.CultureInfo.InvariantCulture);
997 // if((col < 0) || (col >= fieldCount)) // only works on windows UDB DB2 V8?
998 // {
999 // throw new IndexOutOfRangeException("col");
1000 // }
1001 // if(!hasData)
1002 // {
1003 // throw new InvalidOperationException("No data");
1004 // }
1005 // if(_resultSet == null)
1006 // {
1007 // _resultSet = new object[fieldCount];
1008 // }
1009 // if(_resultSet[col] == null)
1010 // {
1011 // int len;
1012 // short sqlRet = Db2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)Db2Constants.SQL_C_DECIMAL_OLEDB, internalBuffer, internalBufferSize, out len);
1013 // if(len == Db2Constants.SQL_NULL_DATA)
1014 // {
1015 // _resultSet[col] = DBNull.Value;
1016 // }
1017 // else
1018 // {
1019 // _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(decimal));
1020 // }
1021 // }
1022 return _resultSet[col];
1024 #endregion
1026 #region GetDouble
1028 /// GetDouble
1029 ///
1030 public Double GetDouble(int col)
1032 return (Double)GetDoubleInternal(col);
1034 internal object GetDoubleInternal(int col)
1036 if((col < 0) || (col >= fieldCount))
1038 throw new IndexOutOfRangeException("col");
1040 if(!hasData)
1042 throw new InvalidOperationException("No data");
1044 if(_resultSet == null)
1046 _resultSet = new object[fieldCount];
1048 if(_resultSet[col] == null)
1050 int len;
1051 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_DOUBLE, internalBuffer, internalBufferSize, out len);
1052 if(len == DB2Constants.SQL_NULL_DATA)
1054 _resultSet[col] = DBNull.Value;
1056 else
1058 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(double));
1061 return _resultSet[col];
1063 #endregion
1065 #region GetFieldType
1067 /// Type GetFieldType
1069 public Type GetFieldType(int col)
1071 if(columnInfo == null)
1073 GetColumnInfo();
1075 return GetManagedType(columnInfo[col].Sqltype);
1077 #endregion
1079 #region GetFloat
1081 /// GetFloat
1082 ///
1083 public float GetFloat(int col)
1085 return (float)GetFloatInternal(col);
1087 internal object GetFloatInternal(int col)
1089 if((col < 0) || (col >= fieldCount))
1091 throw new IndexOutOfRangeException("col");
1093 if(!hasData)
1095 throw new InvalidOperationException("No data");
1097 if(_resultSet == null)
1099 _resultSet = new object[fieldCount];
1101 if(_resultSet[col] == null)
1103 int len;
1104 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_REAL, internalBuffer, internalBufferSize, out len);
1105 if(len == DB2Constants.SQL_NULL_DATA)
1107 _resultSet[col] = DBNull.Value;
1109 else
1111 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(float));
1114 return _resultSet[col];
1116 #endregion
1118 #region The GetInt?? series
1120 ///GetInt16
1122 public short GetInt16(int col)
1124 return (short)GetInt16Internal(col);
1127 internal object GetInt16Internal(int col)
1129 if((col < 0) || (col >= fieldCount))
1131 throw new IndexOutOfRangeException("col");
1133 if(!hasData)
1135 throw new InvalidOperationException("No data");
1137 if(_resultSet == null)
1139 _resultSet = new object[fieldCount];
1141 if(_resultSet[col] == null)
1143 int len;
1144 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SSHORT, internalBuffer, internalBufferSize, out len);
1145 if(len == DB2Constants.SQL_NULL_DATA)
1147 _resultSet[col] = DBNull.Value;
1149 else
1151 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(short));
1154 return _resultSet[col];
1157 ///GetInt32
1159 public int GetInt32(int col)
1161 return (int)GetInt32Internal(col);
1164 internal object GetInt32Internal(int col)
1166 if((col < 0) || (col >= fieldCount))
1168 throw new IndexOutOfRangeException("col");
1170 if(!hasData)
1172 throw new InvalidOperationException("No data");
1174 if(_resultSet == null)
1176 _resultSet = new object[fieldCount];
1178 if(_resultSet[col] == null)
1180 int len;
1181 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SLONG, internalBuffer, internalBufferSize, out len);
1182 if(len == DB2Constants.SQL_NULL_DATA)
1184 _resultSet[col] = DBNull.Value;
1186 else
1188 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(int));
1191 return _resultSet[col];
1195 ///GetInt64
1197 public long GetInt64(int col)
1199 return (long)GetInt64Internal(col);
1202 internal object GetInt64Internal(int col)
1204 if((col < 0) || (col >= fieldCount))
1206 throw new IndexOutOfRangeException("col");
1208 if(!hasData)
1210 throw new InvalidOperationException("No data");
1212 if(_resultSet == null)
1214 _resultSet = new object[fieldCount];
1216 if(_resultSet[col] == null)
1218 int len;
1219 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SBIGINT, internalBuffer, internalBufferSize, out len);
1220 if(len == DB2Constants.SQL_NULL_DATA)
1222 _resultSet[col] = DBNull.Value;
1224 else
1226 _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(long));
1229 return _resultSet[col];
1232 #endregion
1234 #region GetName
1236 ///GetName, returns the name of the field
1238 public string GetName(int col)
1240 if(columnInfo == null)
1242 GetColumnInfo();
1244 return columnInfo[col].Colname;
1246 #endregion
1248 #region GetOrdinal
1250 /// GetOrdinal, return the index of the named column
1251 ///
1252 public int GetOrdinal(string name)
1254 if(columnInfo == null)
1256 GetColumnInfo();
1258 object ordinal = columnsNames[name.ToUpper()];
1259 if(ordinal == null)
1261 throw new IndexOutOfRangeException("name");
1263 return (int)ordinal;
1265 #endregion
1267 #region GetString
1269 /// GetString returns a string
1270 ///
1271 public string GetString(int col)
1273 return (string)GetStringInternal(col);
1276 public object GetStringInternal(int col)
1278 if((col < 0) || (col >= fieldCount))
1280 throw new IndexOutOfRangeException("col");
1282 if(!hasData)
1284 throw new InvalidOperationException("No data");
1286 if(_resultSet == null)
1288 _resultSet = new object[fieldCount];
1290 if(_resultSet[col] == null)
1292 int length;
1293 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_WCHAR, (StringBuilder)null, 0, out length);
1294 if(length == DB2Constants.SQL_NULL_DATA)
1296 _resultSet[col] = DBNull.Value;
1298 else
1300 IntPtr mem = Marshal.AllocHGlobal(length + 2);
1301 sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_WCHAR, mem, length + 2, out length);
1302 _resultSet[col] = Marshal.PtrToStringUni(mem);
1303 Marshal.FreeHGlobal(mem);
1306 return _resultSet[col];
1308 #endregion
1310 #region GetValue
1312 /// GetVCalue, returns an object
1313 ///
1314 public object GetValue(int col)
1316 return this[col];
1318 #endregion
1320 #region GetValues
1322 /// GetValues returns all columns in the row through the argument, and the number of columns in the return value
1323 ///
1324 public int GetValues(object[] values)
1326 int count = Math.Min(fieldCount, values.Length);
1328 for (int i = 0; i < count; i++)
1330 values[i] = this[i];
1334 return count;
1336 #endregion
1338 #region IsDBNull
1340 /// IsDBNull Is the column null
1341 ///
1342 public bool IsDBNull(int col)
1344 //Proper implementation once I get the SQLDescribe/SQLBind/SQLFetch stuff in place
1345 return Convert.IsDBNull(this[col]);
1347 #endregion
1349 #endregion ///For IDataRecord
1351 #region private methods
1353 private DataTable BuildNewSchemaTable()
1355 DataTable schemaTable = new DataTable("SchemaTable");
1357 schemaTable.Columns.Add(new DataColumn("ColumnName", typeof(string)));
1358 schemaTable.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int)));
1359 schemaTable.Columns.Add(new DataColumn("ColumnSize", typeof(int)));
1360 schemaTable.Columns.Add(new DataColumn("NumericPrecision", typeof(short)));
1361 schemaTable.Columns.Add(new DataColumn("NumericScale", typeof(short)));
1362 schemaTable.Columns.Add(new DataColumn("DataType", typeof(System.Type)));
1363 schemaTable.Columns.Add(new DataColumn("ProviderType", typeof(int)));
1364 schemaTable.Columns.Add(new DataColumn("IsLong", typeof(bool)));
1365 schemaTable.Columns.Add(new DataColumn("AllowDBNull", typeof(bool)));
1366 schemaTable.Columns.Add(new DataColumn("IsReadOnly", typeof(bool)));
1367 schemaTable.Columns.Add(new DataColumn("IsRowVersion", typeof(bool)));
1368 schemaTable.Columns.Add(new DataColumn("IsUnique", typeof(bool)));
1369 schemaTable.Columns.Add(new DataColumn("IsKey", typeof(bool)));
1370 schemaTable.Columns.Add(new DataColumn("IsKeyColumn", typeof(bool)));
1371 schemaTable.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool)));
1372 schemaTable.Columns.Add(new DataColumn("BaseSchemaName", typeof(string)));
1373 schemaTable.Columns.Add(new DataColumn("BaseCatalogName", typeof(string)));
1374 schemaTable.Columns.Add(new DataColumn("BaseTableName", typeof(string)));
1375 schemaTable.Columns.Add(new DataColumn("BaseColumnName", typeof(string)));
1377 return schemaTable;
1379 #endregion
1381 private void InitMem(int memSize, ref IntPtr ptr){
1382 if (ptr.ToInt32() == 0){
1383 unsafe{
1384 fixed(byte* arr = new byte[memSize]){
1385 ptr = new IntPtr(arr);
1391 private Type GetManagedType(int sql_type)
1393 switch(sql_type)
1395 case DB2Constants.SQL_INTEGER:
1396 return typeof(int);
1397 case DB2Constants.SQL_SMALLINT:
1398 return typeof(short);
1399 case DB2Constants.SQL_BIGINT:
1400 return typeof(long);
1401 case DB2Constants.SQL_DOUBLE:
1402 return typeof(double);
1403 case DB2Constants.SQL_DECIMAL:
1404 return typeof(decimal);
1405 case DB2Constants.SQL_DATETIME:
1406 case DB2Constants.SQL_TYPE_DATE:
1407 case DB2Constants.SQL_TYPE_TIMESTAMP:
1408 return typeof(DateTime);
1409 case DB2Constants.SQL_TYPE_TIME:
1410 return typeof(TimeSpan);
1411 case DB2Constants.SQL_CHAR:
1412 case DB2Constants.SQL_VARCHAR:
1413 case DB2Constants.SQL_TYPE_CLOB:
1414 return typeof(string);
1415 case DB2Constants.SQL_TYPE_BLOB:
1416 case DB2Constants.SQL_TYPE_BINARY:
1417 case DB2Constants.SQL_LONGVARBINARY:
1418 case DB2Constants.SQL_VARBINARY:
1419 return typeof(byte[]);
1421 throw new NotImplementedException("Unknown SQL type " + sql_type);
1424 private bool IsLong(short sql_type)
1426 switch(sql_type)
1428 case DB2Constants.SQL_TYPE_CLOB:
1429 case DB2Constants.SQL_TYPE_BLOB:
1430 return true;
1432 return false;
1434 private object GetBlobDataInternal(int col)
1436 if((col < 0) || (col >= fieldCount))
1438 throw new IndexOutOfRangeException("col");
1440 if(!hasData)
1442 throw new InvalidOperationException("No data");
1444 if(_resultSet == null)
1446 _resultSet = new object[fieldCount];
1448 if(_resultSet[col] == null)
1450 int length;
1451 short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_BINARY, (StringBuilder)null, 0, out length);
1452 if(length == DB2Constants.SQL_NULL_DATA)
1454 _resultSet[col] = DBNull.Value;
1456 else
1458 byte[] result = new byte[length];
1459 sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_BINARY, result, length, out length);
1460 _resultSet[col] = result;
1463 return _resultSet[col];
1468 #endregion