3 using System
.Data
.SqlTypes
;
4 using System
.Data
.SqlClient
;
5 using System
.Collections
;
6 using System
.Collections
.Generic
;
7 using SCG
= System
.Collections
.Generic
;
14 * A wrapper that will make any object implicitly castable into various
17 * This is useful because IDataRecord is pretty terrible at giving you
18 * objects in the form you actually want. If I ask for an integer, I
19 * want you to *try really hard* to give me an integer, for example by
20 * converting a string to an int. But IDataRecord simply throws an
21 * exception if the object wasn't already an integer.
23 * When converting to bool, we assume any non-zero int is true, just
24 * like C/C++ would do.
26 public struct WvAutoCast
: IEnumerable
<WvAutoCast
>
29 public static readonly WvAutoCast _null
= new WvAutoCast(null);
31 public WvAutoCast(object v
)
36 public bool IsNull { get { return v == null || v is DBNull; }
}
38 public static implicit operator string(WvAutoCast o
)
46 public override string ToString()
49 return "(nil)"; // shouldn't return null since this != null
50 else if (v
is Boolean
)
51 return intify().ToString();
52 else if (v
is IEnumerable
<WvAutoCast
>)
53 return "[" + this.Join(",") + "]";
58 public static implicit operator DateTime(WvAutoCast o
)
61 return DateTime
.MinValue
;
62 else if (o
.v
is DateTime
)
64 else if (o
.v
is SqlDateTime
)
65 return ((SqlDateTime
)o
.v
).Value
;
70 public static implicit operator SqlDateTime(WvAutoCast o
)
73 return SqlDateTime
.MinValue
;
74 else if (o
.v
is SqlDateTime
)
75 return (SqlDateTime
)o
.v
;
76 else if (o
.v
is DateTime
)
82 public static implicit operator byte[](WvAutoCast o
)
87 public static implicit operator SqlBinary(WvAutoCast o
)
91 else if (o
.v
is SqlBinary
)
92 return (SqlBinary
)o
.v
;
94 return new SqlBinary((byte[])o
.v
);
101 else if (v
is Int64
|| v
is Int32
|| v
is Int16
102 || v
is UInt64
|| v
is UInt32
|| v
is UInt16
103 || v
is byte || v
is bool)
121 else if (v
is Boolean
)
122 return (Boolean
)v
? 1 : 0;
127 public static implicit operator Int64(WvAutoCast o
)
132 public static implicit operator Int32(WvAutoCast o
)
134 return (Int32
)o
.intify();
137 public static implicit operator Int16(WvAutoCast o
)
139 return (Int16
)o
.intify();
142 public static implicit operator UInt64(WvAutoCast o
)
144 return (UInt64
) o
.intify();
147 public static implicit operator UInt32(WvAutoCast o
)
149 return (UInt32
)o
.intify();
152 public static implicit operator UInt16(WvAutoCast o
)
154 return (UInt16
)o
.intify();
157 public static implicit operator Byte(WvAutoCast o
)
159 return (Byte
)o
.intify();
162 public static implicit operator bool(WvAutoCast o
)
164 return o
.intify() != 0;
167 public static implicit operator double(WvAutoCast o
)
171 else if (o
.v
is double)
173 else if (o
.v
is Int64
)
175 else if (o
.v
is Int32
)
177 else if (o
.v
is Int16
)
179 else if (o
.v
is Byte
)
181 else if (o
.v
is Boolean
)
182 return (Boolean
)o
.v
? 1.0 : 0.0;
187 public static implicit operator float(WvAutoCast o
)
191 else if (o
.v
is float)
194 return (float)(double)o
;
197 public static implicit operator char(WvAutoCast o
)
200 return Char
.MinValue
;
201 else if (o
.v
is char || o
.isint())
202 return (char)o
.intify();
204 return Char
.MinValue
;
207 public static implicit operator Decimal(WvAutoCast o
)
211 else if (o
.v
is UInt64
)
212 return new Decimal((UInt64
)o
.v
);
214 return new Decimal(o
.intify());
215 else if (o
.v
is double || o
.v
is float)
216 return new Decimal((double)o
);
218 return Decimal
.MinValue
;
221 public static implicit operator SqlDecimal(WvAutoCast o
)
223 if (o
.v
is SqlDecimal
)
224 return (SqlDecimal
)o
.v
;
229 public static implicit operator Guid(WvAutoCast o
)
233 else if (o
.v
is SqlGuid
)
234 return ((SqlGuid
)o
.v
).Value
;
239 public static implicit operator SqlGuid(WvAutoCast o
)
243 else if (o
.v
is Guid
)
249 public object to(Type t
)
251 if (t
== typeof(string))
253 else if (t
== typeof(DateTime
))
254 return (DateTime
)this;
255 else if (t
== typeof(SqlDateTime
))
256 return (SqlDateTime
)this;
257 else if (t
== typeof(byte[]))
259 else if (t
== typeof(SqlBinary
))
260 return (SqlBinary
)this;
261 else if (t
== typeof(Int64
))
263 else if (t
== typeof(UInt64
))
265 else if (t
== typeof(Int32
))
267 else if (t
== typeof(UInt32
))
269 else if (t
== typeof(Int16
))
271 else if (t
== typeof(UInt16
))
273 else if (t
== typeof(byte))
275 else if (t
== typeof(bool))
277 else if (t
== typeof(double))
279 else if (t
== typeof(float))
281 else if (t
== typeof(char))
283 else if (t
== typeof(Decimal
))
284 return (Decimal
)this;
285 else if (t
== typeof(SqlDecimal
))
286 return (SqlDecimal
)this;
287 else if (t
== typeof(Guid
))
289 else if (t
== typeof(SqlGuid
))
290 return (SqlGuid
)this;
295 IEnumerable
<object> _iter()
297 if (!IsNull
&& v
is IEnumerable
)
299 foreach (object i
in (IEnumerable
)v
)
302 yield return ((WvAutoCast
)i
).inner
;
309 IEnumerator System
.Collections
.IEnumerable
.GetEnumerator()
311 foreach (var i
in _iter())
315 public IEnumerator
<WvAutoCast
> GetEnumerator()
317 foreach (object i
in _iter())
318 yield return new WvAutoCast(i
);
323 public struct WvColInfo
327 public bool nullable
;
329 public short precision
;
332 public static IEnumerable
<WvColInfo
> FromDataTable(DataTable schema
)
334 foreach (DataRow col
in schema
.Rows
)
335 yield return new WvColInfo(col
);
338 public WvColInfo(string name
, Type type
, bool nullable
,
339 int size
, short precision
, short scale
)
343 this.nullable
= nullable
;
345 this.precision
= precision
;
349 WvColInfo(DataRow data
)
351 name
= (string)data
["ColumnName"];
352 type
= (Type
) data
["DataType"];
353 nullable
= (bool) data
["AllowDBNull"];
354 size
= (int) wv
.atoi(data
["ColumnSize"]);
355 precision
= (short) wv
.atoi(data
["NumericPrecision"]);
356 scale
= (short) wv
.atoi(data
["NumericScale"]);
361 public class WvSqlRow
: IEnumerable
<WvAutoCast
>
365 Dictionary
<string,int> colnames
= null;
367 public WvSqlRow(object[] data
, IEnumerable
<WvColInfo
> schema
)
370 this.schema
= schema
.ToArray();
372 // This improves behaviour with IronPython, and doesn't seem to
373 // hurt anything else. WvAutoCast knows how to deal with 'real'
374 // nulls anyway. I don't really know what DBNull is even good
376 for (int i
= 0; i
< _data
.Length
; i
++)
377 if (_data
[i
] != null && _data
[i
] is DBNull
)
381 public WvAutoCast
this[int i
]
382 { get { return new WvAutoCast(_data[i]); }
}
386 if (colnames
!= null)
388 colnames
= new Dictionary
<string,int>();
389 for (int i
= 0; i
< schema
.Length
; i
++)
390 colnames
.Add(schema
[i
].name
, i
);
393 public WvAutoCast
this[string s
]
398 return this[colnames
[s
]];
403 { get { return _data.Length; }
}
405 public IEnumerator
<WvAutoCast
> GetEnumerator()
407 foreach (object colval
in _data
)
408 yield return new WvAutoCast(colval
);
411 IEnumerator IEnumerable
.GetEnumerator()
413 foreach (object colval
in _data
)
418 { get { return _data; }
}
420 public IEnumerable
<WvColInfo
> columns
421 { get { return schema; }
}
425 public abstract class WvSqlRows
: IDisposable
, IEnumerable
<WvSqlRow
>
427 public abstract IEnumerable
<WvColInfo
> columns { get; }
429 public virtual void Dispose()
431 // nothing to do here
434 public abstract IEnumerator
<WvSqlRow
> GetEnumerator();
436 IEnumerator IEnumerable
.GetEnumerator()
438 IEnumerator
<WvSqlRow
> e
= GetEnumerator();
443 class WvSqlRows_IDataReader
: WvSqlRows
, IEnumerable
<WvSqlRow
>
448 public WvSqlRows_IDataReader(IDataReader reader
)
450 wv
.assert(reader
!= null);
451 this.reader
= reader
;
452 var st
= reader
.GetSchemaTable();
454 this.schema
= WvColInfo
.FromDataTable(st
).ToArray();
456 this.schema
= new WvColInfo
[0];
459 public override void Dispose()
468 public override IEnumerable
<WvColInfo
> columns
469 { get { return schema; }
}
471 public override IEnumerator
<WvSqlRow
> GetEnumerator()
473 int max
= reader
.FieldCount
;
475 using(this) // handle being called inside a foreach()
476 while (reader
.Read())
478 object[] oa
= new object[max
];
481 reader
.GetValues(oa
);
483 catch (OverflowException
)
485 // This garbage is here because mono gets an
486 // OverflowException when trying to use GetDecimal() on
487 // a very large decimal(38,38) field. But GetSqlDecimal
488 // works. Sadly, GetValues() seems to do some kind of
489 // GetDecimal-like thing internally, so we have to do this
490 // hack if there's ever a decode error.
491 // (Tested with mono 1.9.1.0; failing unit test is
492 // versaplex: verifydata.t.cs/VerifyDecimal)
493 for (int i
= 0; i
< max
; i
++)
495 if (!reader
.IsDBNull(i
)
496 && reader
.GetFieldType(i
) == typeof(Decimal
))
498 if (reader
is SqlDataReader
)
499 oa
[i
] = ((SqlDataReader
)reader
)
500 .GetSqlDecimal(i
).ToString();
502 oa
[i
] = reader
.GetDecimal(i
).ToString();
505 oa
[i
] = reader
.GetValue(i
);
509 yield return new WvSqlRow(oa
, schema
);