aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-12-02 14:24:44 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2022-12-02 14:24:44 -0500
commitcabfb8241dea84206b90578a769e9cbd813ba477 (patch)
treeadd43d5ea440445db86ee124b356e4308b43783a /src
parentb23cd185fd5410e5204683933f848d4583e34b35 (diff)
downloadpostgresql-cabfb8241dea84206b90578a769e9cbd813ba477.tar.gz
postgresql-cabfb8241dea84206b90578a769e9cbd813ba477.zip
Fix psql's \sf and \ef for new-style SQL functions.
Some options of these commands need to be able to identify the start of the function body within the output of pg_get_functiondef(). It used to be that that always began with "AS", but since the introduction of new-style SQL functions, it might also start with "BEGIN" or "RETURN". Fix that on the psql side, and add some regression tests. Noted by me awhile ago, but I didn't do anything about it. Thanks to David Johnston for a nag. Discussion: https://postgr.es/m/AM9PR01MB8268D5CDABDF044EE9F42173FE8C9@AM9PR01MB8268.eurprd01.prod.exchangelabs.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/ruleutils.c4
-rw-r--r--src/bin/psql/command.c46
-rw-r--r--src/test/regress/expected/psql.out62
-rw-r--r--src/test/regress/sql/psql.sql8
4 files changed, 94 insertions, 26 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index a20a1b069bf..641df1cabe2 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2867,8 +2867,8 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
*
* Note: if you change the output format of this function, be careful not
* to break psql's rules (in \ef and \sf) for identifying the start of the
- * function body. To wit: the function body starts on a line that begins
- * with "AS ", and no preceding line will look like that.
+ * function body. To wit: the function body starts on a line that begins with
+ * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
*/
Datum
pg_get_functiondef(PG_FUNCTION_ARGS)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 7672ed9e9d5..de6a3a71f8a 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -168,8 +168,7 @@ static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
PQExpBuffer buf);
static int strip_lineno_from_objdesc(char *obj);
static int count_lines_in_buf(PQExpBuffer buf);
-static void print_with_linenumbers(FILE *output, char *lines,
- const char *header_keyword);
+static void print_with_linenumbers(FILE *output, char *lines, bool is_func);
static void minimal_error_message(PGresult *res);
static void printSSLInfo(void);
@@ -1201,17 +1200,19 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
/*
* lineno "1" should correspond to the first line of the
* function body. We expect that pg_get_functiondef() will
- * emit that on a line beginning with "AS ", and that there
- * can be no such line before the real start of the function
- * body. Increment lineno by the number of lines before that
- * line, so that it becomes relative to the first line of the
- * function definition.
+ * emit that on a line beginning with "AS ", "BEGIN ", or
+ * "RETURN ", and that there can be no such line before the
+ * real start of the function body. Increment lineno by the
+ * number of lines before that line, so that it becomes
+ * relative to the first line of the function definition.
*/
const char *lines = query_buf->data;
while (*lines != '\0')
{
- if (strncmp(lines, "AS ", 3) == 0)
+ if (strncmp(lines, "AS ", 3) == 0 ||
+ strncmp(lines, "BEGIN ", 6) == 0 ||
+ strncmp(lines, "RETURN ", 7) == 0)
break;
lineno++;
/* find start of next line */
@@ -2528,15 +2529,8 @@ exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
if (show_linenumbers)
{
- /*
- * For functions, lineno "1" should correspond to the first
- * line of the function body. We expect that
- * pg_get_functiondef() will emit that on a line beginning
- * with "AS ", and that there can be no such line before the
- * real start of the function body.
- */
- print_with_linenumbers(output, buf->data,
- is_func ? "AS " : NULL);
+ /* add line numbers */
+ print_with_linenumbers(output, buf->data, is_func);
}
else
{
@@ -5611,24 +5605,28 @@ count_lines_in_buf(PQExpBuffer buf)
/*
* Write text at *lines to output with line numbers.
*
- * If header_keyword isn't NULL, then line 1 should be the first line beginning
- * with header_keyword; lines before that are unnumbered.
+ * For functions, lineno "1" should correspond to the first line of the
+ * function body; lines before that are unnumbered. We expect that
+ * pg_get_functiondef() will emit that on a line beginning with "AS ",
+ * "BEGIN ", or "RETURN ", and that there can be no such line before
+ * the real start of the function body.
*
* Caution: this scribbles on *lines.
*/
static void
-print_with_linenumbers(FILE *output, char *lines,
- const char *header_keyword)
+print_with_linenumbers(FILE *output, char *lines, bool is_func)
{
- bool in_header = (header_keyword != NULL);
- size_t header_sz = in_header ? strlen(header_keyword) : 0;
+ bool in_header = is_func;
int lineno = 0;
while (*lines != '\0')
{
char *eol;
- if (in_header && strncmp(lines, header_keyword, header_sz) == 0)
+ if (in_header &&
+ (strncmp(lines, "AS ", 3) == 0 ||
+ strncmp(lines, "BEGIN ", 6) == 0 ||
+ strncmp(lines, "RETURN ", 7) == 0))
in_header = false;
/* increment lineno only for body's lines */
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 1047399ef88..b4cb6ffb5b4 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5226,6 +5226,13 @@ reset work_mem;
pg_catalog | bit_xor | smallint | smallint | agg
(3 rows)
+\df *._pg_expandarray
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------------------+-----------------+------------------+-------------------------------------------+------
+ information_schema | _pg_expandarray | SETOF record | anyarray, OUT x anyelement, OUT n integer | func
+(1 row)
+
\do - pg_catalog.int4
List of operators
Schema | Name | Left arg type | Right arg type | Result type | Description
@@ -5240,6 +5247,61 @@ reset work_mem;
pg_catalog | && | anyarray | anyarray | boolean | overlaps
(1 row)
+-- check \sf
+\sf information_schema._pg_expandarray
+CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
+ RETURNS SETOF record
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT
+AS $function$select $1[s],
+ s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
+ from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
+ pg_catalog.array_upper($1,1),
+ 1) as g(s)$function$
+\sf+ information_schema._pg_expandarray
+ CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
+ RETURNS SETOF record
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT
+1 AS $function$select $1[s],
+2 s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
+3 from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
+4 pg_catalog.array_upper($1,1),
+5 1) as g(s)$function$
+\sf+ interval_pl_time
+ CREATE OR REPLACE FUNCTION pg_catalog.interval_pl_time(interval, time without time zone)
+ RETURNS time without time zone
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+1 RETURN ($2 + $1)
+\sf ts_debug(text)
+CREATE OR REPLACE FUNCTION pg_catalog.ts_debug(document text, OUT alias text, OUT description text, OUT token text, OUT dictionaries regdictionary[], OUT dictionary regdictionary, OUT lexemes text[])
+ RETURNS SETOF record
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+ SELECT ts_debug.alias,
+ ts_debug.description,
+ ts_debug.token,
+ ts_debug.dictionaries,
+ ts_debug.dictionary,
+ ts_debug.lexemes
+ FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
+END
+\sf+ ts_debug(text)
+ CREATE OR REPLACE FUNCTION pg_catalog.ts_debug(document text, OUT alias text, OUT description text, OUT token text, OUT dictionaries regdictionary[], OUT dictionary regdictionary, OUT lexemes text[])
+ RETURNS SETOF record
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+1 BEGIN ATOMIC
+2 SELECT ts_debug.alias,
+3 ts_debug.description,
+4 ts_debug.token,
+5 ts_debug.dictionaries,
+6 ts_debug.dictionary,
+7 ts_debug.lexemes
+8 FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
+9 END
-- AUTOCOMMIT
CREATE TABLE ac_test (a int);
\set AUTOCOMMIT off
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index cf67a979dfe..2da9665a193 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1271,9 +1271,17 @@ reset work_mem;
\df has_database_privilege oid text
\df has_database_privilege oid text -
\dfa bit* small*
+\df *._pg_expandarray
\do - pg_catalog.int4
\do && anyarray *
+-- check \sf
+\sf information_schema._pg_expandarray
+\sf+ information_schema._pg_expandarray
+\sf+ interval_pl_time
+\sf ts_debug(text)
+\sf+ ts_debug(text)
+
-- AUTOCOMMIT
CREATE TABLE ac_test (a int);