Skip to content

Commit cabfb82

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 b23cd18 commit cabfb82

File tree

4 files changed

+94
-26
lines changed

4 files changed

+94
-26
lines changed

src/backend/utils/adt/ruleutils.c

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

src/bin/psql/command.c

+22-24
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,7 @@ static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
168168
PQExpBuffer buf);
169169
static int strip_lineno_from_objdesc(char *obj);
170170
static int count_lines_in_buf(PQExpBuffer buf);
171-
static void print_with_linenumbers(FILE *output, char *lines,
172-
const char *header_keyword);
171+
static void print_with_linenumbers(FILE *output, char *lines, bool is_func);
173172
static void minimal_error_message(PGresult *res);
174173

175174
static void printSSLInfo(void);
@@ -1201,17 +1200,19 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
12011200
/*
12021201
* lineno "1" should correspond to the first line of the
12031202
* function body. We expect that pg_get_functiondef() will
1204-
* emit that on a line beginning with "AS ", and that there
1205-
* can be no such line before the real start of the function
1206-
* body. Increment lineno by the number of lines before that
1207-
* line, so that it becomes relative to the first line of the
1208-
* function definition.
1203+
* emit that on a line beginning with "AS ", "BEGIN ", or
1204+
* "RETURN ", and that there can be no such line before the
1205+
* real start of the function body. Increment lineno by the
1206+
* number of lines before that line, so that it becomes
1207+
* relative to the first line of the function definition.
12091208
*/
12101209
const char *lines = query_buf->data;
12111210

