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
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ 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.4.0] — 2026-10-26

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

### Changed answers

- EF Core: a `StringSet` or `StringSet<TElement>` property with a collation that may not compare bytes throws when the model is built, and so does `EF.Functions.Collate` over one in a query. Under a case-insensitive collation, `Contains("a")` matched `{A}` and `Remove("a")` deleted `A`, unlike in memory ([#496](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/496), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: on a `DateRange`, a `LocalDateRange` and their multiranges, `LowerBound()`, `UpperBound()`, `LowerBoundInclusive()` and `Clamp` read an exclusive `infinity` or `-infinity` in a query as the row loads. On `[2026-01-01,infinity)`, `UpperBound()` returned 9999-12-31 where memory gives 9999-12-30 ([#502](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/502), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: on a `YearMonthRange` and its multirange, `UpperBound()` and `Clamp` compare with a month in a query as in memory. They read the last day of the month, so on `[2026-01,2026-01]` `UpperBound() == 2026-01` was `false` and `UpperBound() > 2026-01` was `true` ([#511](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/511), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: a query that compares a `StringSet` or `StringSet<TElement>` with a value whose column has a collation that may not compare bytes, or with `EF.Functions.Collate(value, …)` under one, throws, because the database compares under it: `Contains(g.Owner)` matched `{A}` for `"a"` on a case-insensitive `Owner`, and `Remove(g.Owner)` in `ExecuteUpdate` deleted `A` ([#512](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/512), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).

### Added

- EF Core: `Clamp` runs as SQL on every range type, no longer only on `Int32Range`, `Int64Range` and `DecimalRange`, and answers as memory does, at the extremes too. A `DateTime` stored as `timestamp with time zone` against a `DateTimeRange` runs in memory, because PostgreSQL would compare them through the session's time zone ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Union`, `Add`, `Intersect` and `Except` on value sets run as SQL for every set type except `StringSet` and `StringSet<TElement>`. They give the deduplicated, sorted set memory gives, so a query can compare, count, group by and store the result ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: on `StringSet` and `StringSet<TElement>`, `Intersect`, `Except` and `Add` run as SQL, left unsorted, where order and repeats don't change the answer. `Count` over them and over `Union` counts the distinct elements, and `==` and `!=` compare by mutual containment. Ordering, grouping and storing one still throw ([Combine sets with Union](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#combine-sets-with-union)).
- EF Core: `Count` on a `RangeSet` runs as SQL, so a query can filter and sort by how many separate ranges a multirange holds. It counts the ranges memory holds, and a `null` multirange gives `null`. Near the element type's extremes, a multirange the database computes can count one range more ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).

### Changed

- Docs: a value set over your own element type, other than `StringSet<TElement>`, needs a `CompareTo` that orders values as the underlying type does, because the database sorts the underlying values ([Value sets](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/value-sets.md#how-your-element-types-sort-and-serialize)).

## [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).
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.3.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>10.4.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.3.x | ✅ |
| < 10.3 | ❌ |
| 10.4.x | ✅ |
| < 10.4 | ❌ |

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
10 changes: 10 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,16 @@ Some calls return a different value. Your code stays the same, so the compiler c
- [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)

## Changed answers in 10.4.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 string set columns with a collation](#check-string-set-columns-with-a-collation)
- [Check date bounds at an exclusive infinity in queries](#check-date-bounds-at-an-exclusive-infinity-in-queries)
- [Check `YearMonthRange` upper bounds compared in queries](#check-yearmonthrange-upper-bounds-compared-in-queries)

## Migration from v10.x

Nothing was removed or renamed. Some queries return different rows, and the compiler can't show you
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.3.0" />
<PackageReference Include="CodoMetis.ValueRanges.EFCore.PostgreSQL" Version="10.4.0" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ 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 and `Clamp` 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.4.0] — 2026-10-26

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

### Changed answers

- EF Core: on a `LocalDateRange` and its multirange, `UpperBound()` and `Clamp` read an exclusive `infinity` as 9999-12-30 in a query, as the row loads, where they returned 9999-12-31, and `LowerBoundInclusive()` is `true` at an exclusive `-infinity`. `LowerBound()` there keeps `LocalDate.MinIsoValue`, because memory's -9998-01-02 isn't a PostgreSQL date ([#502](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/502), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: on a `YearMonthRange` and its multirange, `UpperBound()` and `Clamp` compare with a month in a query as in memory. They read the last day of the month, so on `[2026-01,2026-01]` `UpperBound() == 2026-01` was `false` and `UpperBound() > 2026-01` was `true` ([#511](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/511), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).

### Added

- EF Core: `Union`, `Add`, `Intersect` and `Except` on the NodaTime value sets, their versions with your own element type and `YearMonthSet` run as SQL. They give the deduplicated, sorted set memory gives, so a query can compare, count, group by and store the result ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Count` on a `RangeSet` of the NodaTime ranges and of `YearMonthRange` runs as SQL, so a query can filter and sort by how many separate ranges a multirange holds ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Clamp` runs as SQL on `LocalDateRange`, `LocalDateTimeRange`, `InstantRange` and `YearMonthRange`, so a query can filter and sort by the clamped value. It answers as memory does, at the element types' extremes too ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).

## [10.3.0] — 2026-10-25

### Changed
Expand Down
21 changes: 21 additions & 0 deletions src/CodoMetis.ValueRanges.EFCore.PostgreSQL/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ 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.4.0] — 2026-10-26

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

### Changed answers

- EF Core: a `StringSet` or `StringSet<TElement>` property with a collation that may not compare bytes throws when the model is built, and so does `EF.Functions.Collate` over one in a query. Under a case-insensitive collation, `Contains("a")` matched `{A}` and `Remove("a")` deleted `A`, unlike in memory ([#496](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/496), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: a query that compares a `StringSet` or `StringSet<TElement>` with a value whose column has a collation that may not compare bytes, or with `EF.Functions.Collate(value, …)` under one, throws, because the database compares under it: `Contains(g.Owner)` matched `{A}` for `"a"` on a case-insensitive `Owner`, and `Remove(g.Owner)` in `ExecuteUpdate` deleted `A` ([#512](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/512), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).
- EF Core: on a `DateRange` and its multirange, `LowerBound()`, `UpperBound()`, `LowerBoundInclusive()` and `Clamp` read an exclusive `infinity` or `-infinity` in a query as the row loads. On `[2026-01-01,infinity)`, `UpperBound()` returned 9999-12-31 where memory gives 9999-12-30, and on `(-infinity,2026-01-01)` `LowerBound()` returned 0001-01-01 ([#502](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/issues/502), [Migration](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/migration.md#changed-answers-in-1040)).

### Added

- EF Core: `Clamp` runs as SQL on every range type, no longer only on `Int32Range`, `Int64Range` and `DecimalRange`, and answers as memory does, at the extremes too. A `DateTime` stored as `timestamp with time zone` against a `DateTimeRange` runs in memory, because PostgreSQL would compare them through the session's time zone ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: `Union`, `Add`, `Intersect` and `Except` on value sets run as SQL for every set type except `StringSet` and `StringSet<TElement>`. They give the deduplicated, sorted set memory gives, so a query can compare, count, group by and store the result ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).
- EF Core: on `StringSet` and `StringSet<TElement>`, `Intersect`, `Except` and `Add` run as SQL, left unsorted, where order and repeats don't change the answer. `Count` over them and over `Union` counts the distinct elements, and `==` and `!=` compare by mutual containment. Ordering, grouping and storing one still throw ([Combine sets with Union](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#combine-sets-with-union)).
- EF Core: `Count` on a `RangeSet` runs as SQL, so a query can filter and sort by how many separate ranges a multirange holds. It counts the ranges memory holds, and a `null` multirange gives `null`. Near the element type's extremes, a multirange the database computes can count one range more ([What runs where](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/efcore.md#what-runs-where)).

### Changed

- Docs: a value set over your own element type, other than `StringSet<TElement>`, needs a `CompareTo` that orders values as the underlying type does, because the database sorts the underlying values ([Value sets](https://github.com/CaffeinatedCoder/CodoMetis.ValueRanges/blob/main/docs/value-sets.md#how-your-element-types-sort-and-serialize)).

## [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).
Expand Down
4 changes: 4 additions & 0 deletions src/CodoMetis.ValueRanges.NodaTime/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ 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.4.0] — 2026-10-26

No change in this package.

## [10.3.0] — 2026-10-25

### Changed
Expand Down
4 changes: 4 additions & 0 deletions src/CodoMetis.ValueRanges/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ 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.4.0] — 2026-10-26

No change in this package.

## [10.3.0] — 2026-10-25

### Changed
Expand Down
Loading