Skip to content

[release/11.0] Fix JSON indexes for SQLite - #39089

Open
AndriySvyryd with Copilot wants to merge 8 commits into
release/11.0from
copilot/fix-json-mapped-index-issue
Open

AndriySvyryd with Copilot wants to merge 8 commits into
release/11.0from
copilot/fix-json-mapped-index-issue

Conversation

Copilot AI commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #39064

Description
EF Core 11 supports indexes over members of complex properties, including complex properties stored as JSON. On SQLite, migrations generated these indexes over the entire JSON container column instead of the configured member. This change generates a SQLite expression index using the same ->> expression produced by query translation, preserves JSON index paths when migrations are scaffolded, handles fixed collection elements, and rejects wildcard array indexes that SQLite cannot represent as a usable scalar expression index.

Customer impact
Unique indexes configured over JSON members do not enforce uniqueness for that member, and ordinary indexes cannot be used by matching queries. This can allow duplicate values that the model declares unique, although it does not corrupt existing stored JSON values.

b.ComplexProperty(x => x.Details, d => d.ToJson());
b.HasIndex(x => x.Details.Slug).IsUnique();
// Before: indexes the entire Details document; after: indexes Details ->> 'Slug'.

How found
User reported on EF Core 11.0.0-rc1.

Regression
No, indexes over complex-property members are a new EF Core 11 feature.

Testing
Added or modified 8 tests covering SQLite JSON SQL generation, root and nested fixed collection elements, unsupported wildcard collection indexes, migration annotation handling, and provider-specific model validation.

Risk
Low. The changes are scoped to SQLite migration handling for JSON indexes.

Copilot AI changed the title [WIP] Fix unique index issue on JSON-mapped complex type in SQLite Generate query-compatible SQLite indexes for JSON members Sep 25, 2026
Copilot AI requested a review from AndriySvyryd September 25, 2026 00:03
@AndriySvyryd
AndriySvyryd requested a balanced review from Copilot September 25, 2026 00:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Fixed collection-element indexes still target the entire JSON container instead of the selected element.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Adds query-compatible SQLite expression indexes for JSON-mapped members and validates unsupported wildcard paths.

Changes:

  • Generates SQLite JSON extraction expressions for indexes.
  • Resolves JSON metadata from migration target models.
  • Rejects wildcard collection indexes and adds coverage.
File Description
src/​EFCore.Design/​Migrations/​Design/​CSharpMigrationOperationGenerator.cs Omits model-bound JSON annotations.
src/​EFCore.SqlServer/​Migrations/​SqlServerMigrationsSqlGenerator.cs Resolves JSON indexes from target models.
src/​EFCore.Sqlite.Core/​Infrastructure/​Internal/​SqliteModelValidator.cs Rejects wildcard array indexes.
src/​EFCore.Sqlite.Core/​Migrations/​SqliteMigrationsSqlGenerator.cs Generates SQLite JSON index expressions.
src/​EFCore.Sqlite.Core/​Properties/​SqliteStrings.Designer.cs Exposes the validation message.
src/​EFCore.Sqlite.Core/​Properties/​SqliteStrings.resx Defines the validation message.
test/​EFCore.Sqlite.FunctionalTests/​Migrations/​SqliteMigrationsSqlGeneratorTest.cs Tests JSON index migration SQL.
test/​EFCore.Sqlite.Tests/​Infrastructure/​SqliteModelValidatorTest.cs Tests wildcard rejection.
Files not reviewed (1)
  • src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs: Generated file

Comment thread src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs Outdated
@AndriySvyryd
AndriySvyryd changed the base branch from main to release/11.0 September 25, 2026 00:47
Copilot AI and others added 4 commits September 24, 2026 17:49
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
…for SQLite

The new SqliteModelValidator.ValidateIndex rejection of 'all array
elements' JSON indexes was not reflected in the shared
RelationalModelValidatorTest cases inherited by SqliteModelValidatorTest,
causing 4 previously-passing tests to fail with the new SQLite-specific
exception instead of their expected outcome.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Append the final array segment when a JSON index targets a fixed element of a collection property. Use SQLite's numeric right operand for a root array element and a JSON path for nested collection leaves.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@AndriySvyryd
AndriySvyryd force-pushed the copilot/fix-json-mapped-index-issue branch from 3867d27 to df9afa1 Compare September 25, 2026 01:03
@AndriySvyryd AndriySvyryd changed the title Generate query-compatible SQLite indexes for JSON members [release/11.0] Fix JSON indexes for SQLite Sep 25, 2026
@AndriySvyryd
AndriySvyryd requested a balanced review from Copilot September 25, 2026 01:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Scaffolded operations lose essential JSON-path metadata and can silently generate an index over the container when no matching target model is available.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Files not reviewed (1)
  • src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs: Generated file

AndriySvyryd and others added 3 commits September 24, 2026 18:40
Record the JSON index SQL generator override.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…migrations

Mirrors the existing SQLite Create_unique_json_index_over_complex_property_member
test: verifies that CSharpMigrationOperationGenerator omits the non-literal
RelationalJsonIndex annotation from scaffolded migration code, and that removing
this filter causes CSharpHelper.UnknownLiteral to throw.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Detects_index_on_complex_collection_property already covers indexing all
elements of a JSON collection and asserts the same JsonIndexAllElementsNotSupported
error.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@AndriySvyryd
AndriySvyryd marked this pull request as ready for review September 25, 2026 04:48
@AndriySvyryd
AndriySvyryd requested a review from a team as a code owner September 25, 2026 04:48
Copilot AI review requested due to automatic review settings September 25, 2026 04:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Composite JSON indexes with mixed sort directions cannot be scaffolded because deduplicated columns and per-member sort metadata have different lengths.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Files not reviewed (1)
  • src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs: Generated file

Comment thread src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs Outdated
A composite JSON index whose members share one deduplicated container
column could not represent mixed sort directions: GetIndexColumns
deduplicates JSON members to a single operation column, while
ITableIndex.IsDescending still carried one entry per JSON member,
causing CreateIndexOperation.CreateFrom to throw when the lengths
didn't match.

RelationalJsonIndex now tracks IsDescending per JSON element (with an
IsElementDescending helper honoring the existing empty-list-means-all
shorthand). TableIndex.IsDescending is null whenever the mapped index
is a JSON index, since no database supports mixing a plain column and
its contained JSON members in a single index, and the per-element sort
order can no longer be represented at the column level. SQLite's
GenerateIndexColumnList reads sort order from the RelationalJsonIndex
element list instead of operation.IsDescending.
Copilot AI review requested due to automatic review settings September 25, 2026 05:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The constructor change breaks binary compatibility, and the new equality/hash behavior lacks focused coverage.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
Resolved since last review (1)
Files not reviewed (2)
  • src/EFCore.Relational/Properties/RelationalStrings.Designer.cs: Generated file
  • src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs: Generated file

Comment on lines 47 to +50
public RelationalJsonIndex(
IReadOnlyList<IRelationalJsonElement> elements,
IReadOnlyList<IReadOnlyList<int?>?>? collectionIndices)
IReadOnlyList<IReadOnlyList<int?>?>? collectionIndices,
IReadOnlyList<bool>? isDescending = null)
Comment on lines +109 to +110
|| (CollectionIndices is null) != (other.CollectionIndices is null)
|| (IsDescending is null) != (other.IsDescending is null))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET 11 RC1 - SQLite: index on a member of a JSON-mapped complex type indexes the whole column

3 participants