mirror of
https://github.com/postgres/postgres.git
synced 2025-05-06 19:59:18 +03:00
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
This commit is contained in:
parent
47e1224d59
commit
303b26c1bb
@ -2778,8 +2778,8 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
|
|||||||
*
|
*
|
||||||
* Note: if you change the output format of this function, be careful not
|
* 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
|
* 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
|
* function body. To wit: the function body starts on a line that begins with
|
||||||
* with "AS ", and no preceding line will look like that.
|
* "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
pg_get_functiondef(PG_FUNCTION_ARGS)
|
pg_get_functiondef(PG_FUNCTION_ARGS)
|
||||||
|
@ -164,8 +164,7 @@ static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
|
|||||||
PQExpBuffer buf);
|
PQExpBuffer buf);
|
||||||
static int strip_lineno_from_objdesc(char *obj);
|
static int strip_lineno_from_objdesc(char *obj);
|
||||||
static int count_lines_in_buf(PQExpBuffer buf);
|
static int count_lines_in_buf(PQExpBuffer buf);
|
||||||
static void print_with_linenumbers(FILE *output, char *lines,
|
static void print_with_linenumbers(FILE *output, char *lines, bool is_func);
|
||||||
const char *header_keyword);
|
|
||||||
static void minimal_error_message(PGresult *res);
|
static void minimal_error_message(PGresult *res);
|
||||||
|
|
||||||
static void printSSLInfo(void);
|
static void printSSLInfo(void);
|
||||||
@ -1165,17 +1164,19 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
|
|||||||
/*
|
/*
|
||||||
* lineno "1" should correspond to the first line of the
|
* lineno "1" should correspond to the first line of the
|
||||||
* function body. We expect that pg_get_functiondef() will
|
* function body. We expect that pg_get_functiondef() will
|
||||||
* emit that on a line beginning with "AS ", and that there
|
* emit that on a line beginning with "AS ", "BEGIN ", or
|
||||||
* can be no such line before the real start of the function
|
* "RETURN ", and that there can be no such line before the
|
||||||
* body. Increment lineno by the number of lines before that
|
* real start of the function body. Increment lineno by the
|
||||||
* line, so that it becomes relative to the first line of the
|
* number of lines before that line, so that it becomes
|
||||||
* function definition.
|
* relative to the first line of the function definition.
|
||||||
*/
|
*/
|
||||||
const char *lines = query_buf->data;
|
const char *lines = query_buf->data;
|
||||||
|
|
||||||
while (*lines != '\0')
|
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;
|
break;
|
||||||
lineno++;
|
lineno++;
|
||||||
/* find start of next line */
|
/* find start of next line */
|
||||||
@ -2452,15 +2453,8 @@ exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
|
|||||||
|
|
||||||
if (show_linenumbers)
|
if (show_linenumbers)
|
||||||
{
|
{
|
||||||
/*
|
/* add line numbers */
|
||||||
* For functions, lineno "1" should correspond to the first
|
print_with_linenumbers(output, buf->data, is_func);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -5353,24 +5347,28 @@ count_lines_in_buf(PQExpBuffer buf)
|
|||||||
/*
|
/*
|
||||||
* Write text at *lines to output with line numbers.
|
* Write text at *lines to output with line numbers.
|
||||||
*
|
*
|
||||||
* If header_keyword isn't NULL, then line 1 should be the first line beginning
|
* For functions, lineno "1" should correspond to the first line of the
|
||||||
* with header_keyword; lines before that are unnumbered.
|
* 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.
|
* Caution: this scribbles on *lines.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
print_with_linenumbers(FILE *output, char *lines,
|
print_with_linenumbers(FILE *output, char *lines, bool is_func)
|
||||||
const char *header_keyword)
|
|
||||||
{
|
{
|
||||||
bool in_header = (header_keyword != NULL);
|
bool in_header = is_func;
|
||||||
size_t header_sz = in_header ? strlen(header_keyword) : 0;
|
|
||||||
int lineno = 0;
|
int lineno = 0;
|
||||||
|
|
||||||
while (*lines != '\0')
|
while (*lines != '\0')
|
||||||
{
|
{
|
||||||
char *eol;
|
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;
|
in_header = false;
|
||||||
|
|
||||||
/* increment lineno only for body's lines */
|
/* increment lineno only for body's lines */
|
||||||
|
@ -5165,6 +5165,13 @@ List of access methods
|
|||||||
pg_catalog | bit_xor | smallint | smallint | agg
|
pg_catalog | bit_xor | smallint | smallint | agg
|
||||||
(3 rows)
|
(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
|
\do - pg_catalog.int4
|
||||||
List of operators
|
List of operators
|
||||||
Schema | Name | Left arg type | Right arg type | Result type | Description
|
Schema | Name | Left arg type | Right arg type | Result type | Description
|
||||||
@ -5179,6 +5186,61 @@ List of access methods
|
|||||||
pg_catalog | && | anyarray | anyarray | boolean | overlaps
|
pg_catalog | && | anyarray | anyarray | boolean | overlaps
|
||||||
(1 row)
|
(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
|
||||||
-- check describing invalid multipart names
|
-- check describing invalid multipart names
|
||||||
\dA regression.heap
|
\dA regression.heap
|
||||||
improper qualified name (too many dotted names): regression.heap
|
improper qualified name (too many dotted names): regression.heap
|
||||||
|
@ -1239,9 +1239,17 @@ drop role regress_partitioning_role;
|
|||||||
\df has_database_privilege oid text
|
\df has_database_privilege oid text
|
||||||
\df has_database_privilege oid text -
|
\df has_database_privilege oid text -
|
||||||
\dfa bit* small*
|
\dfa bit* small*
|
||||||
|
\df *._pg_expandarray
|
||||||
\do - pg_catalog.int4
|
\do - pg_catalog.int4
|
||||||
\do && anyarray *
|
\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)
|
||||||
|
|
||||||
-- check describing invalid multipart names
|
-- check describing invalid multipart names
|
||||||
\dA regression.heap
|
\dA regression.heap
|
||||||
\dA nonesuch.heap
|
\dA nonesuch.heap
|
||||||
|
Loading…
x
Reference in New Issue
Block a user