matchrule.cs wasn't being used anymore; delete.
[versaplex.git] / wvdotnet / wvdata.cs
blob07a7eeee04eaacb3af9a361114b382bd77376730
1 using System;
2 using System.Data;
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;
8 using System.Linq;
9 using Wv.Extensions;
11 namespace Wv
13 /**
14 * A wrapper that will make any object implicitly castable into various
15 * basic data types.
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>
28 object v;
29 public static readonly WvAutoCast _null = new WvAutoCast(null);
31 public WvAutoCast(object v)
33 this.v = v;
36 public bool IsNull { get { return v == null || v is DBNull; } }
38 public static implicit operator string(WvAutoCast o)
40 return o.ToString();
43 public object inner
44 { get { return v; } }
46 public override string ToString()
48 if (IsNull)
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(",") + "]";
54 else
55 return v.ToString();
58 public static implicit operator DateTime(WvAutoCast o)
60 if (o.IsNull)
61 return DateTime.MinValue;
62 else if (o.v is DateTime)
63 return (DateTime)o.v;
64 else if (o.v is SqlDateTime)
65 return ((SqlDateTime)o.v).Value;
66 else
67 return wv.date(o.v);
70 public static implicit operator SqlDateTime(WvAutoCast o)
72 if (o.IsNull)
73 return SqlDateTime.MinValue;
74 else if (o.v is SqlDateTime)
75 return (SqlDateTime)o.v;
76 else if (o.v is DateTime)
77 return (DateTime)o.v;
78 else
79 return wv.date(o.v);
82 public static implicit operator byte[](WvAutoCast o)
84 return (byte[])o.v;
87 public static implicit operator SqlBinary(WvAutoCast o)
89 if (o.IsNull)
90 return null;
91 else if (o.v is SqlBinary)
92 return (SqlBinary)o.v;
93 else
94 return new SqlBinary((byte[])o.v);
97 bool isint()
99 if (IsNull)
100 return false;
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)
104 return true;
105 else
106 return false;
109 Int64 intify()
111 if (IsNull)
112 return 0;
113 else if (v is Int64)
114 return (Int64)v;
115 else if (v is Int32)
116 return (Int32)v;
117 else if (v is Int16)
118 return (Int16)v;
119 else if (v is Byte)
120 return (Byte)v;
121 else if (v is Boolean)
122 return (Boolean)v ? 1 : 0;
123 else
124 return wv.atol(v);
127 public static implicit operator Int64(WvAutoCast o)
129 return o.intify();
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)
169 if (o.IsNull)
170 return 0;
171 else if (o.v is double)
172 return (double)o.v;
173 else if (o.v is Int64)
174 return (Int64)o.v;
175 else if (o.v is Int32)
176 return (Int32)o.v;
177 else if (o.v is Int16)
178 return (Int16)o.v;
179 else if (o.v is Byte)
180 return (Byte)o.v;
181 else if (o.v is Boolean)
182 return (Boolean)o.v ? 1.0 : 0.0;
183 else
184 return wv.atod(o.v);
187 public static implicit operator float(WvAutoCast o)
189 if (o.IsNull)
190 return 0;
191 else if (o.v is float)
192 return (float)o.v;
193 else
194 return (float)(double)o;
197 public static implicit operator char(WvAutoCast o)
199 if (o.IsNull)
200 return Char.MinValue;
201 else if (o.v is char || o.isint())
202 return (char)o.intify();
203 else
204 return Char.MinValue;
207 public static implicit operator Decimal(WvAutoCast o)
209 if (o.v is Decimal)
210 return (Decimal)o.v;
211 else if (o.v is UInt64)
212 return new Decimal((UInt64)o.v);
213 else if (o.isint())
214 return new Decimal(o.intify());
215 else if (o.v is double || o.v is float)
216 return new Decimal((double)o);
217 else
218 return Decimal.MinValue;
221 public static implicit operator SqlDecimal(WvAutoCast o)
223 if (o.v is SqlDecimal)
224 return (SqlDecimal)o.v;
225 else
226 return (Decimal)o;
229 public static implicit operator Guid(WvAutoCast o)
231 if (o.v is Guid)
232 return (Guid)o.v;
233 else if (o.v is SqlGuid)
234 return ((SqlGuid)o.v).Value;
235 else
236 return Guid.Empty;
239 public static implicit operator SqlGuid(WvAutoCast o)
241 if (o.v is SqlGuid)
242 return (SqlGuid)o.v;
243 else if (o.v is Guid)
244 return (Guid)o.v;
245 else
246 return SqlGuid.Null;
249 public object to(Type t)
251 if (t == typeof(string))
252 return (string)this;
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[]))
258 return (byte[])this;
259 else if (t == typeof(SqlBinary))
260 return (SqlBinary)this;
261 else if (t == typeof(Int64))
262 return (Int64)this;
263 else if (t == typeof(UInt64))
264 return (UInt64)this;
265 else if (t == typeof(Int32))
266 return (Int32)this;
267 else if (t == typeof(UInt32))
268 return (UInt32)this;
269 else if (t == typeof(Int16))
270 return (Int16)this;
271 else if (t == typeof(UInt16))
272 return (UInt16)this;
273 else if (t == typeof(byte))
274 return (byte)this;
275 else if (t == typeof(bool))
276 return (bool)this;
277 else if (t == typeof(double))
278 return (double)this;
279 else if (t == typeof(float))
280 return (float)this;
281 else if (t == typeof(char))
282 return (char)this;
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))
288 return (Guid)this;
289 else if (t == typeof(SqlGuid))
290 return (SqlGuid)this;
291 else
292 return v;
295 IEnumerable<object> _iter()
297 if (!IsNull && v is IEnumerable)
299 foreach (object i in (IEnumerable)v)
301 if (i is WvAutoCast)
302 yield return ((WvAutoCast)i).inner;
303 else
304 yield return i;
309 IEnumerator System.Collections.IEnumerable.GetEnumerator()
311 foreach (var i in _iter())
312 yield return i;
315 public IEnumerator<WvAutoCast> GetEnumerator()
317 foreach (object i in _iter())
318 yield return new WvAutoCast(i);
323 public struct WvColInfo
325 public string name;
326 public Type type;
327 public bool nullable;
328 public int size;
329 public short precision;
330 public short scale;
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)
341 this.name = name;
342 this.type = type;
343 this.nullable = nullable;
344 this.size = size;
345 this.precision = precision;
346 this.scale = scale;
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>
363 object[] _data;
364 WvColInfo[] schema;
365 Dictionary<string,int> colnames = null;
367 public WvSqlRow(object[] data, IEnumerable<WvColInfo> schema)
369 this._data = data;
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
375 // for.
376 for (int i = 0; i < _data.Length; i++)
377 if (_data[i] != null && _data[i] is DBNull)
378 _data[i] = null;
381 public WvAutoCast this[int i]
382 { get { return new WvAutoCast(_data[i]); } }
384 void init_colnames()
386 if (colnames != null)
387 return;
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]
397 init_colnames();
398 return this[colnames[s]];
402 public int Length
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)
414 yield return colval;
417 public object[] 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();
439 return e;
443 class WvSqlRows_IDataReader : WvSqlRows, IEnumerable<WvSqlRow>
445 IDataReader reader;
446 WvColInfo[] schema;
448 public WvSqlRows_IDataReader(IDataReader reader)
450 wv.assert(reader != null);
451 this.reader = reader;
452 var st = reader.GetSchemaTable();
453 if (st != null)
454 this.schema = WvColInfo.FromDataTable(st).ToArray();
455 else
456 this.schema = new WvColInfo[0];
459 public override void Dispose()
461 if (reader != null)
462 reader.Dispose();
463 reader = null;
465 base.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();
501 else
502 oa[i] = reader.GetDecimal(i).ToString();
504 else
505 oa[i] = reader.GetValue(i);
509 yield return new WvSqlRow(oa, schema);