/* * stolen from * contrib/hstore/hstore_io.c */ #include "postgres.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "lib/stringinfo.h" #include "utils/json.h" #include "utils/builtins.h" #include "json_extra.h" #include "hstore.h" /* * hstore_to_json_loose * * This is a heuristic conversion to json which treats * 't' and 'f' as booleans and strings that look like numbers as numbers, * as long as they don't start with a leading zero followed by another digit * (think zip codes or phone numbers starting with 0). */ PG_FUNCTION_INFO_V1(hstore_to_json_loose); Datum hstore_to_json_loose(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); bool is_number; StringInfo src, dst; if (count == 0) { out = palloc(1); *out = '\0'; PG_RETURN_TEXT_P(cstring_to_text(out)); } buflen = 3; /* * Formula adjusted slightly from the logic in hstore_out. * We have to take account of out treatment of booleans * to be a bit more pessimistic about the length of values. */ for (i = 0; i < count; i++) { /* include "" and colon-space and comma-space */ buflen += 6 + 2 * HS_KEYLEN(entries, i); /* include "" only if nonnull */ buflen += 3 + (HS_VALISNULL(entries, i) ? 1 : 2 * HS_VALLEN(entries, i)); } out = ptr = palloc(buflen); src = makeStringInfo(); dst = makeStringInfo(); *ptr++ = '{'; for (i = 0; i < count; i++) { resetStringInfo(src); resetStringInfo(dst); appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); escape_json(dst, src->data); strncpy(ptr, dst->data, dst->len); ptr += dst->len; *ptr++ = ':'; *ptr++ = ' '; resetStringInfo(dst); if (HS_VALISNULL(entries, i)) appendStringInfoString(dst,"null"); /* guess that values of 't' or 'f' are booleans */ else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') appendStringInfoString(dst,"true"); else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') appendStringInfoString(dst,"false"); else { is_number = false; resetStringInfo(src); appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); /* * don't treat something with a leading zero followed by another digit as numeric - * could be a zip code or similar */ if (src->len > 0 && (src->data[0] != '0' || !isdigit(src->data[1])) && strspn(src->data,"+-0123456789Ee.") == src->len) { /* might be a number. See if we can input it as a numeric value*/ char * endptr; long longres = strtol(src->data,&endptr,10); if (*endptr == '\0') { /* strol man page says this means the whole string is valid */ is_number = true; } else { /* not an int - try a double */ double doubleres = strtod(src->data,&endptr); if (*endptr == '\0') is_number = true; else if (false) { /* shut the compiler up about unused variables */ longres = (long) doubleres; longres = longres / 2; } } } if (is_number) appendBinaryStringInfo(dst,src->data, src->len); else escape_json(dst, src->data); } strncpy(ptr, dst->data, dst->len); ptr += dst->len; if (i + 1 != count) { *ptr++ = ','; *ptr++ = ' '; } } *ptr++ = '}'; *ptr = '\0'; PG_RETURN_TEXT_P(cstring_to_text(out)); } PG_FUNCTION_INFO_V1(hstore_to_json); 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); StringInfo src, dst; if (count == 0) { out = palloc(1); *out = '\0'; PG_RETURN_TEXT_P(cstring_to_text(out)); } buflen = 3; /* * Formula adjusted slightly from the logic in hstore_out. * We have to take account of out treatment of booleans * to be a bit more pessimistic about the length of values. */ for (i = 0; i < count; i++) { /* include "" and colon-space and comma-space */ buflen += 6 + 2 * HS_KEYLEN(entries, i); /* include "" only if nonnull */ buflen += 3 + (HS_VALISNULL(entries, i) ? 1 : 2 * HS_VALLEN(entries, i)); } out = ptr = palloc(buflen); src = makeStringInfo(); dst = makeStringInfo(); *ptr++ = '{'; for (i = 0; i < count; i++) { resetStringInfo(src); resetStringInfo(dst); appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); escape_json(dst, src->data); strncpy(ptr, dst->data, dst->len); ptr += dst->len; *ptr++ = ':'; *ptr++ = ' '; resetStringInfo(dst); if (HS_VALISNULL(entries, i)) appendStringInfoString(dst,"null"); else { resetStringInfo(src); appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); escape_json(dst, src->data); } strncpy(ptr, dst->data, dst->len); ptr += dst->len; if (i + 1 != count) { *ptr++ = ','; *ptr++ = ' '; } } *ptr++ = '}'; *ptr = '\0'; PG_RETURN_TEXT_P(cstring_to_text(out)); }