diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 256ef80..1ecc17a 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -60,6 +60,13 @@ typedef struct JsonbAggState
Oid val_output_func;
} JsonbAggState;
+typedef enum /* type categories for jsonb to string whitespace */
+{
+ JSONBWHITESPACE_DEFAULT, /* Default style with space between keys but no indentation */
+ JSONBWHITESPACE_INDENT, /* Extra indentation of four spaces for each nested level */
+ JSONBWHITESPACE_COMPACT /* Compact style without extra whitespace between keys */
+} JsonbWhitespaceStyle;
+
static inline Datum jsonb_from_cstring(char *json, int len);
static size_t checkStringLen(size_t len);
static void jsonb_in_object_start(void *pstate);
@@ -86,7 +93,7 @@ static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
Oid val_type, bool key_scalar);
static JsonbParseState *clone_parse_state(JsonbParseState *state);
-static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
+static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, JsonbWhitespaceStyle whitespace_style);
static void add_indent(StringInfo out, bool indent, int level);
/*
@@ -427,7 +434,7 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
char *
JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
{
- return JsonbToCStringWorker(out, in, estimated_len, false);
+ return JsonbToCStringWorker(out, in, estimated_len, JSONBWHITESPACE_DEFAULT);
}
/*
@@ -436,14 +443,23 @@ JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
char *
JsonbToCStringIndent(StringInfo out, JsonbContainer *in, int estimated_len)
{
- return JsonbToCStringWorker(out, in, estimated_len, true);
+ return JsonbToCStringWorker(out, in, estimated_len, JSONBWHITESPACE_INDENT);
+}
+
+/*
+ * same thing but in compact form (no extra whitespace)
+ */
+char *
+JsonbToCStringCompact(StringInfo out, JsonbContainer *in, int estimated_len)
+{
+ return JsonbToCStringWorker(out, in, estimated_len, JSONBWHITESPACE_COMPACT);
}
/*
* common worker for above two functions
*/
static char *
-JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent)
+JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, JsonbWhitespaceStyle whitespace_style)
{
bool first = true;
JsonbIterator *it;
@@ -452,8 +468,24 @@ JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool
int level = 0;
bool redo_switch = false;
- /* If we are indenting, don't add a space after a comma */
- int ispaces = indent ? 1 : 2;
+ bool indent = false;
+ char* prop_sep = ", "; /* Separator between successive properties */
+ char* key_value_sep = ": "; /* Separator between a key and it's value */
+ int prop_sep_len;
+ int key_value_sep_len;
+ if (whitespace_style == JSONBWHITESPACE_DEFAULT) {
+ // Use default separators
+ } else if (whitespace_style == JSONBWHITESPACE_INDENT) {
+ indent = true;
+ prop_sep = ",";
+ key_value_sep = ": ";
+ } else if (whitespace_style == JSONBWHITESPACE_COMPACT) {
+ indent = false;
+ prop_sep = ",";
+ key_value_sep = ":";
+ }
+ prop_sep_len = strlen(prop_sep);
+ key_value_sep_len = strlen(key_value_sep);
/*
* Don't indent the very first item. This gets set to the indent flag at
@@ -478,7 +510,7 @@ JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool
{
case WJB_BEGIN_ARRAY:
if (!first)
- appendBinaryStringInfo(out, ", ", ispaces);
+ appendBinaryStringInfo(out, prop_sep, prop_sep_len);
if (!v.val.array.rawScalar)
{
@@ -493,7 +525,7 @@ JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool
break;
case WJB_BEGIN_OBJECT:
if (!first)
- appendBinaryStringInfo(out, ", ", ispaces);
+ appendBinaryStringInfo(out, prop_sep, prop_sep_len);
add_indent(out, use_indent && !last_was_key, level);
appendStringInfoCharMacro(out, '{');
@@ -503,14 +535,14 @@ JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool
break;
case WJB_KEY:
if (!first)
- appendBinaryStringInfo(out, ", ", ispaces);
+ appendBinaryStringInfo(out, prop_sep, prop_sep_len);
first = true;
add_indent(out, use_indent, level);
/* json rules guarantee this is a string */
jsonb_put_escaped_value(out, &v);
- appendBinaryStringInfo(out, ": ", 2);
+ appendBinaryStringInfo(out, key_value_sep, key_value_sep_len);
type = JsonbIteratorNext(&it, &v, false);
if (type == WJB_VALUE)
@@ -532,7 +564,7 @@ JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool
break;
case WJB_ELEM:
if (!first)
- appendBinaryStringInfo(out, ", ", ispaces);
+ appendBinaryStringInfo(out, prop_sep, prop_sep_len);
first = false;
if (!raw_scalar)
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index fb149dc..0ab324b 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3353,6 +3353,22 @@ jsonb_pretty(PG_FUNCTION_ARGS)
}
/*
+ * SQL function jsonb_compact (jsonb)
+ *
+ * Compact-printed text for the jsonb
+ */
+Datum
+jsonb_compact(PG_FUNCTION_ARGS)
+{
+ Jsonb *jb = PG_GETARG_JSONB(0);
+ StringInfo str = makeStringInfo();
+
+ JsonbToCStringCompact(str, &jb->root, VARSIZE(jb));
+
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(str->data, str->len));
+}
+
+/*
* SQL function jsonb_concat (jsonb, jsonb)
*
* function for || operator
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bb539d4..0033071 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4880,6 +4880,8 @@ DATA(insert OID = 3305 ( jsonb_set PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4
DESCR("Set part of a jsonb");
DATA(insert OID = 3306 ( jsonb_pretty PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ ));
DESCR("Indented text from jsonb");
+DATA(insert OID = 3369 ( jsonb_compact PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_compact _null_ _null_ _null_ ));
+DESCR("Compact text from jsonb");
DATA(insert OID = 3579 ( jsonb_insert PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 3802 "3802 1009 3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_insert _null_ _null_ _null_ ));
DESCR("Insert value into a jsonb");
/* txid */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5d8e4a9..4ce4fab 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -397,6 +397,9 @@ extern Datum gin_triconsistent_jsonb_path(PG_FUNCTION_ARGS);
/* pretty printer, returns text */
extern Datum jsonb_pretty(PG_FUNCTION_ARGS);
+/* compact printer, returns text */
+extern Datum jsonb_compact(PG_FUNCTION_ARGS);
+
/* concatenation */
extern Datum jsonb_concat(PG_FUNCTION_ARGS);
@@ -435,6 +438,8 @@ extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
int estimated_len);
extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+extern char *JsonbToCStringCompact(StringInfo out, JsonbContainer *in,
+ int estimated_len);
#endif /* __JSONB_H__ */