Skip to content

fix(filter)!: read a quoted "null" as text for a string property - #172

Merged
pdevito3 merged 1 commit into
v2from
fix/quoted-null-is-text
Oct 2, 2026
Merged

pdevito3 merged 1 commit into
v2from
fix/quoted-null-is-text

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Summary

A quoted "null" on a string property is now the text null. Before this change, the parser read it as a real null. An unquoted null does not change.

 CreateRightExprFromType(type, right, rightIsQuotedLiteral, op)
-  if right == "null"
+  if right == "null" and not (rightIsQuotedLiteral and type is string)
     return null constant
   ...
Filter Before After
Title == "null" x.Title == null x.Title == "null"
Title != "null" x.Title != null x.Title != "null"
Title @= "null" x.Title.Contains(null) x.Title.Contains("null")
Title == null x.Title == null x.Title == null (no change)
Age == "null" x.Age == null x.Age == null (no change, an int has no text value)

Notes:

Evidence

Harness: verify-querykit, run qk-20261002-223925-97048. The seed data has no row with the text null in Directions. One row (Plain Water) has a null Directions.

  • Before (main d81242c):

    • Directions == "null": expression x.Directions == null, returns Plain Water on memory and Postgres.
    • Directions != "null": expression x.Directions != null, returns 3 rows.
    • Directions @= "null": expression Contains(null), memory throws ArgumentNullException, Postgres returns [].

    After (labels with -fixed):

    • Directions == "null": expression x.Directions == "null", returns [] on both targets.
    • Directions != "null": returns all 4 rows on both targets.
    • Directions @= "null": expression Contains("null"), Postgres returns []. Memory throws the fix(operators)!: check for null in case-sensitive string operators #145 NRE on the null row.
    • Unquoted == null and != null return the same rows as before.
  • Tests:

    • Unit: quoted_null_is_text_for_a_string (==, !=, @=, !@=, ==*), unquoted_null_is_null_for_a_string_not_equal, quoted_null_is_null_for_a_type_that_has_no_text, and the enumerable theory quoted_null_matches_the_text_null_and_unquoted_null_matches_null.
    • Postgres: NullFilteringTests.quoted_null_matches_the_text_null_and_unquoted_null_matches_null with ==, !=, @=, ^^ ["null"], == null, and != null. Without the fix, the ==, !=, and @= cases fail.
    • Full suites: 482 unit tests pass. 303 integration tests pass.

Merge Danger

Door: two-way

The change is one condition in the parser. A revert puts back the old behavior.

Blast Radius: consumers

This is a breaking change. A consumer that sends Prop == "null" to find null rows on a string property now gets the rows with the text null. Change the filter to Prop == null (no quotes) to find null rows.

A quoted "null" became a real null, so Title == "null" matched the row
with a null Title and not the row with the text "null". Now a quoted
"null" is text for a string property. An unquoted null is still a null,
and a quoted "null" is still a null for other types.
@pdevito3
pdevito3 force-pushed the fix/quoted-null-is-text branch from 1f51058 to 45ca127 Compare October 2, 2026 20:11
@pdevito3
pdevito3 merged commit 1e17be6 into v2 Oct 2, 2026
2 checks passed
@pdevito3
pdevito3 deleted the fix/quoted-null-is-text branch October 2, 2026 20:12
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