From eab316e200b1041f0c4f1ca346b4d6db997ebb8e Mon Sep 17 00:00:00 2001 From: Evan Jones Date: Mon, 15 May 2023 20:48:35 -0400 Subject: [PATCH] pgtype.Hstore: Fix quoting of whitespace; Add test Before this change, the Hstore text protocol did not quote keys or values containing non-space whitespace ("\r\n\v\t"). This causes inserts with these values to fail with errors like: ERROR: Syntax error near "r" at position 17 (SQLSTATE XX000) The previous version also quoted curly braces ("{}"), but they don't seem to require quoting. It is possible that it would be easier to just always quote the values, which is what Postgres does when encoding its text protocol, but this is a smaller change. --- pgtype/hstore.go | 8 +++++++- pgtype/hstore_test.go | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pgtype/hstore.go b/pgtype/hstore.go index 52fe23060..f3ae78380 100644 --- a/pgtype/hstore.go +++ b/pgtype/hstore.go @@ -272,9 +272,15 @@ func (c HstoreCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) ( } func quoteHstoreElementIfNeeded(src string) string { - if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || strings.ContainsAny(src, ` {},"\=>`) { + // Double-quote keys and values that include whitespace, commas, =s or >s. To include a double + // quote or a backslash in a key or value, escape it with a backslash. + // From: https://www.postgresql.org/docs/current/hstore.html + // whitespace appears to be defined as the isspace() C function: \t\n\v\f\r\n and space + const quoteRequiredChars = `,"\=> ` + "\t\n\v\f\r" + if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || strings.ContainsAny(src, quoteRequiredChars) { return quoteArrayElement(src) } + return src } diff --git a/pgtype/hstore_test.go b/pgtype/hstore_test.go index 8ad5884a8..3d3e722e1 100644 --- a/pgtype/hstore_test.go +++ b/pgtype/hstore_test.go @@ -122,6 +122,12 @@ func TestHstoreCodec(t *testing.T) { `=>`, ` `, `\ / / \\ => " ' " '`, + "line1\nline2", + "tab\tafter", + "vtab\vafter", + "form\\ffeed", + "carriage\rreturn", + "curly{}braces", } for _, s := range specialStrings { // Special key values