Просмотр исходного кода

Fixes to treatment of NULL in SQL filters

Kenric Nugteren 2 месяцев назад
Родитель
Сommit
5c2ec17e4b
2 измененных файлов с 107 добавлено и 76 удалено
  1. 6 4
      InABox.Core/CoreUtils.cs
  2. 101 72
      inabox.database.sqlite/SQLiteProvider.cs

+ 6 - 4
InABox.Core/CoreUtils.cs

@@ -1276,19 +1276,20 @@ namespace InABox.Core
         /// <seealso cref="GetDefault&lt;T&gt;" />
         public static object? GetDefault(this Type type)
         {
+            var typeInfo = type?.GetTypeInfo();
             // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
-            if (type == null || !type.GetTypeInfo().IsValueType || type == typeof(void))
+            if (typeInfo == null || type == null || !typeInfo.IsValueType || type == typeof(void))
                 return null;
 
             // If the supplied Type has generic parameters, its default value cannot be determined
-            if (type.GetTypeInfo().ContainsGenericParameters)
+            if (typeInfo.ContainsGenericParameters)
                 throw new ArgumentException(
                     "Error:\n\nThe supplied value type <" + type.Name +
                     "> contains generic parameters, so the default value cannot be retrieved\n\n");
 
             // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct/enum), return a 
             //  default instance of the value type
-            if (type.GetTypeInfo().IsPrimitive || !type.GetTypeInfo().IsNotPublic)
+            if (typeInfo.IsPrimitive || !typeInfo.IsNotPublic)
                 try
                 {
                     return Activator.CreateInstance(type);
@@ -1392,7 +1393,8 @@ namespace InABox.Core
 
         public static bool IsNumeric(this Type dataType)
         {
-            return dataType.IsFloatingPoint() || dataType.IsOrdinal();
+            return dataType.IsFloatingPoint() || dataType.IsOrdinal()
+                || dataType == typeof(decimal);
         }
 
         public static bool IsFloatingPoint(this Type dataType)

+ 101 - 72
inabox.database.sqlite/SQLiteProvider.cs

@@ -1708,44 +1708,48 @@ public class SQLiteProvider : IProvider
         if (IsNull(o) || type is null)
             return DBNull.Value;
 
-        if (type.IsEnum && o.GetType().UnderlyingSystemType != type)
+        var oType = o.GetType();
+
+        if (type.IsEnum && oType.UnderlyingSystemType != type)
         {
             if (Enum.TryParse(type, o.ToString(), out object? result))
                 return result.ToString();
         }
         
-        if (type == typeof(DateTime) && o.GetType() == typeof(string))
-            o = DateTime.Parse(o.ToString() ?? "");
-
-        if (type == typeof(double) && o.GetType() == typeof(string))
-            o = double.Parse(o.ToString() ?? "");
-
-        if (type == typeof(float) && o.GetType() == typeof(string))
-            o = float.Parse(o.ToString() ?? "");
-
-        if (type == typeof(long) && o.GetType() == typeof(string))
-            o = long.Parse(o.ToString() ?? "");
-
-        if (type.IsEnum && o.GetType() == typeof(string))
-            o = Enum.Parse(type, o.ToString() ?? "");
-
-        if (type == typeof(TimeSpan) && o.GetType() == typeof(string))
-        {
-            if (o.ToString()?.Contains(':') == true)
-            {
-                if (TimeSpan.TryParse(o.ToString(), out var time))
-                    o = time;
+        if(o is string str)
+        {
+            if (type == typeof(DateTime))
+                o = DateTime.Parse(str);
+            else if (type == typeof(double))
+                o = double.Parse(str);
+            else if (type == typeof(float))
+                o = float.Parse(str);
+            else if (type == typeof(long))
+                o = long.Parse(str);
+            else if (type.IsEnum)
+                o = Enum.Parse(type, str);
+            else if (type == typeof(TimeSpan))
+            {
+                if (str.Contains(':') == true)
+                {
+                    if (TimeSpan.TryParse(str, out var time))
+                        o = time;
+                }
+                else if (double.TryParse(str, out var hrs))
+                {
+                    o = TimeSpan.FromHours(hrs);
+                }
             }
-            else if (double.TryParse(o.ToString(), out var hrs))
+            else if(type == typeof(bool))
             {
-                o = TimeSpan.FromHours(hrs);
+                o = bool.Parse(str);
             }
+            else if (type == typeof(Guid))
+                o = Guid.Parse(str);
         }
 
         if (type == typeof(bool))
         {
-            if (o.GetType() == typeof(string))
-                o = bool.Parse(o.ToString() ?? "");
             if (o.Equals(false))
                 return DBNull.Value;
             return o;
@@ -1755,8 +1759,6 @@ public class SQLiteProvider : IProvider
             return DBNull.Value;
 
 
-        if (type == typeof(Guid) && o.GetType() == typeof(string))
-            o = Guid.Parse(o.ToString() ?? "");
 
         if (o is string[] sArray)
         {
@@ -1784,48 +1786,45 @@ public class SQLiteProvider : IProvider
             return ms.ToArray();
         }
 
-        if (o.GetType() == typeof(double) && o.Equals(default(double)))
+        if (o is double d && d == default)
             return DBNull.Value;
 
-        if (o.GetType() == typeof(float) && o.Equals(default(float)))
+        if (o is float f && f == default)
             return DBNull.Value;
 
-        if (o.GetType() == typeof(int) && o.Equals(default(int)))
+        if (o is int i && i == default)
             return DBNull.Value;
 
-        if (o.GetType() == typeof(long) && o.Equals(default(long)))
+        if (o is long l && l == default)
             return DBNull.Value;
 
-        if (o.GetType() == typeof(DateTime))
+        if (o is decimal dec && dec == default)
+            return DBNull.Value;
+
+        if (o is DateTime dateTime)
         {
-            if ((DateTime)o == DateTime.MinValue)
+            if (dateTime == DateTime.MinValue)
                 return DBNull.Value;
             return string.Format("{0:yyyy-MM-dd HH:mm:ss.FFFFFFF}", o);
         }
 
-        if (o.GetType() == typeof(TimeSpan))
+        if (o is TimeSpan timeSpan)
         {
-            if (((TimeSpan)o).Ticks == 0L)
+            if (timeSpan.Ticks == 0L)
                 return DBNull.Value;
-            return ((TimeSpan)o).TotalHours;
+            return timeSpan.TotalHours;
         }
 
-        if (o.GetType() == typeof(Guid))
+        if (o is Guid id)
         {
-            if (o.Equals(Guid.Empty))
+            if (id == Guid.Empty)
                 return DBNull.Value;
             return o.ToString() ?? "";
         }
 
-        if (type == typeof(double) && o.GetType() == typeof(string))
-            if (double.TryParse((string)o, out var value))
-                o = value;
-
         if (o.GetType().IsEnum)
             return o.ToString() ?? "";
 
-
-
         return o;
     }
 
@@ -1858,10 +1857,9 @@ public class SQLiteProvider : IProvider
             return string.Format("hex({0})", BitConverter.ToString(Encoding.ASCII.GetBytes(value.ToString() ?? "")).Replace("-", ""));
         if (value is IColumn col)
             return $"[{col.Property}]";
-        if (value is DateTime)
-            return $"'{((DateTime)value):yyyy-MM-dd HH:mm:ss.FFFFFFF}'";
+        if (value is DateTime date)
+            return $"'{date:yyyy-MM-dd HH:mm:ss.FFFFFFF}'";
         return value.ToString() ?? "";
-
     }
     
     internal static string EscapeValue(string? value, Type type)
@@ -1937,6 +1935,37 @@ public class SQLiteProvider : IProvider
         };
     }
 
+    private static string EncodeParameter(SQLiteCommand command, object? value)
+    {
+        var sParam = string.Format("@p{0}", command.Parameters.Count);
+        command.Parameters.AddWithValue(sParam, value);
+        return sParam;
+    }
+
+    private object? GetFilterDefaultValue(Type type)
+    {
+        if(type == typeof(string))
+        {
+            return "";
+        }
+        else if (type == typeof(DateTime))
+        {
+            return DateTime.MinValue;
+        }
+        else if (type == typeof(TimeSpan))
+        {
+            return TimeSpan.Zero;
+        }
+        else if (type.IsNumeric())
+        {
+            return 0;
+        }
+        else
+        {
+            return null;
+        }
+    }
+
     public string GetFilterClauseNonGeneric(Type T, SQLiteCommand command, char prefix, IFilter? filter, List<Tuple<string, string, string, string>> tables,
         Dictionary<string, string> fieldmap, List<string> columns, bool useparams)
     {
@@ -2017,14 +2046,14 @@ public class SQLiteProvider : IProvider
                     result = string.Format("(" + operators[filter.Operator] + ")", prop, subQueryText);
                 }
             }