12121211
while (*lines != '\0')
12131212
{
1214-
if (strncmp(lines, "AS ", 3) == 0)
1213+
if (strncmp(lines, "AS ", 3) == 0 ||
1214+
strncmp(lines, "BEGIN ", 6) == 0 ||
1215+
strncmp(lines, "RETURN ", 7) == 0)
12151216
break;
12161217
lineno++;
12171218
/* find start of next line */
@@ -2528,15 +2529,8 @@ exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
25282529

25292530
if (show_linenumbers)
25302531
{
2531-
/*
2532-
* For functions, lineno "1" should correspond to the first
2533-
* line of the function body. We expect that
2534-
* pg_get_functiondef() will emit that on a line beginning
2535-
* with "AS ", and that there can be no such line before the
2536-
* real start of the function body.
2537-
*/
2538-
print_with_linenumbers(output, buf->data,
2539-
is_func ? "AS " : NULL);
2532+
/* add line numbers */
2533+
print_with_linenumbers(output, buf->data, is_func);
25402534
}
25412535
else
25422536
{
@@ -5611,24 +5605,28 @@ count_lines_in_buf(PQExpBuffer buf)
56115605
/*
56125606
* Write text at *lines to output with line numbers.
56135607
*
5614-
* If header_keyword isn't NULL, then line 1 should be the first line beginning
5615-
* with header_keyword; lines before that are unnumbered.
5608+
* For functions, lineno "1" should correspond to the first line of the
5609+
* function body; lines before that are unnumbered. We expect that
5610+
* pg_get_functiondef() will emit that on a line beginning with "AS ",
5611+
* "BEGIN ", or "RETURN ", and that there can be no such line before
5612+
* the real start of the function body.
56165613
*
56175614
* Caution: this scribbles on *lines.
56185615
*/
56195616
static void
5620-
print_with_linenumbers(FILE *output, char *lines,
5621-
const char *header_keyword)
5617+
print_with_linenumbers(FILE *output, char *lines, bool is_func)
56225618
{
5623-
bool in_header = (header_keyword != NULL);
5624-
size_t header_sz = in_header ? strlen(header_keyword) : 0;
5619+
bool in_header = is_func;
56255620
int lineno = 0;
56265621

56275622
while (*lines != '\0')
56285623
{
56295624
char *eol;
56305625

5631-
if (in_header && strncmp(lines, header_keyword, header_sz) == 0)
5626+
if (in_header &&
5627+
(strncmp(lines, "AS ", 3) == 0 ||
5628+
strncmp(lines, "BEGIN ", 6) == 0 ||
5629+
strncmp(lines, "RETURN ", 7) == 0))
56325630
in_header = false;
56335631

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

src/test/regress/expected/psql.out

+62
Original file line numberDiff line numberDiff line change
@@ -5226,6 +5226,13 @@ reset work_mem;
52265226
pg_catalog | bit_xor | smallint | smallint | agg
52275227
(3 rows)
52285228

5229+
\df *._pg_expandarray
5230+
List of functions
5231+
Schema | Name | Result data type | Argument data types | Type
5232+
--------------------+-----------------+------------------+-------------------------------------------+------
5233+
information_schema | _pg_expandarray | SETOF record | anyarray, OUT x anyelement, OUT n integer | func
5234+
(1 row)
5235+
52295236
\do - pg_catalog.int4
52305237
List of operators
52315238
Schema | Name | Left arg type | Right arg type | Result type | Description
@@ -5240,6 +5247,61 @@ reset work_mem;
52405247
pg_catalog | && | anyarray | anyarray | boolean | overlaps
52415248
(1 row)
52425249

5250+
-- check \sf
5251+
\sf information_schema._pg_expandarray
5252+
CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
5253+
RETURNS SETOF record
5254+
LANGUAGE sql
5255+
IMMUTABLE PARALLEL SAFE STRICT
5256+
AS $function$select $1[s],
5257+
s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
5258+
from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
5259+
pg_catalog.array_upper($1,1),
5260+
1) as g(s)$function$
5261+
\sf+ information_schema._pg_expandarray
5262+
CREATE OR REPLACE FUNCTION information_schema._pg_expandarray(anyarray, OUT x anyelement, OUT n integer)
5263+
RETURNS SETOF record
5264+
LANGUAGE sql
5265+
IMMUTABLE PARALLEL SAFE STRICT
5266+
1 AS $function$select $1[s],
5267+
2 s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
5268+
3 from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
5269+
4 pg_catalog.array_upper($1,1),
5270+
5 1) as g(s)$function$
5271+
\sf+ interval_pl_time
5272+
CREATE OR REPLACE FUNCTION pg_catalog.interval_pl_time(interval, time without time zone)
5273+
RETURNS time without time zone
5274+
LANGUAGE sql
5275+
IMMUTABLE PARALLEL SAFE STRICT COST 1
5276+
1 RETURN ($2 + $1)
5277+
\sf ts_debug(text)
5278+
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[])
5279+
RETURNS SETOF record
5280+
LANGUAGE sql
5281+
STABLE PARALLEL SAFE STRICT
5282+
BEGIN ATOMIC
5283+
SELECT ts_debug.alias,
5284+
ts_debug.description,
5285+
ts_debug.token,
5286+
ts_debug.dictionaries,
5287+
ts_debug.dictionary,
5288+
ts_debug.lexemes
5289+
FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
5290+
END
5291+
\sf+ ts_debug(text)
5292+
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[])
5293+
RETURNS SETOF record
5294+
LANGUAGE sql
5295+
STABLE PARALLEL SAFE STRICT
5296+
1 BEGIN ATOMIC
5297+
2 SELECT ts_debug.alias,
5298+
3 ts_debug.description,
5299+
4 ts_debug.token,
5300+
5 ts_debug.dictionaries,
5301+
6 ts_debug.dictionary,
5302+
7 ts_debug.lexemes
5303+
8 FROM ts_debug(get_current_ts_config(), ts_debug.document) ts_debug(alias, description, token, dictionaries, dictionary, lexemes);
5304+
9 END
52435305
-- AUTOCOMMIT
52445306
CREATE TABLE ac_test (a int);
52455307
\set AUTOCOMMIT off

src/test/regress/sql/psql.sql

+8
Original file line numberDiff line numberDiff line change
@@ -1271,9 +1271,17 @@ reset work_mem;
12711271
\df has_database_privilege oid text
12721272
\df has_database_privilege oid text -
12731273
\dfa bit* small*
1274+
\df *._pg_expandarray
12741275
\do - pg_catalog.int4
12751276
\do && anyarray *
12761277

1278+
-- check \sf
1279+
\sf information_schema._pg_expandarray
1280+
\sf+ information_schema._pg_expandarray
1281+
\sf+ interval_pl_time
1282+
\sf ts_debug(text)
1283+
\sf+ ts_debug(text)
1284+
12771285
-- AUTOCOMMIT
12781286

12791287
CREATE TABLE ac_test (a int);

0 commit comments

Comments
 (0)