aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Bossart <nathan@postgresql.org>2023-12-08 13:39:08 -0600
committerNathan Bossart <nathan@postgresql.org>2023-12-08 13:39:08 -0600
commitdc3f9bc549d4cc8cb1d4030bdc94e788d70fdcfe (patch)
treee37afcf8a109d3f93f7f5f77bdda822dabf466a5
parent867dd2dc8704e58636b8599f61ada3d83c7e6473 (diff)
downloadpostgresql-dc3f9bc549d4cc8cb1d4030bdc94e788d70fdcfe.tar.gz
postgresql-dc3f9bc549d4cc8cb1d4030bdc94e788d70fdcfe.zip
Micro-optimize JSONTYPE_NUMERIC code path in json.c.
This commit does the following: * In datum_to_json_internal(), the call to IsValidJsonNumber() is replaced with simplified validation code. This avoids an extra call to strlen() in this path, and it avoids validating the entire string (which is okay since we know we're dealing with a numeric data type's output). * In datum_to_json_internal(), the call to escape_json() in the JSONTYPE_NUMERIC path is replaced with code that just surrounds the string with quotes. In passing, some other nearby calls to appendStringInfo() have been replaced with similar code to avoid unnecessary calls to vsnprintf(). * In composite_to_json(), the length of the separator is now determined at compile time to avoid unnecessary calls to strlen(). On my machine, this speeds up a benchmark for the proposed COPY TO (FORMAT json) command with many integers by upwards of 20%. There are likely other code paths that could be given a similar treatment, but that is left as a future exercise. Reviewed-by: Jeff Davis, Tom Lane, David Rowley, John Naylor Discussion: https://postgr.es/m/20231207231251.GB3359478%40nathanxps13
-rw-r--r--src/backend/utils/adt/json.c37
1 files changed, 29 insertions, 8 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 71ae53ff975..0778d8dec92 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -220,13 +220,22 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
outputstr = OidOutputFunctionCall(outfuncoid, val);
/*
- * Don't call escape_json for a non-key if it's a valid JSON
- * number.
+ * Don't quote a non-key if it's a valid JSON number (i.e., not
+ * "Infinity", "-Infinity", or "NaN"). Since we know this is a
+ * numeric data type's output, we simplify and open-code the
+ * validation for better performance.
*/
- if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
+ if (!key_scalar &&
+ ((*outputstr >= '0' && *outputstr <= '9') ||
+ (*outputstr == '-' &&
+ (outputstr[1] >= '0' && outputstr[1] <= '9'))))
appendStringInfoString(result, outputstr);
else
- escape_json(result, outputstr);
+ {
+ appendStringInfoChar(result, '"');
+ appendStringInfoString(result, outputstr);
+ appendStringInfoChar(result, '"');
+ }
pfree(outputstr);
break;
case JSONTYPE_DATE:
@@ -234,7 +243,9 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
char buf[MAXDATELEN + 1];
JsonEncodeDateTime(buf, val, DATEOID, NULL);
- appendStringInfo(result, "\"%s\"", buf);
+ appendStringInfoChar(result, '"');
+ appendStringInfoString(result, buf);
+ appendStringInfoChar(result, '"');
}
break;
case JSONTYPE_TIMESTAMP:
@@ -242,7 +253,9 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
char buf[MAXDATELEN + 1];
JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
- appendStringInfo(result, "\"%s\"", buf);
+ appendStringInfoChar(result, '"');
+ appendStringInfoString(result, buf);
+ appendStringInfoChar(result, '"');
}
break;
case JSONTYPE_TIMESTAMPTZ:
@@ -250,7 +263,9 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
char buf[MAXDATELEN + 1];
JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
- appendStringInfo(result, "\"%s\"", buf);
+ appendStringInfoChar(result, '"');
+ appendStringInfoString(result, buf);
+ appendStringInfoChar(result, '"');
}
break;
case JSONTYPE_JSON:
@@ -503,8 +518,14 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
int i;
bool needsep = false;
const char *sep;
+ int seplen;
+ /*
+ * We can avoid expensive strlen() calls by precalculating the separator
+ * length.
+ */
sep = use_line_feeds ? ",\n " : ",";
+ seplen = use_line_feeds ? strlen(",\n ") : strlen(",");
td = DatumGetHeapTupleHeader(composite);
@@ -533,7 +554,7 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
continue;
if (needsep)
- appendStringInfoString(result, sep);
+ appendBinaryStringInfo(result, sep, seplen);
needsep = true;
attname = NameStr(att->attname);