From 2a86501e866c6580ecdb3cedde7e23af44e418a3 Mon Sep 17 00:00:00 2001 From: Evan Jones Date: Wed, 10 May 2023 22:33:34 -0400 Subject: [PATCH] Fix hstore NULL versus empty When running queries with the hstore type registered, and with simple mode queries, the scan implementation does not correctly distinguish between NULL and empty. Fix the implementation and add a test to verify this. --- pgtype/hstore.go | 4 ++-- pgtype/hstore_test.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pgtype/hstore.go b/pgtype/hstore.go index 4743643e5..bb55331fb 100644 --- a/pgtype/hstore.go +++ b/pgtype/hstore.go @@ -174,7 +174,7 @@ func (scanPlanBinaryHstoreToHstoreScanner) Scan(src []byte, dst any) error { scanner := (dst).(HstoreScanner) if src == nil { - return scanner.ScanHstore(Hstore{}) + return scanner.ScanHstore(Hstore(nil)) } rp := 0 @@ -234,7 +234,7 @@ func (scanPlanTextAnyToHstoreScanner) Scan(src []byte, dst any) error { scanner := (dst).(HstoreScanner) if src == nil { - return scanner.ScanHstore(Hstore{}) + return scanner.ScanHstore(Hstore(nil)) } keys, values, err := parseHstore(string(src)) diff --git a/pgtype/hstore_test.go b/pgtype/hstore_test.go index f8684bf70..9c20ef5cc 100644 --- a/pgtype/hstore_test.go +++ b/pgtype/hstore_test.go @@ -2,6 +2,7 @@ package pgtype_test import ( "context" + "reflect" "testing" "github.com/jackc/pgx/v5" @@ -179,4 +180,26 @@ func TestHstoreCodec(t *testing.T) { } pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, pgxtest.KnownOIDQueryExecModes, "hstore", tests) + + // scan empty and NULL: should be different + pgxtest.RunWithQueryExecModes(context.Background(), t, ctr, pgxtest.AllQueryExecModes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { + h := pgtype.Hstore{"should_be_erased": nil} + err := conn.QueryRow(ctx, `select cast(null as hstore)`).Scan(&h) + if err != nil { + t.Fatal(err) + } + expectedNil := pgtype.Hstore(nil) + if !reflect.DeepEqual(h, expectedNil) { + t.Errorf("plain conn.Scan failed expectedNil=%#v actual=%#v", expectedNil, h) + } + + err = conn.QueryRow(ctx, `select cast('' as hstore)`).Scan(&h) + if err != nil { + t.Fatal(err) + } + expectedEmpty := pgtype.Hstore{} + if !reflect.DeepEqual(h, expectedEmpty) { + t.Errorf("plain conn.Scan failed expectedEmpty=%#v actual=%#v", expectedEmpty, h) + } + }) }