Skip to content

Commit

Permalink
Scaffold included (covered) index columns
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Nov 17, 2018
1 parent eb5efd7 commit 1827dde
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 5 deletions.
32 changes: 27 additions & 5 deletions src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ static void GetIndexes(
cls.relname AS cls_relname,
idxcls.relname AS idx_relname,
indisunique,
{(connection.PostgreSqlVersion >= new Version(11, 0) ? "indnkeyatts" : "indnatts AS indnkeyatts")},
indkey,
amname,
indclass,
Expand Down Expand Up @@ -478,7 +479,10 @@ NOT indisprimary AND
IsUnique = record.GetValueOrDefault<bool>("indisunique")
};

var numKeyColumns = record.GetValueOrDefault<short>("indnkeyatts");
var columnIndices = record.GetValueOrDefault<short[]>("indkey");
var tableColumns = (List<DatabaseColumn>)table.Columns;

if (columnIndices.Any(i => i == 0))
{
// Expression index, not supported
Expand All @@ -493,19 +497,37 @@ NOT indisprimary AND
*/
}

var columns = (List<DatabaseColumn>)table.Columns;
foreach (var i in columnIndices)
// Key columns come before non-key (included) columns, process them first
foreach (var i in columnIndices.Take(numKeyColumns))
{
if (columns[i - 1] is DatabaseColumn indexColumn)
index.Columns.Add(indexColumn);

if (tableColumns[i - 1] is DatabaseColumn indexKeyColumn)
index.Columns.Add(indexKeyColumn);
else
{
logger.UnsupportedColumnIndexSkippedWarning(index.Name, DisplayName(tableSchema, tableName));
goto IndexEnd;
}
}

// Now go over non-key (included columns) if any are present
if (columnIndices.Length > numKeyColumns)
{
var nonKeyColumns = new List<string>();
foreach (var i in columnIndices.Skip(numKeyColumns))
{
if (tableColumns[i - 1] is DatabaseColumn indexKeyColumn)
nonKeyColumns.Add(indexKeyColumn.Name);
else
{
logger.UnsupportedColumnIndexSkippedWarning(index.Name,
DisplayName(tableSchema, tableName));
goto IndexEnd;
}
}

index[NpgsqlAnnotationNames.IndexInclude] = nonKeyColumns.ToArray();
}

if (record.GetValueOrDefault<string>("pred") is string predicate)
index.Filter = predicate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,35 @@ public void Index_operators()
@"DROP TABLE ""IndexOperators""");
}

[Fact]
public void Index_covering()
{
if (Fixture.TestStore.GetPostgresVersion() < new Version(11, 0))
return;

Test(
@"
CREATE TABLE ""IndexCovering"" (a text, b text, c text);
CREATE INDEX ix_with ON ""IndexCovering"" (a) INCLUDE (b, c);
CREATE INDEX ix_without ON ""IndexCovering"" (a, b, c);",
Enumerable.Empty<string>(),
Enumerable.Empty<string>(),
dbModel =>
{
var table = dbModel.Tables.Single();

var indexWith = table.Indexes.Single(i => i.Name == "ix_with");
Assert.Equal("a", indexWith.Columns.Single().Name);
Assert.Equal(new[] { "b", "c" }, indexWith.FindAnnotation(NpgsqlAnnotationNames.IndexInclude).Value);

var indexWithout = table.Indexes.Single(i => i.Name == "ix_without");
Assert.Equal(new[] { "a", "b", "c" }, indexWithout.Columns.Select(i => i.Name).ToArray());
Assert.Null(indexWithout.FindAnnotation(NpgsqlAnnotationNames.IndexInclude));

},
@"DROP TABLE ""IndexCovering""");
}

[Fact]
public void Comments()
{
Expand Down
20 changes: 20 additions & 0 deletions test/EFCore.PG.FunctionalTests/TestUtilities/NpgsqlTestStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,26 @@ private static DbCommand CreateCommand(
return command;
}

public Version GetPostgresVersion()
{
var opened = false;
if (Connection.State == ConnectionState.Closed)
{
Connection.Open();
opened = true;
}

try
{
return ((NpgsqlConnection)Connection).PostgreSqlVersion;
}
finally
{
if (opened)
Connection.Close();
}
}

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

0 comments on commit 1827dde

Please sign in to comment.