diff --git a/src/EntityFramework.Sqlite.Design/EntityFramework.Sqlite.Design.csproj b/src/EntityFramework.Sqlite.Design/EntityFramework.Sqlite.Design.csproj
index 35465528537..6950fb69e6e 100644
--- a/src/EntityFramework.Sqlite.Design/EntityFramework.Sqlite.Design.csproj
+++ b/src/EntityFramework.Sqlite.Design/EntityFramework.Sqlite.Design.csproj
@@ -52,7 +52,6 @@
-
@@ -78,11 +77,11 @@
-
-
\ No newline at end of file
+
diff --git a/src/EntityFramework.Sqlite.Design/Internal/SqliteDmlParser.cs b/src/EntityFramework.Sqlite.Design/Internal/SqliteDmlParser.cs
deleted file mode 100644
index ac27c49ed04..00000000000
--- a/src/EntityFramework.Sqlite.Design/Internal/SqliteDmlParser.cs
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Data.Entity.Scaffolding.Metadata;
-
-namespace Microsoft.Data.Entity.Scaffolding.Internal
-{
- internal static class SqliteDmlParser
- {
- private static readonly ISet _constraintKeyWords = new HashSet(StringComparer.OrdinalIgnoreCase)
- {
- "CONSTRAINT",
- "PRIMARY",
- "UNIQUE",
- "CHECK",
- "FOREIGN"
- };
-
- public static void ParseTableDefinition(TableModel table, string sql)
- {
- var statements = ParseStatements(sql).ToList();
- var i = 0;
- for (; i < statements.Count; i++)
- {
- var firstWord = statements[i].Split(' ', '(')[0];
- if (_constraintKeyWords.Contains(firstWord))
- {
- break; // once we see the first constraint, stop looking for params
- }
- ParseColumnDefinition(table, statements[i]);
- }
- for (; i < statements.Count; i++)
- {
- ParseConstraints(table, statements[i]);
- }
- }
-
- // Extract all column definitions and constraints
- // Splits on commas, accounting for commas with parenthesis and quotes
- public static IEnumerable ParseStatements(string sql)
- {
- char c;
- var start = 0;
- while ((c = sql[start++]) != '(')
- {
- if (c == '"')
- {
- while (sql[start++] != '"')
- {
- ;
- }
- }
- if (c == '\'')
- {
- while (sql[start++] != '\'')
- {
- ;
- }
- }
- }
- var statementsChunk = sql.Substring(start, sql.LastIndexOf(')') - start);
-
- return SafeSplit(statementsChunk, ',').Select(s => s.Trim());
- }
-
- public static void ParseColumnDefinition(TableModel table, string statement)
- {
- var paramName = UnescapeString(SafeSplit(statement, ' ').First());
- var column = table.Columns.FirstOrDefault(c => c.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
- if (column == null)
- {
- return;
- }
-
- if (statement.IndexOf(" UNIQUE", StringComparison.OrdinalIgnoreCase) > 0)
- {
- var indexInfo = table.Indexes.FirstOrDefault(i =>
- i.Columns.SingleOrDefault()?.Name.Equals(column.Name, StringComparison.OrdinalIgnoreCase) == true);
-
- if (indexInfo != null)
- {
- indexInfo.IsUnique = true;
- }
- }
- }
-
- public static void ParseConstraints(TableModel table, string statement)
- {
- var constraint = statement.Split(' ', '(')[0];
- if (constraint.Equals("UNIQUE", StringComparison.OrdinalIgnoreCase))
- {
- ParseInlineUniqueConstraint(table, statement);
- }
- }
-
- public static void ParseInlineUniqueConstraint(TableModel table, string statement)
- {
- var start = statement.IndexOf('(') + 1;
- var paramChunk = statement.Substring(start, statement.LastIndexOf(')') - start);
- var columns = SafeSplit(paramChunk, ',')
- .Select(UnescapeString)
- .ToList();
-
- var index = table.Indexes.FirstOrDefault(i =>
- {
- if (!i.Name.StartsWith("sqlite_autoindex")
- || !i.Table.Name.Equals(table.Name, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- return columns.All(prop => i.Columns.Any(p => p.Name.Equals(prop, StringComparison.OrdinalIgnoreCase)));
- });
-
- if (index != null)
- {
- index.IsUnique = true;
- }
- }
-
- public static string UnescapeString(string identifier)
- {
- identifier = identifier.Trim();
- var firstChar = identifier[0];
- char quote;
- if (firstChar != identifier[identifier.Length - 1])
- {
- return identifier;
- }
-
- if (firstChar == '"')
- {
- quote = '"';
- }
- else if (firstChar == '\'')
- {
- quote = '\'';
- }
- else
- {
- return identifier;
- }
-
- return identifier.Substring(1, identifier.Length - 2).Replace($"{quote}{quote}", quote.ToString());
- }
-
- // For internal use. Only designed to split a valid SQLite statements.
- internal static IEnumerable SafeSplit(string str, char separator)
- {
- // ReSharper disable EmptyEmbeddedStatement
- var idx = -1;
- var lastStart = 0;
- char c;
- while (++idx < str.Length)
- {
- c = str[idx];
- if (c == separator)
- {
- if (idx > lastStart)
- {
- yield return str.Substring(lastStart, idx - lastStart).Trim(separator);
- }
- lastStart = idx + 1;
- }
- else if (c == '(')
- {
- var nestedLevel = 1;
- while (nestedLevel > 0
- && ++idx < str.Length)
- {
- switch (str[idx])
- {
- case ')':
- nestedLevel--;
- break;
- case '(':
- nestedLevel++;
- break;
- case '"':
- while (++idx < str.Length
- && str[idx] != '"')
- {
- ;
- }
- break;
- case '\'':
- while (++idx < str.Length
- && str[idx] != '\'')
- {
- ;
- }
- break;
- }
- }
- }
- else if (c == '"')
- {
- while (++idx < str.Length
- && str[idx] != '"')
- {
- ;
- }
- }
- else if (c == '\'')
- {
- while (++idx < str.Length
- && str[idx] != '\'')
- {
- ;
- }
- }
- }
- if (idx > lastStart)
- {
- yield return str.Substring(lastStart, idx - lastStart).Trim(separator);
- }
- }
- }
-}
diff --git a/src/EntityFramework.Sqlite.Design/SqliteDatabaseModelFactory.cs b/src/EntityFramework.Sqlite.Design/SqliteDatabaseModelFactory.cs
index 70a2f13c484..0792d21437a 100644
--- a/src/EntityFramework.Sqlite.Design/SqliteDatabaseModelFactory.cs
+++ b/src/EntityFramework.Sqlite.Design/SqliteDatabaseModelFactory.cs
@@ -4,9 +4,9 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Migrations;
-using Microsoft.Data.Entity.Scaffolding.Internal;
using Microsoft.Data.Entity.Scaffolding.Metadata;
using Microsoft.Data.Entity.Utilities;
using Microsoft.Data.Sqlite;
@@ -19,8 +19,6 @@ public class SqliteDatabaseModelFactory : IDatabaseModelFactory
private TableSelectionSet _tableSelectionSet;
private DatabaseModel _databaseModel;
private Dictionary _tables;
- private Dictionary _indexDefinitions;
- private Dictionary _tableDefinitions;
private Dictionary _tableColumns;
private static string ColumnKey(TableModel table, string columnName) => "[" + table.Name + "].[" + columnName + "]";
@@ -32,8 +30,6 @@ private void ResetState()
_databaseModel = new DatabaseModel();
_tables = new Dictionary(StringComparer.OrdinalIgnoreCase);
_tableColumns = new Dictionary(StringComparer.OrdinalIgnoreCase);
- _tableDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase);
- _indexDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase);
}
public virtual DatabaseModel Create(
@@ -64,12 +60,6 @@ public virtual DatabaseModel Create(
GetSqliteMaster();
GetColumns();
GetIndexes();
-
- foreach (var table in _databaseModel.Tables)
- {
- SqliteDmlParser.ParseTableDefinition(table, _tableDefinitions[table.Name]);
- }
-
GetForeignKeys();
return _databaseModel;
}
@@ -78,15 +68,14 @@ public virtual DatabaseModel Create(
private void GetSqliteMaster()
{
var command = _connection.CreateCommand();
- command.CommandText = "SELECT type, name, sql, tbl_name FROM sqlite_master ORDER BY type DESC";
+ command.CommandText = "SELECT type, name, tbl_name FROM sqlite_master ORDER BY type DESC";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var type = reader.GetString(0);
var name = reader.GetString(1);
- var sql = reader.GetValue(2) as string; // can be null
- var tableName = reader.GetString(3);
+ var tableName = reader.GetString(2);
if (type == "table"
&& name != "sqlite_sequence"
@@ -98,7 +87,6 @@ private void GetSqliteMaster()
};
_databaseModel.Tables.Add(table);
_tables.Add(name, table);
- _tableDefinitions[name] = sql;
}
else if (type == "index"
&& _tables.ContainsKey(tableName))
@@ -110,8 +98,6 @@ private void GetSqliteMaster()
Name = name,
Table = table
});
-
- _indexDefinitions[name] = sql;
}
}
}
@@ -161,6 +147,15 @@ private void GetColumns()
}
}
+ private enum IndexListColumns
+ {
+ Seqno,
+ Name,
+ Unique,
+ Origin,
+ Partial
+ }
+
private enum IndexInfoColumns
{
Seqno,
@@ -172,13 +167,30 @@ private void GetIndexes()
{
foreach (var table in _databaseModel.Tables)
{
+ var indexInfo = _connection.CreateCommand();
+ indexInfo.CommandText = $"PRAGMA index_list(\"{table.Name.Replace("\"", "\"\"")}\");";
+
+ using (var reader = indexInfo.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var indexName = reader.GetValue((int)IndexListColumns.Name) as string;
+ var isUnique = reader.GetBoolean((int)IndexListColumns.Unique);
+ var index = table.Indexes.FirstOrDefault(i => i.Name.Equals(indexName, StringComparison.OrdinalIgnoreCase));
+ if (index != null)
+ {
+ index.IsUnique = isUnique;
+ }
+ }
+ }
+
foreach (var index in table.Indexes)
{
- var indexInfo = _connection.CreateCommand();
- indexInfo.CommandText = $"PRAGMA index_info(\"{index.Name.Replace("\"", "\"\"")}\");";
+ var indexColumns = _connection.CreateCommand();
+ indexColumns.CommandText = $"PRAGMA index_info(\"{index.Name.Replace("\"", "\"\"")}\");";
index.Columns = new List();
- using (var reader = indexInfo.ExecuteReader())
+ using (var reader = indexColumns.ExecuteReader())
{
while (reader.Read())
{
@@ -191,16 +203,6 @@ private void GetIndexes()
var column = _tableColumns[ColumnKey(table, columnName)];
index.Columns.Add(column);
-
- var sql = _indexDefinitions[index.Name];
-
- if (!string.IsNullOrEmpty(sql))
- {
- var uniqueKeyword = sql.IndexOf("UNIQUE", StringComparison.OrdinalIgnoreCase);
- var indexKeyword = sql.IndexOf("INDEX", StringComparison.OrdinalIgnoreCase);
-
- index.IsUnique = uniqueKeyword > 0 && uniqueKeyword < indexKeyword;
- }
}
}
}
@@ -234,7 +236,7 @@ private void GetForeignKeys()
{
var id = reader.GetInt32((int)ForeignKeyList.Id);
var principalTableName = reader.GetString((int)ForeignKeyList.Table);
-
+
ForeignKeyModel foreignKey;
if (!tableForeignKeys.TryGetValue(id, out foreignKey))
{
@@ -255,7 +257,7 @@ private void GetForeignKeys()
{
var toColumnName = reader.GetString((int)ForeignKeyList.To);
ColumnModel toColumn;
- if(!_tableColumns.TryGetValue(ColumnKey(foreignKey.PrincipalTable, toColumnName), out toColumn))
+ if (!_tableColumns.TryGetValue(ColumnKey(foreignKey.PrincipalTable, toColumnName), out toColumn))
{
toColumn = new ColumnModel { Name = toColumnName };
}
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.csproj b/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.csproj
index 723f8875d0d..4b7fd5857fb 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.csproj
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.csproj
@@ -8,7 +8,7 @@
{2AF716DA-9AFA-43B6-9DEC-B419A59B39FD}
Library
Properties
- EntityFramework.Sqlite.Design.FunctionalTests
+ Microsoft.Data.Entity.Sqlite.Design.FunctionalTests
EntityFramework.Sqlite.Design.FunctionalTests
v4.6
512
@@ -191,4 +191,4 @@
-
\ No newline at end of file
+
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.xproj b/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.xproj
index 7cd0d603923..17a3c5546e6 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.xproj
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/EntityFramework.Sqlite.Design.FunctionalTests.xproj
@@ -7,7 +7,7 @@
d4a29871-788d-4ea3-b637-1f8d14da23b4
- EntityFramework.Sqlite.Design.FunctionalTests
+ Microsoft.Data.Entity.Sqlite.Design.FunctionalTests
..\..\artifacts\obj\$(MSBuildProjectName)
..\..\artifacts\bin\$(MSBuildProjectName)\
@@ -18,4 +18,4 @@
-
\ No newline at end of file
+
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAllFluentApiE2ETest.cs b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAllFluentApiE2ETest.cs
index 717faf0954e..a2887fc15b9 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAllFluentApiE2ETest.cs
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAllFluentApiE2ETest.cs
@@ -4,7 +4,7 @@
using System.IO;
using Xunit.Abstractions;
-namespace EntityFramework.Sqlite.Design.FunctionalTests.ReverseEngineering
+namespace Microsoft.Data.Entity.Sqlite.Design.FunctionalTests.ReverseEngineering
{
public class SqliteAllFluentApiE2ETest : SqliteE2ETestBase
{
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAttributesInsteadOfFluentApiE2ETest.cs b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAttributesInsteadOfFluentApiE2ETest.cs
index af8023d5750..9791c2b9761 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAttributesInsteadOfFluentApiE2ETest.cs
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteAttributesInsteadOfFluentApiE2ETest.cs
@@ -4,7 +4,7 @@
using System.IO;
using Xunit.Abstractions;
-namespace EntityFramework.Sqlite.Design.FunctionalTests.ReverseEngineering
+namespace Microsoft.Data.Entity.Sqlite.Design.FunctionalTests.ReverseEngineering
{
public class SqliteAttributesInsteadOfFluentApiE2ETest : SqliteE2ETestBase
{
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteE2ETestBase.cs b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteE2ETestBase.cs
index c4eea24cbee..74c38ff092c 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteE2ETestBase.cs
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/ReverseEngineering/SqliteE2ETestBase.cs
@@ -14,7 +14,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace EntityFramework.Sqlite.Design.FunctionalTests.ReverseEngineering
+namespace Microsoft.Data.Entity.Sqlite.Design.FunctionalTests.ReverseEngineering
{
public abstract class SqliteE2ETestBase : E2ETestBase
{
@@ -421,7 +421,7 @@ FOREIGN KEY (UserAltId) REFERENCES User (AltId)
protected abstract string ExpectedResultsParentDir { get; }
protected abstract bool UseFluentApiOnly { get; }
- protected override void ConfigureDesignTimeServices(IServiceCollection services)
+ protected override void ConfigureDesignTimeServices(IServiceCollection services)
=> new SqliteDesignTimeServices().ConfigureDesignTimeServices(services);
}
}
diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteDatabaseModelFactoryTest.cs b/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteDatabaseModelFactoryTest.cs
index 61aea34b09b..ebc08dedbaf 100644
--- a/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteDatabaseModelFactoryTest.cs
+++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteDatabaseModelFactoryTest.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using Microsoft.Data.Entity;
using Microsoft.Data.Entity.FunctionalTests;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata;
@@ -17,7 +16,7 @@
using Microsoft.Extensions.Logging;
using Xunit;
-namespace EntityFramework.Sqlite.Design.FunctionalTests
+namespace Microsoft.Data.Entity.Sqlite.Design.FunctionalTests
{
public class SqliteDatabaseModelFactoryTest
{
@@ -228,8 +227,8 @@ FOREIGN KEY (ParentId) REFERENCES Parent (Id)
GetModel(sql);
- Assert.Contains("Warning: " +
- RelationalDesignStrings.ForeignKeyScaffoldErrorPropertyNotFound("Children(ParentId)"),
+ Assert.Contains("Warning: " +
+ RelationalDesignStrings.ForeignKeyScaffoldErrorPropertyNotFound("Children(ParentId)"),
_logger.FullLog);
}
diff --git a/test/EntityFramework.Sqlite.Design.Tests/EntityFramework.Sqlite.Design.Tests.csproj b/test/EntityFramework.Sqlite.Design.Tests/EntityFramework.Sqlite.Design.Tests.csproj
index 76fab317884..7621b8778a0 100644
--- a/test/EntityFramework.Sqlite.Design.Tests/EntityFramework.Sqlite.Design.Tests.csproj
+++ b/test/EntityFramework.Sqlite.Design.Tests/EntityFramework.Sqlite.Design.Tests.csproj
@@ -42,7 +42,6 @@
-
@@ -67,11 +66,11 @@
-
-
\ No newline at end of file
+
diff --git a/test/EntityFramework.Sqlite.Design.Tests/SqliteDmlParserTest.cs b/test/EntityFramework.Sqlite.Design.Tests/SqliteDmlParserTest.cs
deleted file mode 100644
index cad9624e9d9..00000000000
--- a/test/EntityFramework.Sqlite.Design.Tests/SqliteDmlParserTest.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq;
-using Microsoft.Data.Entity.Scaffolding.Internal;
-using Xunit;
-
-namespace Microsoft.Data.Entity.Sqlite.Design
-{
- public class SqliteDmlParserTest
- {
- public static object[][] Statements =
- {
- new object[] { "CREATE TABLE t (a,b,d);", new[] { "a", "b", "d" } },
- new object[] { "CREATE TABLE t ( \"col,name\" text, col2);", new[] { "\"col,name\" text", "col2" } },
- new object[] { "CREATE TABLE t ( \", \"\"col,name\" text, col2);", new[] { "\", \"\"col,name\" text", "col2" } },
- new object[] { "CREATE TABLE t (Int decimal(10,3), col2);", new[] { "Int decimal(10,3)", "col2" } },
- new object[] { "CREATE TABLE t (a string, foreign key (a) references b (\"a)\") );", new[] { "a string", "foreign key (a) references b (\"a)\")" } },
- new object[]
- {
- "CREATE TABLE t ( Int decimal(10,3) default (IF(1 + (3) = 3, 10.2, NULL)) , col2);",
- new[] { "Int decimal(10,3) default (IF(1 + (3) = 3, 10.2, NULL))", "col2" }
- },
- new object[] { "CREATE TABLE '(' ( A,B)", new object[] { "A", "B" } }
- };
-
- [Theory]
- [MemberData(nameof(Statements))]
- public void It_extracts_statements(string sql, string[] statements)
- {
- Assert.Equal(statements, SqliteDmlParser.ParseStatements(sql).ToArray());
- }
-
- [Theory]
- [InlineData("'Escaped''single'", "Escaped'single")]
- [InlineData("\"Escaped\"\"single\"", "Escaped\"single")]
- [InlineData("\"No ending quote", "\"No ending quote")]
- [InlineData("No open quote'", "No open quote'")]
- [InlineData("'''quoted'''", "'quoted'")]
- public void It_unescapes_strings(string input, string result)
- {
- Assert.Equal(result, SqliteDmlParser.UnescapeString(input));
- }
-
- [Theory]
- [InlineData(',', "a,b,c", new[] { "a", "b", "c" })]
- [InlineData(',', "a',' ,b,c", new[] { "a',' ", "b", "c" })]
- [InlineData(',', "(a')' ,b),c", new[] { "(a')' ,b)", "c" })]
- [InlineData(',', "\",,,\",\",,,\"", new[] { "\",,,\"", "\",,,\"" })]
- [InlineData(',', "\",',\",\",',\"", new[] { "\",',\"", "\",',\"" })]
- [InlineData(',', @"`~!@#$%^&*()+=-[];'',.<>/?|\", new[] { @"`~!@#$%^&*()+=-[];''", @".<>/?|\" })]
- [InlineData(' ', @"CREATE TABLE '`~!@#$%^&*()+=-[];''"",.<>/?|\ ' ", new[] { "CREATE", "TABLE", @"'`~!@#$%^&*()+=-[];''"",.<>/?|\ '" })]
- public void It_safely_splits(char sep, string input, string[] results)
- {
- Assert.Equal(results, SqliteDmlParser.SafeSplit(input, sep));
- }
- }
-}