v1-0001-Add-function-name-tab-completion-after-SELECT-in-.patch
text/plain
Filename: v1-0001-Add-function-name-tab-completion-after-SELECT-in-.patch
Type: text/plain
Part: 0
Patch
Format: format-patch
Series: patch v1-0001
Subject: Add function name tab completion after SELECT in psql
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/psql-ref.sgml | 7 | 0 |
| src/bin/psql/t/010_tab_completion.pl | 18 | 1 |
| src/bin/psql/tab-complete.in.c | 24 | 0 |
From eee63a47fee041f3eb1be1944eed2499539e9cd6 Mon Sep 17 00:00:00 2001
From: Florin Irion <florin.irion@enterprisedb.com>
Date: Thu, 12 Feb 2026 12:27:29 +0400
Subject: [PATCH v1] Add function name tab completion after SELECT in psql
Teach psql's tab completion to sugest function names after SELECT,
teh keywords ALL, DISTINCT, and * are also included since they are
the most common non-function tokens that follow SELECT.
To keep the completion list manageable, pg_catalog functions are
not checked unless the text typed so far begins with "pg_". Also add
DISTINCT to the function query results to avoid duplicate entries from
overloaded functions.
Update the documentation to describe the new completion behavior,
and add TAP tests covering both user-defined function completion
and pg_catalog prefix filtering.
---
doc/src/sgml/ref/psql-ref.sgml | 7 +++++++
src/bin/psql/t/010_tab_completion.pl | 19 ++++++++++++++++++-
src/bin/psql/tab-complete.in.c | 24 ++++++++++++++++++++++++
3 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 8b1d948ba05..603702dba8f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -5227,6 +5227,13 @@ testdb=> \set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# '
will fill in the unfinished name, or offer a menu of possible completions
when there's more than one. (Depending on the library in use, you may need to
press <literal>TAB</literal> more than once to get a menu.)
+ Tab completition is also aware of SQL context: for instance, after
+ <command>SELECT</command> it will suggest function names (as well as
+ keywords such as <literal>ALL</literal> and <literal>DISTINCT</literal>),
+ while after <command>FROM</command> it will suggest table names.
+ To avoid overwhelming the list with built-in functions, names from
+ <literal>pg_catalog</literal> are not checked unless the text typed so
+ far begins with <literal>pg_</literal>.
</para>
<para>
diff --git a/src/bin/psql/t/010_tab_completion.pl b/src/bin/psql/t/010_tab_completion.pl
index 7104aba2394..fcdc72b4720 100644
--- a/src/bin/psql/t/010_tab_completion.pl
+++ b/src/bin/psql/t/010_tab_completion.pl
@@ -44,7 +44,8 @@ $node->safe_psql('postgres',
. "CREATE TABLE mytab246 (f1 int, f2 text);\n"
. "CREATE TABLE \"mixedName\" (f1 int, f2 text);\n"
. "CREATE TYPE enum1 AS ENUM ('foo', 'bar', 'baz', 'BLACK');\n"
- . "CREATE PUBLICATION some_publication;\n");
+ . "CREATE PUBLICATION some_publication;\n"
+ . "CREATE FUNCTION tcfunc_test(int) RETURNS int LANGUAGE sql AS 'SELECT 1';\n");
# In a VPATH build, we'll be started in the source directory, but we want
# to run in the build directory so that we can use relative paths to
@@ -423,6 +424,22 @@ check_completion(
clear_line();
+# check completion of function names after SELECT
+check_completion(
+ "SELECT tcfunc_te\t",
+ qr/tcfunc_test\b/,
+ "complete function name after SELECT");
+
+clear_query();
+
+# check that pg_ prefix enables system function completion
+check_completion(
+ "SELECT pg_cancel_b\t",
+ qr/pg_cancel_backend\b/,
+ "complete pg_catalog function with pg_ prefix in SELECT");
+
+clear_query();
+
# send psql an explicit \q to shut it down, else pty won't close properly
$h->quit or die "psql returned $?";
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 8b91bc00062..afeb050f3bb 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -646,12 +646,14 @@ static const SchemaQuery Query_for_list_of_functions[] = {
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
.result = "p.proname",
+ .use_distinct = true,
},
{
.catname = "pg_catalog.pg_proc p",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
.result = "p.proname",
+ .use_distinct = true,
}
};
@@ -663,6 +665,7 @@ static const SchemaQuery Query_for_list_of_procedures[] = {
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
.result = "p.proname",
+ .use_distinct = true,
},
{
/* not supported in older versions */
@@ -675,6 +678,7 @@ static const SchemaQuery Query_for_list_of_routines = {
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
.result = "p.proname",
+ .use_distinct = true,
};
static const SchemaQuery Query_for_list_of_sequences = {
@@ -5401,6 +5405,12 @@ match_previous_words(int pattern_id,
COMPLETE_WITH("'standby_replay'", "'standby_write'", "'standby_flush'", "'primary_flush'");
}
+/* SELECT -- offer functions and SELECT-list keywords */
+ else if (TailMatches("SELECT") &&
+ !HeadMatches("GRANT") && !HeadMatches("REVOKE"))
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_PLUS(Query_for_list_of_functions,
+ "ALL", "DISTINCT", "*");
+
/* WITH [RECURSIVE] */
/*
@@ -5977,6 +5987,20 @@ _complete_from_query(const char *simple_query,
" pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
}
+ /*
+ * Similarly, when fetching function names, don't check
+ * pg_catalog entries unles the input-so-far begins with
+ * "pg_".
+ */
+ if (strcmp(schema_query->catname,
+ "pg_catalog.pg_proc p") == 0 &&
+ strncmp(objectname, "pg_", 3) != 0)
+ {
+ appendPQExpBufferStr(&query_buffer,
+ " AND p.pronamespace <> (SELECT oid FROM"
+ " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
+ }
+
/*
* If the target object type can be schema-qualified, add in
* schema names matching the input-so-far.
--
2.45.1