mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +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:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user