Thanks for the feedback, attached is version two of the patch. Major changes:
* Use booleans not generic "int x" * Build a quick list of abbreviations at the top of the function * Add array mapping for all types * Removed the tab-complete bit, it was too fragile and unhelpful Cheers, Greg
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 221a967bfe..cf3e8c7134 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1574,7 +1574,7 @@ testdb=> <varlistentry> - <term><literal>\df[anptwS+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> + <term><literal>\df[anptwS+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ] [ types ]</literal></term> <listitem> <para> @@ -1587,6 +1587,7 @@ testdb=> If <replaceable class="parameter">pattern</replaceable> is specified, only functions whose names match the pattern are shown. + Any additional words are considered type arguments to help narrow the list of returned functions. By default, only user-created objects are shown; supply a pattern or the <literal>S</literal> modifier to include system objects. @@ -1598,7 +1599,7 @@ testdb=> <tip> <para> - To look up functions taking arguments or returning values of a specific + To look up functions returning values of a specific data type, use your pager's search capability to scroll through the <literal>\df</literal> output. </para> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index c7a83d5dfc..426603b0cb 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -783,6 +783,8 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) case 'f': /* function subsystem */ switch (cmd[2]) { + char *funcargs; + case '\0': case '+': case 'S': @@ -791,7 +793,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) case 'p': case 't': case 'w': - success = describeFunctions(&cmd[2], pattern, show_verbose, show_system); + funcargs = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, true); + success = describeFunctions(&cmd[2], pattern, show_verbose, show_system, funcargs); + free(funcargs); break; default: status = PSQL_CMD_UNKNOWN; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 07d640021c..a8d3f3ba53 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -26,6 +26,7 @@ #include "fe_utils/print.h" #include "fe_utils/string_utils.h" #include "settings.h" +#include "stringutils.h" #include "variables.h" static bool describeOneTableDetails(const char *schemaname, @@ -312,7 +313,7 @@ describeTablespaces(const char *pattern, bool verbose) * and you can mix and match these in any order. */ bool -describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem) +describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem, const char *funcargs) { bool showAggregate = strchr(functypes, 'a') != NULL; bool showNormal = strchr(functypes, 'n') != NULL; @@ -626,6 +627,67 @@ describeFunctions(const char *functypes, const char *pattern, bool verbose, bool "n.nspname", "p.proname", NULL, "pg_catalog.pg_function_is_visible(p.oid)"); + /* + * Check for any additional arguments to narrow down which functions are + * desired + */ + if (funcargs) + { + + bool is_initial_run = true; + bool found_abbreviation; + int argoffset = 0; + char *functoken; + + static const char *type_abbreviations[] = { + "bool", "boolean", "bool[]", "boolean[]", + "char", "character", "char[]", "character[]", + "double", "double precision", "double[]", "double precision[]", + "float", "double precision", "float[]", "double precision[]", + "int", "integer", "int[]", "integer[]", + "time", "time without time zone", "time[]", "time without time zone[]", + "timetz", "time with time zone", "timetz[]", "time with time zone[]", + "timestamp", "timestamp without timestamp zone", "timestamp[]", "timestamp without timestamp zone[]", + "timestamptz", "timestamp with timestamp zone", "timestamptz[]", "timestamp with timestamp zone[]", + "varbit", "bit varying", "varbit[]", "bit varying[]", + "varchar", "character varying", "varchar[]", "character varying[]", + NULL + }; + + while ((functoken = strtokx(is_initial_run ? funcargs : NULL, " \t\n\r", ".,();", "\"", 0, false, true, pset.encoding))) + { + is_initial_run = false; + found_abbreviation = false; + + if (isalpha(functoken[0])) + { + appendPQExpBuffer(&buf, " AND p.proargtypes[%d]::regtype::text = ", argoffset++); + for (int i = 0; NULL != *(type_abbreviations + i); i += 2) + { + const char *shortname = *(type_abbreviations + i); + const char *longname = *(type_abbreviations + i + 1); + + if (pg_strcasecmp(functoken, shortname) == 0) + { + appendPQExpBuffer(&buf, "LOWER('%s')::text\n", longname); + found_abbreviation = true; + break; + } + } + if (!found_abbreviation) + { + appendPQExpBuffer(&buf, "LOWER(%s)::text\n", PQescapeLiteral(pset.db, functoken, strlen(functoken))); + } + + } + else if (functoken[0] == ')' && argoffset) + { /* Force limit the number of args */ + appendPQExpBuffer(&buf, " AND p.pronargs = %d\n", argoffset); + break; + } + } + } + if (!showSystem && !pattern) appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" " AND n.nspname <> 'information_schema'\n"); diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index f0e3ec957c..a3400afb8c 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -19,7 +19,7 @@ extern bool describeAccessMethods(const char *pattern, bool verbose); extern bool describeTablespaces(const char *pattern, bool verbose); /* \df, \dfa, \dfn, \dft, \dfw, etc. */ -extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem); +extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem, const char *funcargs); /* \dT */ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem); diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..fd69956d07 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -5072,3 +5072,110 @@ List of access methods hash | uuid_ops | uuid | uuid | 2 | uuid_hash_extended (5 rows) +-- list specific functions of the same name but different args +create function mtest(int) returns int as $$ select 1; $$ language sql; +create function mtest(int,text) returns int as $$ select 1; $$ language sql; +create function mtest(bool,character(10),varchar(10)) returns int as $$ select 1; $$ language sql; +create function mtest(float,float,int) returns int as $$ select 1; $$ language sql; +create function mtest(time,timetz) returns int as $$ select 1; $$ language sql; +create function mtest(timestamp,timestamptz) returns int as $$ select 1; $$ language sql; +create function mtest(varbit) returns int as $$ select 1; $$ language sql; +-- With no arguments, all functions are shown +\df mtest + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+-------------------------------------------------------+------ + public | mtest | integer | bit varying | func + public | mtest | integer | boolean, character, character varying | func + public | mtest | integer | double precision, double precision, integer | func + public | mtest | integer | integer | func + public | mtest | integer | integer, text | func + public | mtest | integer | timestamp without time zone, timestamp with time zone | func + public | mtest | integer | time without time zone, time with time zone | func +(7 rows) + +-- An invalid argument type matches nothing +\df mtest mint + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+------+------------------+---------------------+------ +(0 rows) + +-- A single argument type matches all functions starting with that type +\df mtest integer + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------+------ + public | mtest | integer | integer | func + public | mtest | integer | integer, text | func +(2 rows) + +-- Two argument types match up +\df mtest integer text + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------+------ + public | mtest | integer | integer, text | func +(1 row) + +-- A single argument type only matches a single argument if a closing paren is added +\df mtest (integer) + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------+------ + public | mtest | integer | integer | func +(1 row) + +-- Allowed abbreviations: bool->boolean, char -> character, varchar -> character varying +\df mtest bool,char,varchar + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------------------------+------ + public | mtest | integer | boolean, character, character varying | func +(1 row) + +-- Allowed abbreviations: double -> double precision, float - double precision, int -> integer +\df mtest double float int + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------------------------------+------ + public | mtest | integer | double precision, double precision, integer | func +(1 row) + +-- Allowed abbreviations: time -> time without time zone, timetz -> time with time zone +\df mtest (time timetz) + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------------------------------+------ + public | mtest | integer | time without time zone, time with time zone | func +(1 row) + +-- Allowed abbreviations: timestamp -> timestamp without time zone, timestamptz -> timestampt with time zone +\df mtest timestamp timestamptz + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+-------------------------------------------------------+------ + public | mtest | integer | timestamp without time zone, timestamp with time zone | func +(1 row) + +-- Allowed abbreviation: varbit -> bit varying +\df mtest varbit + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+-------+------------------+---------------------+------ + public | mtest | integer | bit varying | func +(1 row) + +drop function mtest(int); +drop function mtest(int,text); +drop function mtest(bool,char,varchar); +drop function mtest(float,float,int); +drop function mtest(time,timetz); +drop function mtest(timestamp,timestamptz); +drop function mtest(varbit); +\df mtest + List of functions + Schema | Name | Result data type | Argument data types | Type +--------+------+------------------+---------------------+------ +(0 rows) + diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index 47b28d2a07..73f4d7cf90 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -1225,3 +1225,53 @@ drop role regress_partitioning_role; \dAo * pg_catalog.jsonb_path_ops \dAp+ btree float_ops \dAp * pg_catalog.uuid_ops + +-- list specific functions of the same name but different args + +create function mtest(int) returns int as $$ select 1; $$ language sql; +create function mtest(int,text) returns int as $$ select 1; $$ language sql; +create function mtest(bool,character(10),varchar(10)) returns int as $$ select 1; $$ language sql; +create function mtest(float,float,int) returns int as $$ select 1; $$ language sql; +create function mtest(time,timetz) returns int as $$ select 1; $$ language sql; +create function mtest(timestamp,timestamptz) returns int as $$ select 1; $$ language sql; +create function mtest(varbit) returns int as $$ select 1; $$ language sql; + +-- With no arguments, all functions are shown +\df mtest + +-- An invalid argument type matches nothing +\df mtest mint + +-- A single argument type matches all functions starting with that type +\df mtest integer + +-- Two argument types match up +\df mtest integer text + +-- A single argument type only matches a single argument if a closing paren is added +\df mtest (integer) + +-- Allowed abbreviations: bool->boolean, char -> character, varchar -> character varying +\df mtest bool,char,varchar + +-- Allowed abbreviations: double -> double precision, float - double precision, int -> integer +\df mtest double float int + +-- Allowed abbreviations: time -> time without time zone, timetz -> time with time zone +\df mtest (time timetz) + +-- Allowed abbreviations: timestamp -> timestamp without time zone, timestamptz -> timestampt with time zone +\df mtest timestamp timestamptz + +-- Allowed abbreviation: varbit -> bit varying +\df mtest varbit + +drop function mtest(int); +drop function mtest(int,text); +drop function mtest(bool,char,varchar); +drop function mtest(float,float,int); +drop function mtest(time,timetz); +drop function mtest(timestamp,timestamptz); +drop function mtest(varbit); + +\df mtest