Skip to content

Commit

Permalink
Generated column support for Sqlite
Browse files Browse the repository at this point in the history
Closes #19682
  • Loading branch information
roji committed May 2, 2020
1 parent 500c847 commit cdd3696
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 39 deletions.
8 changes: 4 additions & 4 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
<NetTopologySuiteVersion>2.0.0</NetTopologySuiteVersion>
<NetTopologySuiteIOSpatiaLiteVersion>2.0.0</NetTopologySuiteIOSpatiaLiteVersion>
<NetTopologySuiteIOSqlServerBytesVersion>2.0.0</NetTopologySuiteIOSqlServerBytesVersion>
<SQLitePCLRawBundleESqlite3Version>2.0.2</SQLitePCLRawBundleESqlite3Version>
<SQLitePCLRawBundleESqlcipherVersion>2.0.2</SQLitePCLRawBundleESqlcipherVersion>
<SQLitePCLRawBundleWinsqlite3Version>2.0.2</SQLitePCLRawBundleWinsqlite3Version>
<SQLitePCLRawCoreVersion>2.0.2</SQLitePCLRawCoreVersion>
<SQLitePCLRawBundleESqlite3Version>2.0.3</SQLitePCLRawBundleESqlite3Version>
<SQLitePCLRawBundleESqlcipherVersion>2.0.3</SQLitePCLRawBundleESqlcipherVersion>
<SQLitePCLRawBundleWinsqlite3Version>2.0.3</SQLitePCLRawBundleWinsqlite3Version>
<SQLitePCLRawCoreVersion>2.0.3</SQLitePCLRawCoreVersion>
<IdentityServer4EntityFrameworkVersion>3.0.0</IdentityServer4EntityFrameworkVersion>
<StyleCopAnalyzersVersion>1.1.118</StyleCopAnalyzersVersion>
<BenchmarkDotNetVersion>0.12.0</BenchmarkDotNetVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,26 @@ protected override void ComputedColumnDefinition(
ColumnOperation operation,
IModel model,
MigrationCommandListBuilder builder)
=> throw new NotSupportedException(SqliteStrings.ComputedColumnsNotSupported);
{
builder.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(name));

builder
.Append(" AS (")
.Append(operation.ComputedColumnSql)
.Append(")");

if (operation.ComputedColumnIsStored == true)
{
builder.Append(" STORED");
}

if (operation.Collation != null)
{
builder
.Append(" COLLATE ")
.Append(operation.Collation);
}
}

#endregion

Expand Down
6 changes: 0 additions & 6 deletions src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,6 @@
<value>Found unique constraint with name: {uniqueConstraintName}, table: {tableName}.</value>
<comment>Debug SqliteEventId.UniqueConstraintFound string string</comment>
</data>
<data name="ComputedColumnsNotSupported" xml:space="preserve">
<value>SQLite doesn't support computed columns. For more information, see http://go.microsoft.com/fwlink/?LinkId=723262.</value>
</data>
<data name="OrderByNotSupported" xml:space="preserve">
<value>SQLite cannot order by expressions of type '{type}'. Convert the values to a supported type or use LINQ to Objects to order the results.</value>
</data>
Expand Down
83 changes: 75 additions & 8 deletions test/EFCore.Sqlite.FunctionalTests/MigrationsSqliteTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,36 @@ await Test(
);");
}

