package pgx_test import ( "github.com/jackc/pgx" "testing" ) func TestHstoreTranscode(t *testing.T) { t.Parallel() conn := mustConnect(t, *defaultConnConfig) defer closeConn(t, conn) type test struct { hstore pgx.Hstore description string } tests := []test{ {pgx.Hstore{}, "empty"}, {pgx.Hstore{"foo": "bar"}, "single key/value"}, {pgx.Hstore{"foo": "bar", "baz": "quz"}, "multiple key/values"}, {pgx.Hstore{"NULL": "bar"}, `string "NULL" key`}, {pgx.Hstore{"foo": "NULL"}, `string "NULL" value`}, } specialStringTests := []struct { input string description string }{ {`"`, `double quote (")`}, {`'`, `single quote (')`}, {`\`, `backslash (\)`}, {`\\`, `multiple backslashes (\\)`}, {`=>`, `separator (=>)`}, {` `, `space`}, {`\ / / \\ => " ' " '`, `multiple special characters`}, } for _, sst := range specialStringTests { tests = append(tests, test{pgx.Hstore{sst.input + "foo": "bar"}, "key with " + sst.description + " at beginning"}) tests = append(tests, test{pgx.Hstore{"foo" + sst.input + "foo": "bar"}, "key with " + sst.description + " in middle"}) tests = append(tests, test{pgx.Hstore{"foo" + sst.input: "bar"}, "key with " + sst.description + " at end"}) tests = append(tests, test{pgx.Hstore{sst.input: "bar"}, "key is " + sst.description}) tests = append(tests, test{pgx.Hstore{"foo": sst.input + "bar"}, "value with " + sst.description + " at beginning"}) tests = append(tests, test{pgx.Hstore{"foo": "bar" + sst.input + "bar"}, "value with " + sst.description + " in middle"}) tests = append(tests, test{pgx.Hstore{"foo": "bar" + sst.input}, "value with " + sst.description + " at end"}) tests = append(tests, test{pgx.Hstore{"foo": sst.input}, "value is " + sst.description}) } for _, tt := range tests { var result pgx.Hstore err := conn.QueryRow("select $1::hstore", tt.hstore).Scan(&result) if err != nil { t.Errorf(`%s: QueryRow.Scan returned an error: %v`, tt.description, err) } for key, inValue := range tt.hstore { outValue, ok := result[key] if ok { if inValue != outValue { t.Errorf(`%s: Key %s mismatch - expected %s, received %s`, tt.description, key, inValue, outValue) } } else { t.Errorf(`%s: Missing key %s`, tt.description, key) } } ensureConnValid(t, conn) } } func TestNullHstoreTranscode(t *testing.T) { t.Parallel() conn := mustConnect(t, *defaultConnConfig) defer closeConn(t, conn) type test struct { nullHstore pgx.NullHstore description string } tests := []test{ {pgx.NullHstore{}, "null"}, {pgx.NullHstore{Valid: true}, "empty"}, {pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "bar", Valid: true}}, Valid: true}, "single key/value"}, {pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "bar", Valid: true}, "baz": pgx.NullString{String: "quz", Valid: true}}, Valid: true}, "multiple key/values"}, {pgx.NullHstore{ Hstore: map[string]pgx.NullString{"NULL": pgx.NullString{String: "bar", Valid: true}}, Valid: true}, `string "NULL" key`}, {pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "NULL", Valid: true}}, Valid: true}, `string "NULL" value`}, {pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "", Valid: false}}, Valid: true}, `NULL value`}, } specialStringTests := []struct { input string description string }{ {`"`, `double quote (")`}, {`'`, `single quote (')`}, {`\`, `backslash (\)`}, {`\\`, `multiple backslashes (\\)`}, {`=>`, `separator (=>)`}, {` `, `space`}, {`\ / / \\ => " ' " '`, `multiple special characters`}, } for _, sst := range specialStringTests { tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{sst.input + "foo": pgx.NullString{String: "bar", Valid: true}}, Valid: true}, "key with " + sst.description + " at beginning"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo" + sst.input + "foo": pgx.NullString{String: "bar", Valid: true}}, Valid: true}, "key with " + sst.description + " in middle"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo" + sst.input: pgx.NullString{String: "bar", Valid: true}}, Valid: true}, "key with " + sst.description + " at end"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{sst.input: pgx.NullString{String: "bar", Valid: true}}, Valid: true}, "key is " + sst.description}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: sst.input + "bar", Valid: true}}, Valid: true}, "value with " + sst.description + " at beginning"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "bar" + sst.input + "bar", Valid: true}}, Valid: true}, "value with " + sst.description + " in middle"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: "bar" + sst.input, Valid: true}}, Valid: true}, "value with " + sst.description + " at end"}) tests = append(tests, test{pgx.NullHstore{ Hstore: map[string]pgx.NullString{"foo": pgx.NullString{String: sst.input, Valid: true}}, Valid: true}, "value is " + sst.description}) } for _, tt := range tests { var result pgx.NullHstore err := conn.QueryRow("select $1::hstore", tt.nullHstore).Scan(&result) if err != nil { t.Errorf(`%s: QueryRow.Scan returned an error: %v`, tt.description, err) } if result.Valid != tt.nullHstore.Valid { t.Errorf(`%s: Valid mismatch - expected %v, received %v`, tt.description, tt.nullHstore.Valid, result.Valid) } for key, inValue := range tt.nullHstore.Hstore { outValue, ok := result.Hstore[key] if ok { if inValue != outValue { t.Errorf(`%s: Key %s mismatch - expected %v, received %v`, tt.description, key, inValue, outValue) } } else { t.Errorf(`%s: Missing key %s`, tt.description, key) } } ensureConnValid(t, conn) } }