// // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Data; using System.Runtime.InteropServices; using System.Globalization; using System.Text; namespace IBM.Data.DB2 { /// /// Summary description for DB2ClientDataReader. /// DB2ClientDataReader. /// public sealed class DB2DataReader : MarshalByRefObject, IDataReader { private struct ColumnInfo { public string Colname; public int Sqltype; } private object[] _resultSet; private ColumnInfo[] columnInfo; private Hashtable columnsNames; private const int internalBufferSize = 100; private IntPtr internalBuffer; internal DB2Connection db2Conn; internal DB2Command db2Comm; internal IntPtr hwndStmt; private int recordsAffected; private bool hasData = false; private int fieldCount = -1; private CommandBehavior behavior; private bool hasRows; private bool skipReadOnce; #region Constructors and destructors /// /// /// /// /// internal DB2DataReader(DB2Connection con, DB2Command com, CommandBehavior behavior) { db2Conn = con; db2Comm = com; this.behavior = behavior; hwndStmt = com.statementHandle; //We have access to the results through the statement handle _resultSet = null; GetFieldCountAndAffectedRows(); internalBuffer = Marshal.AllocHGlobal(internalBufferSize); isClosed = false; } #endregion private void GetFieldCountAndAffectedRows() { short sqlRet; recordsAffected = -1; if((behavior & CommandBehavior.SchemaOnly) == 0) { //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 sqlRet = DB2CLIWrapper.SQLRowCount(hwndStmt, out recordsAffected); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "SQLExecDirect error.", db2Conn); } short colCount; sqlRet = DB2CLIWrapper.SQLNumResultCols(hwndStmt, out colCount); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLNumResultCols", db2Conn); fieldCount = colCount; } #region Properties #region Depth property /// ///Depth of nesting for the current row, need to figure out what this translates into ///with DB2. /// private int depth = 0; public int Depth { get { if(isClosed) { throw new InvalidOperationException("Reader is closed"); } return depth; } } #endregion #region IsClosed property /// /// True if the reader is closed. /// private bool isClosed = true; public bool IsClosed { get { return isClosed; } } #endregion #region RecordsAffected property /// /// Number of records affected by this operation. Will be zero until we close the /// reader /// public int RecordsAffected { get { return recordsAffected; } } #endregion #endregion #region Methods #region Close method public void Close() { Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } void Dispose(bool disposing) { if(!isClosed) { if(disposing) { short sqlRet; do { sqlRet = DB2CLIWrapper.SQLMoreResults(this.hwndStmt); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Db2ClientDataReader - SQLMoreResults", db2Conn); } while(sqlRet != DB2Constants.SQL_NO_DATA_FOUND); _resultSet = null; hasData = false; isClosed=true; if(db2Comm != null) { db2Comm.DataReaderClosed(); db2Comm = null; } } Marshal.FreeHGlobal(internalBuffer); } isClosed = true; } ~DB2DataReader() { Dispose(false); } #endregion #region GetSchemaTable public DataTable GetSchemaTable() { if(isClosed) { throw new InvalidOperationException("No data exists for the row/column."); } DataTable _schemaTable = BuildNewSchemaTable(); short sqlRet; IntPtr ptrCharacterAttribute = IntPtr.Zero; InitMem(256, ref ptrCharacterAttribute); short buflen = 256; short strlen = 256; int numericattr = 0; int colsize; string colname; int sqltype; int precision; int scale; int nullable; int updatable; int isautoincrement; string baseschemaname; //string basecatalogname; string basetablename; string basecolumnname; string previousTableName = null; string previousSchemaName = null; bool differentTablesUsed = false; for (short i=1; i<=fieldCount; i++) { sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_COLUMN_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); colname = Marshal.PtrToStringUni(ptrCharacterAttribute); sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_CONCISE_TYPE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); sqltype = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_OCTET_LENGTH, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); colsize = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_PRECISION, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); precision = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_SCALE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); scale = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_NULLABLE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); nullable = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_UPDATABLE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); updatable = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_AUTO_UNIQUE_VALUE, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); isautoincrement = numericattr; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_BASE_COLUMN_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); basecolumnname = Marshal.PtrToStringUni(ptrCharacterAttribute); sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_BASE_TABLE_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); basetablename = Marshal.PtrToStringUni(ptrCharacterAttribute); sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)i, (short)DB2Constants.SQL_DESC_SCHEMA_NAME, ptrCharacterAttribute, buflen, ref strlen, ref numericattr); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable", db2Conn); baseschemaname = Marshal.PtrToStringUni(ptrCharacterAttribute); DataRow r = _schemaTable.NewRow(); r["ColumnName"] = colname; r["ColumnOrdinal"] = i - 1; r["ColumnSize"] = colsize; r["NumericPrecision"] = precision; r["NumericScale"] = scale; r["DataType"] = GetManagedType((short)sqltype); r["ProviderType"] = sqltype; r["IsLong"] = IsLong((short)sqltype); r["AllowDBNull"] = (nullable==0) ? false : true; r["IsReadOnly"] = (basecolumnname == null) || (basecolumnname == ""); r["IsRowVersion"] = false; r["IsUnique"] = false; r["IsKeyColumn"] = false; r["IsAutoIncrement"] = (isautoincrement==0) ? false : true; r["BaseSchemaName"] = baseschemaname; r["BaseCatalogName"] = ""; r["BaseTableName"] = basetablename; r["BaseColumnName"] = basecolumnname; _schemaTable.Rows.Add(r); if(!differentTablesUsed) { if(((previousSchemaName == baseschemaname) && (previousTableName == basetablename)) || (previousTableName == null)) { previousTableName = basetablename; previousSchemaName = baseschemaname; } else { differentTablesUsed = true; } } } if(!differentTablesUsed && ((behavior & CommandBehavior.KeyInfo) != 0) && (db2Comm.Transaction == null) && (previousTableName != null) && (previousTableName != "")) { DB2Command schemaInfoCommand = db2Conn.CreateCommand(); schemaInfoCommand.CommandText = "select concat(concat(INDSCHEMA,'.'),INDNAME), COLNAMES, UNIQUERULE from syscat.INDEXES " + "where TABSCHEMA=? and TABNAME=? and uniquerule in ('P','U') order by UNIQUERULE"; schemaInfoCommand.Parameters.Add("TABSCHEMA", previousSchemaName); schemaInfoCommand.Parameters.Add("TABNAME", previousTableName); using(DB2DataReader reader = schemaInfoCommand.ExecuteReader()) { bool keyColumnSet = false; while(reader.Read()) { string indexName = reader.GetString(0); string[] indexColumns = reader.GetString(1).TrimStart('-', '+').Split('-', '+'); bool primary = reader.GetString(2) == "P"; bool allColumnsFound = true; for(int i= 0; i < indexColumns.Length; i++) { int ordinal = FieldNameLookup(_schemaTable, indexColumns[i]); if(ordinal < 0) { allColumnsFound = false; break; } if(indexColumns.Length == 1) _schemaTable.Rows[ordinal]["IsUnique"] = true; } if(allColumnsFound && !keyColumnSet) { for(int i= 0; i < indexColumns.Length; i++) _schemaTable.Rows[FieldNameLookup(_schemaTable, indexColumns[i])]["IsKeyColumn"] = true; keyColumnSet = true; } } } if(db2Conn.openConnection.MajorVersion >= 8) { try { schemaInfoCommand.CommandText = "select COLNAME from SYSCAT.COLIDENTATTRIBUTES where TABSCHEMA=? and TABNAME=?"; using(DB2DataReader reader = schemaInfoCommand.ExecuteReader()) { while(reader.Read()) { string columnName = reader.GetString(0); int ordinal = FieldNameLookup(_schemaTable, columnName); if(ordinal >= 0) _schemaTable.Rows[ordinal]["IsAutoIncrement"] = true; } } } catch{} } } return _schemaTable; } #endregion #region NextResult public bool NextResult() { hasRows = false; skipReadOnce = false; hasData = false; columnInfo = null; _resultSet = null; if((behavior & (CommandBehavior.SchemaOnly | CommandBehavior.SingleResult)) != 0) return false; short sqlRet = DB2CLIWrapper.SQLMoreResults(this.hwndStmt); if(sqlRet == DB2Constants.SQL_NO_DATA_FOUND) return false; DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "Db2ClientDataReader - SQLMoreResults", db2Conn); return true; } #endregion #region Read #if !NET_1_0 public bool HasRows { get { if(hasData) return true; hasRows = Read(); hasData = false; skipReadOnce = true; return hasRows; } } #endif public bool Read() { if (isClosed) throw new InvalidOperationException("Reader is closed"); if((behavior & CommandBehavior.SchemaOnly) != 0) return false; if(skipReadOnce) { skipReadOnce = false; hasData = hasRows; return hasRows; } _resultSet = null; hasData = false; short sqlRet = DB2CLIWrapper.SQLFetch(hwndStmt); if(sqlRet == DB2Constants.SQL_NO_DATA_FOUND) return false; DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLFetch 1", db2Conn); hasData = true; return true; } #endregion #region GetColumnInfo private void GetColumnInfo() { if(isClosed) throw new InvalidOperationException("Reader is closed"); if(fieldCount <= 0) throw new InvalidOperationException("No Fields found"); // TODO: check error if(columnInfo != null) return; columnInfo = new ColumnInfo[fieldCount]; columnsNames = new Hashtable(fieldCount); StringBuilder sb = new StringBuilder(400); for(int i = 0; i < columnInfo.Length; i++) { short sqlRet; short strlen; int numericAttribute; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)(i + 1), (short)DB2Constants.SQL_DESC_BASE_COLUMN_NAME, sb, (short)sb.Capacity, out strlen, out numericAttribute); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable"); columnInfo[i].Colname = sb.ToString(); columnsNames[columnInfo[i].Colname.ToUpper()] = i; sqlRet = DB2CLIWrapper.SQLColAttribute(hwndStmt, (short)(i + 1), (short)DB2Constants.SQL_DESC_CONCISE_TYPE, sb, (short)sb.Capacity, out strlen, out columnInfo[i].Sqltype); DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_STMT, hwndStmt, "GetSchemaTable"); } } #endregion #region Describe/Bind/Fetch functions /// ///Broke these out so that we can use different paths for Immediate executions and Prepared executions /// /// Does the describe and bind steps for the query result set. Called for both immediate and prepared queries. /// /// /// FetchResults does what it says. /// /// /// /// private int FieldNameLookup(DataTable _schemaTable, string name) { for(int i = 0; i < _schemaTable.Rows.Count; i++) { if(CultureInfo.CurrentCulture.CompareInfo.Compare(name, (string)_schemaTable.Rows[i]["BaseColumnName"], CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0) { return i; } } return -1; } #endregion #region IDataRecord Interface ///Code for the IDataRecord interface /// #region FieldCount /// /// public int FieldCount { get { if (isClosed) throw new InvalidOperationException("Reader is closed"); return fieldCount; } } #endregion #region Item accessors public object this[string name] { get { int ordinal = GetOrdinal(name); return this[ordinal]; } } public object this[int col] { get { if(columnInfo == null) { GetColumnInfo(); } switch(columnInfo[col].Sqltype) { case DB2Constants.SQL_INTEGER: return GetInt32Internal(col); case DB2Constants.SQL_SMALLINT: return GetInt16Internal(col); case DB2Constants.SQL_BIGINT: return GetInt64Internal(col); case DB2Constants.SQL_DOUBLE: return GetDoubleInternal(col); case DB2Constants.SQL_REAL: return GetFloatInternal(col); case DB2Constants.SQL_DECIMAL: return GetDecimalInternal(col); case DB2Constants.SQL_DATETIME: case DB2Constants.SQL_TYPE_TIMESTAMP: return GetDateTimeInternal(col); case DB2Constants.SQL_TYPE_DATE: return GetDateInternal(col); case DB2Constants.SQL_TYPE_TIME: return GetTimeInternal(col); case DB2Constants.SQL_TYPE_CLOB: case DB2Constants.SQL_CHAR: case DB2Constants.SQL_VARCHAR: return GetStringInternal(col); case DB2Constants.SQL_TYPE_BLOB: case DB2Constants.SQL_TYPE_BINARY: case DB2Constants.SQL_LONGVARBINARY: case DB2Constants.SQL_VARBINARY: return GetBlobDataInternal(col); } throw new NotImplementedException("Unknown SQL type " + columnInfo[col].Sqltype); } } #endregion #region GetBytes /// /// GetBytes, return a stream of bytes /// public long GetBytes(int col, long fieldOffset, byte[] buffer, int bufferOffset, int length) { // TODO: need better implementation for big BLOBs byte[] sourceArray = (byte[])this[col]; #if NET_1_0 if(buffer == null) { Array.Copy(sourceArray, (int)fieldOffset, buffer, bufferOffset, length); } return sourceArray.Length; #else if(buffer == null) { Array.Copy(sourceArray, fieldOffset, buffer, bufferOffset, length); } return sourceArray.LongLength; #endif } #endregion #region GetChars /// ///GetChars, returns char array /// public long GetChars(int col, long fieldOffset, char[] buffer, int bufferOffset, int length) { // TODO: need better implementation for big CLOBs string sourceString = GetString(col); if(buffer == null) { sourceString.CopyTo((int)fieldOffset, buffer, bufferOffset, length); } return (long)sourceString.Length; } #endregion #region GetBoolean method public Boolean GetBoolean(int col) { return (Boolean)GetBooleanInternal(col); } internal object GetBooleanInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_BIT, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.ReadByte(internalBuffer) != 0; } } return _resultSet[col]; } #endregion #region GetGuid /// /// GetDateTime method /// public Guid GetGuid(int col) { return (Guid)GetGuidInternal(col); } internal object GetGuidInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_GUID, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(Guid)); } } return _resultSet[col]; } #endregion #region GetByte /// ///GetByte /// public Byte GetByte(int col) { return (Byte)GetByteInternal(col); } internal object GetByteInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_UTINYINT, internalBuffer, 10, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.ReadByte(internalBuffer); } } return _resultSet[col]; } #endregion #region GetChar /// ///GetChar, return column as a char /// public Char GetChar(int col) { return (Char)GetCharInternal(col); } internal object GetCharInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_WCHAR, internalBuffer, 10, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(char)); } } return _resultSet[col]; } #endregion #region GetData /// /// GetData method /// public IDataReader GetData(int col) { //Have to research this one, not quite sure what the docs mean //DB2 does have some structured data types, is that what this is for? throw new NotSupportedException(); } #endregion #region GetDataTypeName /// ///GetDataTypeName return the type of data /// public string GetDataTypeName(int col) { if(columnInfo == null) { GetColumnInfo(); } switch(columnInfo[col].Sqltype) { case DB2Constants.SQL_INTEGER: return "INTEGER"; case DB2Constants.SQL_SMALLINT: return "SMALLINT"; case DB2Constants.SQL_BIGINT: return "BIGINT"; case DB2Constants.SQL_DOUBLE: return "DOUBLE"; case DB2Constants.SQL_REAL: return "REAL"; case DB2Constants.SQL_DECIMAL: return "DECIMAL"; case DB2Constants.SQL_DATETIME: return "DATETIME"; case DB2Constants.SQL_TYPE_TIMESTAMP: return "TIMESTAMP"; case DB2Constants.SQL_TYPE_DATE: return "DATE"; case DB2Constants.SQL_TYPE_TIME: return "TIME"; case DB2Constants.SQL_TYPE_CLOB: return "CLOB"; case DB2Constants.SQL_CHAR: return "CHAR"; case DB2Constants.SQL_VARCHAR: return "VARCHAR"; case DB2Constants.SQL_TYPE_BLOB: return "BLOB"; case DB2Constants.SQL_TYPE_BINARY: return "BINARY"; case DB2Constants.SQL_LONGVARBINARY: return "LONGVARBINARY"; case DB2Constants.SQL_VARBINARY: return "VARBINARY"; } throw new NotImplementedException("Unknown SQL type " + columnInfo[col].Sqltype); } #endregion #region GetDateTime /// /// GetDateTime method /// public DateTime GetDateTime(int col) { return (DateTime)GetDateTimeInternal(col); } internal object GetDateTimeInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_TIMESTAMP, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { DateTime ret = new DateTime( Marshal.ReadInt16(internalBuffer, 0), // year Marshal.ReadInt16(internalBuffer, 2), // month Marshal.ReadInt16(internalBuffer, 4), // day Marshal.ReadInt16(internalBuffer, 6), // hour Marshal.ReadInt16(internalBuffer, 8), // minute Marshal.ReadInt16(internalBuffer, 10));// second _resultSet[col] = ret.AddTicks(Marshal.ReadInt32(internalBuffer, 12) / 100); // nanoseconds } } return _resultSet[col]; } #endregion #region GetDate /// /// GetDate method /// public DateTime GetDate(int col) { return (DateTime)GetDateInternal(col); } internal object GetDateInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_DATE, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = new DateTime( Marshal.ReadInt16(internalBuffer, 0), // year Marshal.ReadInt16(internalBuffer, 2), // month Marshal.ReadInt16(internalBuffer, 4)); // day } } return _resultSet[col]; } #endregion #region GetTime /// /// GetTime method /// public TimeSpan GetTimeSpan(int col) { return (TimeSpan)GetTimeInternal(col); } public TimeSpan GetTime(int col) { return (TimeSpan)GetTimeInternal(col); } internal object GetTimeInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_TIME, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = new TimeSpan( Marshal.ReadInt16(internalBuffer, 0), // Hour Marshal.ReadInt16(internalBuffer, 2), // Minute Marshal.ReadInt16(internalBuffer, 4)); // Second } } return _resultSet[col]; } #endregion #region GetDecimal /// ///GetDecimal method /// public Decimal GetDecimal(int col) { return (Decimal)GetDecimalInternal(col); } internal object GetDecimalInternal(int col) { object tmp = GetStringInternal(col); if(tmp is string) { _resultSet[col] = decimal.Parse(((string)_resultSet[col]).Replace(',','.'), // sometimes we get a '.' and sometimes we get a ',' System.Globalization.CultureInfo.InvariantCulture); } // if((col < 0) || (col >= fieldCount)) // only works on windows UDB DB2 V8? // { // throw new IndexOutOfRangeException("col"); // } // if(!hasData) // { // throw new InvalidOperationException("No data"); // } // if(_resultSet == null) // { // _resultSet = new object[fieldCount]; // } // if(_resultSet[col] == null) // { // int len; // short sqlRet = Db2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)Db2Constants.SQL_C_DECIMAL_OLEDB, internalBuffer, internalBufferSize, out len); // if(len == Db2Constants.SQL_NULL_DATA) // { // _resultSet[col] = DBNull.Value; // } // else // { // _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(decimal)); // } // } return _resultSet[col]; } #endregion #region GetDouble /// /// GetDouble /// public Double GetDouble(int col) { return (Double)GetDoubleInternal(col); } internal object GetDoubleInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_DOUBLE, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(double)); } } return _resultSet[col]; } #endregion #region GetFieldType /// /// Type GetFieldType /// public Type GetFieldType(int col) { if(columnInfo == null) { GetColumnInfo(); } return GetManagedType(columnInfo[col].Sqltype); } #endregion #region GetFloat /// /// GetFloat /// public float GetFloat(int col) { return (float)GetFloatInternal(col); } internal object GetFloatInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_REAL, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(float)); } } return _resultSet[col]; } #endregion #region The GetInt?? series /// ///GetInt16 /// public short GetInt16(int col) { return (short)GetInt16Internal(col); } internal object GetInt16Internal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SSHORT, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(short)); } } return _resultSet[col]; } /// ///GetInt32 /// public int GetInt32(int col) { return (int)GetInt32Internal(col); } internal object GetInt32Internal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SLONG, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(int)); } } return _resultSet[col]; } /// ///GetInt64 /// public long GetInt64(int col) { return (long)GetInt64Internal(col); } internal object GetInt64Internal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int len; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_SBIGINT, internalBuffer, internalBufferSize, out len); if(len == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { _resultSet[col] = Marshal.PtrToStructure(internalBuffer, typeof(long)); } } return _resultSet[col]; } #endregion #region GetName /// ///GetName, returns the name of the field /// public string GetName(int col) { if(columnInfo == null) { GetColumnInfo(); } return columnInfo[col].Colname; } #endregion #region GetOrdinal /// /// GetOrdinal, return the index of the named column /// public int GetOrdinal(string name) { if(columnInfo == null) { GetColumnInfo(); } object ordinal = columnsNames[name.ToUpper()]; if(ordinal == null) { throw new IndexOutOfRangeException("name"); } return (int)ordinal; } #endregion #region GetString /// /// GetString returns a string /// public string GetString(int col) { return (string)GetStringInternal(col); } public object GetStringInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int length; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_WCHAR, (StringBuilder)null, 0, out length); if(length == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { IntPtr mem = Marshal.AllocHGlobal(length + 2); sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_WCHAR, mem, length + 2, out length); _resultSet[col] = Marshal.PtrToStringUni(mem); Marshal.FreeHGlobal(mem); } } return _resultSet[col]; } #endregion #region GetValue /// /// GetVCalue, returns an object /// public object GetValue(int col) { return this[col]; } #endregion #region GetValues /// /// GetValues returns all columns in the row through the argument, and the number of columns in the return value /// public int GetValues(object[] values) { int count = Math.Min(fieldCount, values.Length); for (int i = 0; i < count; i++) { values[i] = this[i]; } return count; } #endregion #region IsDBNull /// /// IsDBNull Is the column null /// public bool IsDBNull(int col) { //Proper implementation once I get the SQLDescribe/SQLBind/SQLFetch stuff in place return Convert.IsDBNull(this[col]); } #endregion #endregion ///For IDataRecord #region private methods private DataTable BuildNewSchemaTable() { DataTable schemaTable = new DataTable("SchemaTable"); schemaTable.Columns.Add(new DataColumn("ColumnName", typeof(string))); schemaTable.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int))); schemaTable.Columns.Add(new DataColumn("ColumnSize", typeof(int))); schemaTable.Columns.Add(new DataColumn("NumericPrecision", typeof(short))); schemaTable.Columns.Add(new DataColumn("NumericScale", typeof(short))); schemaTable.Columns.Add(new DataColumn("DataType", typeof(System.Type))); schemaTable.Columns.Add(new DataColumn("ProviderType", typeof(int))); schemaTable.Columns.Add(new DataColumn("IsLong", typeof(bool))); schemaTable.Columns.Add(new DataColumn("AllowDBNull", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsReadOnly", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsRowVersion", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsUnique", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsKey", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsKeyColumn", typeof(bool))); schemaTable.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool))); schemaTable.Columns.Add(new DataColumn("BaseSchemaName", typeof(string))); schemaTable.Columns.Add(new DataColumn("BaseCatalogName", typeof(string))); schemaTable.Columns.Add(new DataColumn("BaseTableName", typeof(string))); schemaTable.Columns.Add(new DataColumn("BaseColumnName", typeof(string))); return schemaTable; } #endregion private void InitMem(int memSize, ref IntPtr ptr){ if (ptr.ToInt32() == 0){ unsafe{ fixed(byte* arr = new byte[memSize]){ ptr = new IntPtr(arr); } } } } private Type GetManagedType(int sql_type) { switch(sql_type) { case DB2Constants.SQL_INTEGER: return typeof(int); case DB2Constants.SQL_SMALLINT: return typeof(short); case DB2Constants.SQL_BIGINT: return typeof(long); case DB2Constants.SQL_DOUBLE: return typeof(double); case DB2Constants.SQL_DECIMAL: return typeof(decimal); case DB2Constants.SQL_DATETIME: case DB2Constants.SQL_TYPE_DATE: case DB2Constants.SQL_TYPE_TIMESTAMP: return typeof(DateTime); case DB2Constants.SQL_TYPE_TIME: return typeof(TimeSpan); case DB2Constants.SQL_CHAR: case DB2Constants.SQL_VARCHAR: case DB2Constants.SQL_TYPE_CLOB: return typeof(string); case DB2Constants.SQL_TYPE_BLOB: case DB2Constants.SQL_TYPE_BINARY: case DB2Constants.SQL_LONGVARBINARY: case DB2Constants.SQL_VARBINARY: return typeof(byte[]); } throw new NotImplementedException("Unknown SQL type " + sql_type); } private bool IsLong(short sql_type) { switch(sql_type) { case DB2Constants.SQL_TYPE_CLOB: case DB2Constants.SQL_TYPE_BLOB: return true; } return false; } private object GetBlobDataInternal(int col) { if((col < 0) || (col >= fieldCount)) { throw new IndexOutOfRangeException("col"); } if(!hasData) { throw new InvalidOperationException("No data"); } if(_resultSet == null) { _resultSet = new object[fieldCount]; } if(_resultSet[col] == null) { int length; short sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_BINARY, (StringBuilder)null, 0, out length); if(length == DB2Constants.SQL_NULL_DATA) { _resultSet[col] = DBNull.Value; } else { byte[] result = new byte[length]; sqlRet = DB2CLIWrapper.SQLGetData(this.hwndStmt, (short)(col + 1), (short)DB2Constants.SQL_C_TYPE_BINARY, result, length, out length); _resultSet[col] = result; } } return _resultSet[col]; } } } #endregion