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