diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 10d42ca..5224440 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1043,14 +1043,19 @@ testdb=> Lists available functions, together with their argument and - return types. If pattern - is specified, only functions whose names match the pattern are shown. - If the form \df+ is used, additional information about - each function, including volatility, language, source code and description, is shown. - By default, only user-created objects are shown; supply a - pattern or the S modifier to include system - objects. + return types and their function type: 'normal', 'agg', + 'trigger', and 'window'. If pattern is specified, only + functions whose names match the pattern are shown. If the + form \df+ is used, additional information + about each function, including volatility, language, source + code and description, is shown. By default, only user-created + objects are shown; supply a pattern or the + S modifier to include system objects. To + include aggregates in the result set, use \dfa, normal + functions, \dfn, trigger functions, \dft, windowing functions, + \dfw. You may freely mix and match the +, S, a, n, t and w + options. @@ -1064,7 +1069,6 @@ testdb=> - \dF[+] [ pattern ] diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index fc56c3d..09ba686 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -183,6 +183,15 @@ do it for earlier branch release files. + + + In psql, \df now shows which type of function (normal, + aggregate, trigger, or window) it is. You can also specify + mix-and-match options. To get aggregates and windowing + functions, including system ones, for example, invoke \dfwaS+ + + + diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index b39466d..1dc3cc3 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -365,8 +365,22 @@ exec_command(const char *cmd, case 'D': success = listDomains(pattern, show_system); break; - case 'f': - success = describeFunctions(pattern, show_verbose, show_system); + case 'f': /* function subsystem */ + switch (cmd[2]) + { + case '\0': + case '+': + case 'S': + case 'a': + case 'n': + case 't': + case 'w': + success = describeFunctions(&cmd[2], pattern, show_verbose, show_system); + break; + default: + status = PSQL_CMD_UNKNOWN; + break; + } break; case 'g': /* no longer distinct from \du */ diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 731baf8..4ea41af 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -183,15 +183,43 @@ describeTablespaces(const char *pattern, bool verbose) /* \df - * Takes an optional regexp to select particular functions + * Takes an optional regexp to select particular functions. + * + * As with \d, you can specify the kinds of functions you want: + * + * a for aggregates + * n for normal + * t for trigger + * w for window + * + * and you can mix and match these in any order. */ bool -describeFunctions(const char *pattern, bool verbose, bool showSystem) +describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem) { + bool showAggregate = strchr(functypes, 'a') != NULL; + bool showNormal = strchr(functypes, 'n') != NULL; + bool showTrigger = strchr(functypes, 't') != NULL; + bool showWindow = strchr(functypes, 'w') != NULL; + PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; + if (showWindow && pset.sversion < 80400) + { + fprintf(stderr, _("\\df does not take a \"w\" decorator in %d.%d.\n"), + pset.sversion / 10000, (pset.sversion / 100) % 100); + return true; + } + + if (!(showAggregate || showNormal || showTrigger || showWindow)) + { + showAggregate = showNormal = showTrigger = true; + if (pset.sversion >= 80400) + showWindow = true; + } + initPQExpBuffer(&buf); printfPQExpBuffer(&buf, @@ -203,9 +231,21 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem) if (pset.sversion >= 80400) appendPQExpBuffer(&buf, " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n" - " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\"", + " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n" + " CASE\n" + " WHEN p.proisagg THEN '%s'\n" + " WHEN p.proiswindow THEN '%s'\n" + " WHEN pg_catalog.pg_get_function_result(p.oid) = 'trigger' THEN '%s'\n" + " ELSE '%s'\n" + "END as \"%s\"", gettext_noop("Result data type"), - gettext_noop("Argument data types")); + gettext_noop("Argument data types"), + /* translator: "agg" is short for "aggregate" */ + gettext_noop("agg"), + gettext_noop("window"), + gettext_noop("trigger"), + gettext_noop("normal"), + gettext_noop("Type")); else if (pset.sversion >= 80100) appendPQExpBuffer(&buf, " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" @@ -238,16 +278,36 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem) " FROM\n" " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n" " ), ', ')\n" + " END AS \"%s\",\n" + " CASE\n" + " WHEN p.proisagg THEN '%s'\n" + " WHEN 'trigger' = pg_catalog.format_type(p.prorettype, NULL) THEN '%s'\n" + " ELSE '%s'\n" " END AS \"%s\"", gettext_noop("Result data type"), - gettext_noop("Argument data types")); + gettext_noop("Argument data types"), + /* translator: "agg" is short for "aggregate" */ + gettext_noop("agg"), + gettext_noop("trigger"), + gettext_noop("normal"), + gettext_noop("Type")); else appendPQExpBuffer(&buf, " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n" - " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"", + " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n" + " CASE\n" + " WHEN p.proisagg THEN '%s'\n" + " WHEN 'trigger' = pg_catalog.format_type(p.prorettype, NULL) THEN '%s'\n" + " ELSE '%s'\n" + " END AS \"%s\"", gettext_noop("Result data type"), - gettext_noop("Argument data types")); + gettext_noop("Argument data types"), + /* translator: "agg" is short for "aggregate" */ + gettext_noop("agg"), + gettext_noop("trigger"), + gettext_noop("normal"), + gettext_noop("Type")); if (verbose) appendPQExpBuffer(&buf, @@ -274,16 +334,63 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem) appendPQExpBuffer(&buf, " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n"); - appendPQExpBuffer(&buf, "WHERE NOT p.proisagg\n"); + processSQLNamePattern(pset.db, &buf, pattern, false, true, + "n.nspname", "p.proname", NULL, + "pg_catalog.pg_function_is_visible(p.oid)"); + + if (showNormal && showAggregate && showTrigger && showWindow) + /* Do nothing */; + else if (showNormal) + { + if (!showWindow && pset.sversion >= 80400) + appendPQExpBuffer(&buf, " AND NOT p.proiswindow\n"); + if (!showAggregate) + appendPQExpBuffer(&buf, " AND NOT p.proisagg\n"); + if (!showTrigger) + { + if (pset.sversion >= 80400) + appendPQExpBuffer(&buf, + " AND pg_catalog.pg_get_function_result(p.oid) <> 'trigger'\n"); + else + appendPQExpBuffer(&buf, + " AND pg_catalog.format_type(p.prorettype, NULL) <> 'trigger'\n"); + } + } + else + { + bool needs_or = false; + + appendPQExpBuffer(&buf, " AND (\n "); + if (showAggregate) + { + appendPQExpBuffer(&buf,"p.proisagg\n"); + needs_or = true; + } + if (showTrigger) + { + if (needs_or) + appendPQExpBuffer(&buf, " OR "); + if (pset.sversion >= 80400) + appendPQExpBuffer(&buf, + "pg_catalog.pg_get_function_result(p.oid) = 'trigger'\n"); + else + appendPQExpBuffer(&buf, + "'trigger' <> pg_catalog.format_type(p.prorettype, NULL)\n"); + needs_or = true; + } + if (showWindow) + { + if (needs_or) + appendPQExpBuffer(&buf, " OR "); + appendPQExpBuffer(&buf, "p.proiswindow\n"); + } + appendPQExpBuffer(&buf, " )\n"); + } if (!showSystem && !pattern) appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n" " AND n.nspname <> 'information_schema'\n"); - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "p.proname", NULL, - "pg_catalog.pg_function_is_visible(p.oid)"); - appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;"); res = PSQLexec(buf.data, false); diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 57e5c7b..0b32ddf 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -15,8 +15,8 @@ extern bool describeAggregates(const char *pattern, bool verbose, bool showSyste /* \db */ extern bool describeTablespaces(const char *pattern, bool verbose); -/* \df */ -extern bool describeFunctions(const char *pattern, bool verbose, bool showSystem); +/* \df, \dfa, \dfn, \dft, \dfw, etc. */ +extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem); /* \dT */ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 93ff5d0..504d55c 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -196,7 +196,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" (options: S = show system objects, + = additional detail)\n")); fprintf(output, _(" \\d[S+] list tables, views, and sequences\n")); fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n")); - fprintf(output, _(" \\da[S] [PATTERN] list aggregate functions\n")); + fprintf(output, _(" \\da[+] [PATTERN] list aggregates\n")); fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n")); fprintf(output, _(" \\dc[S] [PATTERN] list conversions\n")); fprintf(output, _(" \\dC [PATTERN] list casts\n")); @@ -205,7 +205,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n")); fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n")); fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n")); - fprintf(output, _(" \\df[S+] [PATTERN] list functions\n")); + fprintf(output, _(" \\df[S+] [PATTERN] list functions. Add a, n, t, w for aggregate, normal, trigger, window\n")); fprintf(output, _(" \\dF[+] [PATTERN] list text search configurations\n")); fprintf(output, _(" \\dFd[+] [PATTERN] list text search dictionaries\n")); fprintf(output, _(" \\dFp[+] [PATTERN] list text search parsers\n"));