Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ The first preview of the 11 line: the same public API as 10.0.0, on .NET 11. Not
- Docs: an ordering by `LowerBound()` or `UpperBound()` puts the empty rows and those without that bound where PostgreSQL puts NULLs, last ascending and first descending, also on a required column. A `!= null` key in front sorts them as memory does ([EF Core](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#read-bounds-merge-ranges-and-aggregate)).
- EF Core: an empty captured list of ranges or sets keeps EF Core's ordering column (`_ord`, behind `FirstOrDefault` over the list) an integer, as it is when the list has elements. It was cast to the range or set type. No answer changed, but comparing that column with an integer would have failed for the empty list ([Captured lists](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#match-a-column-against-a-list-of-sets)).

## [10.3.0] — 2026-10-25

Some calls answer differently: [Changed answers in 10.3.0](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030).

### Changed answers

- EF Core: a value set `Union` with a NULL set, or `Remove` with a NULL set or element, is NULL in a query, as `Where(… == null)` already said. A `Select` loaded the other set instead, for a nullable column, a missing outer-join child or a `null` parameter ([#395](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/395), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030)).
- EF Core: any `Compare(x, y)` over ranges or sets, `RangeLowerBoundComparer.Compare` and every other `IComparer` included, runs in memory only in a query, as the method called. A filter or an ordering fails translation. In SQL it compared in the range order, so `Compare(r.Period, p) == 0` missed rows with the same lower bound ([#396](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/396), [#452](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/452), [#464](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/464), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030)).

### Added

- EF Core: `Length` on `Int32Range` and `Int64Range` runs as SQL, so a query can filter and sort by it. It counts the integers as memory does: 0 for the empty range, and `null` for an unbounded side or, on `Int64Range`, a count larger than `long.MaxValue` ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Clamp` on `Int32Range`, `Int64Range` and `DecimalRange` runs as SQL, so a query can filter and sort by the clamped value. It answers as memory does: the value inside the range, otherwise the bound it passes, and `null` for the empty range or a `null` range or value ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `DateRange.Month`, `Quarter`, `Year` and `IsoWeek` run as SQL when they read a column, so `DateRange.Month(b.Day)` works in a `Where` and an `OrderBy`. They build the range memory builds, the extremes included, and an argument memory rejects fails in PostgreSQL too. The NodaTime forms stay in memory ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- Docs: the guides, the package READMEs, the changelog and an API reference are published as a [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/) with one set of pages per line. It opens on the stable 10.x line, and the 11.x previews are under `/11.x/`.
- Docs: a [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md) follows one booking through a minimal API and PostgreSQL, from the request to the stored row and back, and says what changes at each step. CI runs its sample against the packages, so the responses and SQL on the page stay true.

### Changed

- Docs: each package's Project website link on NuGet opens the [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/), and its README links the site and the [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md).

## [10.2.0] — 2026-10-25

Some calls answer differently: [Changed answers in 10.2.0](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1020).
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
-->
<PropertyGroup>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>10.2.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>10.3.0</PackageValidationBaselineVersion>
</PropertyGroup>

<!--
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ maintained.

| Version | Supported |
|---|---|
| 10.2.x | ✅ |
| < 10.2 | ❌ |
| 10.3.x | ✅ |
| < 10.3 | ❌ |

Previews (`11.0.0-preview.N`) are not supported versions and have no row: a fix reaches a preview
in the next preview.
Expand Down
20 changes: 15 additions & 5 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,15 @@ Some calls return a different value. Your code stays the same, so the compiler c
- [Keep `null` out of a range set](#keep-null-out-of-a-range-set)
- [Check predicates over a `null` range or a NULL column](#check-predicates-over-a-null-range-or-a-null-column)

## Changed answers in 10.3.0

Some calls return a different value. Your code stays the same, so the compiler can't show you where.
11.0.0 makes the same changes, so each one is described under
[Migration from v10.x](#migration-from-v10x):

- [Check value set `Union` and `Remove` over NULL in queries](#check-value-set-union-and-remove-over-null-in-queries)
- [Move `RangeLowerBoundComparer` out of queries](#move-rangelowerboundcomparer-out-of-queries)

## Migration from v10.x

Nothing was removed or renamed. Some queries return different rows, and the compiler can't show you
Expand All @@ -820,7 +829,8 @@ where, so check these:
ordering it fails translation. In SQL it compared in the range order, so it selected other rows
([details](#move-rangelowerboundcomparer-out-of-queries)).

The first four also shipped in 10.2.0. If you upgrade from 10.2.0 or later, you've already met them.
The first four also shipped in 10.2.0, and the last two in 10.3.0. If you upgrade from 10.2.0 or later,
you've already met the first four, and from 10.3.0 or later, all six.

### Check bound comparisons with `null` in queries

Expand Down Expand Up @@ -955,11 +965,11 @@ Check EF Core queries that load a value set `Union` or `Remove` where a set or a
a nullable column, a column of a missing child in an outer join, or a `null` parameter.

PostgreSQL's `array_cat` and `array_remove` skip a NULL: `array_cat(NULL, '{z}')` is `{z}`, and
`array_remove('{a}', NULL)` is `{a}`. Before 11.0.0, the EF Core package called them as they are,
`array_remove('{a}', NULL)` is `{a}`. Before 10.3.0, the EF Core package called them as they are,
while a filter treated the result as NULL. So the same expression was NULL in a `Where` and a set in a
`Select`. Now it is NULL in both. With `more` as `{z}`:

| Query | Before 11.0.0 | Now |
| Query | Before 10.3.0 | Now |
|---|---|---|
| `Select(b => b.MaybeTags!.Union(more))`, for a NULL `MaybeTags` | `{z}` | `null` |
| `Select(b => b.Tags.Remove(b.Label!))`, for a NULL `Label` | `Tags` as stored | `null` |
Expand All @@ -982,11 +992,11 @@ Check EF Core queries that call `Compare` on `RangeLowerBoundComparer`, on
`RangeSet.LowerBoundComparer`, or on another `IComparer` of ranges or sets.

EF Core reads every `Compare(x, y)` as the natural order of `x` and `y`, whatever the comparer. For a
range, that is the range order `CompareTo` uses, not the lower bound. Before 11.0.0 the comparer ran
range, that is the range order `CompareTo` uses, not the lower bound. Before 10.3.0 the comparer ran
in SQL in that order. Now it runs in memory only, like the other members under
[What runs where](efcore.md#what-runs-where). With `p` as `[2026-03-01,2026-03-10]`:

| Query | Before 11.0.0 | Now |
| Query | Before 10.3.0 | Now |
|---|---|---|
| `Where(r => comparer.Compare(r.Period, p) == 0)` | only the rows equal to `p` | throws (`InvalidOperationException`): could not be translated |
| `OrderBy(r => comparer.Compare(r.Period, p))` | sorted in the range order | throws, as above |
Expand Down
2 changes: 1 addition & 1 deletion samples/Bookings/Bookings.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
ships before it builds the sample, so CI runs it against the packages from the working tree.
-->
<ItemGroup>
<PackageReference Include="CodoMetis.ValueRanges.EFCore.PostgreSQL" Version="10.2.0" />
<PackageReference Include="CodoMetis.ValueRanges.EFCore.PostgreSQL" Version="10.3.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ The first preview of the 11 line: the same public API as 10.0.0, on .NET 11, EF
- Docs: [What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where), the READMEs and the XML docs give each in-memory operation's real reason. `Shift` and `Expand` keep a bound at `infinity`, so that reason was stale. `Length`, `Values`, `Split`, the bridge conversions, `Clamp`, `Intersect` and `Except` would take composed SQL, not a missing operator. In-memory operations run in the final `Select` only.
- Docs: the [What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where) tables list every public member a query can call. `Equals` runs as `=`. `Values` on a value set, `Count` and the indexer on a `RangeSet`, `From`, `Parse`, the NodaTime conversions, `ToString`, `GetHashCode` and `GetEnumerator` run in memory.

## [10.3.0] — 2026-10-25

### Changed

- Docs: each package's Project website link on NuGet opens the [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/), and its README links the site and the [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md).

## [10.2.0] — 2026-10-25

### Changed answers
Expand Down
19 changes: 19 additions & 0 deletions src/CodoMetis.ValueRanges.EFCore.PostgreSQL/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ The first preview of the 11 line: the same public API as 10.0.0, on .NET 11, EF
- Docs: an ordering by `LowerBound()` or `UpperBound()` puts the empty rows and those without that bound where PostgreSQL puts NULLs, last ascending and first descending, also on a required column. A `!= null` key in front sorts them as memory does ([EF Core](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#read-bounds-merge-ranges-and-aggregate)).
- EF Core: an empty captured list of ranges or sets keeps EF Core's ordering column (`_ord`, behind `FirstOrDefault` over the list) an integer, as it is when the list has elements. It was cast to the range or set type. No answer changed, but comparing that column with an integer would have failed for the empty list ([Captured lists](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#match-a-column-against-a-list-of-sets)).

## [10.3.0] — 2026-10-25

Some calls answer differently: [Changed answers in 10.3.0](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030).

### Changed answers

- EF Core: a value set `Union` with a NULL set, or `Remove` with a NULL set or element, is NULL in a query, as `Where(… == null)` already said. A `Select` loaded the other set instead, for a nullable column, a missing outer-join child or a `null` parameter ([#395](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/395), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030)).
- EF Core: any `Compare(x, y)` over ranges or sets, `RangeLowerBoundComparer.Compare` and every other `IComparer` included, runs in memory only in a query, as the method called. A filter or an ordering fails translation. In SQL it compared in the range order, so `Compare(r.Period, p) == 0` missed rows with the same lower bound ([#396](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/396), [#452](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/452), [#464](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/464), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1030)).

### Added

- EF Core: `Length` on `Int32Range` and `Int64Range` runs as SQL, so a query can filter and sort by it. It counts the integers as memory does: 0 for the empty range, and `null` for an unbounded side or, on `Int64Range`, a count larger than `long.MaxValue` ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Clamp` on `Int32Range`, `Int64Range` and `DecimalRange` runs as SQL, so a query can filter and sort by the clamped value. It answers as memory does: the value inside the range, otherwise the bound it passes, and `null` for the empty range or a `null` range or value ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `DateRange.Month`, `Quarter`, `Year` and `IsoWeek` run as SQL when they read a column, so `DateRange.Month(b.Day)` works in a `Where` and an `OrderBy`. They build the range memory builds, the extremes included, and an argument memory rejects fails in PostgreSQL too. The NodaTime forms stay in memory ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).

### Changed

- Docs: each package's Project website link on NuGet opens the [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/), and its README links the site and the [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md).

## [10.2.0] — 2026-10-25

### Changed answers
Expand Down
6 changes: 6 additions & 0 deletions src/CodoMetis.ValueRanges.NodaTime/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ The first preview of the 11 line: the same public API as 10.0.0, on .NET 11. The
- Docs: [What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where), the READMEs and the XML docs give each in-memory operation's real reason. `Shift` and `Expand` keep a bound at `infinity`, so that reason was stale. `Length`, `Values`, `Split`, the bridge conversions, `Clamp`, `Intersect` and `Except` would take composed SQL, not a missing operator. In-memory operations run in the final `Select` only.
- Value sets: every set type implements `IFormattable` itself, so `ToString()` and formatting no longer run the interface's default, whose shared-generic stub traps under Native AOT with ILCompiler 11 RC2 ([Native AOT](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/serialization.md#native-aot)).

## [10.3.0] — 2026-10-25

### Changed

- Docs: each package's Project website link on NuGet opens the [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/), and its README links the site and the [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md).

## [10.2.0] — 2026-10-25

### Changed answers
Expand Down
6 changes: 6 additions & 0 deletions src/CodoMetis.ValueRanges/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ The first preview of the 11 line: the same public API as 10.0.0, on .NET 11. The
- Docs: [What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where), the READMEs and the XML docs give each in-memory operation's real reason. `Shift` and `Expand` keep a bound at `infinity`, so that reason was stale. `Length`, `Values`, `Split`, the bridge conversions, `Clamp`, `Intersect` and `Except` would take composed SQL, not a missing operator. In-memory operations run in the final `Select` only.
- Value sets: every set type implements `IFormattable` itself, so `ToString()` and formatting no longer run the interface's default, whose shared-generic stub traps under Native AOT with ILCompiler 11 RC2 ([Native AOT](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/serialization.md#native-aot)).

## [10.3.0] — 2026-10-25

### Changed

- Docs: each package's Project website link on NuGet opens the [documentation site](https://caffeinatedcoder.github.io/CodoMetis.ValueRanges/), and its README links the site and the [walkthrough](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/walkthrough.md).

## [10.2.0] — 2026-10-25

### Changed answers
Expand Down
Loading