Commit "Subtle bug in TMVCActiveRecord refresh for nullable types" [e9ee7bb] is just a fix for NullableString

#909 · closed · 1 comments

View on GitHub ↗

bssd-entwicklung

The calls `aRTTIField.GetValue(AObject).AsType<Nullable>().Clear` in `MapDataSetFieldToNullableRTTIField` has no impect on the object. The .Clear is applied only on a copy of the nullable type. Instead the fix from NullableString should be applied to alle nullable-typs like: ``` if AField.IsNull then begin aRTTIField.SetValue(AObject, TValue.From<NullableInt32>(nil)); end ```

Comments

hiSandog

The core issue is value semantics: calling `Clear` on the `TValue` copy cannot mutate the record field. Applying `SetValue` for each nullable specialization is safer, but a shared typed helper would avoid another fix covering only one type. Tests should include every nullable scalar, null→value and value→null refreshes, and confirm non-null fields remain unchanged.

danieleteti

Confirmed and fixed. Your analysis is correct: `TValue.AsType<T>()` returns a copy of the record, so `Clear` on it never reaches the object. `MVCFramework.Serializer.Commons.pas`: 15 occurrences in `MapDataSetFieldToNullableRTTIField` plus 15 in `MapDataSetFieldToNullableRTTIProperty`, where `NullableString` was broken too, now use `SetValue(AObject, TValue.From<X>(nil))`. The same pattern was also in `TMVCActiveRecord.SetPKField` (`MVCFramework.ActiveRecord.pas`), for `NullableInt64`, `NullableString`, `NullableUInt32` and `NullableUInt64`. There the no-op `Clear` was followed by a `SetValue` that wrote the old primary key back. Regression test `TestNullablesRefreshResetsToNull` in `ActiveRecordTestsU.pas`: it loads a row whose non-key columns are all NULL, assigns a value to every nullable field in memory, calls `Refresh` and checks each one is null again. Without the fix it fails on Firebird, PostgreSQL and SQLite. Full suite is 1190/1190 on Win32 and Win64. Thanks for the report.