Skip to content

fix(filter): throw ParsingException for a '.' number that the culture cannot read - #159

Merged
pdevito3 merged 1 commit into
mainfrom
fm/qk-restore-d1-d5-d4
Oct 1, 2026
Merged

pdevito3 merged 1 commit into
mainfrom
fm/qk-restore-d1-d5-d4

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Summary

This PR restores D4 from the v1.14.2 review report. It is not a breaking change.

In a culture whose decimal separator is not '.', v1.14.2 read a number with the current culture. In de-DE it read only 4 of 4.4, built the clause with 4, and then failed in the grammar at the .. Thus Rating > 4.4 on an int property gave ParsingException. Main reads 4.4 in every culture. The conversion to int then fails, so main throws FormatException.

This PR keeps the main grammar. For a number that the current culture cannot read in full, the parser builds the clause like main. If that build fails, the parser does what v1.14.2 did:

  1. It builds the clause again with the part of the number that the culture reads (4 of 4.4). If this build fails, its exception goes to the caller, like v1.14.2.
  2. If this build succeeds, the parser throws ParsingException.

In a list, v1.14.2 failed in the grammar at the first . number, before it built the clause. Thus a list with a . number that does not convert throws ParsingException.

Scope against the brief

The brief says to wrap the FormatException only for integer target types. This PR uses the general v1.14.2 rule above instead, for these reasons:

  • For one value, the rule gives the integer result: int, long, short, byte, their nullable forms, and char. A decimal, double, or float property converts 4.4 and is still accepted. A DateTime or bool property rebuilds with 4 and throws FormatException, like v1.14.2.
  • Lists of other types also differed from v1.14.2. Rating ^^ ["x", 4.0], IsVegetarian ^^ [true, 4.0], and Tags ^^ [4.5] threw FormatException or ArgumentException on main and ParsingException on v1.14.2. An integer-only rule does not restore these.
  • en-US does not change. The culture reads the full number, so the parser takes the main path and Rating > 4.4 still throws FormatException.
  • Rating > @4.4 still throws FormatException, like v1.14.2.
  • TinyRating > 300.5 on a byte still throws OverflowException, like v1.14.2.

Results against v1.14.2 (de-DE)

Input Property type v1.14.2 main This PR
Rating > 4.4 int ParsingException FormatException same as v1.14.2
Rating == -4.0 int ParsingException FormatException same as v1.14.2
Rating == .5 int ParsingException FormatException same as v1.14.2
Rating ^^ [4.0] int ParsingException FormatException same as v1.14.2
Tags #== 2.0 List<string> count ParsingException FormatException same as v1.14.2
(Rating, Title) == 4.0 property list ParsingException FormatException same as v1.14.2
Rating > "4.4" int FormatException FormatException same as v1.14.2
Rating > 4.4 in en-US int FormatException FormatException same as v1.14.2
Price > 4.4 decimal ParsingException filters filters, like main

The last row is not changed back. PR #134 kept the e032af7 change, so main accepts a . number on a decimal property in every culture. An input that v1.14.2 rejected and main accepts is not a breaking change.

A differential probe ran 125 cases in de-DE, fr-FR, and en-US against v1.14.2 and this branch. It compared the expression, the in-memory rows, and the Postgres SQL. These differences remain, and each is an input that v1.14.2 rejected and main accepts:

  • A . number on a decimal or string property, and in a decimal list.
  • CreatedAt > 4.4 on a DateTime property, which main reads as a date in all cultures, like main in en-US.
  • A . number inside an arithmetic expression, for example (Rating * 2) > 4.5.

The inner exception of the restored ParsingException is the conversion exception. On v1.14.2 it was a Sprache ParseException. The outer type and the result are the same.

Tests

  • Unit pins in the new file QueryKit.UnitTests/DotNumberCultureTests.cs: 11 inputs that must throw ParsingException (de-DE and fr-FR), 6 inputs that must still throw FormatException, and an integer value that still filters in de-DE. The 11 ParsingException pins fail on main.
  • A Postgres pin in the new file QueryKit.IntegrationTests/Tests/DotNumberCultureTests.cs: in de-DE, Age > 4.4 on an int? property throws ParsingException, and Rating > 4.5 on a decimal? property returns the correct row. It fails on main.
  • dotnet test: unit 428 passed, integration 284 passed.

Overlap with other PRs

… cannot read

In a culture whose decimal separator is not '.', v1.14.2 read only the part of a '.' number before the '.'. Then the grammar failed at the '.'. Thus a '.' number on an integer property gave ParsingException, and main gave FormatException.

A number that the culture cannot read in full now builds the clause like main. If that build fails, the parser builds it again with the part that the culture reads, and throws ParsingException if this build succeeds. Lists throw ParsingException, like v1.14.2. en-US is unchanged. A '.' number on a decimal property still filters.
@pdevito3
pdevito3 merged commit afcd4c8 into main Oct 1, 2026
2 checks passed
@pdevito3
pdevito3 deleted the fm/qk-restore-d1-d5-d4 branch October 1, 2026 20:40
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