Skip to content

fix(operators)!: drop constant lists in case-insensitive in and not-in - #147

Open
pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-in-constant-list
Open

pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-in-constant-list

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 list read to keep v1.x compatible (restore commit e6d55b3). This PR re-applies the list read of 5db8c4b (#110). The captain decides on this PR separately.

Summary

-                // A caller can pass the list as a constant, like v1.14.2.
-                var originalList = (values ?? ((ConstantExpression)right).Value) as IEnumerable<string>;
+                var originalList = values as IEnumerable<string>;

The change is in the case-insensitive string path of InType and NotInType, in QueryKit/Operators/ComparisonOperator.cs only.

v1.14.2 behavior (and main)

InOperator(true) and NotInOperator(true) read the list from a NewArrayExpression (the shape that the parser makes) or from a ConstantExpression that holds the list.

New behavior

The case-insensitive path reads the list only from a NewArrayExpression. With a ConstantExpression list, originalList is null and the operator throws NullReferenceException. The case-sensitive path does not change: it still accepts a ConstantExpression list.

Example

Expression<Func<TestingPerson, string?>> title = x => x.Title;
ComparisonOperator.InOperator(true)
    .GetExpression<TestingPerson>(title.Body, Expression.Constant(new List<string> { "LAMB" }), null);
  • v1.14.2 and main: the expression is x => ((x.Title != null) AndAlso <list>.Contains(x.Title.ToLower())), with the list ["lamb"].
  • This PR: the call throws NullReferenceException.

Filter strings (Title ^^* ["LAMB"]) do not change, because the parser makes a NewArrayExpression.

Justification

This change is a side effect of #110, which moved the in-list values to a parameter holder. The operator now builds the list from one input shape only. The justification for this PR alone is weak: the result for a ConstantExpression list is a NullReferenceException, not a clear error. If a major version wants one input shape, a clear exception is better than this change. The captain can close this PR to keep the v1.14.2 behavior.

Migration

If you call InOperator(true) or NotInOperator(true) directly with a ConstantExpression list, pass a NewArrayExpression of constants (Expression.NewArrayInit(typeof(string), ...)) instead. Or use a filter string.

README

No change. The README does not document the operator factories.

Tests

Unit FilterParsingRegressionTests.case_insensitive_in_operator_factory_reads_a_constant_list (theory, In and NotIn) is removed. #134 added this test. Main before #134 had no test for this input, so no test comes back. No integration test changes.

dotnet test: 468 unit tests and 297 Postgres integration tests (Testcontainers) pass, 0 failures.

Rebase on main

This branch is rebased on current main (#169). The rebase had no conflicts. The breaking change did not change.

InOperator(true) and NotInOperator(true) read the list from the NewArrayExpression that the parser makes. v1.14.2 also read a list that a caller passed as a ConstantExpression. Remove this second path. The operators read the list only from the parsed array.

BREAKING CHANGE: a caller that passes a ConstantExpression list to InOperator(true) or NotInOperator(true) gets NullReferenceException. Pass the values as a NewArrayExpression of constants, or use a filter string.
@pdevito3
pdevito3 force-pushed the fm/qk-breaking-in-constant-list branch from 3edbd86 to 42ca1cc Compare October 1, 2026 21:48
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