Skip to content

Commit 97299cf

Browse files
committed
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
1 parent ebf87c0 commit 97299cf

File tree

4 files changed

+94
-26
lines changed

4 files changed

+94
-26
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,8 +2872,8 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
28722872
*
28732873
* Note: if you change the output format of this function, be careful not
28742874
* to break psql's rules (in \ef and \sf) for identifying the start of the
2875-
* function body. To wit: the function body starts on a line that begins
2876-
* with "AS ", and no preceding line will look like that.
2875+
* function body. To wit: the function body starts on a line that begins with
2876+
* "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
28772877
*/
28782878
Datum
28792879
pg_get_functiondef(PG_FUNCTION_ARGS)

src/bin/psql/command.c

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
167167
PQExpBuffer buf);
168168
static int strip_lineno_from_objdesc(char *obj);
169169
static int count_lines_in_buf(PQExpBuffer buf);
170-
static void print_with_linenumbers(FILE *output, char *lines,
171-
const char *header_keyword);
170+
static void print_with_linenumbers(FILE *output, char *lines, bool is_func);
172171
static void minimal_error_message(PGresult *res);
173172

174173
static void printSSLInfo(void);
@@ -1170,17 +1169,19 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
11701169
/*
11711170
* lineno "1" should correspond to the first line of the
11721171
* function body. We expect that pg_get_functiondef() will
1173-
* emit that on a line beginning with "AS ", and that there
1174-
* can be no such line before the real start of the function
1175-
* body. Increment lineno by the number of lines before that
1176-
* line, so that it becomes relative to the first line of the
1177-
* function definition.
1172+
* emit that on a line beginning with "AS ", "BEGIN ", or
1173+
* "RETURN ", and that there can be no such line before the
1174+
* real start of the function body. Increment lineno by the
1175+
* number of lines before that line, so that it becomes
1176+
* relative to the first line of the function definition.
11781177
*/
11791178
const char *lines = query_buf->data;
11801179