[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
[InlineData(null)]
public virtual async Task Create_table_with_computed_column(bool? computedColumnStored)
{
await Test(
builder => { },
builder => builder.Entity(
"People", e =>
{
e.Property<int>("Id");
e.Property<int>("X");
e.Property<int>("Y");
e.Property<string>("Sum").HasComputedColumnSql($"{DelimitIdentifier("X")} + {DelimitIdentifier("Y")}",
computedColumnStored);
}),
model => { /* Scaffolding computed columns isn't supported in Sqlite */ });

var computedColumnTypeSql = computedColumnStored == true ? " STORED" : "";

AssertSql(
$@"CREATE TABLE ""People"" (
""Id"" INTEGER NOT NULL,
""Sum"" AS (""X"" + ""Y""){computedColumnTypeSql},
""X"" INTEGER NOT NULL,
""Y"" INTEGER NOT NULL
);");
}

// In Sqlite, comments are only generated when creating a table
public override async Task Alter_table_add_comment()
{
Expand Down Expand Up @@ -212,10 +242,32 @@ public override async Task Add_column_with_defaultValueSql()
Assert.Contains("Cannot add a column with non-constant default", ex.Message);
}

public override Task Add_column_with_computedSql(bool? computedColumnStored)
=> AssertNotSupportedAsync(
() => base.Add_column_with_computedSql(computedColumnStored),
SqliteStrings.ComputedColumnsNotSupported);
public override async Task Add_column_with_computedSql(bool? computedColumnStored)
{
if (computedColumnStored == true)
{
var ex = await Assert.ThrowsAsync<SqliteException>
(() => base.Add_column_with_computedSql(computedColumnStored));
Assert.Contains("cannot add a STORED column", ex.Message);
return;
}

await Test(
builder => builder.Entity(
"People", e =>
{
e.Property<int>("Id");
e.Property<int>("X");
e.Property<int>("Y");
}),
builder => { },
builder => builder.Entity("People").Property<string>("Sum")
.HasComputedColumnSql($"{DelimitIdentifier("X")} + {DelimitIdentifier("Y")}", computedColumnStored),
model => { /* Scaffolding computed columns isn't supported in Sqlite */ });

AssertSql(
@"ALTER TABLE ""People"" ADD ""Sum"" AS (""X"" + ""Y"");");
}

public override async Task Add_column_with_max_length()
{
Expand Down Expand Up @@ -260,8 +312,19 @@ await Test(
@"ALTER TABLE ""People"" ADD ""Name"" TEXT COLLATE NOCASE NULL;");
}

public override Task Add_column_computed_with_collation()
=> AssertNotSupportedAsync(base.Add_column_computed_with_collation, SqliteStrings.ComputedColumnsNotSupported);
public override async Task Add_column_computed_with_collation()
{
await Test(
builder => builder.Entity("People").Property<int>("Id"),
builder => { },
builder => builder.Entity("People").Property<string>("Name")
.HasComputedColumnSql("'hello'")
.UseCollation(NonDefaultCollation),
model => { /* Scaffolding computed columns isn't supported in Sqlite */ });

AssertSql(
@"ALTER TABLE ""People"" ADD ""Name"" AS ('hello') COLLATE NOCASE;");
}

public override Task Alter_column_make_required()
=> AssertNotSupportedAsync(base.Alter_column_make_required, SqliteStrings.InvalidMigrationOperation("AlterColumnOperation"));
Expand All @@ -280,10 +343,14 @@ public override Task Alter_column_make_computed(bool? computedColumnStored)
SqliteStrings.InvalidMigrationOperation("AlterColumnOperation"));

public override Task Alter_column_change_computed()
=> AssertNotSupportedAsync(base.Alter_column_change_computed, SqliteStrings.ComputedColumnsNotSupported);
=> AssertNotSupportedAsync(
() => base.Alter_column_change_computed(),
SqliteStrings.InvalidMigrationOperation("AlterColumnOperation"));

public override Task Alter_column_change_computed_type()
=> AssertNotSupportedAsync(base.Alter_column_change_computed, SqliteStrings.ComputedColumnsNotSupported);
=> AssertNotSupportedAsync(
() => base.Alter_column_change_computed_type(),
SqliteStrings.InvalidMigrationOperation("AlterColumnOperation"));

public override Task Alter_column_add_comment()
=> AssertNotSupportedAsync(base.Alter_column_add_comment, SqliteStrings.InvalidMigrationOperation("AlterColumnOperation"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,23 +179,6 @@ public override void AddColumnOperation_with_maxLength_overridden()
");
}

[ConditionalFact]
public void AddColumnOperation_with_computed_column_SQL()
{
var ex = Assert.Throws<NotSupportedException>(
() => Generate(
new AddColumnOperation
{
Table = "People",
Name = "Birthday",
ClrType = typeof(DateTime),
ColumnType = "TEXT",
IsNullable = true,
ComputedColumnSql = "CURRENT_TIMESTAMP"
}));
Assert.Equal(SqliteStrings.ComputedColumnsNotSupported, ex.Message);
}

public override void AddColumnOperation_with_unicode_no_model()
{
base.AddColumnOperation_with_unicode_no_model();
Expand Down

0 comments on commit cdd3696

Please sign in to comment.