1433 lines
37 KiB
C#
Raw Normal View History

//
// System.Data.SqlClient.SqlDataReader.cs
//
// Author:
// Rodrigo Moya (rodrigo@ximian.com)
// Daniel Morgan (danmorg@sc.rr.com)
// Tim Coleman (tim@timcoleman.com)
//
// (C) Ximian, Inc 2002
// (C) Daniel Morgan 2002
// Copyright (C) Tim Coleman, 2002
//
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// 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 Mono.Data.Tds.Protocol;
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Globalization;
using System.Xml;
namespace System.Data.SqlClient
{
public class SqlDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord
{
#region Fields
SqlCommand command;
bool disposed;
bool isClosed;
bool moreResults;
int resultsRead;
int rowsRead;
DataTable schemaTable;
bool haveRead;
bool readResult;
bool readResultUsed;
int visibleFieldCount;
#endregion // Fields
const int COLUMN_NAME_IDX = 0;
const int COLUMN_ORDINAL_IDX = 1;
const int COLUMN_SIZE_IDX = 2;
const int NUMERIC_PRECISION_IDX = 3;
const int NUMERIC_SCALE_IDX = 4;
const int IS_UNIQUE_IDX = 5;
const int IS_KEY_IDX = 6;
const int BASE_SERVER_NAME_IDX = 7;
const int BASE_CATALOG_NAME_IDX = 8;
const int BASE_COLUMN_NAME_IDX = 9;
const int BASE_SCHEMA_NAME_IDX = 10;
const int BASE_TABLE_NAME_IDX = 11;
const int DATA_TYPE_IDX = 12;
const int ALLOW_DBNULL_IDX = 13;
const int PROVIDER_TYPE_IDX = 14;
const int IS_ALIASED_IDX = 15;
const int IS_EXPRESSION_IDX = 16;
const int IS_IDENTITY_IDX = 17;
const int IS_AUTO_INCREMENT_IDX = 18;
const int IS_ROW_VERSION_IDX = 19;
const int IS_HIDDEN_IDX = 20;
const int IS_LONG_IDX = 21;
const int IS_READ_ONLY_IDX = 22;
const int PROVIDER_SPECIFIC_TYPE_IDX = 23;
const int DATA_TYPE_NAME_IDX = 24;
const int XML_SCHEMA_COLLCTN_DB_IDX = 25;
const int XML_SCHEMA_COLLCTN_OWN_SCHEMA_IDX = 26;
const int XML_SCHEMA_COLLCTN_NAME_IDX = 27;
const int UDT_ASMBLY_QUALIFIED_NAME_IDX = 28;
const int NON_VER_PROVIDER_TYPE_IDX = 29;
const int IS_COLUMN_SET = 30;
#region Constructors
internal SqlDataReader (SqlCommand command)
{
this.command = command;
command.Tds.RecordsAffected = -1;
NextResult ();
}
#endregion // Constructors
#region Properties
public
override
int Depth {
get { return 0; }
}
public
override
int FieldCount {
get {
ValidateState ();
return command.Tds.Columns.Count;
}
}
public
override
bool IsClosed {
get { return isClosed; }
}
public
override
object this [int i] {
get { return GetValue (i); }
}
public
override
object this [string name] {
get { return GetValue (GetOrdinal (name)); }
}
public
override
int RecordsAffected {
get {
return command.Tds.RecordsAffected;
}
}
public
override
bool HasRows {
get {
ValidateState ();
if (rowsRead > 0)
return true;
if (!haveRead)
readResult = ReadRecord ();
return readResult;
}
}
public override int VisibleFieldCount {
get { return visibleFieldCount; }
}
protected SqlConnection Connection {
get { return command.Connection; }
}
protected bool IsCommandBehavior (CommandBehavior condition) {
return condition == command.CommandBehavior;
}
#endregion // Properties
#region Methods
public
override
void Close ()
{
if (IsClosed)
return;
// skip to end & read output parameters.
while (NextResult ())
;
isClosed = true;
command.CloseDataReader ();
}
private static DataTable ConstructSchemaTable ()
{
Type booleanType = typeof (bool);
Type stringType = typeof (string);
Type intType = typeof (int);
Type typeType = typeof (Type);
Type shortType = typeof (short);
DataTable schemaTable = new DataTable ("SchemaTable");
schemaTable.Columns.Add ("ColumnName", stringType);
schemaTable.Columns.Add ("ColumnOrdinal", intType);
schemaTable.Columns.Add ("ColumnSize", intType);
schemaTable.Columns.Add ("NumericPrecision", shortType);
schemaTable.Columns.Add ("NumericScale", shortType);
schemaTable.Columns.Add ("IsUnique", booleanType);
schemaTable.Columns.Add ("IsKey", booleanType);
schemaTable.Columns.Add ("BaseServerName", stringType);
schemaTable.Columns.Add ("BaseCatalogName", stringType);
schemaTable.Columns.Add ("BaseColumnName", stringType);
schemaTable.Columns.Add ("BaseSchemaName", stringType);
schemaTable.Columns.Add ("BaseTableName", stringType);
schemaTable.Columns.Add ("DataType", typeType);
schemaTable.Columns.Add ("AllowDBNull", booleanType);
schemaTable.Columns.Add ("ProviderType", intType);
schemaTable.Columns.Add ("IsAliased", booleanType);
schemaTable.Columns.Add ("IsExpression", booleanType);
schemaTable.Columns.Add ("IsIdentity", booleanType);
schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
schemaTable.Columns.Add ("IsRowVersion", booleanType);
schemaTable.Columns.Add ("IsHidden", booleanType);
schemaTable.Columns.Add ("IsLong", booleanType);
schemaTable.Columns.Add ("IsReadOnly", booleanType);
schemaTable.Columns.Add ("ProviderSpecificDataType", typeType);
schemaTable.Columns.Add ("DataTypeName", stringType);
schemaTable.Columns.Add ("XmlSchemaCollectionDatabase", stringType);
schemaTable.Columns.Add ("XmlSchemaCollectionOwningSchema", stringType);
schemaTable.Columns.Add ("XmlSchemaCollectionName", stringType);
schemaTable.Columns.Add ("UdtAssemblyQualifiedName", stringType);
schemaTable.Columns.Add ("NonVersionedProviderType", intType);
schemaTable.Columns.Add ("IsColumnSet", booleanType);
return schemaTable;
}
private string GetSchemaRowTypeName (TdsColumnType ctype, int csize, short precision, short scale)
{
int dbType;
bool isLong;
Type fieldType;
string typeName;
GetSchemaRowType (ctype, csize, precision, scale,
out dbType, out fieldType, out isLong,
out typeName);
return typeName;
}
private Type GetSchemaRowFieldType (TdsColumnType ctype, int csize, short precision, short scale)
{
int dbType;
bool isLong;
Type fieldType;
string typeName;
GetSchemaRowType (ctype, csize, precision, scale,
out dbType, out fieldType, out isLong,
out typeName);
return fieldType;
}
SqlDbType GetSchemaRowDbType (int ordinal)
{
int csize;
short precision, scale;
TdsColumnType ctype;
TdsDataColumn column;
if (ordinal < 0 || ordinal >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
column = command.Tds.Columns [ordinal];
ctype = (TdsColumnType) column.ColumnType;
csize = (int) column.ColumnSize;
precision = (short) (column.NumericPrecision ?? 0);
scale = (short) (column.NumericScale ?? 0);
return GetSchemaRowDbType (ctype, csize, precision, scale);
}
private SqlDbType GetSchemaRowDbType (TdsColumnType ctype, int csize, short precision, short scale)
{
Type fieldType;
bool isLong;
string typeName;
int dbType;
GetSchemaRowType (ctype, csize, precision, scale,
out dbType, out fieldType, out isLong,
out typeName);
return (SqlDbType) dbType;
}
private void GetSchemaRowType (TdsColumnType ctype, int csize,
short precision, short scale,
out int dbType, out Type fieldType,
out bool isLong, out string typeName)
{
dbType = -1;
typeName = string.Empty;
isLong = false;
fieldType = typeof (Type);
switch (ctype) {
case TdsColumnType.Int1:
case TdsColumnType.Int2:
case TdsColumnType.Int4:
case TdsColumnType.IntN:
case TdsColumnType.BigInt:
switch (csize) {
case 1:
typeName = "tinyint";
dbType = (int) SqlDbType.TinyInt;
fieldType = typeof (byte);
isLong = false;
break;
case 2:
typeName = "smallint";
dbType = (int) SqlDbType.SmallInt;
fieldType = typeof (short);
isLong = false;
break;
case 4:
typeName = "int";
dbType = (int) SqlDbType.Int;
fieldType = typeof (int);
isLong = false;
break;
case 8:
typeName = "bigint";
dbType = (int) SqlDbType.BigInt;
fieldType = typeof (long);
isLong = false;
break;
}
break;
case TdsColumnType.Real:
case TdsColumnType.Float8:
case TdsColumnType.FloatN:
switch (csize) {
case 4:
typeName = "real";
dbType = (int) SqlDbType.Real;
fieldType = typeof (float);
isLong = false;
break;
case 8:
typeName = "float";
dbType = (int) SqlDbType.Float;
fieldType = typeof (double);
isLong = false;
break;
}
break;
case TdsColumnType.Image :
typeName = "image";
dbType = (int) SqlDbType.Image;
fieldType = typeof (byte[]);
isLong = true;
break;
case TdsColumnType.Text :
typeName = "text";
dbType = (int) SqlDbType.Text;
fieldType = typeof (string);
isLong = true;
break;
case TdsColumnType.UniqueIdentifier :
typeName = "uniqueidentifier";
dbType = (int) SqlDbType.UniqueIdentifier;
fieldType = typeof (Guid);
isLong = false;
break;
case TdsColumnType.VarBinary :
case TdsColumnType.BigVarBinary :
typeName = "varbinary";
dbType = (int) SqlDbType.VarBinary;
fieldType = typeof (byte[]);
isLong = false;
break;
case TdsColumnType.VarChar :
case TdsColumnType.BigVarChar :
typeName = "varchar";
dbType = (int) SqlDbType.VarChar;
fieldType = typeof (string);
isLong = false;
break;
case TdsColumnType.Binary :
case TdsColumnType.BigBinary :
typeName = "binary";
dbType = (int) SqlDbType.Binary;
fieldType = typeof (byte[]);
isLong = false;
break;
case TdsColumnType.Char :
case TdsColumnType.BigChar :
typeName = "char";
dbType = (int) SqlDbType.Char;
fieldType = typeof (string);
isLong = false;
break;
case TdsColumnType.Bit :
case TdsColumnType.BitN :
typeName = "bit";
dbType = (int) SqlDbType.Bit;
fieldType = typeof (bool);
isLong = false;
break;
case TdsColumnType.DateTime4 :
case TdsColumnType.DateTime :
case TdsColumnType.DateTimeN :
switch (csize) {
case 4:
typeName = "smalldatetime";
dbType = (int) SqlDbType.SmallDateTime;
fieldType = typeof (DateTime);
isLong = false;
break;
case 8:
typeName = "datetime";
dbType = (int) SqlDbType.DateTime;
fieldType = typeof (DateTime);
isLong = false;
break;
}
break;
case TdsColumnType.Money :
case TdsColumnType.MoneyN :
case TdsColumnType.Money4 :
switch (csize) {
case 4:
typeName = "smallmoney";
dbType = (int) SqlDbType.SmallMoney;
fieldType = typeof (decimal);
isLong = false;
break;
case 8:
typeName = "money";
dbType = (int) SqlDbType.Money;
fieldType = typeof (decimal);
isLong = false;
break;
}
break;
case TdsColumnType.NText :
typeName = "ntext";
dbType = (int) SqlDbType.NText;
fieldType = typeof (string);
isLong = true;
break;
case TdsColumnType.NVarChar :
typeName = "nvarchar";
dbType = (int) SqlDbType.NVarChar;
fieldType = typeof (string);
isLong = false;
break;
case TdsColumnType.Decimal :
case TdsColumnType.Numeric :
// TDS 7.0 returns bigint as decimal(19,0)
if (precision == 19 && scale == 0) {
typeName = "bigint";
dbType = (int) SqlDbType.BigInt;
fieldType = typeof (long);
} else {
typeName = "decimal";
dbType = (int) SqlDbType.Decimal;
fieldType = typeof (decimal);
}
isLong = false;
break;
case TdsColumnType.NChar :
typeName = "nchar";
dbType = (int) SqlDbType.NChar;
fieldType = typeof (string);
isLong = false;
break;
case TdsColumnType.SmallMoney :
typeName = "smallmoney";
dbType = (int) SqlDbType.SmallMoney;
fieldType = typeof (decimal);
isLong = false;
break;
default :
typeName = "variant";
dbType = (int) SqlDbType.Variant;
fieldType = typeof (object);
isLong = false;
break;
}
}
new
void Dispose (bool disposing)
{
if (!disposed) {
if (disposing) {
if (schemaTable != null)
schemaTable.Dispose ();
Close ();
command = null;
}
disposed = true;
}
}
public
override
bool GetBoolean (int i)
{
object value = GetValue (i);
if (!(value is bool)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (bool) value;
}
public
override
byte GetByte (int i)
{
object value = GetValue (i);
if (!(value is byte)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (byte) value;
}
public
override
long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
{
if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
ValidateState ();
EnsureDataAvailable ();
try {
long len = ((Tds)command.Tds).GetSequentialColumnValue (i, dataIndex, buffer, bufferIndex, length);
if (len == -1)
throw CreateGetBytesOnInvalidColumnTypeException (i);
if (len == -2)
throw new SqlNullValueException ();
return len;
} catch (TdsInternalException ex) {
command.Connection.Close ();
throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
}
}
object value = GetValue (i);
if (!(value is byte [])) {
SqlDbType type = GetSchemaRowDbType (i);
switch (type) {
case SqlDbType.Image:
if (value is DBNull)
throw new SqlNullValueException ();
break;
case SqlDbType.Text:
string text = value as string;
if (text != null)
value = Encoding.Default.GetBytes (text);
else
value = null;
break;
case SqlDbType.NText:
string ntext = value as string;
if (ntext != null)
value = Encoding.Unicode.GetBytes (ntext);
else
value = null;
break;
default:
throw CreateGetBytesOnInvalidColumnTypeException (i);
}
}
if (buffer == null)
return ((byte []) value).Length; // Return length of data
// Copy data into buffer
int availLen = (int) ( ( (byte []) value).Length - dataIndex);
if (availLen < length)
length = availLen;
if (dataIndex < 0)
return 0;
Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
return length; // return actual read count
}
[EditorBrowsableAttribute (EditorBrowsableState.Never)]
public
override
char GetChar (int i)
{
throw new NotSupportedException ();
}
public
override
long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
{
if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
ValidateState ();
EnsureDataAvailable ();
if (i < 0 || i >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
Encoding encoding = null;
byte mul = 1;
TdsColumnType colType = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
switch (colType) {
case TdsColumnType.Text :
case TdsColumnType.VarChar:
case TdsColumnType.Char:
case TdsColumnType.BigVarChar:
encoding = Encoding.ASCII;
break;
case TdsColumnType.NText :
case TdsColumnType.NVarChar:
case TdsColumnType.NChar:
encoding = Encoding.Unicode;
mul = 2;
break;
default :
return -1;
}
long count = 0;
if (buffer == null) {
count = GetBytes (i,0,(byte[]) null,0,0);
return (count/mul);
}
length *= mul;
byte[] arr = new byte [length];
count = GetBytes (i, dataIndex, arr, 0, length);
if (count == -1)
throw new InvalidCastException ("Specified cast is not valid");
Char[] val = encoding.GetChars (arr, 0, (int)count);
val.CopyTo (buffer, bufferIndex);
return val.Length;
}
char [] valueBuffer;
object value = GetValue (i);
if (value is char[])
valueBuffer = (char[])value;
else if (value is string)
valueBuffer = ((string)value).ToCharArray();
else {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
if ( buffer == null ) {
// Return length of data
return valueBuffer.Length;
}
else {
// Copy data into buffer
Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length);
return valueBuffer.Length - dataIndex;
}
}
public
override
string GetDataTypeName (int i)
{
TdsDataColumn column;
TdsColumnType ctype;
int csize;
short precision;
short scale;
ValidateState ();
if (i < 0 || i >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
column = command.Tds.Columns [i];
ctype = (TdsColumnType) column.ColumnType;
csize = (int) column.ColumnSize;
precision = (short) (column.NumericPrecision ?? 0);
scale = (short) (column.NumericScale ?? 0);
return GetSchemaRowTypeName (ctype, csize, precision, scale);
}
public
override
DateTime GetDateTime (int i)
{
object value = GetValue (i);
if (!(value is DateTime)) {
if (value is DBNull) throw new SqlNullValueException ();
else throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (DateTime) value;
}
[MonoTODO]
public virtual DateTimeOffset GetDateTimeOffset (int i)
{
throw new NotImplementedException ();
}
[MonoTODO]
public virtual TimeSpan GetTimeSpan (int i)
{
throw new NotImplementedException ();
}
[MonoTODO]
public virtual SqlChars GetSqlChars (int i)
{
throw new NotImplementedException ();
}
public
override
decimal GetDecimal (int i)
{
object value = GetValue (i);
if (!(value is decimal)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (decimal) value;
}
public
override
double GetDouble (int i)
{
object value = GetValue (i);
if (!(value is double)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (double) value;
}
public
override
Type GetFieldType (int i)
{
TdsDataColumn column;
TdsColumnType ctype;
int csize;
short precision;
short scale;
ValidateState ();
if (i < 0 || i >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
column = command.Tds.Columns [i];
ctype = (TdsColumnType) column.ColumnType;
csize = (int) column.ColumnSize;
precision = (short) (column.NumericPrecision ?? 0);
scale = (short) (column.NumericScale ?? 0);
return GetSchemaRowFieldType (ctype, csize, precision,
scale);
}
public
override
float GetFloat (int i)
{
object value = GetValue (i);
if (!(value is float)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (float) value;
}
public
override
Guid GetGuid (int i)
{
object value = GetValue (i);
if (!(value is Guid)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (Guid) value;
}
public
override
short GetInt16 (int i)
{
object value = GetValue (i);
if (!(value is short)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (short) value;
}
public
override
int GetInt32 (int i)
{
object value = GetValue (i);
if (!(value is int)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (int) value;
}
public
override
long GetInt64 (int i)
{
object value = GetValue (i);
if (!(value is long)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (long) value;
}
public
override
string GetName (int i)
{
ValidateState ();
if (i < 0 || i >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
return (string) command.Tds.Columns[i].ColumnName;
}
public
override
int GetOrdinal (string name)
{
ValidateState ();
if (name == null)
throw new ArgumentNullException ("fieldName");
string colName;
foreach (TdsDataColumn schema in command.Tds.Columns) {
colName = schema.ColumnName;
if (colName.Equals (name) || String.Compare (colName, name, true) == 0)
return (int) schema.ColumnOrdinal;
}
throw new IndexOutOfRangeException ();
}
public
override
DataTable GetSchemaTable ()
{
ValidateState ();
if (schemaTable == null)
schemaTable = ConstructSchemaTable ();
if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
return schemaTable;
if (!moreResults)
return null;
foreach (TdsDataColumn schema in command.Tds.Columns) {
DataRow row = schemaTable.NewRow ();
row [COLUMN_NAME_IDX] = GetSchemaValue (schema.ColumnName);
row [COLUMN_ORDINAL_IDX] = GetSchemaValue (schema.ColumnOrdinal);
row [IS_UNIQUE_IDX] = GetSchemaValue (schema.IsUnique);
row [IS_AUTO_INCREMENT_IDX] = GetSchemaValue (schema.IsAutoIncrement);
row [IS_ROW_VERSION_IDX] = GetSchemaValue (schema.IsRowVersion);
row [IS_HIDDEN_IDX] = GetSchemaValue (schema.IsHidden);
row [IS_IDENTITY_IDX] = GetSchemaValue (schema.IsIdentity);
row [NUMERIC_PRECISION_IDX] = GetSchemaValue (schema.NumericPrecision);
row [IS_KEY_IDX] = GetSchemaValue (schema.IsKey);
row [IS_ALIASED_IDX] = GetSchemaValue (schema.IsAliased);
row [IS_EXPRESSION_IDX] = GetSchemaValue (schema.IsExpression);
row [IS_READ_ONLY_IDX] = GetSchemaValue (schema.IsReadOnly);
row [BASE_SERVER_NAME_IDX] = GetSchemaValue (schema.BaseServerName);
row [BASE_CATALOG_NAME_IDX] = GetSchemaValue (schema.BaseCatalogName);
row [BASE_COLUMN_NAME_IDX] = GetSchemaValue (schema.BaseColumnName);
row [BASE_SCHEMA_NAME_IDX] = GetSchemaValue (schema.BaseSchemaName);
row [BASE_TABLE_NAME_IDX] = GetSchemaValue (schema.BaseTableName);
row [ALLOW_DBNULL_IDX] = GetSchemaValue (schema.AllowDBNull);
row [PROVIDER_SPECIFIC_TYPE_IDX] = DBNull.Value;
row [DATA_TYPE_NAME_IDX] = GetSchemaValue (schema.DataTypeName);
row [XML_SCHEMA_COLLCTN_DB_IDX] = DBNull.Value;
row [XML_SCHEMA_COLLCTN_OWN_SCHEMA_IDX] = DBNull.Value;
row [XML_SCHEMA_COLLCTN_NAME_IDX] = DBNull.Value;
row [UDT_ASMBLY_QUALIFIED_NAME_IDX] = DBNull.Value;
row [NON_VER_PROVIDER_TYPE_IDX] = DBNull.Value;
row [IS_COLUMN_SET] = DBNull.Value;
// We don't always get the base column name.
if (row [BASE_COLUMN_NAME_IDX] == DBNull.Value)
row [BASE_COLUMN_NAME_IDX] = row [COLUMN_NAME_IDX];
TdsColumnType ctype;
int csize, dbType;
Type fieldType;
bool isLong;
string typeName;
short precision;
short scale;
ctype = (TdsColumnType) schema.ColumnType;
csize = (int) schema.ColumnSize;
precision = (short) GetSchemaValue (schema.NumericPrecision);
scale = (short) GetSchemaValue (schema.NumericScale);
GetSchemaRowType (ctype, csize, precision, scale,
out dbType, out fieldType, out isLong,
out typeName);
row [COLUMN_SIZE_IDX] = csize;
row [NUMERIC_PRECISION_IDX] = precision;
row [NUMERIC_SCALE_IDX] = scale;
row [PROVIDER_TYPE_IDX] = dbType;
row [DATA_TYPE_IDX] = fieldType;
row [IS_LONG_IDX] = isLong;
if ((bool)row [IS_HIDDEN_IDX] == false)
visibleFieldCount += 1;
schemaTable.Rows.Add (row);
}
return schemaTable;
}
private static object GetSchemaValue (TdsDataColumn schema, string key)
{
object val = schema [key];
if (val != null)
return val;
else
return DBNull.Value;
}
static object GetSchemaValue (object value)
{
if (value == null)
return DBNull.Value;
return value;
}
public
virtual
SqlBinary GetSqlBinary (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlBinary))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlBinary) value;
}
public
virtual
SqlBoolean GetSqlBoolean (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlBoolean))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlBoolean) value;
}
public
virtual
SqlByte GetSqlByte (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlByte))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlByte) value;
}
public
virtual
SqlDateTime GetSqlDateTime (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlDateTime))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlDateTime) value;
}
public
virtual
SqlDecimal GetSqlDecimal (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlDecimal))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlDecimal) value;
}
public
virtual
SqlDouble GetSqlDouble (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlDouble))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlDouble) value;
}
public
virtual
SqlGuid GetSqlGuid (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlGuid))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlGuid) value;
}
public
virtual
SqlInt16 GetSqlInt16 (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlInt16))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlInt16) value;
}
public
virtual
SqlInt32 GetSqlInt32 (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlInt32))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlInt32) value;
}
public
virtual
SqlInt64 GetSqlInt64 (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlInt64))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlInt64) value;
}
public
virtual
SqlMoney GetSqlMoney (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlMoney))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlMoney) value;
}
public
virtual
SqlSingle GetSqlSingle (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlSingle))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlSingle) value;
}
public
virtual
SqlString GetSqlString (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlString))
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
return (SqlString) value;
}
public virtual SqlXml GetSqlXml (int i)
{
object value = GetSqlValue (i);
if (!(value is SqlXml)) {
if (value is DBNull) {
throw new SqlNullValueException ();
} else if (command.Tds.TdsVersion <= TdsVersion.tds80 && value is SqlString) {
// Workaround for TDS 7/8/8.1 clients
// Xml column types are supported only from Sql Server 2005 / TDS 9, however
// when a TDS 7/8/8.1 client requests for Xml column data, Sql Server 2005 returns
// it as NTEXT
MemoryStream stream = null;
if (!((SqlString) value).IsNull)
stream = new MemoryStream (Encoding.Unicode.GetBytes (value.ToString()));
value = new SqlXml (stream);
} else {
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
}
return (SqlXml) value;
}
public
virtual
object GetSqlValue (int i)
{
object value = GetValue (i);
//Console.WriteLine ("Type of value: {0}", value.GetType ());
SqlDbType type = GetSchemaRowDbType (i);
switch (type) {
case SqlDbType.BigInt:
if (value == DBNull.Value)
return SqlInt64.Null;
return (SqlInt64) ((long) value);
case SqlDbType.Binary:
case SqlDbType.Image:
case SqlDbType.VarBinary:
case SqlDbType.Timestamp:
if (value == DBNull.Value)
return SqlBinary.Null;
return (SqlBinary) ((byte[]) value);
case SqlDbType.Bit:
if (value == DBNull.Value)
return SqlBoolean.Null;
return (SqlBoolean) ((bool) value);
case SqlDbType.Char:
case SqlDbType.NChar:
case SqlDbType.NText:
case SqlDbType.NVarChar:
case SqlDbType.Text:
case SqlDbType.VarChar:
if (value == DBNull.Value)
return SqlString.Null;
return (SqlString) ((string) value);
case SqlDbType.DateTime:
case SqlDbType.SmallDateTime:
if (value == DBNull.Value)
return SqlDateTime.Null;
return (SqlDateTime) ((DateTime) value);
case SqlDbType.Decimal:
if (value == DBNull.Value)
return SqlDecimal.Null;
if (value is TdsBigDecimal)
return SqlDecimalExtensions.FromTdsBigDecimal ((TdsBigDecimal) value);
if (value is Int64)
return (SqlDecimal)((long) value);
return (SqlDecimal) ((decimal) value);
case SqlDbType.Float:
if (value == DBNull.Value)
return SqlDouble.Null;
return (SqlDouble) ((double) value);
case SqlDbType.Int:
if (value == DBNull.Value)
return SqlInt32.Null;
return (SqlInt32) ((int) value);
case SqlDbType.Money:
case SqlDbType.SmallMoney:
if (value == DBNull.Value)
return SqlMoney.Null;
return (SqlMoney) ((decimal) value);
case SqlDbType.Real:
if (value == DBNull.Value)
return SqlSingle.Null;
return (SqlSingle) ((float) value);
case SqlDbType.UniqueIdentifier:
if (value == DBNull.Value)
return SqlGuid.Null;
return (SqlGuid) ((Guid) value);
case SqlDbType.SmallInt:
if (value == DBNull.Value)
return SqlInt16.Null;
return (SqlInt16) ((short) value);
case SqlDbType.TinyInt:
if (value == DBNull.Value)
return SqlByte.Null;
return (SqlByte) ((byte) value);
case SqlDbType.Xml:
if (value == DBNull.Value)
return SqlByte.Null;
return (SqlXml) value;
}
throw new InvalidOperationException ("The type of this column is unknown.");
}
public
virtual
int GetSqlValues (object[] values)
{
ValidateState ();
EnsureDataAvailable ();
if (values == null)
throw new ArgumentNullException ("values");
int count = 0;
int columnCount = command.Tds.Columns.Count;
int arrayCount = values.Length;
if (arrayCount > columnCount)
count = columnCount;
else
count = arrayCount;
for (int i = 0; i < count; i += 1)
values [i] = GetSqlValue (i);
return count;
}
public
override
string GetString (int i)
{
object value = GetValue (i);
if (!(value is string)) {
if (value is DBNull) throw new SqlNullValueException ();
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
return (string) value;
}
public
override
object GetValue (int i)
{
ValidateState ();
EnsureDataAvailable ();
if (i < 0 || i >= command.Tds.Columns.Count)
throw new IndexOutOfRangeException ();
try {
if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
return ((Tds)command.Tds).GetSequentialColumnValue (i);
}
} catch (TdsInternalException ex) {
command.Connection.Close ();
throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
}
return command.Tds.ColumnValues [i];
}
public
override
int GetValues (object[] values)
{
ValidateState ();
EnsureDataAvailable ();
if (values == null)
throw new ArgumentNullException ("values");
int len = values.Length;
var tds = command.Tds;
int columns = Math.Min (len, tds.Columns.Count);
if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
for (int i = 0; i < columns; ++i) {
values [i] = tds.GetSequentialColumnValue (i);
}
} else {
int bigDecimalIndex = tds.ColumnValues.BigDecimalIndex;
// If a four-byte decimal is stored, then we can't convert to
// a native type. Throw an OverflowException.
if (bigDecimalIndex >= 0 && bigDecimalIndex < len)
throw new OverflowException ();
try {
tds.ColumnValues.CopyTo (0, values, 0, columns);
} catch (TdsInternalException ex) {
command.Connection.Close ();
throw SqlException.FromTdsInternalException ((TdsInternalException)ex);
}
}
return columns;
}
public override IEnumerator GetEnumerator ()
{
return new DbEnumerator (this);
}
public
override
bool IsDBNull (int i)
{
return GetValue (i) == DBNull.Value;
}
public
override
bool NextResult ()
{
ValidateState ();
if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0) {
moreResults = false;
rowsRead = 0;
haveRead = false;
return false;
}
try {
moreResults = command.Tds.NextResult ();
} catch (TdsInternalException ex) {
command.Connection.Close ();
throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
}
if (!moreResults)
command.GetOutputParameters ();
else {
// new schema - don't do anything except reset schemaTable as command.Tds.Columns is already updated
schemaTable = null;
}
rowsRead = 0;
haveRead = false;
resultsRead += 1;
return moreResults;
}
public
override
bool Read ()
{
ValidateState ();
if (!haveRead || readResultUsed)
readResult = ReadRecord ();
readResultUsed = true;
return readResult;
}
internal bool ReadRecord ()
{
readResultUsed = false;
if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && haveRead)
return false;
if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
return false;
if (!moreResults)
return false;
try {
bool result = command.Tds.NextRow ();
if (result)
rowsRead++;
haveRead = true;
return result;
} catch (TdsInternalException ex) {
command.Connection.Close ();
throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
}
}
void ValidateState ()
{
if (IsClosed)
throw new InvalidOperationException ("Invalid attempt to read data when reader is closed");
}
void EnsureDataAvailable ()
{
if (!readResult || !haveRead || !readResultUsed)
throw new InvalidOperationException ("No data available.");
}
InvalidCastException CreateGetBytesOnInvalidColumnTypeException (int ordinal)
{
string message = string.Format (CultureInfo.InvariantCulture,
"Invalid attempt to GetBytes on column '{0}'." +
"The GetBytes function can only be used on " +
"columns of type Text, NText, or Image.",
GetName (ordinal));
return new InvalidCastException (message);
}
public override Type GetProviderSpecificFieldType (int i)
{
return (GetSqlValue (i).GetType());
}
public override object GetProviderSpecificValue (int i)
{
return (GetSqlValue (i));
}
public override int GetProviderSpecificValues (object [] values)
{
return (GetSqlValues (values));
}
public virtual SqlBytes GetSqlBytes (int i)
{
//object value = GetSqlValue (i);
//if (!(value is SqlBinary))
// throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
Byte[] val = (byte[])GetValue(i);
SqlBytes sb = new SqlBytes (val);
return (sb);
}
[MonoTODO]
public override T GetFieldValue<T> (int i)
{
throw new NotImplementedException ();
}
[MonoTODO]
public virtual XmlReader GetXmlReader (int i)
{
throw new NotImplementedException ();
}
#endregion // Methods
}
}