11811180
while (*lines != '\0')
11821181
{
1183-
if (strncmp(lines, "AS ", 3) == 0)
1182+
if (strncmp(lines, "AS ", 3) == 0 ||
1183+
strncmp(lines, "BEGIN ", 6) == 0 ||
1184+
strncmp(lines, "RETURN ", 7) == 0)
11841185
break;
11851186
lineno++;
11861187
/* find start of next line */
@@ -2502,15 +2503,8 @@ exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
25022503

25032504
if (show_linenumbers)
25042505
{
2505-
/*
2506-
* For functions, lineno "1" should correspond to the first
2507-
* line of the function body. We expect that
2508-
* pg_get_functiondef() will emit that on a line beginning
2509-
* with "AS ", and that there can be no such line before the
2510-
* real start of the function body.
2511-
*/
2512-
print_with_linenumbers(output, buf->data,
2513-
is_func ? "AS " : NULL);
2506+
/* add line numbers */
2507+
print_with_linenumbers(output, buf->data, is_func);
25142508
}
25152509
else
25162510
{
@@ -5530,24 +5524,28 @@ count_lines_in_buf(PQExpBuffer buf)
55305524
/*
55315525
* Write text at *lines to output with line numbers.
55325526
*
5533-
* If header_keyword isn't NULL, then line 1 should be the first line beginning
5534-
* with header_keyword; lines before that are unnumbered.
5527+
* For functions, lineno "1" should correspond to the first line of the
5528+
* function body; lines before that are unnumbered. We expect that
5529+
* pg_get_functiondef() will emit that on a line beginning with "AS ",
5530+
* "BEGIN ", or "RETURN ", and that there can be no such line before
5531+
* the real start of the function body.
55355532
*
55365533
* Caution: this scribbles on *lines.
55375534
*/
55385535
static void
5539-
print_with_linenumbers(FILE *output, char *lines,
5540-
const char *header_keyword)
5536+
print_with_linenumbers(FILE *output, char *lines, bool is_func)
55415537
{
5542-
bool in_header = (header_keyword != NULL);
5543-
size_t header_sz = in_header ? strlen(header_keyword) : 0;
5538+
bool in_header = is_func;
55445539
int lineno = 0;
55455540

55465541
while (*lines != '\0')
55475542
{
55485543
char *eol;
55495544

5550-
if (in_header && strncmp(lines, header_keyword, header_sz) == 0)
5545+
if (in_header &&
5546+
(strncmp(lines, "AS ", 3) == 0 ||
5547+
strncmp(lines, "BEGIN ", 6) == 0 ||
5548+
strncmp(lines, "RETURN ", 7) == 0))
55515549
in_header = false;
55525550

55535551
/* increment lineno only for body's lines */

src/test/regress/expected/psql.out

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5194,6 +5194,13 @@ reset work_mem;
51945194
pg_catalog | bit_xor | smallint | smallint | agg
51955195
(3 rows)
51965196

5197+
\df *._pg_expandarray
5198+
List of functions
5199+
Schema | Name | Result data type | Argument data types | Type
5200+
--------------------+-----------------+------------------+-------------------------------------------+------
5201+
information_schema | _pg_expandarray | SETOF record | anyarray, OUT x anyelement, OUT n integer | func
5202+
(1 row)
5203+
51975204
\do - pg_catalog.int4
51985205
List of operators
51995206
Schema | Name | Left arg type | Right arg type | Result type | Description
@@ -5208,6 +5215,61 @@ reset work_mem;
52085215
pg_catalog | && | anyarray | anyarray | boolean | overlaps
52095216
(1 row)
52105217

5218+
-- check \sf
5219+
\sf information_schema._pg_expandarray
5220+
CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
5221+
RETURNS SETOF record
5222+
LANGUAGE sql
5223+
IMMUTABLE PARALLEL SAFE STRICT
5224+
AS $function$select $1[s],
5225+
s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
5226+
from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
5227+
pg_catalog.array_upper($1,1),
5228+
1) as g(s)$function$
5229+
\sf+ information_schema._pg_expandarray
5230+
CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
5231+
RETURNS SETOF record
5232+
LANGUAGE sql
5233+
IMMUTABLE PARALLEL SAFE STRICT
5234+
1 AS $function$select $1[s],
5235+
2 s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
5236+
3 from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
5237+
4 pg_catalog.array_upper($1,1),
5238+
5 1) as g(s)$function$
5239+
\sf+ interval_pl_time
5240+
CREATE OR REPLACE FUNCTION pg_catalog.interval_pl_time(interval, time without time zone)
5241+
RETURNS time without time zone
5242+
LANGUAGE sql
5243+
IMMUTABLE PARALLEL SAFE STRICT COST 1
5244+
1 RETURN ($2 + $1)
5245+
\sf ts_debug(text)
5246+
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[])
5247+
RETURNS SETOF record
5248+
LANGUAGE sql
5249+
STABLE PARALLEL SAFE STRICT
5250+
BEGIN ATOMIC
5251+
SELECT ts_debug.alias,
5252+
ts_debug.description,
5253+
ts_debug.token,
5254+
ts_debug.dictionaries,
5255+
ts_debug.dictionary,
5256+
ts_debug.lexemes
5257+
FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
5258+
END
5259+
\sf+ ts_debug(text)
5260+
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[])
5261+
RETURNS SETOF record
5262+
LANGUAGE sql
5263+
STABLE PARALLEL SAFE STRICT
5264+
1 BEGIN ATOMIC
5265+
2 SELECT ts_debug.alias,
5266+
3 ts_debug.description,
5267+
4 ts_debug.token,
5268+
5 ts_debug.dictionaries,
5269+
6 ts_debug.dictionary,
5270+
7 ts_debug.lexemes
5271+
8 FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
5272+
9 END
52115273
-- AUTOCOMMIT
52125274
CREATE TABLE ac_test (a int);
52135275
\set AUTOCOMMIT off

src/test/regress/sql/psql.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,9 +1257,17 @@ reset work_mem;
12571257
\df has_database_privilege oid text
12581258
\df has_database_privilege oid text -
12591259
\dfa bit* small*
1260+
\df *._pg_expandarray
12601261
\do - pg_catalog.int4
12611262
\do && anyarray *
12621263

1264+
-- check \sf
1265+
\sf information_schema._pg_expandarray
1266+
\sf+ information_schema._pg_expandarray
1267+
\sf+ interval_pl_time
1268+
\sf ts_debug(text)
1269+
\sf+ ts_debug(text)
1270+
12631271
-- AUTOCOMMIT
12641272

12651273
CREATE TABLE ac_test (a int);

0 commit comments

Comments
 (0)