Hi

rebase

Regards

Pavel
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 402c4b1b26..429e0d5708 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2199,6 +2199,16 @@ CREATE INDEX
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><literal>\gf <replaceable class="parameter">format</replaceable> [ <replaceable class="parameter">filename</replaceable> ]</literal></term>
+        <term><literal>\gf <replaceable class="parameter">format</replaceable> [ |<replaceable class="parameter">command</replaceable> ]</literal></term>
+        <listitem>
+        <para>
+        <literal>\gf</literal> is equivalent to <literal>\g</literal>, but
+        forces specified format.  See <literal>\pset format</literal>.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\gset [ <replaceable class="parameter">prefix</replaceable> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index abb18a19c2..5b460fb656 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -163,6 +163,8 @@ static void printGSSInfo(void);
 static bool printPsetInfo(const char *param, struct printQueryOpt *popt);
 static char *pset_value_string(const char *param, struct printQueryOpt *popt);
 
+static bool format_number(const char *value, int vallen, enum printFormat *numformat);
+
 #ifdef WIN32
 static void checkWin32Codepage(void);
 #endif
@@ -333,7 +335,8 @@ exec_command(const char *cmd,
 		status = exec_command_errverbose(scan_state, active_branch);
 	else if (strcmp(cmd, "f") == 0)
 		status = exec_command_f(scan_state, active_branch);
-	else if (strcmp(cmd, "g") == 0 || strcmp(cmd, "gx") == 0)
+	else if (strcmp(cmd, "g") == 0 || strcmp(cmd, "gx") == 0 ||
+			 strcmp(cmd, "gf") == 0)
 		status = exec_command_g(scan_state, active_branch, cmd);
 	else if (strcmp(cmd, "gdesc") == 0)
 		status = exec_command_gdesc(scan_state, active_branch);
@@ -1290,7 +1293,42 @@ exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd)
 
 	if (active_branch)
 	{
-		char	   *fname = psql_scan_slash_option(scan_state,
+		char	   *fname;
+
+		if (strcmp(cmd, "gf") == 0)
+		{
+			char	   *fmtname = psql_scan_slash_option(scan_state,
+														 OT_NORMAL, NULL, false);
+
+			if (!fmtname)
+			{
+				pg_log_error("no format name");
+
+				return PSQL_CMD_ERROR;
+			}
+			else
+			{
+				enum printFormat	format;
+				bool		result;
+
+				result = format_number(fmtname, strlen(fmtname), &format);
+
+				free(fmtname);
+
+				if (result)
+				{
+					pset.gf_format = format;
+				}
+				else
+				{
+					pg_log_error("\\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped");
+
+					return PSQL_CMD_ERROR;
+				}
+			}
+		}
+
+		fname  = psql_scan_slash_option(scan_state,
 												   OT_FILEPIPE, NULL, false);
 
 		if (!fname)
@@ -3777,6 +3815,68 @@ _unicode_linestyle2string(int linestyle)
 	return "unknown";
 }
 
+/*
+ * Returns true if format name was recognized.
+ */
+static bool
+format_number(const char *value, int vallen, enum printFormat *numformat)
+{
+	static const struct fmt
+	{
+		const char *name;
+		enum printFormat number;
+	}			formats[] =
+	{
+		/* remember to update error message below when adding more */
+		{"aligned", PRINT_ALIGNED},
+		{"asciidoc", PRINT_ASCIIDOC},
+		{"csv", PRINT_CSV},
+		{"html", PRINT_HTML},
+		{"latex", PRINT_LATEX},
+		{"troff-ms", PRINT_TROFF_MS},
+		{"unaligned", PRINT_UNALIGNED},
+		{"wrapped", PRINT_WRAPPED}
+	};
+
+	int			match_pos = -1;
+
+	for (int i = 0; i < lengthof(formats); i++)
+	{
+		if (pg_strncasecmp(formats[i].name, value, vallen) == 0)
+		{
+			if (match_pos < 0)
+				match_pos = i;
+			else
+			{
+				pg_log_error("\\pset: ambiguous abbreviation \"%s\" matches both \"%s\" and \"%s\"",
+							 value,
+							 formats[match_pos].name, formats[i].name);
+				return false;
+			}
+		}
+	}
+	if (match_pos >= 0)
+	{
+		*numformat = formats[match_pos].number;
+
+		return true;
+	}
+	else if (pg_strncasecmp("latex-longtable", value, vallen) == 0)
+	{
+		/*
+		 * We must treat latex-longtable specially because latex is a
+		 * prefix of it; if both were in the table above, we'd think
+		 * "latex" is ambiguous.
+		 */
+		*numformat = PRINT_LATEX_LONGTABLE;
+
+		return true;
+	}
+
+	return false;
+}
+
+
 /*
  * do_pset
  *
@@ -3794,55 +3894,14 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	/* set format */
 	if (strcmp(param, "format") == 0)
 	{
-		static const struct fmt
-		{
-			const char *name;
-			enum printFormat number;
-		}			formats[] =
-		{
-			/* remember to update error message below when adding more */
-			{"aligned", PRINT_ALIGNED},
-			{"asciidoc", PRINT_ASCIIDOC},
-			{"csv", PRINT_CSV},
-			{"html", PRINT_HTML},
-			{"latex", PRINT_LATEX},
-			{"troff-ms", PRINT_TROFF_MS},
-			{"unaligned", PRINT_UNALIGNED},
-			{"wrapped", PRINT_WRAPPED}
-		};
+		enum printFormat number;
 
 		if (!value)
 			;
 		else
 		{
-			int			match_pos = -1;
-
-			for (int i = 0; i < lengthof(formats); i++)
-			{
-				if (pg_strncasecmp(formats[i].name, value, vallen) == 0)
-				{
-					if (match_pos < 0)
-						match_pos = i;
-					else
-					{
-						pg_log_error("\\pset: ambiguous abbreviation \"%s\" matches both \"%s\" and \"%s\"",
-									 value,
-									 formats[match_pos].name, formats[i].name);
-						return false;
-					}
-				}
-			}
-			if (match_pos >= 0)
-				popt->topt.format = formats[match_pos].number;
-			else if (pg_strncasecmp("latex-longtable", value, vallen) == 0)
-			{
-				/*
-				 * We must treat latex-longtable specially because latex is a
-				 * prefix of it; if both were in the table above, we'd think
-				 * "latex" is ambiguous.
-				 */
-				popt->topt.format = PRINT_LATEX_LONGTABLE;
-			}
+			if (format_number(value, vallen, &number))
+				popt->topt.format = number;
 			else
 			{
 				pg_log_error("\\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped");
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 396a40089c..d90359fd09 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -714,6 +714,9 @@ PrintQueryTuples(const PGresult *results)
 	if (pset.g_expanded)
 		my_popt.topt.expanded = 1;
 
+	if (pset.gf_format != PRINT_NOTHING)
+		my_popt.topt.format = pset.gf_format;
+
 	/* write output to \g argument, if any */
 	if (pset.gfname)
 	{
@@ -1421,6 +1424,9 @@ sendquery_cleanup:
 	/* reset \gx's expanded-mode flag */
 	pset.g_expanded = false;
 
+	/* reset \gf's format */
+	pset.gf_format = PRINT_NOTHING;
+
 	/* reset \gset trigger */
 	if (pset.gset_prefix)
 	{
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9a18cb3059..438513f853 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -169,7 +169,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(128, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -178,6 +178,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\g [FILE] or ;         execute query (and send results to file or |pipe)\n"));
 	fprintf(output, _("  \\gdesc                 describe result of query, without executing it\n"));
 	fprintf(output, _("  \\gexec                 execute query, then execute each value in its result\n"));
+	fprintf(output, _("  \\gf format [FILE]      as \\g, but forces output format\n"));
 	fprintf(output, _("  \\gset [PREFIX]         execute query and store results in psql variables\n"));
 	fprintf(output, _("  \\gx [FILE]             as \\g, but forces expanded output mode\n"));
 	fprintf(output, _("  \\q                     quit psql\n"));
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 2b384a38a1..0f1f469292 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -96,6 +96,7 @@ typedef struct _psqlSettings
 	bool		gdesc_flag;		/* one-shot request to describe query results */
 	bool		gexec_flag;		/* one-shot request to execute query results */
 	bool		crosstab_flag;	/* one-shot request to crosstab results */
+	enum printFormat gf_format;	/* one-shot print format requested by \gf */
 	char	   *ctv_args[4];	/* \crosstabview arguments */
 
 	bool		notty;			/* stdin or stdout is not a tty (as determined
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index ae35fa4aa9..ed6945a7f1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1479,7 +1479,7 @@ psql_completion(const char *text, int start, int end)
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
-		"\\g", "\\gdesc", "\\gexec", "\\gset", "\\gx",
+		"\\g", "\\gdesc", "\\gexec", "\\gf", "\\gset", "\\gx",
 		"\\h", "\\help", "\\H",
 		"\\i", "\\if", "\\ir",
 		"\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
@@ -3781,6 +3781,12 @@ psql_completion(const char *text, int start, int end)
 
 	else if (TailMatchesCS("\\encoding"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
+
+	else if (TailMatchesCS("\\gf"))
+		COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
+						 "latex-longtable", "troff-ms", "unaligned",
+						 "wrapped");
+
 	else if (TailMatchesCS("\\h|\\help"))
 		COMPLETE_WITH_LIST(sql_commands);
 	else if (TailMatchesCS("\\h|\\help", MatchAny))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 2423ae2f37..79d702d056 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4174,6 +4174,7 @@ deallocate q;
 -- check ambiguous format requests
 \pset format a
 \pset: ambiguous abbreviation "a" matches both "aligned" and "asciidoc"
+\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped
 \pset format l
 -- clean up after output format tests
 drop table psql_serial_tab;
@@ -4971,3 +4972,28 @@ List of access methods
  hash  | uuid_ops        | uuid          | uuid           |      2 | uuid_hash_extended
 (5 rows)
 
+-- execution with specified format
+select 1,2;
+ ?column? | ?column? 
+----------+----------
+        1 |        2
+(1 row)
+
+select 1,2 \gf csv
+?column?,?column?
+1,2
+select 1,2;
+ ?column? | ?column? 
+----------+----------
+        1 |        2
+(1 row)
+
+select 1,2 \gf latex
+\begin{tabular}{r | r}
+\textit{?column?} & \textit{?column?} \\
+\hline
+1 & 2 \\
+\end{tabular}
+
+\noindent (1 row) \\
+
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 3c876d2699..ae1c1ae797 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1200,3 +1200,9 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp brin uuid_minmax_ops
 \dAp * pg_catalog.uuid_ops
+
+-- execution with specified format
+select 1,2;
+select 1,2 \gf csv
+select 1,2;
+select 1,2 \gf latex

Reply via email to