[threads] Don't ignore abort requests in abort protected blocks
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLiteConvert.cs
1 /********************************************************\r
2  * ADO.NET 2.0 Data Provider for SQLite Version 3.X\r
3  * Written by Robert Simpson (robert@blackcastlesoft.com)\r
4  * \r
5  * Released to the public domain, use at your own risk!\r
6  ********************************************************/\r
7 \r
8 namespace Mono.Data.Sqlite\r
9 {\r
10   using System;\r
11   using System.Data;\r
12   using System.Runtime.InteropServices;\r
13   using System.Collections.Generic;\r
14   using System.ComponentModel;\r
15   using System.Globalization;\r
16   using System.Text;\r
17 \r
18 #if !PLATFORM_COMPACTFRAMEWORK \r
19   using System.ComponentModel.Design;\r
20 #endif\r
21 \r
22   /// <summary>\r
23   /// This base class provides datatype conversion services for the SQLite provider.\r
24   /// </summary>\r
25   public abstract class SqliteConvert\r
26   {\r
27     /// <summary>\r
28     /// The value for the Unix epoch (e.g. January 1, 1970 at midnight, in UTC).\r
29     /// </summary>\r
30     protected static readonly DateTime UnixEpoch =\r
31         new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\r
32     /// <summary>\r
33     /// An array of ISO8601 datetime formats we support conversion from\r
34     /// </summary>\r
35     private static string[] _datetimeFormats = new string[] {\r
36       "THHmmssK",\r
37       "THHmmK",\r
38       "HH:mm:ss.FFFFFFFK",\r
39       "HH:mm:ssK",\r
40       "HH:mmK",\r
41       "yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */\r
42       "yyyy-MM-dd HH:mm:ssK",\r
43       "yyyy-MM-dd HH:mmK",\r
44       "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",\r
45       "yyyy-MM-ddTHH:mmK",\r
46       "yyyy-MM-ddTHH:mm:ssK",\r
47       "yyyyMMddHHmmssK",\r
48       "yyyyMMddHHmmK",\r
49       "yyyyMMddTHHmmssFFFFFFFK",\r
50       "THHmmss",\r
51       "THHmm",\r
52       "HH:mm:ss.FFFFFFF",\r
53       "HH:mm:ss",\r
54       "HH:mm",\r
55       "yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */\r
56       "yyyy-MM-dd HH:mm:ss",\r
57       "yyyy-MM-dd HH:mm",\r
58       "yyyy-MM-ddTHH:mm:ss.FFFFFFF",\r
59       "yyyy-MM-ddTHH:mm",\r
60       "yyyy-MM-ddTHH:mm:ss",\r
61       "yyyyMMddHHmmss",\r
62       "yyyyMMddHHmm",\r
63       "yyyyMMddTHHmmssFFFFFFF",\r
64       "yyyy-MM-dd",\r
65       "yyyyMMdd",\r
66       "yy-MM-dd"\r
67     };\r
68 \r
69     /// <summary>\r
70     /// An UTF-8 Encoding instance, so we can convert strings to and from UTF-8\r
71     /// </summary>\r
72     private static Encoding _utf8 = new UTF8Encoding();\r
73     /// <summary>\r
74     /// The default DateTime format for this instance\r
75     /// </summary>\r
76     internal SQLiteDateFormats _datetimeFormat;\r
77     /// <summary>\r
78     /// Initializes the conversion class\r
79     /// </summary>\r
80     /// <param name="fmt">The default date/time format to use for this instance</param>\r
81     internal SqliteConvert(SQLiteDateFormats fmt)\r
82     {\r
83       _datetimeFormat = fmt;\r
84     }\r
85 \r
86     #region UTF-8 Conversion Functions\r
87     /// <summary>\r
88     /// Converts a string to a UTF-8 encoded byte array sized to include a null-terminating character.\r
89     /// </summary>\r
90     /// <param name="sourceText">The string to convert to UTF-8</param>\r
91     /// <returns>A byte array containing the converted string plus an extra 0 terminating byte at the end of the array.</returns>\r
92     public static byte[] ToUTF8(string sourceText)\r
93     {\r
94       Byte[] byteArray;\r
95       int nlen = _utf8.GetByteCount(sourceText) + 1;\r
96 \r
97       byteArray = new byte[nlen];\r
98       nlen = _utf8.GetBytes(sourceText, 0, sourceText.Length, byteArray, 0);\r
99       byteArray[nlen] = 0;\r
100 \r
101       return byteArray;\r
102     }\r
103 \r
104     /// <summary>\r
105     /// Convert a DateTime to a UTF-8 encoded, zero-terminated byte array.\r
106     /// </summary>\r
107     /// <remarks>\r
108     /// This function is a convenience function, which first calls ToString() on the DateTime, and then calls ToUTF8() with the\r
109     /// string result.\r
110     /// </remarks>\r
111     /// <param name="dateTimeValue">The DateTime to convert.</param>\r
112     /// <returns>The UTF-8 encoded string, including a 0 terminating byte at the end of the array.</returns>\r
113     public byte[] ToUTF8(DateTime dateTimeValue)\r
114     {\r
115       return ToUTF8(ToString(dateTimeValue));\r
116     }\r
117 \r
118     /// <summary>\r
119     /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string\r
120     /// </summary>\r
121     /// <param name="nativestring">The pointer to the memory where the UTF-8 string is encoded</param>\r
122     /// <param name="nativestringlen">The number of bytes to decode</param>\r
123     /// <returns>A string containing the translated character(s)</returns>\r
124     public virtual string ToString(IntPtr nativestring, int nativestringlen)\r
125     {\r
126       return UTF8ToString(nativestring, nativestringlen);\r
127     }\r
128 \r
129     /// <summary>\r
130     /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string\r
131     /// </summary>\r
132     /// <param name="nativestring">The pointer to the memory where the UTF-8 string is encoded</param>\r
133     /// <param name="nativestringlen">The number of bytes to decode</param>\r
134     /// <returns>A string containing the translated character(s)</returns>\r
135     public static string UTF8ToString(IntPtr nativestring, int nativestringlen)\r
136     {\r
137       if (nativestringlen == 0 || nativestring == IntPtr.Zero) return "";\r
138       if (nativestringlen == -1)\r
139       {\r
140         do\r
141         {\r
142           nativestringlen++;\r
143         } while (Marshal.ReadByte(nativestring, nativestringlen) != 0);\r
144       }\r
145 \r
146       byte[] byteArray = new byte[nativestringlen];\r
147       \r
148       Marshal.Copy(nativestring, byteArray, 0, nativestringlen);\r
149 \r
150       return _utf8.GetString(byteArray, 0, nativestringlen);\r
151     }\r
152 \r
153 \r
154     #endregion\r
155 \r
156     #region DateTime Conversion Functions\r
157     /// <summary>\r
158     /// Converts a string into a DateTime, using the current DateTimeFormat specified for the connection when it was opened.\r
159     /// </summary>\r
160     /// <remarks>\r
161     /// Acceptable ISO8601 DateTime formats are:\r
162     ///   yyyy-MM-dd HH:mm:ss\r
163     ///   yyyyMMddHHmmss\r
164     ///   yyyyMMddTHHmmssfffffff\r
165     ///   yyyy-MM-dd\r
166     ///   yy-MM-dd\r
167     ///   yyyyMMdd\r
168     ///   HH:mm:ss\r
169     ///   THHmmss\r
170     /// </remarks>\r
171     /// <param name="dateText">The string containing either a Tick value, a JulianDay double, or an ISO8601-format string</param>\r
172     /// <returns>A DateTime value</returns>\r
173     public DateTime ToDateTime(string dateText)\r
174     {\r
175       switch (_datetimeFormat)\r
176       {\r
177         case SQLiteDateFormats.Ticks:\r
178           return new DateTime(Convert.ToInt64(dateText, CultureInfo.InvariantCulture));\r
179         case SQLiteDateFormats.JulianDay:\r
180           return ToDateTime(Convert.ToDouble(dateText, CultureInfo.InvariantCulture));\r
181         case SQLiteDateFormats.UnixEpoch:\r
182           return UnixEpoch.AddSeconds(Convert.ToInt32(dateText, CultureInfo.InvariantCulture));\r
183         default:\r
184           return DateTime.ParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None);\r
185       }\r
186     }\r
187 \r
188     /// <summary>\r
189     /// Converts a julianday value into a DateTime\r
190     /// </summary>\r
191     /// <param name="julianDay">The value to convert</param>\r
192     /// <returns>A .NET DateTime</returns>\r
193     public DateTime ToDateTime(double julianDay)\r
194     {\r
195       return DateTime.FromOADate(julianDay - 2415018.5);\r
196     }\r
197 \r
198     /// <summary>\r
199     /// Converts a DateTime struct to a JulianDay double\r
200     /// </summary>\r
201     /// <param name="value">The DateTime to convert</param>\r
202     /// <returns>The JulianDay value the Datetime represents</returns>\r
203     public double ToJulianDay(DateTime value)\r
204     {\r
205       return value.ToOADate() + 2415018.5;\r
206     }\r
207 \r
208     /// <summary>\r
209     /// Converts a DateTime to a string value, using the current DateTimeFormat specified for the connection when it was opened.\r
210     /// </summary>\r
211     /// <param name="dateValue">The DateTime value to convert</param>\r
212     /// <returns>Either a string consisting of the tick count for DateTimeFormat.Ticks, a JulianDay double, or a date/time in ISO8601 format.</returns>\r
213     public string ToString(DateTime dateValue)\r
214     {\r
215       switch (_datetimeFormat)\r
216       {\r
217         case SQLiteDateFormats.Ticks:\r
218           return dateValue.Ticks.ToString(CultureInfo.InvariantCulture);\r
219         case SQLiteDateFormats.JulianDay:\r
220           return ToJulianDay(dateValue).ToString(CultureInfo.InvariantCulture);\r
221         case SQLiteDateFormats.UnixEpoch:\r
222           return ((long)(dateValue.Subtract(UnixEpoch).Ticks / TimeSpan.TicksPerSecond)).ToString();\r
223         default:\r
224           return dateValue.ToString(_datetimeFormats[5], CultureInfo.InvariantCulture);\r
225       }\r
226     }\r
227 \r
228     /// <summary>\r
229     /// Internal function to convert a UTF-8 encoded IntPtr of the specified length to a DateTime.\r
230     /// </summary>\r
231     /// <remarks>\r
232     /// This is a convenience function, which first calls ToString() on the IntPtr to convert it to a string, then calls\r
233     /// ToDateTime() on the string to return a DateTime.\r
234     /// </remarks>\r
235     /// <param name="ptr">A pointer to the UTF-8 encoded string</param>\r
236     /// <param name="len">The length in bytes of the string</param>\r
237     /// <returns>The parsed DateTime value</returns>\r
238     internal DateTime ToDateTime(IntPtr ptr, int len)\r
239     {\r
240       return ToDateTime(ToString(ptr, len));\r
241     }\r
242 \r
243     #endregion\r
244 \r
245     /// <summary>\r
246     /// Smart method of splitting a string.  Skips quoted elements, removes the quotes.\r
247     /// </summary>\r
248     /// <remarks>\r
249     /// This split function works somewhat like the String.Split() function in that it breaks apart a string into\r
250     /// pieces and returns the pieces as an array.  The primary differences are:\r
251     /// <list type="bullet">\r
252     /// <item><description>Only one character can be provided as a separator character</description></item>\r
253     /// <item><description>Quoted text inside the string is skipped over when searching for the separator, and the quotes are removed.</description></item>\r
254     /// </list>\r
255     /// Thus, if splitting the following string looking for a comma:<br/>\r
256     /// One,Two, "Three, Four", Five<br/>\r
257     /// <br/>\r
258     /// The resulting array would contain<br/>\r
259     /// [0] One<br/>\r
260     /// [1] Two<br/>\r
261     /// [2] Three, Four<br/>\r
262     /// [3] Five<br/>\r
263     /// <br/>\r
264     /// Note that the leading and trailing spaces were removed from each item during the split.\r
265     /// </remarks>\r
266     /// <param name="source">Source string to split apart</param>\r
267     /// <param name="separator">Separator character</param>\r
268     /// <returns>A string array of the split up elements</returns>\r
269     public static string[] Split(string source, char separator)\r
270     {\r
271       char[] toks = new char[2] { '\"', separator };\r
272       char[] quot = new char[1] { '\"' };\r
273       int n = 0;\r
274       List<string> ls = new List<string>();\r
275       string s;\r
276 \r
277       while (source.Length > 0)\r
278       {\r
279         n = source.IndexOfAny(toks, n);\r
280         if (n == -1) break;\r
281         if (source[n] == toks[0])\r
282         {\r
283           //source = source.Remove(n, 1);\r
284           n = source.IndexOfAny(quot, n + 1);\r
285           if (n == -1)\r
286           {\r
287             //source = "\"" + source;\r
288             break;\r
289           }\r
290           n++;\r
291           //source = source.Remove(n, 1);\r
292         }\r
293         else\r
294         {\r
295           s = source.Substring(0, n).Trim();\r
296           if (s.Length > 1 && s[0] == quot[0] && s[s.Length - 1] == s[0])\r
297             s = s.Substring(1, s.Length - 2);\r
298 \r
299           source = source.Substring(n + 1).Trim();\r
300           if (s.Length > 0) ls.Add(s);\r
301           n = 0;\r
302         }\r
303       }\r
304       if (source.Length > 0)\r
305       {\r
306         s = source.Trim();\r
307         if (s.Length > 1 && s[0] == quot[0] && s[s.Length - 1] == s[0])\r
308           s = s.Substring(1, s.Length - 2);\r
309         ls.Add(s);\r
310       }\r
311 \r
312       string[] ar = new string[ls.Count];\r
313       ls.CopyTo(ar, 0);\r
314 \r
315       return ar;\r
316     }\r
317 \r
318     /// <summary>\r
319     /// Convert a value to true or false.\r
320     /// </summary>\r
321     /// <param name="source">A string or number representing true or false</param>\r
322     /// <returns></returns>\r
323     public static bool ToBoolean(object source)\r
324     {\r
325       if (source is bool) return (bool)source;\r
326 \r
327       return ToBoolean(source.ToString());\r
328     }\r
329 \r
330     /// <summary>\r
331     /// Convert a string to true or false.\r
332     /// </summary>\r
333     /// <param name="source">A string representing true or false</param>\r
334     /// <returns></returns>\r
335     /// <remarks>\r
336     /// "yes", "no", "y", "n", "0", "1", "on", "off" as well as Boolean.FalseString and Boolean.TrueString will all be\r
337     /// converted to a proper boolean value.\r
338     /// </remarks>\r
339     public static bool ToBoolean(string source)\r
340     {\r
341       if (String.Compare(source, bool.TrueString, StringComparison.OrdinalIgnoreCase) == 0) return true;\r
342       else if (String.Compare(source, bool.FalseString, StringComparison.OrdinalIgnoreCase) == 0) return false;\r
343 \r
344       switch(source.ToLower())\r
345       {\r
346         case "yes":\r
347         case "y":\r
348         case "1":\r
349         case "on":\r
350           return true;\r
351         case "no":\r
352         case "n":\r
353         case "0":\r
354         case "off":\r
355           return false;\r
356         default:\r
357           throw new ArgumentException("source");\r
358       }\r
359     }\r
360 \r
361     #region Type Conversions\r
362     /// <summary>\r
363     /// Determines the data type of a column in a statement\r
364     /// </summary>\r
365     /// <param name="stmt">The statement to retrieve information for</param>\r
366     /// <param name="i">The column to retrieve type information on</param>\r
367     /// <param name="typ">The SQLiteType to receive the affinity for the given column</param>\r
368     internal static void ColumnToType(SqliteStatement stmt, int i, SQLiteType typ)\r
369     {\r
370       typ.Type = TypeNameToDbType(stmt._sql.ColumnType(stmt, i, out typ.Affinity));\r
371     }\r
372 \r
373     /// <summary>\r
374     /// Converts a SQLiteType to a .NET Type object\r
375     /// </summary>\r
376     /// <param name="t">The SQLiteType to convert</param>\r
377     /// <returns>Returns a .NET Type object</returns>\r
378     internal static Type SQLiteTypeToType(SQLiteType t)\r
379     {\r
380       if (t.Type == DbType.Object)\r
381         return _affinitytotype[(int)t.Affinity];\r
382       else\r
383         return SqliteConvert.DbTypeToType(t.Type);\r
384     }\r
385 \r
386     private static Type[] _affinitytotype = {\r
387       typeof(object),\r
388       typeof(Int64),\r
389       typeof(Double),\r
390       typeof(string),\r
391       typeof(byte[]),\r
392       typeof(object),\r
393       typeof(DateTime),\r
394       typeof(object)\r
395     };\r
396 \r
397     /// <summary>\r
398     /// For a given intrinsic type, return a DbType\r
399     /// </summary>\r
400     /// <param name="typ">The native type to convert</param>\r
401     /// <returns>The corresponding (closest match) DbType</returns>\r
402     internal static DbType TypeToDbType(Type typ)\r
403     {\r
404       TypeCode tc = Type.GetTypeCode(typ);\r
405       if (tc == TypeCode.Object)\r
406       {\r
407         if (typ == typeof(byte[])) return DbType.Binary;\r
408         if (typ == typeof(Guid)) return DbType.Guid;\r
409         return DbType.String;\r
410       }\r
411       return _typetodbtype[(int)tc];\r
412     }\r
413 \r
414     private static DbType[] _typetodbtype = {\r
415       DbType.Object,\r
416       DbType.Binary,\r
417       DbType.Object,\r
418       DbType.Boolean,\r
419       DbType.SByte,\r
420       DbType.SByte,\r
421       DbType.Byte,\r
422       DbType.Int16, // 7\r
423       DbType.UInt16,\r
424       DbType.Int32,\r
425       DbType.UInt32,\r
426       DbType.Int64, // 11\r
427       DbType.UInt64,\r
428       DbType.Single,\r
429       DbType.Double,\r
430       DbType.Decimal,\r
431       DbType.DateTime,\r
432       DbType.Object,\r
433       DbType.String,\r
434     };\r
435 \r
436     /// <summary>\r
437     /// Returns the ColumnSize for the given DbType\r
438     /// </summary>\r
439     /// <param name="typ">The DbType to get the size of</param>\r
440     /// <returns></returns>\r
441     internal static int DbTypeToColumnSize(DbType typ)\r
442     {\r
443       return _dbtypetocolumnsize[(int)typ];\r
444     }\r
445 \r
446     private static int[] _dbtypetocolumnsize = {\r
447       2147483647,   // 0\r
448       2147483647,   // 1\r
449       1,     // 2\r
450       1,     // 3\r
451       8,  // 4\r
452       8, // 5\r
453       8, // 6\r
454       8,  // 7\r
455       8,   // 8\r
456       16,     // 9\r
457       2,\r
458       4,\r
459       8,\r
460       2147483647,\r
461       1,\r
462       4,\r
463       2147483647,\r
464       8,\r
465       2,\r
466       4,\r
467       8,\r
468       8,\r
469       2147483647,\r
470       2147483647,\r
471       2147483647,\r
472       2147483647,   // 25 (Xml)\r
473     };\r
474 \r
475     internal static object DbTypeToNumericPrecision(DbType typ)\r
476     {\r
477       return _dbtypetonumericprecision[(int)typ];\r
478     }\r
479 \r
480     private static object[] _dbtypetonumericprecision = {\r
481       DBNull.Value, // 0\r
482       DBNull.Value, // 1\r
483       3,\r
484       DBNull.Value,\r
485       19,\r
486       DBNull.Value, // 5\r
487       DBNull.Value, // 6\r
488       53,\r
489       53,\r
490       DBNull.Value,\r
491       5,\r
492       10,\r
493       19,\r
494       DBNull.Value,\r
495       3,\r
496       24,\r
497       DBNull.Value,\r
498       DBNull.Value,\r
499       5,\r
500       10,\r
501       19,\r
502       53,\r
503       DBNull.Value,\r
504       DBNull.Value,\r
505       DBNull.Value\r
506     };\r
507 \r
508     internal static object DbTypeToNumericScale(DbType typ)\r
509     {\r
510       return _dbtypetonumericscale[(int)typ];\r
511     }\r
512 \r
513     private static object[] _dbtypetonumericscale = {\r
514       DBNull.Value, // 0\r
515       DBNull.Value, // 1\r
516       0,\r
517       DBNull.Value,\r
518       4,\r
519       DBNull.Value, // 5\r
520       DBNull.Value, // 6\r
521       DBNull.Value,\r
522       DBNull.Value,\r
523       DBNull.Value,\r
524       0,\r
525       0,\r
526       0,\r
527       DBNull.Value,\r
528       0,\r
529       DBNull.Value,\r
530       DBNull.Value,\r
531       DBNull.Value,\r
532       0,\r
533       0,\r
534       0,\r
535       0,\r
536       DBNull.Value,\r
537       DBNull.Value,\r
538       DBNull.Value\r
539     };\r
540 \r
541     internal static string DbTypeToTypeName(DbType typ)\r
542     {\r
543       for (int n = 0; n < _dbtypeNames.Length; n++)\r
544       {\r
545         if (_dbtypeNames[n].dataType == typ)\r
546           return _dbtypeNames[n].typeName;\r
547       }\r
548 \r
549       return String.Empty;\r
550     }\r
551 \r
552     private static SQLiteTypeNames[] _dbtypeNames = {\r
553       new SQLiteTypeNames("INTEGER", DbType.Int64),\r
554       new SQLiteTypeNames("TINYINT", DbType.Byte),\r
555       new SQLiteTypeNames("INT", DbType.Int32),\r
556       new SQLiteTypeNames("VARCHAR", DbType.AnsiString),\r
557       new SQLiteTypeNames("NVARCHAR", DbType.String),\r
558       new SQLiteTypeNames("CHAR", DbType.AnsiStringFixedLength),\r
559       new SQLiteTypeNames("NCHAR", DbType.StringFixedLength),\r
560       new SQLiteTypeNames("FLOAT", DbType.Double),\r
561       new SQLiteTypeNames("REAL", DbType.Single),          \r
562       new SQLiteTypeNames("BIT", DbType.Boolean),\r
563       new SQLiteTypeNames("DECIMAL", DbType.Decimal),\r
564       new SQLiteTypeNames("DATETIME", DbType.DateTime),\r
565       new SQLiteTypeNames("BLOB", DbType.Binary),\r
566       new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid),\r
567       new SQLiteTypeNames("SMALLINT", DbType.Int16),\r
568     };\r
569     /// <summary>\r
570     /// Convert a DbType to a Type\r
571     /// </summary>\r
572     /// <param name="typ">The DbType to convert from</param>\r
573     /// <returns>The closest-match .NET type</returns>\r
574     internal static Type DbTypeToType(DbType typ)\r
575     {\r
576       return _dbtypeToType[(int)typ];\r
577     }\r
578 \r
579     private static Type[] _dbtypeToType = {\r
580       typeof(string),   // 0\r
581       typeof(byte[]),   // 1\r
582       typeof(byte),     // 2\r
583       typeof(bool),     // 3\r
584       typeof(decimal),  // 4\r
585       typeof(DateTime), // 5\r
586       typeof(DateTime), // 6\r
587       typeof(decimal),  // 7\r
588       typeof(double),   // 8\r
589       typeof(Guid),     // 9\r
590       typeof(Int16),\r
591       typeof(Int32),\r
592       typeof(Int64),\r
593       typeof(object),\r
594       typeof(sbyte),\r
595       typeof(float),\r
596       typeof(string),\r
597       typeof(DateTime),\r
598       typeof(UInt16),\r
599       typeof(UInt32),\r
600       typeof(UInt64),\r
601       typeof(double),\r
602       typeof(string),\r
603       typeof(string),\r
604       typeof(string),\r
605       typeof(string),   // 25 (Xml)\r
606     };\r
607 \r
608     /// <summary>\r
609     /// For a given type, return the closest-match SQLite TypeAffinity, which only understands a very limited subset of types.\r
610     /// </summary>\r
611     /// <param name="typ">The type to evaluate</param>\r
612     /// <returns>The SQLite type affinity for that type.</returns>\r
613     internal static TypeAffinity TypeToAffinity(Type typ)\r
614     {\r
615       TypeCode tc = Type.GetTypeCode(typ);\r
616       if (tc == TypeCode.Object)\r
617       {\r
618         if (typ == typeof(byte[]) || typ == typeof(Guid))\r
619           return TypeAffinity.Blob;\r
620         else\r
621           return TypeAffinity.Text;\r
622       }\r
623       return _typecodeAffinities[(int)tc];\r
624     }\r
625 \r
626     private static TypeAffinity[] _typecodeAffinities = {\r
627       TypeAffinity.Null,\r
628       TypeAffinity.Blob,\r
629       TypeAffinity.Null,\r
630       TypeAffinity.Int64,\r
631       TypeAffinity.Int64,\r
632       TypeAffinity.Int64,\r
633       TypeAffinity.Int64,\r
634       TypeAffinity.Int64, // 7\r
635       TypeAffinity.Int64,\r
636       TypeAffinity.Int64,\r
637       TypeAffinity.Int64,\r
638       TypeAffinity.Int64, // 11\r
639       TypeAffinity.Int64,\r
640       TypeAffinity.Double,\r
641       TypeAffinity.Double,\r
642       TypeAffinity.Double,\r
643       TypeAffinity.DateTime,\r
644       TypeAffinity.Null,\r
645       TypeAffinity.Text,\r
646     };\r
647 \r
648     /// <summary>\r
649     /// For a given type name, return a closest-match .NET type\r
650     /// </summary>\r
651     /// <param name="Name">The name of the type to match</param>\r
652     /// <returns>The .NET DBType the text evaluates to.</returns>\r
653     internal static DbType TypeNameToDbType(string Name)\r
654     {\r
655       if (String.IsNullOrEmpty(Name)) return DbType.Object;\r
656 \r
657       string nameToCompare = Name;\r
658       int parenthesis = nameToCompare.IndexOf ('(');\r
659       if (parenthesis > 0)\r
660         nameToCompare = nameToCompare.Substring (0, parenthesis);\r
661         \r
662       for (int n = 0; n < _typeNames.Length; n++)\r
663       {\r
664         if (string.Compare(nameToCompare, _typeNames[n].typeName, true, CultureInfo.InvariantCulture) == 0)\r
665           return _typeNames[n].dataType; \r
666       }\r
667       \r
668       /* http://www.sqlite.org/datatype3.html\r
669        * 2.1 Determination Of Column Affinity\r
670        * The affinity of a column is determined by the declared type of the column, according to the following rules in the order shown:\r
671        *   1. If the declared type contains the string "INT" then it is assigned INTEGER affinity.\r
672        *   2. If the declared type of the column contains any of the strings "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity. Notice that the type VARCHAR contains the string "CHAR" and is thus assigned TEXT affinity.\r
673        *   3. If the declared type for a column contains the string "BLOB" or if no type is specified then the column has affinity NONE.\r
674        *   4. If the declared type for a column contains any of the strings "REAL", "FLOA", or "DOUB" then the column has REAL affinity.\r
675        *   5. Otherwise, the affinity is NUMERIC.\r
676        */\r
677       \r
678       if (Name.IndexOf ("INT", StringComparison.OrdinalIgnoreCase) >= 0) {\r
679         return DbType.Int64;\r
680       } else if (Name.IndexOf ("CHAR", StringComparison.OrdinalIgnoreCase) >= 0\r
681               || Name.IndexOf ("CLOB", StringComparison.OrdinalIgnoreCase) >= 0\r
682               || Name.IndexOf ("TEXT", StringComparison.OrdinalIgnoreCase) >= 0) {\r
683         return DbType.String;\r
684       } else if (Name.IndexOf ("BLOB", StringComparison.OrdinalIgnoreCase) >= 0 /* || Name == string.Empty // handled at the top of this functin */) {\r
685         return DbType.Object;\r
686       } else if (Name.IndexOf ("REAL", StringComparison.OrdinalIgnoreCase) >= 0\r
687               || Name.IndexOf ("FLOA", StringComparison.OrdinalIgnoreCase) >= 0\r
688               || Name.IndexOf ("DOUB", StringComparison.OrdinalIgnoreCase) >= 0) {\r
689         return DbType.Double;\r
690       } else {\r
691         return DbType.Object; // This can be anything, so use Object instead of Decimal (which we use otherwise where the type affinity is NUMERIC)\r
692       }\r
693     }\r
694     #endregion\r
695 \r
696     private static SQLiteTypeNames[] _typeNames = {\r
697       new SQLiteTypeNames("COUNTER", DbType.Int64),\r
698       new SQLiteTypeNames("AUTOINCREMENT", DbType.Int64),\r
699       new SQLiteTypeNames("IDENTITY", DbType.Int64),\r
700       new SQLiteTypeNames("LONGTEXT", DbType.String),\r
701       new SQLiteTypeNames("LONGCHAR", DbType.String),\r
702       new SQLiteTypeNames("LONGVARCHAR", DbType.String),\r
703       new SQLiteTypeNames("LONG", DbType.Int64),\r
704       new SQLiteTypeNames("TINYINT", DbType.Byte),\r
705       new SQLiteTypeNames("INTEGER", DbType.Int64),\r
706       new SQLiteTypeNames("INT", DbType.Int32),\r
707       new SQLiteTypeNames("VARCHAR", DbType.String),\r
708       new SQLiteTypeNames("NVARCHAR", DbType.String),\r
709       new SQLiteTypeNames("CHAR", DbType.String),\r
710       new SQLiteTypeNames("NCHAR", DbType.String),\r
711       new SQLiteTypeNames("TEXT", DbType.String),\r
712       new SQLiteTypeNames("NTEXT", DbType.String),\r
713       new SQLiteTypeNames("STRING", DbType.String),\r
714       new SQLiteTypeNames("DOUBLE", DbType.Double),\r
715       new SQLiteTypeNames("FLOAT", DbType.Double),\r
716       new SQLiteTypeNames("REAL", DbType.Single),          \r
717       new SQLiteTypeNames("BIT", DbType.Boolean),\r
718       new SQLiteTypeNames("YESNO", DbType.Boolean),\r
719       new SQLiteTypeNames("LOGICAL", DbType.Boolean),\r
720       new SQLiteTypeNames("BOOL", DbType.Boolean),\r
721       new SQLiteTypeNames("BOOLEAN", DbType.Boolean),\r
722       new SQLiteTypeNames("NUMERIC", DbType.Decimal),\r
723       new SQLiteTypeNames("DECIMAL", DbType.Decimal),\r
724       new SQLiteTypeNames("MONEY", DbType.Decimal),\r
725       new SQLiteTypeNames("CURRENCY", DbType.Decimal),\r
726       new SQLiteTypeNames("TIME", DbType.DateTime),\r
727       new SQLiteTypeNames("DATE", DbType.DateTime),\r
728       new SQLiteTypeNames("SMALLDATE", DbType.DateTime),\r
729       new SQLiteTypeNames("BLOB", DbType.Binary),\r
730       new SQLiteTypeNames("BINARY", DbType.Binary),\r
731       new SQLiteTypeNames("VARBINARY", DbType.Binary),\r
732       new SQLiteTypeNames("IMAGE", DbType.Binary),\r
733       new SQLiteTypeNames("GENERAL", DbType.Binary),\r
734       new SQLiteTypeNames("OLEOBJECT", DbType.Binary),\r
735       new SQLiteTypeNames("GUID", DbType.Guid),\r
736       new SQLiteTypeNames("GUIDBLOB", DbType.Guid),\r
737       new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid),\r
738       new SQLiteTypeNames("MEMO", DbType.String),\r
739       new SQLiteTypeNames("NOTE", DbType.String),\r
740       new SQLiteTypeNames("SMALLINT", DbType.Int16),\r
741       new SQLiteTypeNames("BIGINT", DbType.Int64),\r
742       new SQLiteTypeNames("TIMESTAMP", DbType.DateTime),\r
743       new SQLiteTypeNames("DATETIME", DbType.DateTime),\r
744     };\r
745   }\r
746 \r
747   /// <summary>\r
748   /// SQLite has very limited types, and is inherently text-based.  The first 5 types below represent the sum of all types SQLite\r
749   /// understands.  The DateTime extension to the spec is for internal use only.\r
750   /// </summary>\r
751   public enum TypeAffinity\r
752   {\r
753     /// <summary>\r
754     /// Not used\r
755     /// </summary>\r
756     Uninitialized = 0,\r
757     /// <summary>\r
758     /// All integers in SQLite default to Int64\r
759     /// </summary>\r
760     Int64 = 1,\r
761     /// <summary>\r
762     /// All floating point numbers in SQLite default to double\r
763     /// </summary>\r
764     Double = 2,\r
765     /// <summary>\r
766     /// The default data type of SQLite is text\r
767     /// </summary>\r
768     Text = 3,\r
769     /// <summary>\r
770     /// Typically blob types are only seen when returned from a function\r
771     /// </summary>\r
772     Blob = 4,\r
773     /// <summary>\r
774     /// Null types can be returned from functions\r
775     /// </summary>\r
776     Null = 5,\r
777     /// <summary>\r
778     /// Used internally by this provider\r
779     /// </summary>\r
780     DateTime = 10,\r
781     /// <summary>\r
782     /// Used internally\r
783     /// </summary>\r
784     None = 11,\r
785   }\r
786 \r
787   /// <summary>\r
788   /// This implementation of SQLite for ADO.NET can process date/time fields in databases in only one of three formats.  Ticks, ISO8601\r
789   /// and JulianDay.\r
790   /// </summary>\r
791   /// <remarks>\r
792   /// ISO8601 is more compatible, readable, fully-processable, but less accurate as it doesn't provide time down to fractions of a second.\r
793   /// JulianDay is the numeric format the SQLite uses internally and is arguably the most compatible with 3rd party tools.  It is\r
794   /// not readable as text without post-processing.\r
795   /// Ticks less compatible with 3rd party tools that query the database, and renders the DateTime field unreadable as text without post-processing.\r
796   /// \r
797   /// The preferred order of choosing a datetime format is JulianDay, ISO8601, and then Ticks.  Ticks is mainly present for legacy \r
798   /// code support.\r
799   /// </remarks>\r
800   public enum SQLiteDateFormats\r
801   {\r
802     /// <summary>\r
803     /// Using ticks is not recommended and is not well supported with LINQ.\r
804     /// </summary>\r
805     Ticks = 0,\r
806     /// <summary>\r
807     /// The default format for this provider.\r
808     /// </summary>\r
809     ISO8601 = 1,\r
810     /// <summary>\r
811     /// JulianDay format, which is what SQLite uses internally\r
812     /// </summary>\r
813     JulianDay = 2,\r
814     /// <summary>\r
815     /// The whole number of seconds since the Unix epoch (January 1, 1970).\r
816     /// </summary>\r
817     UnixEpoch = 3,\r
818   }\r
819 \r
820   /// <summary>\r
821   /// This enum determines how SQLite treats its journal file.\r
822   /// </summary>\r
823   /// <remarks>\r
824   /// By default SQLite will create and delete the journal file when needed during a transaction.\r
825   /// However, for some computers running certain filesystem monitoring tools, the rapid\r
826   /// creation and deletion of the journal file can cause those programs to fail, or to interfere with SQLite.\r
827   /// \r
828   /// If a program or virus scanner is interfering with SQLite's journal file, you may receive errors like "unable to open database file"\r
829   /// when starting a transaction.  If this is happening, you may want to change the default journal mode to Persist.\r
830   /// </remarks>\r
831   public enum SQLiteJournalModeEnum\r
832   {\r
833     /// <summary>\r
834     /// The default mode, this causes SQLite to create and destroy the journal file as-needed.\r
835     /// </summary>\r
836     Delete = 0,\r
837     /// <summary>\r
838     /// When this is set, SQLite will keep the journal file even after a transaction has completed.  It's contents will be erased,\r
839     /// and the journal re-used as often as needed.  If it is deleted, it will be recreated the next time it is needed.\r
840     /// </summary>\r
841     Persist = 1,\r
842     /// <summary>\r
843     /// This option disables the rollback journal entirely.  Interrupted transactions or a program crash can cause database\r
844     /// corruption in this mode!\r
845     /// </summary>\r
846     Off = 2\r
847   }\r
848 \r
849   /// <summary>\r
850   /// Struct used internally to determine the datatype of a column in a resultset\r
851   /// </summary>\r
852   internal class SQLiteType\r
853   {\r
854     /// <summary>\r
855     /// The DbType of the column, or DbType.Object if it cannot be determined\r
856     /// </summary>\r
857     internal DbType Type;\r
858     /// <summary>\r
859     /// The affinity of a column, used for expressions or when Type is DbType.Object\r
860     /// </summary>\r
861     internal TypeAffinity Affinity;\r
862   }\r
863 \r
864   internal struct SQLiteTypeNames\r
865   {\r
866     internal SQLiteTypeNames(string newtypeName, DbType newdataType)\r
867     {\r
868       typeName = newtypeName;\r
869       dataType = newdataType;\r
870     }\r
871 \r
872     internal string typeName;\r
873     internal DbType dataType;\r
874   }\r
875 }\r