Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions src/SchemaSaurus.PostgreSql/PostgreSqlSchemaReader.Tables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,25 @@ private static async Task ReadTableIndexesAsync(
am.amname,
ARRAY(
SELECT attr.attname
FROM unnest(idx.indkey, idx.indoption) WITH ORDINALITY AS key_column(attnum, option, ordinal_position)
LEFT JOIN pg_attribute AS attr ON attr.attrelid = cls.oid AND attr.attnum = key_column.attnum
FROM unnest(idx.indkey) WITH ORDINALITY AS key_column(attnum, ordinal_position)
JOIN pg_attribute AS attr ON attr.attrelid = cls.oid AND attr.attnum = key_column.attnum
WHERE key_column.attnum > 0
ORDER BY key_column.ordinal_position
) AS columns,
ARRAY(
SELECT (key_column.option & 1) <> 0
FROM unnest(idx.indkey, idx.indoption) WITH ORDINALITY AS key_column(attnum, option, ordinal_position)
SELECT (COALESCE(idx.indoption[key_column.ordinal_position - 1], 0) & 1) <> 0
FROM unnest(idx.indkey) WITH ORDINALITY AS key_column(attnum, ordinal_position)
JOIN pg_attribute AS attr ON attr.attrelid = cls.oid AND attr.attnum = key_column.attnum
WHERE key_column.attnum > 0
ORDER BY key_column.ordinal_position
) AS descending_columns,
ARRAY(
SELECT key_column.ordinal_position > idx.indnkeyatts
FROM unnest(idx.indkey) WITH ORDINALITY AS key_column(attnum, ordinal_position)
JOIN pg_attribute AS attr ON attr.attrelid = cls.oid AND attr.attnum = key_column.attnum
WHERE key_column.attnum > 0
ORDER BY key_column.ordinal_position
) AS included_columns,
ARRAY(
SELECT pg_get_indexdef(idx.indexrelid, key_column.ordinal_position::integer, false)
FROM unnest(idx.indkey) WITH ORDINALITY AS key_column(attnum, ordinal_position)
Expand Down Expand Up @@ -257,9 +265,10 @@ FROM pg_constraint con
const int methodOrdinal = 5;
const int columnsOrdinal = 6;
const int descendingOrdinal = 7;
const int expressionsOrdinal = 8;
const int predicateOrdinal = 9;
const int optionsOrdinal = 10;
const int includedOrdinal = 8;
const int expressionsOrdinal = 9;
const int predicateOrdinal = 10;
const int optionsOrdinal = 11;

while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
Expand All @@ -272,6 +281,7 @@ FROM pg_constraint con
var indexType = reader.GetString(methodOrdinal);
var columns = reader.GetFieldValue<string[]>(columnsOrdinal);
var descendingColumns = reader.GetFieldValue<bool[]>(descendingOrdinal);
var includedColumns = reader.GetFieldValue<bool[]>(includedOrdinal);
var expressions = reader.GetFieldValue<string[]>(expressionsOrdinal);
var predicate = reader.GetStringNull(predicateOrdinal);
var storageParameters = reader.GetFieldValueNull<string[]>(optionsOrdinal);
Expand All @@ -287,6 +297,12 @@ FROM pg_constraint con

for (var i = 0; i < columns.Length; i++)
{
if (includedColumns[i])
{
indexBuilder.AddIncludedColumn(columns[i]);
continue;
}

var sortDirection = descendingColumns[i]
? SortDirection.Descending
: SortDirection.Ascending;
Expand Down
26 changes: 26 additions & 0 deletions test/SchemaSaurus.PostgreSql.Tests/IndexTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ public async Task WhenReadingDescendingIndexColumnThenSortDirectionIsDescending(
keyColumn.SortDirection.Should().Be(SortDirection.Descending);
}

[Fact]
public async Task WhenReadingCoveringIndexThenKeyColumnsKeepTheirSortOrder()
{
var model = await GetDatabaseModelAsync();
var taskTable = model.Tables.First(t => t.QualifiedName.Name == "Task");

var index = taskTable.Indexes.First(i => i.Name == "IX_Task_Covering");
var keyColumns = index.Columns.Where(c => !c.IsIncludedColumn).ToList();

keyColumns.Select(c => c.ColumnName).Should().Equal("StatusId", "PriorityId");
keyColumns[0].SortDirection.Should().Be(SortDirection.Ascending);
keyColumns[1].SortDirection.Should().Be(SortDirection.Descending);
}

[Fact]
public async Task WhenReadingCoveringIndexThenIncludedColumnsAreMarked()
{
var model = await GetDatabaseModelAsync();
var taskTable = model.Tables.First(t => t.QualifiedName.Name == "Task");

var index = taskTable.Indexes.First(i => i.Name == "IX_Task_Covering");
var includedColumns = index.Columns.Where(c => c.IsIncludedColumn).ToList();

includedColumns.Select(c => c.ColumnName).Should().Equal("Title", "Created");
}

[Fact]
public async Task WhenReadingExpressionIndexThenIndexIsReturnedWithExpressionAnnotation()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ $$;
COMMENT ON PROCEDURE public."StatusPaged"(integer, integer) IS 'Reads a page of statuses.';
COMMENT ON FUNCTION public."FormatAddress"(text, text, text, text) IS 'Formats an address.';
COMMENT ON FUNCTION public."NormalizeEmailAddress"(public."EmailAddress") IS 'Normalizes an email address.';

CREATE INDEX "IX_Task_Covering" ON public."Task" ("StatusId", "PriorityId" DESC) INCLUDE ("Title", "Created");