Skip to content

fix(filter)!: find HasConversion by the property path - #153

Open
pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-has-conversion-query-name
Open

pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-has-conversion-query-name

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

For later consideration in a major version. Do not merge now. #134 restored the v1.14.2 behavior to keep v1.x compatible (restore commit 76d3a7d). This PR re-applies the behavior of b817eb3 (#107). The captain decides on this PR separately.

Split. The non-breaking part of this PR moved to #169. That PR fixes issue #106 and every other input that threw on v1.14.2. #169 merged to main as 3114c96. This PR is rebased on current main and has one commit (fix(filter)!: find HasConversion by the property path). This commit carries only the changes to results that v1.14.2 gave.

Summary

ParseFilter replaces a query name with the property path before the parser runs. On v1.14.2, the HasConversion lookups then search by query name, with the property path as the input. If the query name is different from the property name, the lookup finds nothing.

#169 uses the property path only where v1.14.2 threw. This PR makes every lookup in QueryKit/FilterParser.cs use the property path:

  • CreateRightExpr and the left-side member lookup use GetPropertyInfo(path) or reference.Mapping, not GetPropertyInfoByQueryName.
  • The child-of-a-converted-parent check uses GetPropertyInfo.
  • A null literal on a converted reference type or a converted nullable struct gives a null constant.
  • A Guid string operator builds the right side for the string in all cases.

Breaking changes (results that v1.14.2 gave)

A differential driver compares this branch with QueryKit 1.14.2 on 4840 filters (expression text, rows in memory, and EF Core Npgsql SQL). The results of this branch are identical to the results of this PR before the split.

  • Email == null (also != null and == "null") on a converted reference type without a query name. v1.14.2 builds new EmailAddress("null"), the SQL is = 'null', and the filter matches no row. This PR compares the property against null.
  • Email.Value == "a@x.com" on a converted Email with a query name. v1.14.2 compares the child x.Email.Value. This PR compares the parent, like the same filter without a query name. Note: without a query name, v1.14.2 already compares the parent.
  • A converted int, int?, or enum with a query name and HasConversion<string>(). v1.14.2 compares by type (for example count == 2 gives x.Number == 2). This PR sends the value through the string conversion, and count == 2 throws ParsingException ("The binary operator Equal is not defined for the types 'System.Int32' and 'System.String'").
  • A converted Guid with a query name and HasConversion<string>(). v1.14.2 gives a Guid constant. This PR gives new Guid("..."). The rows are the same, but the expression text changes. Text that is not a Guid (for example qn == "ab7afb17") gives new Guid("ab7afb17"), which still throws when the query runs.

Decision needed before a merge: the int, int?, and enum change makes filters that work on v1.14.2 throw. The original PR text did not list it, and it looks unintended. The unit test int_with_query_name_and_has_conversion_throws records the current result of this PR.

Migration

  • If a filter uses null on a converted reference type without a query name, it now matches rows where the property is null. Before, it matched rows that equal a value built from the text null.
  • If a filter uses a child path of a converted parent with a query name, it now compares the parent, like the same filter without a query name.
  • If a converted int, int?, or enum property has a query name and HasConversion<string>(), a filter on it now throws.

README

The "HasConversion Support" section gets one new paragraph. It says that QueryKit finds the conversion by the query name or by the property name, that null compares against null, and that a nullable struct property uses the conversion.

Interaction with other PRs

J5 and J-bis (#154) remove the query-name rewrite from ParseFilter and resolve query names in the grammar. This PR uses the resolved property path, so the lookups also work with J5 and J-bis.

Tests

Changes on top of #169:

Unit (QueryKit.UnitTests/HasConversionTests.cs):

  • child_property_of_converted_parent_with_query_name_compares_the_child becomes child_property_of_converted_parent_with_query_name_compares_parent.
  • null_on_reference_type_with_has_conversion_matches_no_row becomes can_filter_null_on_reference_type_with_has_conversion.
  • int_with_query_name_and_has_conversion_compares_the_int becomes int_with_query_name_and_has_conversion_throws.
  • guid_with_query_name_and_has_conversion_compares_a_guid_constant becomes guid_with_query_name_and_has_conversion_builds_a_guid_from_the_string.

Postgres integration (QueryKit.IntegrationTests/Tests/HasConversionTests.cs):

  • email_value_with_query_name_and_has_conversion_compares_the_child becomes can_filter_by_email_value_with_query_name_and_has_conversion.
  • null_email_with_has_conversion_matches_no_row becomes can_filter_by_null_email_with_has_conversion.

dotnet build succeeds. dotnet test passes 470 unit tests and 297 Postgres integration tests (Testcontainers), with 0 failures.

The 1.x fix finds the conversion by the property path only where v1.14.2 threw. This change uses the property path for every HasConversion lookup, so it also changes results that v1.14.2 gave for accepted filters.

BREAKING CHANGE: a null literal on a converted reference type without a query name matches null rows, not rows equal to a value built from the text null. A child path such as Email.Value on a converted parent with a query name compares the parent. A converted int, int?, enum, or Guid with a query name and HasConversion<string>() goes through the string conversion: an int filter such as count == 2 throws, and a Guid filter builds new Guid("...") instead of a Guid constant.
@pdevito3
pdevito3 force-pushed the fm/qk-breaking-has-conversion-query-name branch from f3fcc5e to 6709a0d Compare October 1, 2026 21:59
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.

1 participant