From 2a098995c42f834b2d63d1316a7ac4ef4759483f Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 20 Oct 2026 16:57:37 +0200 Subject: [PATCH] [10.0] Fix type mapping for derived tsquery parameters Register built-in NpgsqlTsQuery subclasses with the existing tsquery mapping and cover mapping lookup and Matches query execution. (cherry picked from commit baa07f7a6b6c5f5efe3ee97487a4052c06b0d719) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37ecc207-3a95-4ce4-ac27-35975dc5c74f --- .../Internal/NpgsqlTypeMappingSource.cs | 7 ++++ .../FullTextSearchTranslationsTest.cs | 38 +++++++++++++++++++ .../Storage/NpgsqlTypeMappingSourceTest.cs | 16 ++++++++ 3 files changed, 61 insertions(+) create mode 100644 test/EFCore.PG.FunctionalTests/Query/Translations/FullTextSearchTranslationsTest.cs diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs index ed7ac6ebc..29712673c 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs @@ -352,6 +352,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/Translations/FullTextSearchTranslationsTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/FullTextSearchTranslationsTest.cs new file mode 100644 index 000000000..765e2bbbd --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/FullTextSearchTranslationsTest.cs @@ -0,0 +1,38 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations; + +public class FullTextSearchTranslationsTest : IClassFixture +{ + private BasicTypesQueryNpgsqlFixture Fixture { get; } + + public FullTextSearchTranslationsTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + { + Fixture = fixture; + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Matches_with_derived_tsquery_parameter(bool async) + { + using var context = Fixture.CreateContext(); + var tsQuery = new NpgsqlTsQueryLexeme("firs", NpgsqlTsQueryLexeme.Weight.None, true); + var query = context.BasicTypesEntities + .Select(b => EF.Functions.ToTsVector("simple", "first").Matches(tsQuery)); + + var results = async ? await query.ToArrayAsync() : query.ToArray(); + + Assert.NotEmpty(results); + Assert.All(results, Assert.True); + Fixture.TestSqlLoggerFactory.AssertBaseline( + [ + """ +@tsQuery=''firs':*' (DbType = Object) + +SELECT to_tsvector('simple', 'first') @@ @tsQuery +FROM "BasicTypesEntities" AS b +""" + ]); + } +} diff --git a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs index e05035581..203b4aff1 100644 --- a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs +++ b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs @@ -140,6 +140,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(typeof(decimal), "numeric(5)")] [InlineData(typeof(DateTime), "timestamp(5) with time zone")]