-            else
+            else if(filter.Operator == Operator.IsEqualTo || filter.Operator == Operator.IsNotEqualTo)
             {
-                if (filter.Value is FilterConstant constant)
+                if(filter.Value is FilterConstant constant)
                 {
                     if (constant == FilterConstant.Null)
                     {
                         result = string.Format("({0} {1} NULL)", prop,
-                            filter.Operator == Operator.IsEqualTo 
+                            filter.Operator == Operator.IsEqualTo
                                 ? "IS"
                                 : "IS NOT");
                     }
@@ -2035,37 +2064,37 @@ public class SQLiteProvider : IProvider
                 else
                 {
                     var value = Encode(filter.Value, filter.Type);
-                    if (IsNull(value) && ((filter.Operator == Operator.IsEqualTo) || (filter.Operator == Operator.IsNotEqualTo)))
+                    if (IsNull(value))
                     {
                         result = string.Format("({0} {1} NULL)", prop,
-                            filter.Operator == Operator.IsEqualTo 
+                            filter.Operator == Operator.IsEqualTo
                                 ? "IS"
                                 : "IS NOT");
                     }
-                    else
+                    else if (filter.Type == typeof(string[]) && useparams)
                     {
-                        if (useparams)
-                        {
-                            var sParam = string.Format("@p{0}", command.Parameters.Count);
+                        var bytes = value is byte[] b ? b : Encoding.ASCII.GetBytes(value.ToString() ?? "");
+                        value = BitConverter.ToString(bytes).Replace("-", "");
 
-                            if (filter.Type == typeof(string[]))
-                            {
-                                var bytes = Encoding.ASCII.GetBytes(value.ToString() ?? "");
-                                value = BitConverter.ToString(bytes).Replace("-", "");
-                                result = string.Format("(" + operators[filter.Operator] + ")", string.Format("hex({0})", prop), sParam);
-                            }
-                            else
-                            {
-                                result = string.Format("(" + operators[filter.Operator] + ")", prop, sParam);
-                            }
-                            command.Parameters.AddWithValue(sParam, value);
-                        }
-                        else
-                            result = string.Format("(" + operators[filter.Operator] + ")", prop, EscapeValue(filter.Value));
+                        var sParam = EncodeParameter(command, value);
+                        result = string.Format("(" + operators[filter.Operator] + ")", string.Format("hex({0})", prop), sParam);
+                    }
+                    else
+                    {
+                        value = useparams ? EncodeParameter(command, value) : EscapeValue(filter.Value);
+                        result = string.Format("(" + operators[filter.Operator] + ")", prop, value);
                     }
-
                 }
             }
+            else
+            {
+                var strProp = $"IFNULL({prop},{EscapeValue(GetFilterDefaultValue(filter.Type))})";
+                var strValue = filter.Value is FilterConstant constant
+                    ? constant == FilterConstant.Null ? EscapeValue(GetFilterDefaultValue(filter.Type)) : GetFilterConstant(constant)
+                    : useparams ? EncodeParameter(command, filter.Value) : EscapeValue(filter.Value);
+
+                result = string.Format($"({operators[filter.Operator]})", strProp, strValue);
+            }
 
             if (filter.IsNot)
             {