diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out index 34db7f5..c844d88 100644 *** a/contrib/hstore/expected/hstore.out --- b/contrib/hstore/expected/hstore.out *************** select count(*) from testhstore where h *** 1458,1460 **** --- 1458,1575 ---- 1 (1 row) + -- hstore_to_json() + select hstore_to_json(''::hstore); + hstore_to_json + ---------------- + {} + (1 row) + + select hstore_to_json('a=>b'::hstore); + hstore_to_json + ---------------- + {"a":"b"} + (1 row) + + select hstore_to_json('a=>b'); + hstore_to_json + ---------------- + {"a":"b"} + (1 row) + + select hstore_to_json('a=>b,x=>y'::hstore); + hstore_to_json + -------------------- + {"a":"b", "x":"y"} + (1 row) + + select hstore_to_json('foo=>bar'::hstore); + hstore_to_json + ---------------- + {"foo":"bar"} + (1 row) + + select hstore_to_json('foo=>" "'::hstore); + hstore_to_json + ---------------- + {"foo":" "} + (1 row) + + select hstore_to_json('foo=>"0"'::hstore); + hstore_to_json + ---------------- + {"foo":"0"} + (1 row) + + select hstore_to_json('foo=>"0 0"'::hstore); + hstore_to_json + ---------------- + {"foo":"0 0"} + (1 row) + + select hstore_to_json('foo=>"0:0"'::hstore); + hstore_to_json + ---------------- + {"foo":"0:0"} + (1 row) + + select hstore_to_json('foo' => '0:0'); + hstore_to_json + ---------------- + {"foo":"0:0"} + (1 row) + + select hstore_to_json('aa=>null'::hstore); + hstore_to_json + ---------------- + {"aa":null} + (1 row) + + select hstore_to_json('aa=>NuLl'::hstore); + hstore_to_json + ---------------- + {"aa":null} + (1 row) + + select hstore_to_json('aa=>"NuLl"'::hstore); + hstore_to_json + ---------------- + {"aa":"NuLl"} + (1 row) + + select hstore_to_json('\\=a=>q=w'::hstore); + hstore_to_json + ---------------- + {"=a":"q=w"} + (1 row) + + select hstore_to_json('"=a"=>q\\=w'::hstore); + hstore_to_json + ---------------- + {"=a":"q=w"} + (1 row) + + select hstore_to_json('"\\"a"=>q>w'::hstore); + hstore_to_json + ---------------- + {"\"a":"q>w"} + (1 row) + + select hstore_to_json('\\"a=>q"w'::hstore); + hstore_to_json + ---------------- + {"\"a":"q\"w"} + (1 row) + + select hstore_to_json(''::hstore); + hstore_to_json + ---------------- + {} + (1 row) + + select hstore_to_json(' '::hstore); + hstore_to_json + ---------------- + {} + (1 row) + diff --git a/contrib/hstore/hstore.sql.in b/contrib/hstore/hstore.sql.in index 972557a..d806a84 100644 *** a/contrib/hstore/hstore.sql.in --- b/contrib/hstore/hstore.sql.in *************** RETURNS cstring *** 15,20 **** --- 15,25 ---- AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; + CREATE OR REPLACE FUNCTION hstore_to_json(hstore) + RETURNS text + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + CREATE OR REPLACE FUNCTION hstore_recv(internal) RETURNS hstore AS 'MODULE_PATHNAME' diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c index 31303e4..2bf5416 100644 *** a/contrib/hstore/hstore_io.c --- b/contrib/hstore/hstore_io.c *************** *** 12,17 **** --- 12,18 ---- #include "libpq/pqformat.h" #include "utils/lsyscache.h" #include "utils/typcache.h" + #include "utils/builtins.h" #include "hstore.h" *************** hstore_send(PG_FUNCTION_ARGS) *** 1208,1210 **** --- 1209,1291 ---- PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } + + PG_FUNCTION_INFO_V1(hstore_to_json); + Datum hstore_to_json(PG_FUNCTION_ARGS); + Datum + hstore_to_json(PG_FUNCTION_ARGS) + { + HStore *in = PG_GETARG_HS(0); + int buflen, + i; + int count = HS_COUNT(in); + char *out, + *ptr; + char *base = STRPTR(in); + HEntry *entries = ARRPTR(in); + + if (count == 0) + { + out = ptr = palloc(3); + *ptr++ = '{'; + *ptr++ = '}'; + *ptr = '\0'; + PG_RETURN_CSTRING(cstring_to_text(out)); + } + + buflen = 0; + + /* + * this loop overestimates due to pessimistic assumptions about + * escaping, so very large hstore values can't be output. this + * could be fixed, but many other data types probably have the + * same issue. This replaced code that used the original varlena + * size for calculations, which was wrong in some subtle ways. + */ + + /* include { and } */ + buflen += 2; + for (i = 0; i < count; i++) + { + /* include "" and => and comma-space */ + buflen += 6 + 1 * HS_KEYLEN(entries,i); + /* include "" only if nonnull */ + buflen += 2 + (HS_VALISNULL(entries,i) + ? 2 + : 2 * HS_VALLEN(entries,i)); + } + + out = ptr = palloc(buflen); + + *ptr++ = '{'; + for (i = 0; i < count; i++) + { + *ptr++ = '"'; + ptr = cpw(ptr, HS_KEY(entries,base,i), HS_KEYLEN(entries,i)); + *ptr++ = '"'; + *ptr++ = ':'; + if (HS_VALISNULL(entries,i)) + { + *ptr++ = 'n'; + *ptr++ = 'u'; + *ptr++ = 'l'; + *ptr++ = 'l'; + } + else + { + *ptr++ = '"'; + ptr = cpw(ptr, HS_VAL(entries,base,i), HS_VALLEN(entries,i)); + *ptr++ = '"'; + } + + if (i + 1 != count) + { + *ptr++ = ','; + *ptr++ = ' '; + } + } + *ptr++ = '}'; + *ptr = '\0'; + + PG_RETURN_TEXT_P(cstring_to_text(out)); + } diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql index a88ff1d..073c9e3 100644 *** a/contrib/hstore/sql/hstore.sql --- b/contrib/hstore/sql/hstore.sql *************** set enable_seqscan=off; *** 336,338 **** --- 336,361 ---- select count(*) from testhstore where h #># 'p=>1'; select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t'; + + -- hstore_to_json() + select hstore_to_json(''::hstore); + select hstore_to_json('a=>b'::hstore); + select hstore_to_json('a=>b'); + select hstore_to_json('a=>b,x=>y'::hstore); + select hstore_to_json('foo=>bar'::hstore); + select hstore_to_json('foo=>" "'::hstore); + select hstore_to_json('foo=>"0"'::hstore); + select hstore_to_json('foo=>"0 0"'::hstore); + select hstore_to_json('foo=>"0:0"'::hstore); + select hstore_to_json('foo' => '0:0'); + + select hstore_to_json('aa=>null'::hstore); + select hstore_to_json('aa=>NuLl'::hstore); + select hstore_to_json('aa=>"NuLl"'::hstore); + + select hstore_to_json('\\=a=>q=w'::hstore); + select hstore_to_json('"=a"=>q\\=w'::hstore); + select hstore_to_json('"\\"a"=>q>w'::hstore); + select hstore_to_json('\\"a=>q"w'::hstore); + select hstore_to_json(''::hstore); + select hstore_to_json(' '::hstore); diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml index c7a9d92..8378f8f 100644 *** a/doc/src/sgml/hstore.sgml --- b/doc/src/sgml/hstore.sgml *************** b *** 322,327 **** --- 322,335 ---- + hstore_to_json(hstore) + text + Convert hstore to text in JSON format + hstore_to_json('a=>1,b=>2') + {"a":"1","b":"2"} + + + each(hstore) setof(key text, value text) get hstore's keys and values as a set