From 05c54895977cad36a9b39be8844524fb47f4c254 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 20 Oct 2026 16:56:59 +0200 Subject: [PATCH] [9.0] Fix type mapping for derived tsquery parameters Backport #4058 to hotfix/9.0.5. Register the built-in NpgsqlTsQuery subclasses with the existing tsquery mapping and adapt sync/async regression coverage to the 9.0 full-text test fixture. (cherry picked from commit baa07f7a6b6c5f5efe3ee97487a4052c06b0d719) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 080d9558-9c5e-4d3a-be78-d5b52c37ea3a --- .../Internal/NpgsqlTypeMappingSource.cs | 7 ++++++ .../FullTextSearchDbFunctionsNpgsqlTest.cs | 23 +++++++++++++++++++ .../Storage/NpgsqlTypeMappingSourceTest.cs | 16 +++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs index 85cd2740f4..0c2937a934 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs @@ -342,6 +342,13 @@ public NpgsqlTypeMappingSource( { typeof(NpgsqlRange), _tstzrange }, { typeof(NpgsqlRange), _dateOnlyDaterange }, { typeof(NpgsqlTsQuery), _tsquery }, + { typeof(NpgsqlTsQueryLexeme), _tsquery }, + { typeof(NpgsqlTsQueryNot), _tsquery }, + { typeof(NpgsqlTsQueryBinOp), _tsquery }, + { typeof(NpgsqlTsQueryAnd), _tsquery }, + { typeof(NpgsqlTsQueryOr), _tsquery }, + { typeof(NpgsqlTsQueryFollowedBy), _tsquery }, + { typeof(NpgsqlTsQueryEmpty), _tsquery }, { typeof(NpgsqlTsVector), _tsvector }, { typeof(NpgsqlTsRankingNormalization), _rankingNormalization }, { typeof(LTree), _ltree } diff --git a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs index 1f2b577796..fbc1270aba 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs @@ -623,6 +623,29 @@ LIMIT 1 """); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Matches_with_derived_tsquery_parameter(bool async) + { + using var context = CreateContext(); + var tsQuery = new NpgsqlTsQueryLexeme("firs", NpgsqlTsQueryLexeme.Weight.None, true); + var query = context.Customers + .Select(c => EF.Functions.ToTsVector("simple", "first").Matches(tsQuery)); + + var results = async ? await query.ToArrayAsync() : query.ToArray(); + + Assert.NotEmpty(results); + Assert.All(results, Assert.True); + AssertSql( + """ +@__tsQuery_1=''firs':*' (DbType = Object) + +SELECT to_tsvector('simple', 'first') @@ @__tsQuery_1 +FROM "Customers" AS c +"""); + } + [Fact] public void TsVector_Concat() { diff --git a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs index 5fd6fa3737..4a7f4ae399 100644 --- a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs +++ b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs @@ -123,6 +123,22 @@ public void By_ClrType(Type clrType, string expectedStoreType) Assert.Same(clrType, mapping.ClrType); } + [Theory] + [InlineData(typeof(NpgsqlTsQuery))] + [InlineData(typeof(NpgsqlTsQueryLexeme))] + [InlineData(typeof(NpgsqlTsQueryNot))] + [InlineData(typeof(NpgsqlTsQueryBinOp))] + [InlineData(typeof(NpgsqlTsQueryAnd))] + [InlineData(typeof(NpgsqlTsQueryOr))] + [InlineData(typeof(NpgsqlTsQueryFollowedBy))] + [InlineData(typeof(NpgsqlTsQueryEmpty))] + public void TsQuery(Type clrType) + { + var mapping = Assert.IsType(CreateTypeMappingSource().FindMapping(clrType)); + Assert.Equal("tsquery", mapping.StoreType); + Assert.Same(typeof(NpgsqlTsQuery), mapping.ClrType); + } + [Theory] [InlineData(null)] [InlineData("json")]