* MainsoftWebApp20.Tomcat.vmwcsproj: converted to csproj
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLiteConvert.cs
1 //
2 // Mono.Data.Sqlite.SQLiteConvert.cs
3 //
4 // Author(s):
5 //   Robert Simpson (robert@blackcastlesoft.com)
6 //
7 // Adapted and modified for the Mono Project by
8 //   Marek Habersack (grendello@gmail.com)
9 //
10 //
11 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2007 Marek Habersack
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 /********************************************************
35  * ADO.NET 2.0 Data Provider for Sqlite Version 3.X
36  * Written by Robert Simpson (robert@blackcastlesoft.com)
37  * 
38  * Released to the public domain, use at your own risk!
39  ********************************************************/
40 #if NET_2_0
41 namespace Mono.Data.Sqlite
42 {
43   using System;
44   using System.Data;
45   using System.Runtime.InteropServices;
46   using System.Collections.Generic;
47   using System.ComponentModel;
48   using System.Globalization;
49   using System.Text;
50
51 #if !PLATFORM_COMPACTFRAMEWORK 
52   using System.ComponentModel.Design;
53 #endif
54
55   /// <summary>
56   /// Sqlite has very limited types, and is inherently text-based.  The first 5 types below represent the sum of all types Sqlite
57   /// understands.  The DateTime extension to the spec is for internal use only.
58   /// </summary>
59   public enum TypeAffinity
60   {
61     /// <summary>
62     /// Not used
63     /// </summary>
64     Uninitialized = 0,
65     /// <summary>
66     /// All integers in Sqlite default to Int64
67     /// </summary>
68     Int64 = 1,
69     /// <summary>
70     /// All floating point numbers in Sqlite default to double
71     /// </summary>
72     Double = 2,
73     /// <summary>
74     /// The default data type of Sqlite is text
75     /// </summary>
76     Text = 3,
77     /// <summary>
78     /// Typically blob types are only seen when returned from a function
79     /// </summary>
80     Blob = 4,
81     /// <summary>
82     /// Null types can be returned from functions
83     /// </summary>
84     Null = 5,
85     /// <summary>
86     /// Used internally by this provider
87     /// </summary>
88     DateTime = 10,
89     /// <summary>
90     /// Used internally
91     /// </summary>
92     None = 11,
93   }
94
95   /// <summary>
96   /// This implementation of Sqlite for ADO.NET can process date/time fields in databases in only one of two formats.  Ticks and ISO8601.
97   /// Ticks is inherently more accurate, but less compatible with 3rd party tools that query the database, and renders the DateTime field
98   /// unreadable without post-processing.
99   /// ISO8601 is more compatible, readable, fully-processable, but less accurate as it doesn't provide time down to fractions of a second.
100   /// </summary>
101   public enum SqliteDateFormats
102   {
103     /// <summary>
104     /// Using ticks is more accurate but less compatible with other viewers and utilities that access your database.
105     /// </summary>
106     Ticks = 0,
107     /// <summary>
108     /// The default format for this provider.
109     /// </summary>
110     ISO8601 = 1,
111   }
112
113   /// <summary>
114   /// Struct used internally to determine the datatype of a column in a resultset
115   /// </summary>
116   internal struct SqliteType
117   {
118     /// <summary>
119     /// The DbType of the column, or DbType.Object if it cannot be determined
120     /// </summary>
121     internal DbType Type;
122     /// <summary>
123     /// The affinity of a column, used for expressions or when Type is DbType.Object
124     /// </summary>
125     internal TypeAffinity Affinity;
126   }
127
128   internal struct SqliteTypeNames
129   {
130     internal SqliteTypeNames(string newtypeName, DbType newdataType)
131     {
132       typeName = newtypeName;
133       dataType = newdataType;
134     }
135
136     internal string typeName;
137     internal DbType dataType;
138   }
139
140   /// <summary>
141   /// This base class provides datatype conversion services for the Sqlite provider.
142   /// </summary>
143   public abstract class SqliteConvert
144   {
145     /// <summary>
146     /// An array of ISO8601 datetime formats we support conversion from
147     /// </summary>
148     private static string[] _datetimeFormats = new string[] {
149       "yyyy-MM-dd HH:mm:ss.fffffff",
150       "yyyy-MM-dd HH:mm:ss",
151       "yyyy-MM-dd HH:mm",                               
152       "yyyyMMddHHmmss",
153       "yyyyMMddHHmm",
154       "yyyyMMddTHHmmssfffffff",
155       "yyyy-MM-dd",
156       "yy-MM-dd",
157       "yyyyMMdd",
158       "HH:mm:ss",
159       "HH:mm",
160       "THHmmss",
161       "THHmm",
162       "yyyy-MM-dd HH:mm:ss.fff",
163       "yyyy-MM-ddTHH:mm",
164       "yyyy-MM-ddTHH:mm:ss",
165       "yyyy-MM-ddTHH:mm:ss.fff",
166       "yyyy-MM-ddTHH:mm:ss.ffffff",
167       "HH:mm:ss.fff"
168     };
169
170     /// <summary>
171     /// An UTF-8 Encoding instance, so we can convert strings to and from UTF-8
172     /// </summary>
173     private Encoding _utf8 = new UTF8Encoding();
174     /// <summary>
175     /// The default DateTime format for this instance
176     /// </summary>
177     internal SqliteDateFormats _datetimeFormat;
178     /// <summary>
179     /// Initializes the conversion class
180     /// </summary>
181     /// <param name="fmt">The default date/time format to use for this instance</param>
182     internal SqliteConvert(SqliteDateFormats fmt)
183     {
184       _datetimeFormat = fmt;
185     }
186
187     #region UTF-8 Conversion Functions
188     /// <summary>
189     /// Converts a string to a UTF-8 encoded byte array sized to include a null-terminating character.
190     /// </summary>
191     /// <param name="sourceText">The string to convert to UTF-8</param>
192     /// <returns>A byte array containing the converted string plus an extra 0 terminating byte at the end of the array.</returns>
193     public byte[] ToUTF8(string sourceText)
194     {
195       Byte[] byteArray;
196       int nlen = _utf8.GetByteCount(sourceText) + 1;
197
198       byteArray = new byte[nlen];
199       nlen = _utf8.GetBytes(sourceText, 0, sourceText.Length, byteArray, 0);
200       byteArray[nlen] = 0;
201
202       return byteArray;
203     }
204
205     /// <summary>
206     /// Convert a DateTime to a UTF-8 encoded, zero-terminated byte array.
207     /// </summary>
208     /// <remarks>
209     /// This function is a convenience function, which first calls ToString() on the DateTime, and then calls ToUTF8() with the
210     /// string result.
211     /// </remarks>
212     /// <param name="dateTimeValue">The DateTime to convert.</param>
213     /// <returns>The UTF-8 encoded string, including a 0 terminating byte at the end of the array.</returns>
214     public byte[] ToUTF8(DateTime dateTimeValue)
215     {
216       return ToUTF8(ToString(dateTimeValue));
217     }
218
219     /// <summary>
220     /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string
221     /// </summary>
222     /// <param name="nativestring">The pointer to the memory where the UTF-8 string is encoded</param>
223     /// <param name="nativestringlen">The number of bytes to decode</param>
224     /// <returns>A string containing the translated character(s)</returns>
225     public virtual string ToString(IntPtr nativestring)
226     {
227       return UTF8ToString(nativestring);
228     }
229
230     /// <summary>
231     /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string
232     /// </summary>
233     /// <param name="nativestring">The pointer to the memory where the UTF-8 string is encoded</param>
234     /// <param name="nativestringlen">The number of bytes to decode</param>
235     /// <returns>A string containing the translated character(s)</returns>
236     public virtual string UTF8ToString(IntPtr nativestring)
237     {
238             return Marshal.PtrToStringAuto (nativestring);
239     }
240
241
242     #endregion
243
244     #region DateTime Conversion Functions
245     /// <summary>
246     /// Converts a string into a DateTime, using the current DateTimeFormat specified for the connection when it was opened.
247     /// </summary>
248     /// <remarks>
249     /// Acceptable ISO8601 DateTime formats are:
250     ///   yyyy-MM-dd HH:mm:ss
251     ///   yyyyMMddHHmmss
252     ///   yyyyMMddTHHmmssfffffff
253     ///   yyyy-MM-dd
254     ///   yy-MM-dd
255     ///   yyyyMMdd
256     ///   HH:mm:ss
257     ///   THHmmss
258     /// </remarks>
259     /// <param name="dateText">The string containing either a Tick value or an ISO8601-format string</param>
260     /// <returns>A DateTime value</returns>
261     public DateTime ToDateTime(string dateText)
262     {
263       switch (_datetimeFormat)
264       {
265         case SqliteDateFormats.Ticks:
266           return new DateTime(Convert.ToInt64(dateText, CultureInfo.InvariantCulture));
267         default:
268           return DateTime.ParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None);
269       }
270     }
271
272     /// <summary>
273     /// Converts a DateTime to a string value, using the current DateTimeFormat specified for the connection when it was opened.
274     /// </summary>
275     /// <param name="dateValue">The DateTime value to convert</param>
276     /// <returns>Either a string consisting of the tick count for DateTimeFormat.Ticks, or a date/time in ISO8601 format.</returns>
277     public string ToString(DateTime dateValue)
278     {
279       switch (_datetimeFormat)
280       {
281         case SqliteDateFormats.Ticks:
282           return dateValue.Ticks.ToString(CultureInfo.InvariantCulture);
283         default:
284           return dateValue.ToString(_datetimeFormats[0], CultureInfo.InvariantCulture);
285       }
286     }
287
288     /// <summary>
289     /// Internal function to convert a UTF-8 encoded IntPtr of the specified length to a DateTime.
290     /// </summary>
291     /// <remarks>
292     /// This is a convenience function, which first calls ToString() on the IntPtr to convert it to a string, then calls
293     /// ToDateTime() on the string to return a DateTime.
294     /// </remarks>
295     /// <param name="ptr">A pointer to the UTF-8 encoded string</param>
296     /// <param name="len">The length in bytes of the string</param>
297     /// <returns>The parsed DateTime value</returns>
298     internal DateTime ToDateTime(IntPtr ptr)
299     {
300       return ToDateTime(ToString(ptr));
301     }
302     #endregion
303
304     /// <summary>
305     /// Smart method of splitting a string.  Skips quoted elements, removes the quotes.
306     /// </summary>
307     /// <remarks>
308     /// This split function works somewhat like the String.Split() function in that it breaks apart a string into
309     /// pieces and returns the pieces as an array.  The primary differences are:
310     /// <list type="bullet">
311     /// <item><description>Only one character can be provided as a separator character</description></item>
312     /// <item><description>Quoted text inside the string is skipped over when searching for the separator, and the quotes are removed.</description></item>
313     /// </list>
314     /// Thus, if splitting the following string looking for a comma:<br/>
315     /// One,Two, "Three, Four", Five<br/>
316     /// <br/>
317     /// The resulting array would contain<br/>
318     /// [0] One<br/>
319     /// [1] Two<br/>
320     /// [2] Three, Four<br/>
321     /// [3] Five<br/>
322     /// <br/>
323     /// Note that the leading and trailing spaces were removed from each item during the split.
324     /// </remarks>
325     /// <param name="source">Source string to split apart</param>
326     /// <param name="separator">Separator character</param>
327     /// <returns>A string array of the split up elements</returns>
328     public static string[] Split(string source, char separator)
329     {
330       char[] toks = new char[2] { '\"', separator };
331       char[] quot = new char[1] { '\"' };
332       int n = 0;
333       List<string> ls = new List<string>();
334       string s;
335
336       while (source.Length > 0)
337       {
338         n = source.IndexOfAny(toks, n);
339         if (n == -1) break;
340         if (source[n] == toks[0])
341         {
342           source = source.Remove(n, 1);
343           n = source.IndexOfAny(quot, n);
344           if (n == -1)
345           {
346             source = "\"" + source;
347             break;
348           }
349           source = source.Remove(n, 1);
350         }
351         else
352         {
353           s = source.Substring(0, n).Trim();
354           source = source.Substring(n + 1).Trim();
355           if (s.Length > 0) ls.Add(s);
356           n = 0;
357         }
358       }
359       if (source.Length > 0) ls.Add(source);
360
361       string[] ar = new string[ls.Count];
362       ls.CopyTo(ar, 0);
363
364       return ar;
365     }
366
367     #region Type Conversions
368     /// <summary>
369     /// Determines the data type of a column in a statement
370     /// </summary>
371     /// <param name="stmt">The statement to retrieve information for</param>
372     /// <param name="i">The column to retrieve type information on</param>
373     /// <returns>Returns a SqliteType struct</returns>
374     internal static SqliteType ColumnToType(SqliteStatement stmt, int i)
375     {
376       SqliteType typ;
377
378       typ.Type = TypeNameToDbType(stmt._sql.ColumnType(stmt, i, out typ.Affinity));
379
380       return typ;
381     }
382
383     /// <summary>
384     /// Converts a SqliteType to a .NET Type object
385     /// </summary>
386     /// <param name="t">The SqliteType to convert</param>
387     /// <returns>Returns a .NET Type object</returns>
388     internal static Type SqliteTypeToType(SqliteType t)
389     {
390       if (t.Type != DbType.Object)
391         return SqliteConvert.DbTypeToType(t.Type);
392
393       return _typeaffinities[(int)t.Affinity];
394     }
395
396     static Type[] _typeaffinities = {
397       null,
398       typeof(Int64),
399       typeof(Double),
400       typeof(string),
401       typeof(byte[]),
402       typeof(DBNull),
403       null,
404       null,
405       null,
406       null,
407       typeof(DateTime),
408       null,
409     };
410
411     /// <summary>
412     /// For a given intrinsic type, return a DbType
413     /// </summary>
414     /// <param name="typ">The native type to convert</param>
415     /// <returns>The corresponding (closest match) DbType</returns>
416     internal static DbType TypeToDbType(Type typ)
417     {
418       TypeCode tc = Type.GetTypeCode(typ);
419       if (tc == TypeCode.Object)
420       {
421         if (typ == typeof(byte[])) return DbType.Binary;
422         if (typ == typeof(Guid)) return DbType.Guid;
423         return DbType.String;
424       }
425       return _typetodbtype[(int)tc];
426     }
427
428     private static DbType[] _typetodbtype = {
429       DbType.Object,
430       DbType.Binary,
431       DbType.Object,
432       DbType.Boolean,
433       DbType.SByte,
434       DbType.SByte,
435       DbType.Byte,
436       DbType.Int16, // 7
437       DbType.UInt16,
438       DbType.Int32,
439       DbType.UInt32,
440       DbType.Int64, // 11
441       DbType.UInt64,
442       DbType.Single,
443       DbType.Double,
444       DbType.Decimal,
445       DbType.DateTime,
446       DbType.Object,
447       DbType.String,
448     };
449
450     /// <summary>
451     /// Returns the ColumnSize for the given DbType
452     /// </summary>
453     /// <param name="typ">The DbType to get the size of</param>
454     /// <returns></returns>
455     internal static int DbTypeToColumnSize(DbType typ)
456     {
457       return _dbtypetocolumnsize[(int)typ];
458     }
459
460     private static int[] _dbtypetocolumnsize = {
461       2147483647,   // 0
462       2147483647,   // 1
463       1,     // 2
464       1,     // 3
465       8,  // 4
466       8, // 5
467       8, // 6
468       8,  // 7
469       8,   // 8
470       16,     // 9
471       2,
472       4,
473       8,
474       2147483647,
475       1,
476       4,
477       2147483647,
478       8,
479       2,
480       4,
481       8,
482       8,
483       2147483647,
484       2147483647,
485       2147483647,
486       2147483647,   // 25 (Xml)
487     };
488
489     /// <summary>
490     /// Convert a DbType to a Type
491     /// </summary>
492     /// <param name="typ">The DbType to convert from</param>
493     /// <returns>The closest-match .NET type</returns>
494     internal static Type DbTypeToType(DbType typ)
495     {
496       return _dbtypeToType[(int)typ];
497     }
498
499     private static Type[] _dbtypeToType = {
500       typeof(string),   // 0
501       typeof(byte[]),   // 1
502       typeof(byte),     // 2
503       typeof(bool),     // 3
504       typeof(decimal),  // 4
505       typeof(DateTime), // 5
506       typeof(DateTime), // 6
507       typeof(decimal),  // 7
508       typeof(double),   // 8
509       typeof(Guid),     // 9
510       typeof(Int16),
511       typeof(Int32),
512       typeof(Int64),
513       typeof(object),
514       typeof(sbyte),
515       typeof(float),
516       typeof(string),
517       typeof(DateTime),
518       typeof(UInt16),
519       typeof(UInt32),
520       typeof(UInt64),
521       typeof(double),
522       typeof(string),
523       typeof(string),
524       typeof(string),
525       typeof(string),   // 25 (Xml)
526     };
527
528     /// <summary>
529     /// For a given type, return the closest-match Sqlite TypeAffinity, which only understands a very limited subset of types.
530     /// </summary>
531     /// <param name="typ">The type to evaluate</param>
532     /// <returns>The Sqlite type affinity for that type.</returns>
533     internal static TypeAffinity TypeToAffinity(Type typ)
534     {
535       TypeCode tc = Type.GetTypeCode(typ);
536       if (tc == TypeCode.Object)
537       {
538         if (typ == typeof(byte[]) || typ == typeof(Guid))
539           return TypeAffinity.Blob;
540         else
541           return TypeAffinity.Text;
542       }
543       return _typecodeAffinities[(int)tc];
544     }
545
546     private static TypeAffinity[] _typecodeAffinities = {
547       TypeAffinity.Null,
548       TypeAffinity.Blob,
549       TypeAffinity.Null,
550       TypeAffinity.Int64,
551       TypeAffinity.Int64,
552       TypeAffinity.Int64,
553       TypeAffinity.Int64,
554       TypeAffinity.Int64, // 7
555       TypeAffinity.Int64,
556       TypeAffinity.Int64,
557       TypeAffinity.Int64,
558       TypeAffinity.Int64, // 11
559       TypeAffinity.Int64,
560       TypeAffinity.Double,
561       TypeAffinity.Double,
562       TypeAffinity.Double,
563       TypeAffinity.DateTime,
564       TypeAffinity.Null,
565       TypeAffinity.Text,
566     };
567
568     /// <summary>
569     /// For a given type name, return a closest-match .NET type
570     /// </summary>
571     /// <param name="Name">The name of the type to match</param>
572     /// <returns>The .NET DBType the text evaluates to.</returns>
573     internal static DbType TypeNameToDbType(string Name)
574     {
575       if (String.IsNullOrEmpty(Name)) return DbType.Object;
576
577       int x = _typeNames.Length;
578       for (int n = 0; n < x; n++)
579       {
580         if (String.Compare(Name, 0, _typeNames[n].typeName, 0, _typeNames[n].typeName.Length, true, CultureInfo.InvariantCulture) == 0)
581           return _typeNames[n].dataType; 
582       }
583       return DbType.Object;
584     }
585     #endregion
586
587     private static SqliteTypeNames[] _typeNames = {
588       new SqliteTypeNames("COUNTER", DbType.Int64),
589       new SqliteTypeNames("AUTOINCREMENT", DbType.Int64),
590       new SqliteTypeNames("IDENTITY", DbType.Int64),
591       new SqliteTypeNames("LONGTEXT", DbType.String),
592       new SqliteTypeNames("LONGCHAR", DbType.String),
593       new SqliteTypeNames("LONGVARCHAR", DbType.String),
594       new SqliteTypeNames("LONG", DbType.Int64),
595       new SqliteTypeNames("TINYINT", DbType.Byte),
596       new SqliteTypeNames("INTEGER", DbType.Int64),
597       new SqliteTypeNames("INT", DbType.Int32),
598       new SqliteTypeNames("VARCHAR", DbType.String),
599       new SqliteTypeNames("NVARCHAR", DbType.String),
600       new SqliteTypeNames("CHAR", DbType.String),
601       new SqliteTypeNames("NCHAR", DbType.String),
602       new SqliteTypeNames("TEXT", DbType.String),
603       new SqliteTypeNames("NTEXT", DbType.String),
604       new SqliteTypeNames("STRING", DbType.String),
605       new SqliteTypeNames("DOUBLE", DbType.Double),
606       new SqliteTypeNames("FLOAT", DbType.Double),
607       new SqliteTypeNames("REAL", DbType.Single),          
608       new SqliteTypeNames("BIT", DbType.Boolean),
609       new SqliteTypeNames("YESNO", DbType.Boolean),
610       new SqliteTypeNames("LOGICAL", DbType.Boolean),
611       new SqliteTypeNames("BOOL", DbType.Boolean),
612       new SqliteTypeNames("NUMERIC", DbType.Decimal),
613       new SqliteTypeNames("DECIMAL", DbType.Decimal),
614       new SqliteTypeNames("MONEY", DbType.Decimal),
615       new SqliteTypeNames("CURRENCY", DbType.Decimal),
616       new SqliteTypeNames("TIME", DbType.DateTime),
617       new SqliteTypeNames("DATE", DbType.DateTime),
618       new SqliteTypeNames("SMALLDATE", DbType.DateTime),
619       new SqliteTypeNames("BLOB", DbType.Binary),
620       new SqliteTypeNames("BINARY", DbType.Binary),
621       new SqliteTypeNames("VARBINARY", DbType.Binary),
622       new SqliteTypeNames("IMAGE", DbType.Binary),
623       new SqliteTypeNames("GENERAL", DbType.Binary),
624       new SqliteTypeNames("OLEOBJECT", DbType.Binary),
625       new SqliteTypeNames("GUID", DbType.Guid),
626       new SqliteTypeNames("UNIQUEIDENTIFIER", DbType.Guid),
627       new SqliteTypeNames("MEMO", DbType.String),
628       new SqliteTypeNames("NOTE", DbType.String),
629       new SqliteTypeNames("SMALLINT", DbType.Int16),
630       new SqliteTypeNames("BIGINT", DbType.Int64),
631     };
632   }
633 }
634 #endif