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:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
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.
23 using System
.Collections
;
25 using System
.Runtime
.InteropServices
;
26 using System
.Globalization
;
29 namespace IBM
.Data
.DB2
32 /// Summary description for DB2ClientDataReader.
33 /// DB2ClientDataReader.
35 public sealed class DB2DataReader
: MarshalByRefObject
, IDataReader
37 private struct ColumnInfo
39 public string Colname
;
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
;
56 private bool skipReadOnce
;
59 #region Constructors and destructors
63 /// <param name="con"></Connection object to Db2>
64 /// <param name="com"></Command object>
65 internal DB2DataReader(DB2Connection con
, DB2Command com
, CommandBehavior behavior
)
69 this.behavior
= behavior
;
70 hwndStmt
= com
.statementHandle
; //We have access to the results through the statement handle
74 GetFieldCountAndAffectedRows();
75 internalBuffer
= Marshal
.AllocHGlobal(internalBufferSize
);
82 private void GetFieldCountAndAffectedRows()
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
);
93 sqlRet
= DB2CLIWrapper
.SQLNumResultCols(hwndStmt
, out colCount
);
94 DB2ClientUtils
.DB2CheckReturn(sqlRet
, DB2Constants
.SQL_HANDLE_STMT
, hwndStmt
, "DB2ClientDataReader - SQLNumResultCols", db2Conn
);
95 fieldCount
= colCount
;
100 #region Depth property
102 ///Depth of nesting for the current row, need to figure out what this translates into
105 private int depth
= 0;
112 throw new InvalidOperationException("Reader is closed");
119 #region IsClosed property
121 /// True if the reader is closed.
123 private bool isClosed
= true;
133 #region RecordsAffected property
135 /// Number of records affected by this operation. Will be zero until we close the
139 public int RecordsAffected
143 return recordsAffected
;
161 public void Dispose()
164 GC
.SuppressFinalize(this);
167 void Dispose(bool disposing
)
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
);
186 db2Comm
.DataReaderClosed();
190 Marshal
.FreeHGlobal(internalBuffer
);
202 #region GetSchemaTable
204 public DataTable
GetSchemaTable()
208 throw new InvalidOperationException("No data exists for the row/column.");
211 DataTable _schemaTable
= BuildNewSchemaTable();
214 IntPtr ptrCharacterAttribute
= IntPtr
.Zero
;
215 InitMem(256, ref ptrCharacterAttribute
);
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
);
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
;
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;
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
]);
345 allColumnsFound
= false;
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;
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())
369 string columnName
= reader
.GetString(0);
371 int ordinal
= FieldNameLookup(_schemaTable
, columnName
);
373 _schemaTable
.Rows
[ordinal
]["IsAutoIncrement"] = true;
386 public bool NextResult()
389 skipReadOnce
= false;
394 if((behavior
& (CommandBehavior
.SchemaOnly
| CommandBehavior
.SingleResult
)) != 0)
397 short sqlRet
= DB2CLIWrapper
.SQLMoreResults(this.hwndStmt
);
398 if(sqlRet
== DB2Constants
.SQL_NO_DATA_FOUND
)
400 DB2ClientUtils
.DB2CheckReturn(sqlRet
, DB2Constants
.SQL_HANDLE_STMT
, hwndStmt
, "Db2ClientDataReader - SQLMoreResults", db2Conn
);
426 throw new InvalidOperationException("Reader is closed");
427 if((behavior
& CommandBehavior
.SchemaOnly
) != 0)
432 skipReadOnce
= false;
440 short sqlRet
= DB2CLIWrapper
.SQLFetch(hwndStmt
);
441 if(sqlRet
== DB2Constants
.SQL_NO_DATA_FOUND
)
443 DB2ClientUtils
.DB2CheckReturn(sqlRet
, DB2Constants
.SQL_HANDLE_STMT
, hwndStmt
, "DB2ClientDataReader - SQLFetch 1", db2Conn
);
450 #region GetColumnInfo
451 private void GetColumnInfo()
454 throw new InvalidOperationException("Reader is closed");
456 throw new InvalidOperationException("No Fields found"); // TODO: check error
457 if(columnInfo
!= null)
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
++)
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");
483 #region Describe/Bind/Fetch functions
485 ///Broke these out so that we can use different paths for Immediate executions and Prepared executions
487 /// Does the describe and bind steps for the query result set. Called for both immediate and prepared queries.
491 /// FetchResults does what it says.
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)
511 #region IDataRecord Interface
512 ///Code for the IDataRecord interface
517 public int FieldCount
522 throw new InvalidOperationException("Reader is closed");
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)
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
);
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
];
594 Array
.Copy(sourceArray
, (int)fieldOffset
, buffer
, bufferOffset
, length
);
596 return sourceArray
.Length
;
600 Array
.Copy(sourceArray
, fieldOffset
, buffer
, bufferOffset
, length
);
602 return sourceArray
.LongLength
;
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
);
618 sourceString
.CopyTo((int)fieldOffset
, buffer
, bufferOffset
, length
);
620 return (long)sourceString
.Length
;
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");
638 throw new InvalidOperationException("No data");
640 if(_resultSet
== null)
642 _resultSet
= new object[fieldCount
];
644 if(_resultSet
[col
] == null)
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
;
654 _resultSet
[col
] = Marshal
.ReadByte(internalBuffer
) != 0;
657 return _resultSet
[col
];
663 /// GetDateTime method
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");
677 throw new InvalidOperationException("No data");
679 if(_resultSet
== null)
681 _resultSet
= new object[fieldCount
];
683 if(_resultSet
[col
] == null)
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
;
693 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(Guid
));
696 return _resultSet
[col
];
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");
717 throw new InvalidOperationException("No data");
719 if(_resultSet
== null)
721 _resultSet
= new object[fieldCount
];
723 if(_resultSet
[col
] == null)
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
;
733 _resultSet
[col
] = Marshal
.ReadByte(internalBuffer
);
736 return _resultSet
[col
];
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");
756 throw new InvalidOperationException("No data");
758 if(_resultSet
== null)
760 _resultSet
= new object[fieldCount
];
762 if(_resultSet
[col
] == null)
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
;
772 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(char));
775 return _resultSet
[col
];
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();
791 #region GetDataTypeName
793 ///GetDataTypeName return the type of data
795 public string GetDataTypeName(int col
)
797 if(columnInfo
== null)
801 switch(columnInfo
[col
].Sqltype
)
803 case DB2Constants
.SQL_INTEGER
:
805 case DB2Constants
.SQL_SMALLINT
:
807 case DB2Constants
.SQL_BIGINT
:
809 case DB2Constants
.SQL_DOUBLE
:
811 case DB2Constants
.SQL_REAL
:
813 case DB2Constants
.SQL_DECIMAL
:
815 case DB2Constants
.SQL_DATETIME
:
817 case DB2Constants
.SQL_TYPE_TIMESTAMP
:
819 case DB2Constants
.SQL_TYPE_DATE
:
821 case DB2Constants
.SQL_TYPE_TIME
:
823 case DB2Constants
.SQL_TYPE_CLOB
:
825 case DB2Constants
.SQL_CHAR
:
827 case DB2Constants
.SQL_VARCHAR
:
829 case DB2Constants
.SQL_TYPE_BLOB
:
831 case DB2Constants
.SQL_TYPE_BINARY
:
833 case DB2Constants
.SQL_LONGVARBINARY
:
834 return "LONGVARBINARY";
835 case DB2Constants
.SQL_VARBINARY
:
838 throw new NotImplementedException("Unknown SQL type " + columnInfo
[col
].Sqltype
);
844 /// GetDateTime method
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");
859 throw new InvalidOperationException("No data");
861 if(_resultSet
== null)
863 _resultSet
= new object[fieldCount
];
865 if(_resultSet
[col
] == null)
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
;
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
];
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");
905 throw new InvalidOperationException("No data");
907 if(_resultSet
== null)
909 _resultSet
= new object[fieldCount
];
911 if(_resultSet
[col
] == null)
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
;
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
];
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");
952 throw new InvalidOperationException("No data");
954 if(_resultSet
== null)
956 _resultSet
= new object[fieldCount
];
958 if(_resultSet
[col
] == null)
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
;
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
];
985 public Decimal
GetDecimal(int col
)
987 return (Decimal
)GetDecimalInternal(col
);
989 internal object GetDecimalInternal(int col
)
991 object tmp
= GetStringInternal(col
);
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?
999 // throw new IndexOutOfRangeException("col");
1003 // throw new InvalidOperationException("No data");
1005 // if(_resultSet == null)
1007 // _resultSet = new object[fieldCount];
1009 // if(_resultSet[col] == null)
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)
1015 // _resultSet[col] = DBNull.Value;
1019 // _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(decimal));
1022 return _resultSet
[col
];
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");
1042 throw new InvalidOperationException("No data");
1044 if(_resultSet
== null)
1046 _resultSet
= new object[fieldCount
];
1048 if(_resultSet
[col
] == null)
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
;
1058 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(double));
1061 return _resultSet
[col
];
1065 #region GetFieldType
1067 /// Type GetFieldType
1069 public Type
GetFieldType(int col
)
1071 if(columnInfo
== null)
1075 return GetManagedType(columnInfo
[col
].Sqltype
);
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");
1095 throw new InvalidOperationException("No data");
1097 if(_resultSet
== null)
1099 _resultSet
= new object[fieldCount
];
1101 if(_resultSet
[col
] == null)
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
;
1111 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(float));
1114 return _resultSet
[col
];
1118 #region The GetInt?? series
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");
1135 throw new InvalidOperationException("No data");
1137 if(_resultSet
== null)
1139 _resultSet
= new object[fieldCount
];
1141 if(_resultSet
[col
] == null)
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
;
1151 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(short));
1154 return _resultSet
[col
];
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");
1172 throw new InvalidOperationException("No data");
1174 if(_resultSet
== null)
1176 _resultSet
= new object[fieldCount
];
1178 if(_resultSet
[col
] == null)
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
;
1188 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(int));
1191 return _resultSet
[col
];
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");
1210 throw new InvalidOperationException("No data");
1212 if(_resultSet
== null)
1214 _resultSet
= new object[fieldCount
];
1216 if(_resultSet
[col
] == null)
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
;
1226 _resultSet
[col
] = Marshal
.PtrToStructure(internalBuffer
, typeof(long));
1229 return _resultSet
[col
];
1236 ///GetName, returns the name of the field
1238 public string GetName(int col
)
1240 if(columnInfo
== null)
1244 return columnInfo
[col
].Colname
;
1250 /// GetOrdinal, return the index of the named column
1252 public int GetOrdinal(string name
)
1254 if(columnInfo
== null)
1258 object ordinal
= columnsNames
[name
.ToUpper()];
1261 throw new IndexOutOfRangeException("name");
1263 return (int)ordinal
;
1269 /// GetString returns a string
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");
1284 throw new InvalidOperationException("No data");
1286 if(_resultSet
== null)
1288 _resultSet
= new object[fieldCount
];
1290 if(_resultSet
[col
] == null)
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
;
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
];
1312 /// GetVCalue, returns an object
1314 public object GetValue(int col
)
1322 /// GetValues returns all columns in the row through the argument, and the number of columns in the return value
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
];
1340 /// IsDBNull Is the column null
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
]);
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)));
1381 private void InitMem(int memSize
, ref IntPtr ptr
){
1382 if (ptr
.ToInt32() == 0){
1384 fixed(byte* arr
= new byte[memSize
]){
1385 ptr
= new IntPtr(arr
);
1391 private Type
GetManagedType(int sql_type
)
1395 case DB2Constants
.SQL_INTEGER
:
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
)
1428 case DB2Constants
.SQL_TYPE_CLOB
:
1429 case DB2Constants
.SQL_TYPE_BLOB
:
1434 private object GetBlobDataInternal(int col
)
1436 if((col
< 0) || (col
>= fieldCount
))
1438 throw new IndexOutOfRangeException("col");
1442 throw new InvalidOperationException("No data");
1444 if(_resultSet
== null)
1446 _resultSet
= new object[fieldCount
];
1448 if(_resultSet
[col
] == null)
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
;
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
];