From 87c9c2571c8146f1594830072253ba39e62ccbc8 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 14 Mar 2020 15:31:44 -0400 Subject: [PATCH] Rearrange pseudotypes.c to get rid of duplicative code. Commit a5954de10 replaced a lot of manually-coded stub I/O routines with code generated by macros. That was a good idea but it didn't go far enough, because there were still manually-coded stub input routines for types that had live output routines. Refactor the macro so that we can generate just a stub input routine at need. Also create similar macros to generate stub binary I/O routines, since we have some of those now. The only stub functions that remain hand-coded are shell_in() and shell_out(), which need to be separate because they use different error messages. While here, rearrange the commentary to discuss each type not each function. This provides a better way to explain the *why* of which types need which support, rather than just duplicatively annotating the functions. Discussion: https://postgr.es/m/24137.1584139352@sss.pgh.pa.us --- src/backend/utils/adt/pseudotypes.c | 610 ++++++++++++---------------- 1 file changed, 254 insertions(+), 356 deletions(-) diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 4653fc33e6..9eee03c143 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -29,362 +29,11 @@ /* - * cstring_in - input routine for pseudo-type CSTRING. - * - * We might as well allow this to support constructs like "foo_in('blah')". + * These macros generate input and output functions for a pseudo-type that + * will reject all input and output attempts. (But for some types, only + * the input function need be dummy.) */ -Datum -cstring_in(PG_FUNCTION_ARGS) -{ - char *str = PG_GETARG_CSTRING(0); - - PG_RETURN_CSTRING(pstrdup(str)); -} - -/* - * cstring_out - output routine for pseudo-type CSTRING. - * - * We allow this mainly so that "SELECT some_output_function(...)" does - * what the user will expect. - */ -Datum -cstring_out(PG_FUNCTION_ARGS) -{ - char *str = PG_GETARG_CSTRING(0); - - PG_RETURN_CSTRING(pstrdup(str)); -} - -/* - * cstring_recv - binary input routine for pseudo-type CSTRING. - */ -Datum -cstring_recv(PG_FUNCTION_ARGS) -{ - StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); - char *str; - int nbytes; - - str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); - PG_RETURN_CSTRING(str); -} - -/* - * cstring_send - binary output routine for pseudo-type CSTRING. - */ -Datum -cstring_send(PG_FUNCTION_ARGS) -{ - char *str = PG_GETARG_CSTRING(0); - StringInfoData buf; - - pq_begintypsend(&buf); - pq_sendtext(&buf, str, strlen(str)); - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); -} - -/* - * anyarray_in - input routine for pseudo-type ANYARRAY. - */ -Datum -anyarray_in(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "anyarray"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * anyarray_out - output routine for pseudo-type ANYARRAY. - * - * We may as well allow this, since array_out will in fact work. - */ -Datum -anyarray_out(PG_FUNCTION_ARGS) -{ - return array_out(fcinfo); -} - -/* - * anyarray_recv - binary input routine for pseudo-type ANYARRAY. - * - * XXX this could actually be made to work, since the incoming array - * data will contain the element type OID. Need to think through - * type-safety issues before allowing it, however. - */ -Datum -anyarray_recv(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "anyarray"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * anyarray_send - binary output routine for pseudo-type ANYARRAY. - * - * We may as well allow this, since array_send will in fact work. - */ -Datum -anyarray_send(PG_FUNCTION_ARGS) -{ - return array_send(fcinfo); -} - - -/* - * anyenum_in - input routine for pseudo-type ANYENUM. - */ -Datum -anyenum_in(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "anyenum"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * anyenum_out - output routine for pseudo-type ANYENUM. - * - * We may as well allow this, since enum_out will in fact work. - */ -Datum -anyenum_out(PG_FUNCTION_ARGS) -{ - return enum_out(fcinfo); -} - -/* - * anyrange_in - input routine for pseudo-type ANYRANGE. - */ -Datum -anyrange_in(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "anyrange"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * anyrange_out - output routine for pseudo-type ANYRANGE. - * - * We may as well allow this, since range_out will in fact work. - */ -Datum -anyrange_out(PG_FUNCTION_ARGS) -{ - return range_out(fcinfo); -} - -/* - * void_in - input routine for pseudo-type VOID. - * - * We allow this so that PL functions can return VOID without any special - * hack in the PL handler. Whatever value the PL thinks it's returning - * will just be ignored. - */ -Datum -void_in(PG_FUNCTION_ARGS) -{ - PG_RETURN_VOID(); /* you were expecting something different? */ -} - -/* - * void_out - output routine for pseudo-type VOID. - * - * We allow this so that "SELECT function_returning_void(...)" works. - */ -Datum -void_out(PG_FUNCTION_ARGS) -{ - PG_RETURN_CSTRING(pstrdup("")); -} - -/* - * void_recv - binary input routine for pseudo-type VOID. - * - * Note that since we consume no bytes, an attempt to send anything but - * an empty string will result in an "invalid message format" error. - */ -Datum -void_recv(PG_FUNCTION_ARGS) -{ - PG_RETURN_VOID(); -} - -/* - * void_send - binary output routine for pseudo-type VOID. - * - * We allow this so that "SELECT function_returning_void(...)" works - * even when binary output is requested. - */ -Datum -void_send(PG_FUNCTION_ARGS) -{ - StringInfoData buf; - - /* send an empty string */ - pq_begintypsend(&buf); - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); -} - -/* - * shell_in - input routine for "shell" types (those not yet filled in). - */ -Datum -shell_in(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of a shell type"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * shell_out - output routine for "shell" types. - */ -Datum -shell_out(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot display a value of a shell type"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - - -/* - * pg_node_tree_in - input routine for type PG_NODE_TREE. - * - * pg_node_tree isn't really a pseudotype --- it's real enough to be a table - * column --- but it presently has no operations of its own, and disallows - * input too, so its I/O functions seem to fit here as much as anywhere. - */ -Datum -pg_node_tree_in(PG_FUNCTION_ARGS) -{ - /* - * We disallow input of pg_node_tree values because the SQL functions that - * operate on the type are not secure against malformed input. - */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "pg_node_tree"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - - -/* - * pg_node_tree_out - output routine for type PG_NODE_TREE. - * - * The internal representation is the same as TEXT, so just pass it off. - */ -Datum -pg_node_tree_out(PG_FUNCTION_ARGS) -{ - return textout(fcinfo); -} - -/* - * pg_node_tree_recv - binary input routine for type PG_NODE_TREE. - */ -Datum -pg_node_tree_recv(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "pg_node_tree"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * pg_node_tree_send - binary output routine for type PG_NODE_TREE. - */ -Datum -pg_node_tree_send(PG_FUNCTION_ARGS) -{ - return textsend(fcinfo); -} - -/* - * pg_ddl_command_in - input routine for type PG_DDL_COMMAND. - * - * Like pg_node_tree, pg_ddl_command isn't really a pseudotype; it's here for - * the same reasons as that one. - */ -Datum -pg_ddl_command_in(PG_FUNCTION_ARGS) -{ - /* - * Disallow input of pg_ddl_command value. - */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "pg_ddl_command"))); - - PG_RETURN_VOID(); /* keep compiler quiet */ -} - -/* - * pg_ddl_command_out - output routine for type PG_DDL_COMMAND. - * - * We don't have any good way to output this type directly, so punt. - */ -Datum -pg_ddl_command_out(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot output a value of type %s", "pg_ddl_command"))); - - PG_RETURN_VOID(); -} - -/* - * pg_ddl_command_recv - binary input routine for type PG_DDL_COMMAND. - */ -Datum -pg_ddl_command_recv(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type %s", "pg_ddl_command"))); - - PG_RETURN_VOID(); -} - -/* - * pg_ddl_command_send - binary output routine for type PG_DDL_COMMAND. - */ -Datum -pg_ddl_command_send(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot output a value of type %s", "pg_ddl_command"))); - - PG_RETURN_VOID(); -} - - -/* - * Generate input and output functions for a pseudotype that will reject all - * input and output attempts. - */ -#define PSEUDOTYPE_DUMMY_IO_FUNCS(typname) \ -\ +#define PSEUDOTYPE_DUMMY_INPUT_FUNC(typname) \ Datum \ typname##_in(PG_FUNCTION_ARGS) \ { \ @@ -395,6 +44,11 @@ typname##_in(PG_FUNCTION_ARGS) \ PG_RETURN_VOID(); /* keep compiler quiet */ \ } \ \ +extern int no_such_variable + +#define PSEUDOTYPE_DUMMY_IO_FUNCS(typname) \ +PSEUDOTYPE_DUMMY_INPUT_FUNC(typname); \ +\ Datum \ typname##_out(PG_FUNCTION_ARGS) \ { \ @@ -407,14 +61,258 @@ typname##_out(PG_FUNCTION_ARGS) \ \ extern int no_such_variable +/* + * Likewise for binary send/receive functions. We don't bother with these + * at all for many pseudotypes, but some have them. (By convention, if + * a type has a send function it should have a receive function, even if + * that's only dummy.) + */ +#define PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname) \ +Datum \ +typname##_recv(PG_FUNCTION_ARGS) \ +{ \ + ereport(ERROR, \ + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ + errmsg("cannot accept a value of type %s", #typname))); \ +\ + PG_RETURN_VOID(); /* keep compiler quiet */ \ +} \ +\ +extern int no_such_variable + +#define PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(typname) \ +PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname); \ +\ +Datum \ +typname##_send(PG_FUNCTION_ARGS) \ +{ \ + ereport(ERROR, \ + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ + errmsg("cannot display a value of type %s", #typname))); \ +\ + PG_RETURN_VOID(); /* keep compiler quiet */ \ +} \ +\ +extern int no_such_variable + + +/* + * cstring + * + * cstring is marked as a pseudo-type because we don't want people using it + * in tables. But it's really a perfectly functional type, so provide + * a full set of working I/O functions for it. Among other things, this + * allows manual invocation of datatype I/O functions, along the lines of + * "SELECT foo_in('blah')" or "SELECT foo_out(some-foo-value)". + */ +Datum +cstring_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + + PG_RETURN_CSTRING(pstrdup(str)); +} + +Datum +cstring_out(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + + PG_RETURN_CSTRING(pstrdup(str)); +} + +Datum +cstring_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + char *str; + int nbytes; + + str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + PG_RETURN_CSTRING(str); +} + +Datum +cstring_send(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendtext(&buf, str, strlen(str)); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/* + * anyarray + * + * We need to allow output of anyarray so that, e.g., pg_statistic columns + * can be printed. Input has to be disallowed, however. + * + * XXX anyarray_recv could actually be made to work, since the incoming + * array data would contain the element type OID. It seems unlikely that + * it'd be sufficiently type-safe, though. + */ +PSEUDOTYPE_DUMMY_INPUT_FUNC(anyarray); +PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anyarray); + +Datum +anyarray_out(PG_FUNCTION_ARGS) +{ + return array_out(fcinfo); +} + +Datum +anyarray_send(PG_FUNCTION_ARGS) +{ + return array_send(fcinfo); +} + +/* + * anyenum + * + * We may as well allow output, since enum_out will in fact work. + */ +PSEUDOTYPE_DUMMY_INPUT_FUNC(anyenum); + +Datum +anyenum_out(PG_FUNCTION_ARGS) +{ + return enum_out(fcinfo); +} + +/* + * anyrange + * + * We may as well allow output, since range_out will in fact work. + */ +PSEUDOTYPE_DUMMY_INPUT_FUNC(anyrange); + +Datum +anyrange_out(PG_FUNCTION_ARGS) +{ + return range_out(fcinfo); +} + +/* + * void + * + * We support void_in so that PL functions can return VOID without any + * special hack in the PL handler. Whatever value the PL thinks it's + * returning will just be ignored. Conversely, void_out and void_send + * are needed so that "SELECT function_returning_void(...)" works. + */ +Datum +void_in(PG_FUNCTION_ARGS) +{ + PG_RETURN_VOID(); /* you were expecting something different? */ +} + +Datum +void_out(PG_FUNCTION_ARGS) +{ + PG_RETURN_CSTRING(pstrdup("")); +} + +Datum +void_recv(PG_FUNCTION_ARGS) +{ + /* + * Note that since we consume no bytes, an attempt to send anything but an + * empty string will result in an "invalid message format" error. + */ + PG_RETURN_VOID(); +} + +Datum +void_send(PG_FUNCTION_ARGS) +{ + StringInfoData buf; + + /* send an empty string */ + pq_begintypsend(&buf); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/* + * shell + * + * shell_in and shell_out are entered in pg_type for "shell" types + * (those not yet filled in). They should be unreachable, but we + * set them up just in case some code path tries to do I/O without + * having checked pg_type.typisdefined anywhere along the way. + */ +Datum +shell_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of a shell type"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +Datum +shell_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot display a value of a shell type"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * pg_node_tree + * + * pg_node_tree isn't really a pseudotype --- it's real enough to be a table + * column --- but it presently has no operations of its own, and disallows + * input too, so its I/O functions seem to fit here as much as anywhere. + * + * We must disallow input of pg_node_tree values because the SQL functions + * that operate on the type are not secure against malformed input. + * We do want to allow output, though. + */ +PSEUDOTYPE_DUMMY_INPUT_FUNC(pg_node_tree); +PSEUDOTYPE_DUMMY_RECEIVE_FUNC(pg_node_tree); + +Datum +pg_node_tree_out(PG_FUNCTION_ARGS) +{ + return textout(fcinfo); +} + +Datum +pg_node_tree_send(PG_FUNCTION_ARGS) +{ + return textsend(fcinfo); +} + +/* + * pg_ddl_command + * + * Like pg_node_tree, pg_ddl_command isn't really a pseudotype; it's here + * for the same reasons as that one. + * + * We don't have any good way to output this type directly, so punt + * for output as well as input. + */ +PSEUDOTYPE_DUMMY_IO_FUNCS(pg_ddl_command); +PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(pg_ddl_command); + + +/* + * Dummy I/O functions for various other pseudotypes. + */ PSEUDOTYPE_DUMMY_IO_FUNCS(any); PSEUDOTYPE_DUMMY_IO_FUNCS(trigger); PSEUDOTYPE_DUMMY_IO_FUNCS(event_trigger); PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler); +PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(internal); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray); -PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);