aboutsummaryrefslogtreecommitdiff
path: root/src/backend/nodes/readfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-07-20 13:04:33 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2022-07-20 13:04:33 -0400
commit2d04277121f2f8e18b65190f0dfa8ca97e9c29cb (patch)
tree7e71eab88b213e02699d836ece9adadbcbb81896 /src/backend/nodes/readfuncs.c
parentf77ff083350eb5a2625a4dbfca61d15b66c4918b (diff)
downloadpostgresql-2d04277121f2f8e18b65190f0dfa8ca97e9c29cb.tar.gz
postgresql-2d04277121f2f8e18b65190f0dfa8ca97e9c29cb.zip
Make serialization of Nodes' scalar-array fields more robust.
When the ability to print variable-length-array fields was first added to outfuncs.c, there was no corresponding read capability, as it was used only for debug dumps of planner-internal Nodes. Not a lot of thought seems to have been put into the output format: it's just the space-separated array elements and nothing else. Later such fields appeared in Plan nodes, and still later we grew read support so that Plans could be transferred to parallel workers, but the original text format wasn't rethought. It seems inadequate to me because (a) no cross-check is possible that we got the right number of array entries, (b) we can't tell the difference between a NULL pointer and a zero-length array, and (c) except for WRITE_INDEX_ARRAY, we'd crash if a non-zero length is specified when the pointer is NULL, a situation that can arise in some fields that we currently conveniently avoid printing. Since we're currently in a campaign to make the Node infrastructure generally more it-just-works-without-thinking-about-it, now seems like a good time to improve this. Let's adopt a format similar to that used for Lists, that is "<>" for a NULL pointer or "(item item item)" otherwise. Also retool the code to not have so many copies of the identical logic. I bumped catversion out of an abundance of caution, although I think that we don't use any such array fields in Nodes that can get into the catalogs. Discussion: https://postgr.es/m/1528424.1658272135@sss.pgh.pa.us
Diffstat (limited to 'src/backend/nodes/readfuncs.c')
-rw-r--r--src/backend/nodes/readfuncs.c128
1 files changed, 39 insertions, 89 deletions
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index a2391280bea..bee62fc15cd 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -502,97 +502,47 @@ readDatum(bool typbyval)
}
/*
- * readAttrNumberCols
- */
-AttrNumber *
-readAttrNumberCols(int numCols)
-{
- int tokenLength,
- i;
- const char *token;
- AttrNumber *attr_vals;
-
- if (numCols <= 0)
- return NULL;
-
- attr_vals = (AttrNumber *) palloc(numCols * sizeof(AttrNumber));
- for (i = 0; i < numCols; i++)
- {
- token = pg_strtok(&tokenLength);
- attr_vals[i] = atoi(token);
- }
-
- return attr_vals;
-}
-
-/*
- * readOidCols
- */
-Oid *
-readOidCols(int numCols)
-{
- int tokenLength,
- i;
- const char *token;
- Oid *oid_vals;
-
- if (numCols <= 0)
- return NULL;
-
- oid_vals = (Oid *) palloc(numCols * sizeof(Oid));
- for (i = 0; i < numCols; i++)
- {
- token = pg_strtok(&tokenLength);
- oid_vals[i] = atooid(token);
- }
-
- return oid_vals;
-}
-
-/*
- * readIntCols
+ * common implementation for scalar-array-reading functions
+ *
+ * The data format is either "<>" for a NULL pointer (in which case numCols
+ * is ignored) or "(item item item)" where the number of items must equal
+ * numCols. The convfunc must be okay with stopping at whitespace or a
+ * right parenthesis, since pg_strtok won't null-terminate the token.
*/
-int *
-readIntCols(int numCols)
-{
- int tokenLength,
- i;
- const char *token;
- int *int_vals;
-
- if (numCols <= 0)
- return NULL;
-
- int_vals = (int *) palloc(numCols * sizeof(int));
- for (i = 0; i < numCols; i++)
- {
- token = pg_strtok(&tokenLength);
- int_vals[i] = atoi(token);
- }
-
- return int_vals;
+#define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \
+datatype * \
+fnname(int numCols) \
+{ \
+ datatype *vals; \
+ READ_TEMP_LOCALS(); \
+ token = pg_strtok(&length); \
+ if (token == NULL) \
+ elog(ERROR, "incomplete scalar array"); \
+ if (length == 0) \
+ return NULL; /* it was "<>", so return NULL pointer */ \
+ if (length != 1 || token[0] != '(') \
+ elog(ERROR, "unrecognized token: \"%.*s\"", length, token); \
+ vals = (datatype *) palloc(numCols * sizeof(datatype)); \
+ for (int i = 0; i < numCols; i++) \
+ { \
+ token = pg_strtok(&length); \
+ if (token == NULL || token[0] == ')') \
+ elog(ERROR, "incomplete scalar array"); \
+ vals[i] = convfunc(token); \
+ } \
+ token = pg_strtok(&length); \
+ if (token == NULL || length != 1 || token[0] != ')') \
+ elog(ERROR, "incomplete scalar array"); \
+ return vals; \
}
/*
- * readBoolCols
+ * Note: these functions are exported in nodes.h for possible use by
+ * extensions, so don't mess too much with their names or API.
*/
-bool *
-readBoolCols(int numCols)
-{
- int tokenLength,
- i;
- const char *token;
- bool *bool_vals;
-
- if (numCols <= 0)
- return NULL;
-
- bool_vals = (bool *) palloc(numCols * sizeof(bool));
- for (i = 0; i < numCols; i++)
- {
- token = pg_strtok(&tokenLength);
- bool_vals[i] = strtobool(token);
- }
-
- return bool_vals;
-}
+READ_SCALAR_ARRAY(readAttrNumberCols, int16, atoi)
+READ_SCALAR_ARRAY(readOidCols, Oid, atooid)
+/* outfuncs.c has writeIndexCols, but we don't yet need that here */
+/* READ_SCALAR_ARRAY(readIndexCols, Index, atoui) */
+READ_SCALAR_ARRAY(readIntCols, int, atoi)
+READ_SCALAR_ARRAY(readBoolCols, bool, strtobool)