[Sqlite] Rework type mapping between sqllite and .net using sqlite's substring matchi...
authorRolf Bjarne Kvinge <rolf@xamarin.com>
Thu, 9 Aug 2012 10:24:02 +0000 (12:24 +0200)
committerRolf Bjarne Kvinge <rolf@xamarin.com>
Thu, 9 Aug 2012 13:06:17 +0000 (15:06 +0200)
mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteConvert.cs
mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteDataReader.cs
mcs/class/Mono.Data.Sqlite/Test/SqliteDataReaderTest.cs

index fe751b822af63971e2f2abfd420f975681f72822..99923147dbbcd0abbe27d020a49bd19e7e8d1cbc 100644 (file)
@@ -640,13 +640,42 @@ namespace Mono.Data.Sqlite
     {\r
       if (String.IsNullOrEmpty(Name)) return DbType.Object;\r
 \r
-      int x = _typeNames.Length;\r
-      for (int n = 0; n < x; n++)\r
+      string nameToCompare = Name;\r
+      int parenthesis = nameToCompare.IndexOf ('(');\r
+      if (parenthesis > 0)\r
+        nameToCompare = nameToCompare.Substring (0, parenthesis);\r
+        \r
+      for (int n = 0; n < _typeNames.Length; n++)\r
       {\r
-        if (String.Compare(Name, _typeNames[n].typeName, true, CultureInfo.InvariantCulture) == 0)\r
+        if (string.Compare(nameToCompare, _typeNames[n].typeName, true, CultureInfo.InvariantCulture) == 0)\r
           return _typeNames[n].dataType; \r
       }\r
-      return DbType.Object;\r
+      \r
+      /* http://www.sqlite.org/datatype3.html\r
+       * 2.1 Determination Of Column Affinity\r
+       * The affinity of a column is determined by the declared type of the column, according to the following rules in the order shown:\r
+       *   1. If the declared type contains the string "INT" then it is assigned INTEGER affinity.\r
+       *   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
+       *   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
+       *   4. If the declared type for a column contains any of the strings "REAL", "FLOA", or "DOUB" then the column has REAL affinity.\r
+       *   5. Otherwise, the affinity is NUMERIC.\r
+       */\r
+      \r
+      if (Name.IndexOf ("INT", StringComparison.OrdinalIgnoreCase) >= 0) {\r
+        return DbType.Int64;\r
+      } else if (Name.IndexOf ("CHAR", StringComparison.OrdinalIgnoreCase) >= 0\r
+              || Name.IndexOf ("CLOB", StringComparison.OrdinalIgnoreCase) >= 0\r
+              || Name.IndexOf ("TEXT", StringComparison.OrdinalIgnoreCase) >= 0) {\r
+        return DbType.String;\r
+      } else if (Name.IndexOf ("BLOB", StringComparison.OrdinalIgnoreCase) >= 0 /* || Name == string.Empty // handled at the top of this functin */) {\r
+        return DbType.Object;\r
+      } else if (Name.IndexOf ("REAL", StringComparison.OrdinalIgnoreCase) >= 0\r
+              || Name.IndexOf ("FLOA", StringComparison.OrdinalIgnoreCase) >= 0\r
+              || Name.IndexOf ("DOUB", StringComparison.OrdinalIgnoreCase) >= 0) {\r
+        return DbType.Double;\r
+      } else {\r
+        return DbType.Object; // This can be anything, so use Object instead of Decimal (which we use otherwise where the type affinity is NUMERIC)\r
+      }\r
     }\r
     #endregion\r
 \r
index a295098e050c95ed620070b4b5245225882e5d3d..b271779000a7b99ce56953217bd987f6b450ec29 100644 (file)
@@ -379,7 +379,6 @@ namespace Mono.Data.Sqlite
         return _keyInfo.GetDataTypeName(i - VisibleFieldCount);\r
 \r
       SQLiteType typ = GetSQLiteType(i);\r
-      if (typ.Type == DbType.Object) return SqliteConvert.SQLiteTypeToType(typ).Name;\r
       return _activeStatement._sql.ColumnType(_activeStatement, i, out typ.Affinity);\r
     }\r
 \r
index deeb6877358447341bc19718d2aaae8439504b37..5fc27764535c1c8151316703c3455cad371e9ea5 100644 (file)
 
 
 using System;
