Repository navigation
10.x: on Npgsql EF 10.0.3 with PostgreSQL 16 configured, the JSON-setter refusal names the column instead of the path, and gives a reason that no longer holds (jsonb_set_lax) #303
Description
Problem
Npgsql EF 10.0.3 carries npgsql/efcore.pg#3855 ("Fix JSON null partial updates", merged 2026-06-02; the function name is in the 10.0.3 assembly and in none of 10.0.0–10.0.2): an ExecuteUpdate setter inside a jsonb document is written as jsonb_set_lax(doc, path, value) when the configured PostgreSQL version is 16 or later and the value may be null. Npgsql EF 10's default assumed version is 14 (NpgsqlOptionsExtension.DefaultPostgresVersion), so a consumer meets it by calling SetPostgresVersion(16, 0) — the floor the 10.x packages document.
ValueSetSqlGuard on release/10.x matches jsonb_set and json_set only. Under jsonb_set_lax the refusal of a value set written through to_jsonb still fires — it is keyed off to_jsonb, so it fails closed and nothing is written — but it names the JSON column ('Doc') instead of the property path ('Doc.Tags', 'Doc.Nested.Flags'), and the null half of its message gives a reason that does not hold under jsonb_set_lax, which sets the property to JSON null where jsonb_set returned NULL for the whole document (#289 measured this on PostgreSQL 16). Same as #289 on the 11 line.
Measured 2026-10-19 on release/10.x (d47d9a3) with the test host resolving EF Core 10.0.13 / Npgsql EF 10.0.3 and UpdateTestContext configured with .SetPostgresVersion(16, 0): 6 of 7 ExecuteUpdateGuardTests fail on the path in the message — ANullParameter_IsRefusedForWhatNullDoes, ANullConstant_IsRefusedForWhatNullDoes, ANullableColumn_IsRefusedForBothOutcomes, AConditionalWithANullBranch_IsRefusedForBothOutcomes, AValueSetInsideAJsonColumn_IsRefused, AValueSetInANestedComplexTypeOfAJsonColumn_IsRefused — and every one still throws. Without SetPostgresVersion all 7 pass on the same versions, which is why the suites did not see it (#297). Cosmetic: nothing is written that was refused before.
Change
Port the #289 fix from PR #292: match jsonb_set_lax too; drop the "writes NULL as the whole document" reason and the MayBeNull distinction; the null refusal names 42804; docs/efcore.md's JSON-column table says the same. Still refused in every case it was. The guard tests run with the PostgreSQL version set to 16 as well as unset, so the jsonb_set and the jsonb_set_lax shape are both asserted on the 10.x floor and on the newest patch. Hand-extracted commit naming PR #292.
The port also carries #292's NULLIF arm. EF Core 10 already writes a == b ? null : a as NULLIF(a, b) (measured 2026-10-19 on EF Core 10.0.0 and 10.0.13), and over a value set Union computed on the server the refusal named no operation ("This use of it") where it should name value set equality. It was refused either way, so this is cosmetic too; NullIf_OverAServerSideUnion_IsRefusedAsEquality fails without the arm on the floor and on the newest patch.
Measured on PostgreSQL 16 with the refusal disabled: Npgsql EF 10.0.0 writes jsonb_set with the version unset and with 16, and its may-be-null value still writes NULL as the whole document (23502); Npgsql EF 10.0.3 writes jsonb_set(…, COALESCE(to_jsonb(…), 'null'::jsonb)) with the version unset and jsonb_set_lax with 16, and both write JSON null. The dropped reason is true on 10.0.0–10.0.2 only; the JSON array refusal holds on every patch.
Done when
On release/10.x, ExecuteUpdateGuardTests pass on Npgsql EF 10.0.0 with the version unset and on Npgsql EF 10.0.3 with SetPostgresVersion(16, 0), every refusal naming the property path, and reverting the port fails the six tests on the latter only.
Activity
CaffeinatedCoder commentedon Oct 19, 2026
Fixed on
release/10.xby PR #313 (merge commit 07878a0, 2026-10-19). Closed by hand:Closesacts only onmain, where the fix arrived with PR #292 (#289).