|
|
@@ -464,6 +464,8 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
|
|
|
#region Database Structure Management
|
|
|
|
|
|
+ #region MetaData
|
|
|
+
|
|
|
private class MetadataEntry
|
|
|
{
|
|
|
public String Name { get; set; }
|
|
|
@@ -485,6 +487,124 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
Triggers = new List<MetadataEntry>();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // Skipped starting '[' already
|
|
|
+ private void ParseField(string sql, ref int i, Dictionary<string, string> fields)
|
|
|
+ {
|
|
|
+ var j = i;
|
|
|
+ while(j < sql.Length)
|
|
|
+ {
|
|
|
+ if (sql[j] == ']')
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ++j;
|
|
|
+ }
|
|
|
+
|
|
|
+ var fieldName = sql[i..j];
|
|
|
+
|
|
|
+ ++j;
|
|
|
+ while(j < sql.Length && char.IsWhiteSpace(sql[j]))
|
|
|
+ {
|
|
|
+ ++j;
|
|
|
+ }
|
|
|
+
|
|
|
+ i = j;
|
|
|
+
|
|
|
+ while(i < sql.Length)
|
|
|
+ {
|
|
|
+ if (sql[i] == '\'')
|
|
|
+ {
|
|
|
+ while(i < sql.Length && sql[i] != '\'')
|
|
|
+ {
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ else if (sql[i] == ',')
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if (sql[i] == ')')
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fields[fieldName] = sql[j..i].Trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Dictionary<string, string> ParseTableMetadata(string table, string sql, bool isTable)
|
|
|
+ {
|
|
|
+ var tableFields = new Dictionary<string, string>();
|
|
|
+
|
|
|
+ if (isTable)
|
|
|
+ {
|
|
|
+ var i = sql.IndexOf('(') + 1;
|
|
|
+ if(i == 0)
|
|
|
+ {
|
|
|
+ throw new Exception($"Expected 'CREATE TABLE {table} ('");
|
|
|
+ }
|
|
|
+
|
|
|
+ while (i < sql.Length)
|
|
|
+ {
|
|
|
+ var c = sql[i];
|
|
|
+ if (Char.IsWhiteSpace(c))
|
|
|
+ {
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ else if(c == ')')
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if(c == '[')
|
|
|
+ {
|
|
|
+ ++i;
|
|
|
+ ParseField(sql, ref i, tableFields);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sql = sql.Replace("\"", "")
|
|
|
+ .Replace("DISTINCT ", "");
|
|
|
+ sql = sql.Split(new String[] { " AS SELECT " }, StringSplitOptions.TrimEntries).Last();
|
|
|
+ sql = sql.Split(new String[] { " FROM " }, StringSplitOptions.TrimEntries).First();
|
|
|
+
|
|
|
+ var fields = sql.Replace("\n\t", "").Replace("\t", " ").Replace("\"", "").Trim().Split(',');
|
|
|
+ foreach (var fld in fields)
|
|
|
+ {
|
|
|
+ var field = fld.Trim()
|
|
|
+ .Replace("\t", " ")
|
|
|
+ .Replace("\"", "")
|
|
|
+ .Replace("[", "").Replace("]", "");
|
|
|
+
|
|
|
+ var parts = field.Split(" as ");
|
|
|
+ if(parts.Length == 1)
|
|
|
+ {
|
|
|
+ tableFields[field] = "";
|
|
|
+ }
|
|
|
+ else if(parts.Length == 2)
|
|
|
+ {
|
|
|
+ field = parts[1];
|
|
|
+ if (parts[0] != "NULL")
|
|
|
+ {
|
|
|
+ tableFields[field] = "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return tableFields;
|
|
|
+ }
|
|
|
|
|
|
private Dictionary<string, Tuple<Dictionary<string, string>, Dictionary<string, string>, Dictionary<string, string>>> LoadMetaData()
|
|
|
{
|
|
|
@@ -505,77 +625,18 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
if (reader.HasRows)
|
|
|
while (reader.Read())
|
|
|
{
|
|
|
- var tblinfo = new Tuple<Dictionary<string, string>, Dictionary<string, string>, Dictionary<string, string>>(
|
|
|
- new Dictionary<string, string>(), new Dictionary<string, string>(), new Dictionary<string, string>());
|
|
|
-
|
|
|
var table = reader.GetString(0);
|
|
|
var sql = reader.GetString(1);
|
|
|
- bool istable = String.Equals(reader.GetString(2),"table");
|
|
|
+ var istable = String.Equals(reader.GetString(2),"table");
|
|
|
|
|
|
- if (istable)
|
|
|
+ try
|
|
|
{
|
|
|
- sql = sql.Replace("\"", "")
|
|
|
- .Replace(string.Format("CREATE TABLE {0} (", table), "");
|
|
|
- sql = sql.Remove(sql.Length - 1).Trim();
|
|
|
- var fields = sql.Replace("\n\t", "").Replace("\t", " ").Replace("\"", "").Trim().Split(',');
|
|
|
- var primarykey = "";
|
|
|
- foreach (var fld in fields)
|
|
|
- {
|
|
|
- var field = fld.Trim().Replace("\t", " ").Replace("\"", "").Replace("[", "").Replace("]", "");
|
|
|
- //if (field.ToUpper().StartsWith("CONSTRAINT"))
|
|
|
- // tblinfo.Item2.Add(field);
|
|
|
- if (field.ToUpper().StartsWith("PRIMARY KEY"))
|
|
|
- {
|
|
|
- primarykey = field.Replace("PRIMARY KEY(", "").Replace(")", "");
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- var comps = field.Split(' ');
|
|
|
-
|
|
|
- tblinfo.Item1[comps[0]] = string.Format("{0}{1}", comps[1],
|
|
|
- field.Contains("PRIMARY KEY") ? " PRIMARY KEY" : "");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(primarykey))
|
|
|
- {
|
|
|
- var pkfld = tblinfo.Item1[primarykey];
|
|
|
- if (!pkfld.ToUpper().Contains("PRIMARY KEY"))
|
|
|
- tblinfo.Item1[primarykey] = string.Format("{0} PRIMARY KEY", pkfld.Trim());
|
|
|
- }
|
|
|
+ metadata[table] = new(ParseTableMetadata(table, sql, istable), new(), new());
|
|
|
}
|
|
|
- else
|
|
|
+ catch(Exception e)
|
|
|
{
|
|
|
- sql = sql.Replace("\"", "")
|
|
|
- .Replace("DISTINCT ", "");
|
|
|
- sql = sql.Split(new String[] { " AS SELECT " }, StringSplitOptions.TrimEntries).Last();
|
|
|
- sql = sql.Split(new String[] { " FROM " }, StringSplitOptions.TrimEntries).First();
|
|
|
-
|
|
|
- var fields = sql.Replace("\n\t", "").Replace("\t", " ").Replace("\"", "").Trim().Split(',');
|
|
|
- foreach (var fld in fields)
|
|
|
- {
|
|
|
- var field = fld.Trim()
|
|
|
- .Replace("\t", " ")
|
|
|
- .Replace("\"", "")
|
|
|
- .Replace("[", "").Replace("]", "");
|
|
|
-
|
|
|
- var parts = field.Split(" as ");
|
|
|
- if(parts.Length == 1)
|
|
|
- {
|
|
|
- tblinfo.Item1[field] = "";
|
|
|
- }
|
|
|
- else if(parts.Length == 2)
|
|
|
- {
|
|
|
- field = parts[1];
|
|
|
- if (parts[0] != "NULL")
|
|
|
- {
|
|
|
- tblinfo.Item1[field] = "";
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ Log(LogType.Error, $"Invalid table metadata for {table}: {CoreUtils.FormatException(e)}");
|
|
|
}
|
|
|
-
|
|
|
- metadata[table] = tblinfo;
|
|
|
}
|
|
|
|
|
|
reader.Close();
|
|
|
@@ -622,6 +683,8 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
return metadata;
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
private static void LoadType(Type type, List<Type> into)
|
|
|
{
|
|
|
if (into.Contains(type))
|
|
|
@@ -684,6 +747,12 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
return "TEXT";
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// For the given <paramref name="type"/>, load all the fields for the corresponding DB table, populating
|
|
|
+ /// <paramref name="fields"/> with entries <c>'fieldName': 'SQL definition'</c>, e.g., <c>"Description": "TEXT"</c>
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="type"></param>
|
|
|
+ /// <param name="fields"></param>
|
|
|
private void LoadFields(Type type, Dictionary<string, string> fields)
|
|
|
{
|
|
|
AutoEntity? view = type.GetCustomAttribute<AutoEntity>();
|
|
|
@@ -693,9 +762,11 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
|
|
|
foreach(var property in DatabaseSchema.Properties(definition).Where(x => x.IsDBColumn))
|
|
|
{
|
|
|
+ var defaultValue = SQLiteProvider.EscapeValue(SQLiteProvider.GetColumnDefaultValue(property.PropertyType));
|
|
|
fields[property.Name] =
|
|
|
ColumnType(property.PropertyType)
|
|
|
- + (property.Name.Equals("ID") ? " PRIMARY KEY" : "");
|
|
|
+ + (property.Name.Equals("ID") ? " PRIMARY KEY" : "")
|
|
|
+ + $" DEFAULT {defaultValue}";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -782,7 +853,7 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
{
|
|
|
foreach(var field in fields)
|
|
|
{
|
|
|
- actions.Add($"UPDATE {otherType.Name} SET [{field}] = NULL WHERE [{field}] = old.ID;");
|
|
|
+ actions.Add($"UPDATE {otherType.Name} SET [{field}] = '{Guid.Empty}' WHERE [{field}] = old.ID;");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1269,13 +1340,19 @@ public class SQLiteProviderFactory : IProviderFactory
|
|
|
Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
|
|
|
}
|
|
|
|
|
|
- if (!current_fields.ContainsKey(field) || (!String.IsNullOrWhiteSpace(current_fields[field])) && (current_fields[field] != type_fields[field]))
|
|
|
+ if (!current_fields.TryGetValue(field, out string? value) || (!String.IsNullOrWhiteSpace(value)) && (value != type_fields[field]))
|
|
|
bRebuild = true;
|
|
|
}
|
|
|
|
|
|
- foreach (var field in current_fields.Keys)
|
|
|
- if (!type_fields.ContainsKey(field))
|
|
|
- bRebuild = true;
|
|
|
+ if (!bRebuild)
|
|
|
+ {
|
|
|
+ foreach (var field in current_fields.Keys)
|
|
|
+ if (!type_fields.ContainsKey(field))
|
|
|
+ {
|
|
|
+ bRebuild = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (bForceRebuild || bRebuild)
|
|
|
RebuildTable(access, type, current_fields, type_fields, customproperties);
|
|
|
@@ -1949,7 +2026,7 @@ public class SQLiteProvider : IProvider
|
|
|
return sParam;
|
|
|
}
|
|
|
|
|
|
- private static object? GetFilterDefaultValue(Type type)
|
|
|
+ internal static object? GetColumnDefaultValue(Type type)
|
|
|
{
|
|
|
if(type == typeof(string))
|
|
|
{
|
|
|
@@ -2096,7 +2173,7 @@ public class SQLiteProvider : IProvider
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var strProp = $"IFNULL({prop},{EscapeValue(GetFilterDefaultValue(filter.Type))})";
|
|
|
+ var strProp = $"IFNULL({prop},{EscapeValue(GetColumnDefaultValue(filter.Type))})";
|
|
|
value = useparams ? EncodeParameter(command, value) : EscapeValue(filter.Value);
|
|
|
result = string.Format("(" + operators[filter.Operator] + ")", strProp, value);
|
|
|
}
|
|
|
@@ -2104,9 +2181,9 @@ public class SQLiteProvider : IProvider
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var strProp = $"IFNULL({prop},{EscapeValue(GetFilterDefaultValue(filter.Type))})";
|
|
|
+ var strProp = $"IFNULL({prop},{EscapeValue(GetColumnDefaultValue(filter.Type))})";
|
|
|
var strValue = filter.Value is FilterConstant constant
|
|
|
- ? constant == FilterConstant.Null ? EscapeValue(GetFilterDefaultValue(filter.Type)) : GetFilterConstant(constant)
|
|
|
+ ? constant == FilterConstant.Null ? EscapeValue(GetColumnDefaultValue(filter.Type)) : GetFilterConstant(constant)
|
|
|
: useparams ? EncodeParameter(command, Encode(filter.Value, filter.Type, convertToNull: false)) : EscapeValue(filter.Value);
|
|
|
|
|
|
result = string.Format($"({operators[filter.Operator]})", strProp, strValue);
|
|
|
@@ -3053,13 +3130,11 @@ public class SQLiteProvider : IProvider
|
|
|
var insertvalues = new List<string>();
|
|
|
var updatecommands = new List<string>();
|
|
|
|
|
|
- var iParam = 0;
|
|
|
foreach (var (key, v) in insert)
|
|
|
{
|
|
|
if (v is UserProperties)
|
|
|
continue;
|
|
|
|
|
|
- var sParam = string.Format("@p{0}", iParam++);
|
|
|
object? value = null;
|
|
|
try
|
|
|
{
|
|
|
@@ -3070,7 +3145,8 @@ public class SQLiteProvider : IProvider
|
|
|
Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
|
|
|
}
|
|
|
|
|
|
- command.Parameters.AddWithValue(sParam, value);
|
|
|
+
|
|
|
+ var sParam = EncodeParameter(command, value);
|
|
|
|
|
|
insertfields.Add(string.Format("[{0}]", key));
|
|
|
|