+using System.Collections.Generic;
 using System.Data;
 using System.IO;
+using System.Text;
 using Mono.Data.Sqlite;
 
 using NUnit.Framework;
@@ -176,34 +178,109 @@ namespace MonoTests.Mono.Data.Sqlite
                        }
                }
 
+               void AddParameter (System.Data.Common.DbCommand cm, string name, object value)
+               {
+                       var param = cm.CreateParameter ();
+                       param.ParameterName = ":" + name;
+                       param.Value = value;
+                       cm.Parameters.Add (param);
+               }
+
+               class D
+               {
+                       public string Sql;
+                       public Type Expected;
+                       public object Value;
+               }
+
                [Test]
                public void TestDataTypes ()
                {
                        SqliteParameter param;
                        
+                       var data = new List<D> ()
+                       {
+                               new D () { Sql = "DATETIME",            Expected = typeof (DateTime),   Value = DateTime.Now              },
+                               new D () { Sql = "GUIDBLOB NOT NULL",   Expected = typeof (Guid),       Value = new byte [] { 3, 14, 15 } },
+                               new D () { Sql = "BOOLEAN",             Expected = typeof (bool),       Value = true                      },
+                               new D () { Sql = "INT32",               Expected = typeof (long),       Value = 1                         },
+                               new D () { Sql = "INT32 NOT NULL",      Expected = typeof (long),       Value = 2                         },
+                               new D () { Sql = "UINT1",               Expected = typeof (long),       Value = 3                         },
+                               
+                               // these map to the INTEGER affinity
+                               new D () { Sql = "INT",                 Expected = typeof (int),       Value = 4  },
+                               new D () { Sql = "INTEGER",             Expected = typeof (long),      Value = 5 },
+                               new D () { Sql = "TINYINT",             Expected = typeof (byte),      Value = 6 },
+                               new D () { Sql = "SMALLINT",            Expected = typeof (short),     Value = 7 },
+                               new D () { Sql = "MEDIUMINT",           Expected = typeof (long),      Value = 8 },
+                               new D () { Sql = "BIGINT",              Expected = typeof (long),      Value = 9 },
+                               new D () { Sql = "UNSIGNED BIG INT",    Expected = typeof (long),      Value = 0 },
+                               new D () { Sql = "INT2",                Expected = typeof (long),      Value = 1 },
+                               new D () { Sql = "INT4",                Expected = typeof (long),      Value = 2 },
+                               
+                               // these map to the TEXT affinity
+                               new D () { Sql = "CHARACTER(20)",          Expected = typeof (string),       Value = "a" },
+                               new D () { Sql = "VARCHAR(255)",           Expected = typeof (string),       Value = "b" },
+                               new D () { Sql = "VARYING CHARACTER(255)", Expected = typeof (string),       Value = "c" },
+                               new D () { Sql = "NCHAR(55)",              Expected = typeof (string),       Value = "d" },
+                               new D () { Sql = "NATIVE CHARACTER(70)",   Expected = typeof (string),       Value = "e" },
+                               new D () { Sql = "NVARCHAR(100)",          Expected = typeof (string),       Value = "f" },
+                               new D () { Sql = "TEXT",                   Expected = typeof (string),       Value = "g" },
+                               new D () { Sql = "CLOB",                   Expected = typeof (string),       Value = "h" },
+                               
+                               // these map to the NONE affinity
+                               new D () { Sql = "BLOB",           Expected = typeof (byte[]),       Value = new byte [] { 3, 14, 15 } },
+                               new D () { Sql = "",               Expected = typeof (object),       Value = null                      },
+                               
+                               // these map to the REAL affinity
+                               new D () { Sql = "REAL",               Expected = typeof (float),        Value = 3.2 },
+                               new D () { Sql = "DOUBLE",             Expected = typeof (double),       Value = 4.2 },
+                               new D () { Sql = "DOUBLE PRECISION",   Expected = typeof (double),       Value = 5.2 },
+                               new D () { Sql = "FLOAT",              Expected = typeof (double),       Value = 6.2 },
+                               
+                               // these map to the NUMERIC affinity
+                               new D () { Sql = "NUMERIC",            Expected = typeof (decimal),        Value = 3.2          },
+                               new D () { Sql = "DECIMAL(10,5)",      Expected = typeof (decimal),        Value = null         },
+                               new D () { Sql = "BOOLEAN",            Expected = typeof (bool),           Value = true         },
+                               new D () { Sql = "DATE",               Expected = typeof (DateTime),       Value = DateTime.Now },
+                               new D () { Sql = "DATETIME",           Expected = typeof (DateTime),       Value = DateTime.Now },
+                               
+                               
+                       };
+                       
                        _conn.ConnectionString = _connectionString;
                        using (_conn) {
                                _conn.Open();
                                
                                using (var cm = _conn.CreateCommand ()) {
-                                       cm.CommandText = @"
-DROP TABLE TEST;
-CREATE TABLE TEST (F1 DATETIME, F2 GUIDBLOB NOT NULL, F3 BOOLEAN);
-INSERT INTO TEST (F1, F2, F3) VALUES (:F1, :F2, :F3)";
-
-                                       param = cm.CreateParameter ();
-                                       param.ParameterName = ":F1";
-                                       param.Value = DateTime.Now;
-                                       cm.Parameters.Add (param);
-                                       param = cm.CreateParameter ();
-                                       param.ParameterName = ":F2";
-                                       param.Value = new byte [] { 3, 14, 15 };
-                                       cm.Parameters.Add (param);
-                                       param = cm.CreateParameter ();
-                                       param.ParameterName = ":F3";
-                                       param.Value = true;
-                                       cm.Parameters.Add (param);
+                                       var sql = new StringBuilder ();
+                                       var args = new StringBuilder ();
+                                       var vals = new StringBuilder ();
+                                       sql.AppendLine ("DROP TABLE TEST;");
+                                       sql.Append ("CREATE TABLE TEST (");
                                        
+                                       bool comma = false;
+                                       for (int i = 0; i < data.Count; i++) {
+                                               if (comma) {
+                                                       sql.Append (", ");
+                                                       args.Append (", ");
+                                                       vals.Append (", ");
+                                               }
+                                               comma = true;
+                                               sql.AppendFormat ("F{0} {1}", i, data [i].Sql);
+                                               args.AppendFormat ("F{0}", i);
+                                               vals.AppendFormat (":F{0}", i);
+                                               AddParameter (cm, "F" + i.ToString (), data [i].Value);
+                                       }
+                                       sql.AppendLine (");");
+                                       sql.Append ("INSERT INTO TEST (");
+                                       sql.Append (args.ToString ());
+                                       sql.Append (") VALUES (");
+                                       sql.Append (vals.ToString ());
+                                       sql.Append (");");
+               
+                                       cm.CommandText = sql.ToString ();
+
                                        cm.ExecuteNonQuery ();
                                }
                                
@@ -212,11 +289,12 @@ INSERT INTO TEST (F1, F2, F3) VALUES (:F1, :F2, :F3)";
                                        using (var dr = cm.ExecuteReader ()) {
                                                dr.Read ();
                                                
-                                               Assert.AreEqual ("System.DateTime", dr.GetFieldType (dr.GetOrdinal ("F1")).ToString (), "F1");
-                                               Assert.AreEqual ("GUIDBLOB", dr.GetDataTypeName (dr.GetOrdinal ("F2")), "F2");
-                                               Assert.AreEqual ("System.Guid", dr.GetFieldType (dr.GetOrdinal ("F2")).ToString (), "F2-#2");
-                                               Assert.AreEqual ("System.Boolean", dr.GetFieldType (dr.GetOrdinal ("F3")).ToString (), "F3");
-                                               Assert.AreEqual ("BOOLEAN", dr.GetDataTypeName (dr.GetOrdinal ("F3")), "F3-#2");
+                                               for (int i = 0; i < data.Count; i++) {
+                                                       string tn = data [i].Sql.Replace (" NOT NULL", "");
+                                                       int index = dr.GetOrdinal ("F" + i.ToString ());
+                                                       Assert.AreEqual (tn, dr.GetDataTypeName (index), "F" + i.ToString () + " (" + data [i].Sql + ")");
+                                                       Assert.AreEqual (data [i].Expected.FullName, dr.GetFieldType (index).ToString (), "F" + i.ToString () + " (" + data [i].Sql + ")");
+                                               }
                                        }
                                }
                        }