DML5I2X2ZZSOWUTWKMTAQN3B3RTCOHCV5U375NOJRQ73EXWINDPQC })t.Run("*int nil", func(t *testing.T) {var p *intresult := marshalParam(p)assertNil(t, result)})t.Run("*int with value", func(t *testing.T) {v := 42result := marshalParam(&v)assertEqual(t, 42, result)})t.Run("*int64 nil", func(t *testing.T) {var p *int64result := marshalParam(p)assertNil(t, result)})t.Run("*int64 with value", func(t *testing.T) {v := int64(1234567890123)result := marshalParam(&v)assertEqual(t, int64(1234567890123), result)
t.Run("*float64 nil", func(t *testing.T) {var p *float64result := marshalParam(p)assertNil(t, result)})t.Run("*float64 with value", func(t *testing.T) {v := 3.14159result := marshalParam(&v)assertEqual(t, 3.14159, result)})t.Run("*float32 nil", func(t *testing.T) {var p *float32result := marshalParam(p)assertNil(t, result)})t.Run("*float32 with value", func(t *testing.T) {v := float32(2.71)result := marshalParam(&v)assertEqual(t, float32(2.71), result)})
t.Run("*bool nil", func(t *testing.T) {var p *boolresult := marshalParam(p)assertNil(t, result)})t.Run("*bool with true", func(t *testing.T) {v := trueresult := marshalParam(&v)assertEqual(t, true, result)})t.Run("*bool with false", func(t *testing.T) {v := falseresult := marshalParam(&v)assertEqual(t, false, result)})
})t.Run("named type alias (like GainLevel)", func(t *testing.T) {type GainLevel stringg := GainLevel("medium")result := marshalParam(g)// Named type aliases fall through to default caseassertEqual(t, "medium", result)})t.Run("pointer to named type alias", func(t *testing.T) {type GainLevel stringg := GainLevel("high")// Pointer to named type also falls through to defaultresult := marshalParam(&g)// Should serialize the value, not the pointer addressassertEqual(t, "high", result)
if v == nil {return nil}return *vcase int:return vcase *int:if v == nil {return nil}return *vcase int8:return vcase *int8:if v == nil {return nil}return *vcase int16:return vcase *int16:if v == nil {return nil}return *vcase int32:return vcase *int32:if v == nil {return nil}return *vcase int64:return vcase *int64:if v == nil {return nil}return *vcase uint:return vcase *uint:
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
case uint8:return vcase *uint8:if v == nil {return nil}return *vcase uint16:return vcase *uint16:if v == nil {return nil}return *vcase uint32:return vcase *uint32:if v == nil {return nil}return *vcase uint64:return vcase *uint64:if v == nil {return nil}return *vcase float32:return vcase *float32:if v == nil {return nil}return *vcase float64:
// For other types, try to convert to string
// Handle pointer types via reflection (e.g., *GainLevel, *CustomType)rv := reflect.ValueOf(param)if rv.Kind() == reflect.Ptr {if rv.IsNil() {return nil}// Dereference and recursively marshal the underlying valuereturn marshalParam(rv.Elem().Interface())}// For other types, try to convert to string via fmt.Sprintf
## [2026-02-21] Fix Event Log Pointer Serialization**Bug fix:** Event log contained pointer addresses instead of values for nullable database fields (`*float64`, `*GainLevel`, etc.), causing replay failures.**Root cause:** `marshalParam()` in `db/tx_logger.go` didn't handle pointer types for numeric values or named type aliases (like `db.GainLevel`). These fell through to `fmt.Sprintf("%v", pointer)` which printed memory addresses like `"0x38a7bfb12078"`.**Example of corrupted data:**```json"parameters": ["file_id", "2025-05-18T18:30:00+13:00", "248AB50053AB1B4A", "0x38a7bfb12078", "0x38a7bfb12088", "0x38a7bfb12090"]```The last three values should have been `gain`, `battery_v`, `temp_c` but were pointer addresses.
**Fixed:**- `db/tx_logger.go` — Added explicit cases for all pointer types (`*int`, `*int64`, `*float64`, `*bool`, etc.)- `db/tx_logger.go` — Added reflection-based fallback in default case to handle pointer-to-named-type (e.g., `*GainLevel`)- `cmd/replay.go` — Increased `bufio.Scanner` buffer from 64KB to 10MB to handle large event lines (6,672 queries in one transaction = 2.1 MB JSON line)**Tests added:**- `db/tx_logger_test.go` — Tests for `*int`, `*int64`, `*float64`, `*float32`, `*bool` with nil and value cases- `db/tx_logger_test.go` — Tests for named type aliases and pointer-to-named-type---