Skip to content

Commit

Permalink
Changes for new relational model
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Mar 29, 2020
1 parent cd815fe commit 4c9fea7
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Query;
Expand All @@ -14,6 +15,7 @@
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
Expand Down Expand Up @@ -61,7 +63,7 @@ public static IServiceCollection AddEntityFrameworkNpgsql([NotNull] this IServic
.TryAdd<IValueGeneratorCache>(p => p.GetService<INpgsqlValueGeneratorCache>())
.TryAdd<IRelationalTypeMappingSource, NpgsqlTypeMappingSource>()
.TryAdd<ISqlGenerationHelper, NpgsqlSqlGenerationHelper>()
.TryAdd<IMigrationsAnnotationProvider, NpgsqlMigrationsAnnotationProvider>()
.TryAdd<IRelationalAnnotationProvider, NpgsqlAnnotationProvider>()
.TryAdd<IModelValidator, NpgsqlModelValidator>()
.TryAdd<IProviderConventionSetBuilder, NpgsqlConventionSetBuilder>()
.TryAdd<IRelationalValueBufferFactoryFactory, TypedRelationalValueBufferFactoryFactory>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal
{
public class NpgsqlMigrationsAnnotationProvider : MigrationsAnnotationProvider
public class NpgsqlAnnotationProvider : RelationalAnnotationProvider
{
public NpgsqlMigrationsAnnotationProvider([NotNull] MigrationsAnnotationProviderDependencies dependencies)
: base(dependencies) {}
public NpgsqlAnnotationProvider([NotNull] RelationalAnnotationProviderDependencies dependencies)
: base(dependencies)
{
}

public override IEnumerable<IAnnotation> For(IEntityType entityType)
public override IEnumerable<IAnnotation> For(ITable table)
{
// Model validation ensures that these facets are the same on all mapped entity types
var entityType = table.EntityTypeMappings.First().EntityType;

if (entityType.GetIsUnlogged())
yield return new Annotation(NpgsqlAnnotationNames.UnloggedTable, entityType.GetIsUnlogged());
if (entityType[CockroachDbAnnotationNames.InterleaveInParent] != null)
Expand All @@ -29,11 +31,13 @@ public override IEnumerable<IAnnotation> For(IEntityType entityType)
}
}

public override IEnumerable<IAnnotation> For(IProperty property)
public override IEnumerable<IAnnotation> For(IColumn column)
{
var valueGenerationStrategy = property.GetValueGenerationStrategy();
if (valueGenerationStrategy != NpgsqlValueGenerationStrategy.None)
var property = column.PropertyMappings.Select(m => m.Property)
.FirstOrDefault(p => p.GetValueGenerationStrategy() != NpgsqlValueGenerationStrategy.None);
if (property != null)
{
var valueGenerationStrategy = property.GetValueGenerationStrategy();
yield return new Annotation(NpgsqlAnnotationNames.ValueGenerationStrategy, valueGenerationStrategy);

if (valueGenerationStrategy == NpgsqlValueGenerationStrategy.IdentityByDefaultColumn ||
Expand All @@ -46,43 +50,51 @@ public override IEnumerable<IAnnotation> For(IProperty property)
}
}

if (property.GetTsVectorConfig() is string tsVectorConfig)
if (column.PropertyMappings.Select(m => m.Property.GetTsVectorConfig())
.FirstOrDefault(c => c != null) is string tsVectorConfig)
{
yield return new Annotation(NpgsqlAnnotationNames.TsVectorConfig, tsVectorConfig);
}

if (property.GetTsVectorProperties() is IReadOnlyList<string> tsVectorProperties)
property = column.PropertyMappings.Select(m => m.Property)
.FirstOrDefault(p => p.GetTsVectorProperties() != null);
if (property != null)
{
yield return new Annotation(
NpgsqlAnnotationNames.TsVectorProperties,
tsVectorProperties
.Select(p => property.DeclaringEntityType.FindProperty(p).GetColumnName())
property.GetTsVectorProperties()
.Select(p2 => property.DeclaringEntityType.FindProperty(p2).GetColumnName())
.ToArray());
}
}

public override IEnumerable<IAnnotation> For(IIndex index)
public override IEnumerable<IAnnotation> For(ITableIndex index)
{
if (index.GetMethod() is string method)
// Model validation ensures that these facets are the same on all mapped indexes
var modelIndex = index.MappedIndexes.First();

if (modelIndex.GetMethod() is string method)
yield return new Annotation(NpgsqlAnnotationNames.IndexMethod, method);
if (index.GetOperators() is IReadOnlyList<string> operators)
if (modelIndex.GetOperators() is IReadOnlyList<string> operators)
yield return new Annotation(NpgsqlAnnotationNames.IndexOperators, operators);
if (index.GetCollation() is IReadOnlyList<string> collation)
if (modelIndex.GetCollation() is IReadOnlyList<string> collation)
yield return new Annotation(NpgsqlAnnotationNames.IndexCollation, collation);
if (index.GetSortOrder() is IReadOnlyList<SortOrder> sortOrder)
if (modelIndex.GetSortOrder() is IReadOnlyList<SortOrder> sortOrder)
yield return new Annotation(NpgsqlAnnotationNames.IndexSortOrder, sortOrder);
if (index.GetNullSortOrder() is IReadOnlyList<SortOrder> nullSortOrder)
if (modelIndex.GetNullSortOrder() is IReadOnlyList<SortOrder> nullSortOrder)
yield return new Annotation(NpgsqlAnnotationNames.IndexNullSortOrder, nullSortOrder);
if (index.GetTsVectorConfig() is string configName)
if (modelIndex.GetTsVectorConfig() is string configName)
yield return new Annotation(NpgsqlAnnotationNames.TsVectorConfig, configName);
if (index.GetIncludeProperties() is IReadOnlyList<string> includeProperties)
if (modelIndex.GetIncludeProperties() is IReadOnlyList<string> includeProperties)
{
yield return new Annotation(
NpgsqlAnnotationNames.IndexInclude,
includeProperties
.Select(p => index.DeclaringEntityType.FindProperty(p).GetColumnName())
.Select(p => modelIndex.DeclaringEntityType.FindProperty(p).GetColumnName())
.ToArray());
}

var isCreatedConcurrently = index.IsCreatedConcurrently();
var isCreatedConcurrently = modelIndex.IsCreatedConcurrently();
if (isCreatedConcurrently.HasValue)
{
yield return new Annotation(
Expand All @@ -91,8 +103,8 @@ public override IEnumerable<IAnnotation> For(IIndex index)
}
}

public override IEnumerable<IAnnotation> For(IModel model)
=> model.GetAnnotations().Where(a =>
public override IEnumerable<IAnnotation> For(IRelationalModel model)
=> model.Model.GetAnnotations().Where(a =>
a.Name.StartsWith(NpgsqlAnnotationNames.PostgresExtensionPrefix, StringComparison.Ordinal) ||
a.Name.StartsWith(NpgsqlAnnotationNames.EnumPrefix, StringComparison.Ordinal) ||
a.Name.StartsWith(NpgsqlAnnotationNames.RangePrefix, StringComparison.Ordinal));
Expand Down
29 changes: 13 additions & 16 deletions src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Migrations
{
public class NpgsqlMigrationsSqlGenerator : MigrationsSqlGenerator
{
readonly IMigrationsAnnotationProvider _migrationsAnnotations;
readonly RelationalTypeMapping _stringTypeMapping;

/// <summary>
Expand All @@ -30,12 +29,10 @@ public class NpgsqlMigrationsSqlGenerator : MigrationsSqlGenerator

public NpgsqlMigrationsSqlGenerator(
[NotNull] MigrationsSqlGeneratorDependencies dependencies,
[NotNull] IMigrationsAnnotationProvider migrationsAnnotations,
[NotNull] INpgsqlOptions npgsqlOptions)
: base(dependencies)
{
_postgresVersion = npgsqlOptions.PostgresVersion;
_migrationsAnnotations = migrationsAnnotations;
_stringTypeMapping = dependencies.TypeMappingSource.GetMapping(typeof(string))
?? throw new InvalidOperationException("No string type mapping found");
}
Expand Down Expand Up @@ -305,12 +302,12 @@ protected override void Generate(AlterColumnOperation operation, IModel model, M
if (IsSystemColumn(operation.Name))
return;

var column = model?.GetRelationalModel().FindTable(operation.Table, operation.Schema)
?.Columns.FirstOrDefault(c => c.Name == operation.Name);
var type = operation.ColumnType ?? GetColumnType(operation.Schema, operation.Table, operation.Name, operation, model);

if (operation.ComputedColumnSql != null)
{
var property = FindProperty(model, operation.Schema, operation.Table, operation.Name);

// TODO: The following will fail if the column being altered is part of an index.
// SqlServer recreates indexes, but wait to see if PostgreSQL will introduce a proper ALTER TABLE ALTER COLUMN
// that allows us to do this cleanly.
Expand All @@ -321,8 +318,8 @@ protected override void Generate(AlterColumnOperation operation, IModel model, M
Name = operation.Name
};

if (property != null)
dropColumnOperation.AddAnnotations(_migrationsAnnotations.ForRemove(property));
if (column != null)
dropColumnOperation.AddAnnotations(column.GetAnnotations());

Generate(dropColumnOperation, model, builder);

Expand Down Expand Up @@ -351,9 +348,8 @@ protected override void Generate(AlterColumnOperation operation, IModel model, M
string newSequenceName = null;
var defaultValueSql = operation.DefaultValueSql;

var table = Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema);
var column = Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name);
var alterBase = $"ALTER TABLE {table} ALTER COLUMN {column} ";
var alterBase = $"ALTER TABLE {Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema)} " +
$"ALTER COLUMN {Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name)} ";

// TYPE
builder.Append(alterBase)
Expand Down Expand Up @@ -396,8 +392,8 @@ protected override void Generate(AlterColumnOperation operation, IModel model, M
var oldSequenceWithoutSchema = Dependencies.SqlGenerationHelper.DelimitIdentifier($"{operation.Table}_{operation.Name}_old_seq");
builder
.AppendLine($"ALTER SEQUENCE {sequence} RENAME TO {oldSequenceWithoutSchema};")
.AppendLine($"ALTER TABLE {table} ALTER COLUMN {column} DROP DEFAULT;")
.AppendLine($"ALTER TABLE {table} ALTER COLUMN {column} ADD GENERATED {identityTypeClause} AS IDENTITY;")
.AppendLine($"{alterBase}DROP DEFAULT;")
.AppendLine($"{alterBase}ADD GENERATED {identityTypeClause} AS IDENTITY;")
.AppendLine($"SELECT * FROM setval('{sequence}', nextval('{oldSequence}'), false);")
.AppendLine($"DROP SEQUENCE {oldSequence};");
break;
Expand Down Expand Up @@ -1477,11 +1473,12 @@ string IndexColumnList(IndexColumn[] columns, string method)

string ColumnsToTsVector(IEnumerable<string> columns, string tsVectorConfig, IModel model, string schema, string table)
{
string GetTsVectorColumnExpression(string column)
string GetTsVectorColumnExpression(string columnName)
{
var delimitedColumnName = Dependencies.SqlGenerationHelper.DelimitIdentifier(column);
var property = FindProperty(model, schema, table, column);
return property?.IsColumnNullable() == true
var delimitedColumnName = Dependencies.SqlGenerationHelper.DelimitIdentifier(columnName);
var column = model?.GetRelationalModel()
.FindTable(table, schema)?.Columns.FirstOrDefault(c => c.Name == columnName);
return column?.IsNullable != false
? $"coalesce({delimitedColumnName}, '')"
: delimitedColumnName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ public class NpgsqlSqlTranslatingExpressionVisitor : RelationalSqlTranslatingExp
readonly RelationalTypeMapping _boolMapping;

public NpgsqlSqlTranslatingExpressionVisitor(
RelationalSqlTranslatingExpressionVisitorDependencies dependencies,
IModel model,
QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor)
: base(dependencies, model, queryableMethodTranslatingExpressionVisitor)
[NotNull] RelationalSqlTranslatingExpressionVisitorDependencies dependencies,
[NotNull] QueryCompilationContext queryCompilationContext,
[NotNull] QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor)
: base(dependencies, queryCompilationContext, queryableMethodTranslatingExpressionVisitor)
{
_sqlExpressionFactory = (NpgsqlSqlExpressionFactory)dependencies.SqlExpressionFactory;
_jsonPocoTranslator = ((NpgsqlMemberTranslatorProvider)Dependencies.MemberTranslatorProvider).JsonPocoTranslator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ public class NpgsqlSqlTranslatingExpressionVisitorFactory : IRelationalSqlTransl
=> _dependencies = dependencies;

public virtual RelationalSqlTranslatingExpressionVisitor Create(
IModel model,
QueryCompilationContext queryCompilationContext,
QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor)
=> new NpgsqlSqlTranslatingExpressionVisitor(
_dependencies,
model,
queryCompilationContext,
queryableMethodTranslatingExpressionVisitor);
}
}
4 changes: 2 additions & 2 deletions src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ await Dependencies.MigrationCommandExecutor

public override void CreateTables()
{
var operations = Dependencies.ModelDiffer.GetDifferences(null, Dependencies.Model);
var operations = Dependencies.ModelDiffer.GetDifferences(null, Dependencies.Model.GetRelationalModel());
var commands = Dependencies.MigrationsSqlGenerator.Generate(operations, Dependencies.Model);

// If a PostgreSQL extension, enum or range was added, we want Npgsql to reload all types at the ADO.NET level.
Expand Down Expand Up @@ -256,7 +256,7 @@ public override void CreateTables()

public override async Task CreateTablesAsync(CancellationToken cancellationToken = default)
{
var operations = Dependencies.ModelDiffer.GetDifferences(null, Dependencies.Model);
var operations = Dependencies.ModelDiffer.GetDifferences(null, Dependencies.Model.GetRelationalModel());
var commands = Dependencies.MigrationsSqlGenerator.Generate(operations, Dependencies.Model);

// If a PostgreSQL extension, enum or range was added, we want Npgsql to reload all types at the ADO.NET level.
Expand Down
Loading

0 comments on commit 4c9fea7

Please sign in to comment.