Re: Convert planner's AggInfo and AggTransInfo to Nodes - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: Convert planner's AggInfo and AggTransInfo to Nodes |
Date | |
Msg-id | 1528424.1658272135@sss.pgh.pa.us Whole thread Raw |
In response to | Re: Convert planner's AggInfo and AggTransInfo to Nodes (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-hackers |
I wrote: > * WRITE_OID_ARRAY and WRITE_BOOL_ARRAY needed extension to handle a null > array pointer. I think we should make all the WRITE_FOO_ARRAY macros > work alike, so I added that to all of them. I first tried to make the > rest work like WRITE_INDEX_ARRAY, but that failed because readfuncs.c > isn't expecting "<>" for an empty array; it's expecting nothing at > all. (Note there is no readfuncs equivalent to WRITE_INDEX_ARRAY.) > What I've done here is to change WRITE_INDEX_ARRAY to work like the > others and print nothing for an empty array, but I wonder if now > wouldn't be a good time to redefine the serialized representation > to be more robust. I'm imagining "<>" for a NULL array pointer and > "(item item item)" otherwise, allowing a cross-check that we're > getting the right number of items. Concretely, about like this. regards, tom lane diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2b85f97f39..fe97d559b6 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -16,6 +16,7 @@ #include <ctype.h> +#include "access/attnum.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "nodes/bitmapset.h" @@ -96,48 +97,30 @@ static void outChar(StringInfo str, char c); (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ outBitmapset(str, node->fldname)) +/* Write a variable-length array of AttrNumber */ #define WRITE_ATTRNUMBER_ARRAY(fldname, len) \ - do { \ - appendStringInfoString(str, " :" CppAsString(fldname) " "); \ - for (int i = 0; i < len; i++) \ - appendStringInfo(str, " %d", node->fldname[i]); \ - } while(0) + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + writeAttrNumberCols(str, node->fldname, len)) +/* Write a variable-length array of Oid */ #define WRITE_OID_ARRAY(fldname, len) \ - do { \ - appendStringInfoString(str, " :" CppAsString(fldname) " "); \ - for (int i = 0; i < len; i++) \ - appendStringInfo(str, " %u", node->fldname[i]); \ - } while(0) + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + writeOidCols(str, node->fldname, len)) -/* - * This macro supports the case that the field is NULL. For the other array - * macros, that is currently not needed. - */ +/* Write a variable-length array of Index */ #define WRITE_INDEX_ARRAY(fldname, len) \ - do { \ - appendStringInfoString(str, " :" CppAsString(fldname) " "); \ - if (node->fldname) \ - for (int i = 0; i < len; i++) \ - appendStringInfo(str, " %u", node->fldname[i]); \ - else \ - appendStringInfoString(str, "<>"); \ - } while(0) + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + writeIndexCols(str, node->fldname, len)) +/* Write a variable-length array of int */ #define WRITE_INT_ARRAY(fldname, len) \ - do { \ - appendStringInfoString(str, " :" CppAsString(fldname) " "); \ - for (int i = 0; i < len; i++) \ - appendStringInfo(str, " %d", node->fldname[i]); \ - } while(0) + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + writeIntCols(str, node->fldname, len)) +/* Write a variable-length array of bool */ #define WRITE_BOOL_ARRAY(fldname, len) \ - do { \ - appendStringInfoString(str, " :" CppAsString(fldname) " "); \ - for (int i = 0; i < len; i++) \ - appendStringInfo(str, " %s", booltostr(node->fldname[i])); \ - } while(0) - + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + writeBoolCols(str, node->fldname, len)) #define booltostr(x) ((x) ? "true" : "false") @@ -196,6 +179,37 @@ outChar(StringInfo str, char c) outToken(str, in); } +/* + * common implementation for scalar-array-writing functions + * + * The data format is either "<>" for a NULL pointer or "(item item item)". + * fmtstr must include a leading space. + * convfunc can be empty, or the name of a conversion macro or function. + */ +#define WRITE_SCALAR_ARRAY(fnname, datatype, fmtstr, convfunc) \ +static void \ +fnname(StringInfo str, const datatype *arr, int len) \ +{ \ + if (arr != NULL) \ + { \ + appendStringInfoChar(str, '('); \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, fmtstr, convfunc(arr[i])); \ + appendStringInfoChar(str, ')'); \ + } \ + else \ + appendStringInfoString(str, "<>"); \ +} + +WRITE_SCALAR_ARRAY(writeAttrNumberCols, AttrNumber, " %d",) +WRITE_SCALAR_ARRAY(writeOidCols, Oid, " %u",) +WRITE_SCALAR_ARRAY(writeIndexCols, Index, " %u",) +WRITE_SCALAR_ARRAY(writeIntCols, int, " %d",) +WRITE_SCALAR_ARRAY(writeBoolCols, bool, " %s", booltostr) + +/* + * Print a List. + */ static void _outList(StringInfo str, const List *node) { diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index a2391280be..c77c3984ca 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -502,97 +502,45 @@ 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) \ + 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) +READ_SCALAR_ARRAY(readIntCols, int, atoi) +READ_SCALAR_ARRAY(readBoolCols, bool, strtobool)
pgsql-hackers by date: