*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 1617,1622 **** Tue Oct 26 21:40:57 CEST 1999
--- 1617,1648 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>\gset</literal> <replaceable class="parameter">prefix</replaceable></term>
+ 
+         <listitem>
+         <para>
+          Sends the current query input buffer to the server and stores the
+          query's output into corresponding <replaceable
+          class="parameter">variable</replaceable>.  The preceding query must
+          return only one row. You can define <replaceable
+          class="parameter">prefix</replaceable>, that will be used as prefix
+          for target variables.
+          Example:
+ <programlisting>
+ foo=&gt; SELECT 'hello' AS var1, 'wonderful' AS var2, 'world!' AS var3 \gset result_
+ foo=&gt; \echo :result_var1 :result_var3
+ hello world!
+ </programlisting>
+         </para>
+         <para>
+          When this command fails, then related <replaceable
+          class="parameter">variables</replaceable> has undefined content.
+         </para>
+         </listitem>
+       </varlistentry>
+ 
+ 
+       <varlistentry>
          <term><literal>\h</literal> or <literal>\help</literal> <literal>[ <replaceable class="parameter">command</replaceable> ]</literal></term>
          <listitem>
          <para>
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 748,753 **** exec_command(const char *cmd,
--- 748,767 ----
  		status = PSQL_CMD_SEND;
  	}
  
+ 	/* \gset send a query and store result */
+ 	else if (strcmp(cmd, "gset") == 0)
+ 	{
+ 		char	   *prefix = psql_scan_slash_option(scan_state,
+ 												   OT_NORMAL, NULL, false);
+ 
+ 		if (!prefix)
+ 			pset.gvprefix = pg_strdup("");
+ 		else
+ 			pset.gvprefix = prefix;
+ 
+ 		status = PSQL_CMD_SEND;
+ 	}
+ 
  	/* help */
  	else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
  	{
*** a/src/bin/psql/common.c
--- b/src/bin/psql/common.c
***************
*** 833,838 **** PrintQueryResults(PGresult *results)
--- 833,947 ----
  
  
  /*
+  * StoreQueryTuple: assuming query result is OK, save first tuple
+  *
+  * Returns true if successful, false otherwise.
+  */
+ static bool
+ StoreQueryTuple(PGresult *result)
+ {
+ 	bool	success = true;
+ 
+ 	if (PQntuples(result) < 1)
+ 	{
+ 		psql_error("no data found\n");
+ 		success = false;
+ 	}
+ 	else if (PQntuples(result) > 1)
+ 	{
+ 		psql_error("too many rows\n");
+ 		success = false;
+ 	}
+ 	else
+ 	{
+ 		int	i;
+ 
+ 		for (i = 0; i < PQnfields(result); i++)
+ 		{
+ 			char	*value;
+ 			char		*varname;
+ 			char		*colname = PQfname(result, i);
+ 
+ 			if (PQgetisnull(result, 0, i))
+ 				value = pset.popt.nullPrint ? pset.popt.nullPrint : "";
+ 			else
+ 				value = PQgetvalue(result, 0, i);
+ 
+ 			if (strcmp(pset.gvprefix, "") != 0)
+ 			{
+ 				/* concate prefix and column name */
+ 				varname = pg_malloc(strlen(pset.gvprefix) + strlen(colname) + 1);
+ 				strcpy(varname, pset.gvprefix);
+ 				strcat(varname, colname);
+ 			}
+ 			else
+ 				varname = pg_strdup(colname);
+ 
+ 			if (!SetVariable(pset.vars, varname, value))
+ 			{
+ 				psql_error("invalid variable name: \"%s\"\n",
+ 						   varname);
+ 				success = false;
+ 				break;
+ 			}
+ 
+ 			free(varname);
+ 		}
+ 	}
+ 
+ 	return success;
+ }
+ 
+ 
+ /*
+  * StoreQueryResult: store first row of result to selected variables
+  *
+  * Note: Utility function for use by SendQuery() only.
+  *
+  * Returns true if the query executed successfully, false otherwise.
+  */
+ static bool
+ StoreQueryResult(PGresult *result)
+ {
+ 	bool success;
+ 
+ 	switch (PQresultStatus(result))
+ 	{
+ 		case PGRES_TUPLES_OK:
+ 			success = StoreQueryTuple(result);
+ 			break;
+ 
+ 		case PGRES_COMMAND_OK:
+ 		case PGRES_EMPTY_QUERY:
+ 			psql_error("no data found\n");
+ 			success = false;
+ 			break;
+ 
+ 		case PGRES_COPY_OUT:
+ 		case PGRES_COPY_IN:
+ 			psql_error("COPY is not supported by \\gset command\n");
+ 			success = false;
+ 			break;
+ 
+ 		case PGRES_BAD_RESPONSE:
+ 		case PGRES_NONFATAL_ERROR:
+ 		case PGRES_FATAL_ERROR:
+ 			success = false;
+ 			psql_error("bad response\n");
+ 			break;
+ 
+ 		default:
+ 			success = false;
+ 			psql_error("unexpected PQresultStatus: %d\n",
+ 					   PQresultStatus(result));
+ 			break;
+ 	}
+ 
+ 	return success;
+ }
+ 
+ 
+ /*
   * SendQuery: send the query string to the backend
   * (and print out results)
   *
***************
*** 958,964 **** SendQuery(const char *query)
  
  		/* but printing results isn't: */
  		if (OK && results)
! 			OK = PrintQueryResults(results);
  	}
  	else
  	{
--- 1067,1078 ----
  
  		/* but printing results isn't: */
  		if (OK && results)
! 		{
! 			if (pset.gvprefix)
! 				OK = StoreQueryResult(results);
! 			else
! 				OK = PrintQueryResults(results);
! 		}
  	}
  	else
  	{
***************
*** 1034,1039 **** SendQuery(const char *query)
--- 1148,1160 ----
  
  	PQclear(results);
  
+ 	/* clean gset prefix if was used */
+ 	if (pset.gvprefix)
+ 	{
+ 		free(pset.gvprefix);
+ 		pset.gvprefix = NULL;
+ 	}
+ 
  	/* Possible microtiming output */
  	if (pset.timing)
  		printf(_("Time: %.3f ms\n"), elapsed_msec);
***************
*** 1082,1087 **** ExecQueryUsingCursor(const char *query, double *elapsed_msec)
--- 1203,1209 ----
  	instr_time	before,
  				after;
  	int			flush_error;
+ 	bool			first_iteration = true;
  
  	*elapsed_msec = 0;
  
***************
*** 1182,1187 **** ExecQueryUsingCursor(const char *query, double *elapsed_msec)
--- 1304,1336 ----
  
  		ntuples = PQntuples(results);
  
+ 		if (pset.gvprefix)
+ 		{
+ 			/* second iteration should to return zero rows */
+ 			if (first_iteration)
+ 			{
+ 				OK = StoreQueryResult(results);
+ 				PQclear(results);
+ 				first_iteration = false;
+ 			}
+ 			else if (ntuples > 0)
+ 			{
+ 				psql_error("too many rows\n");
+ 				OK = false;
+ 			}
+ 
+ 			if (!OK)
+ 			{
+ 				flush_error = fflush(pset.queryFout);
+ 				break;
+ 			}
+ 
+ 			if (ntuples < pset.fetch_count || cancel_pressed)
+ 				break;
+ 			else
+ 				continue;
+ 		}
+ 
  		if (ntuples < pset.fetch_count)
  		{
  			/* this is the last result set, so allow footer decoration */
***************
*** 1237,1242 **** ExecQueryUsingCursor(const char *query, double *elapsed_msec)
--- 1386,1396 ----
  		free(pset.gfname);
  		pset.gfname = NULL;
  	}
+ 	else if (pset.gvprefix)
+ 	{
+ 		free(pset.gvprefix);
+ 		pset.gvprefix = NULL;
+ 	}
  	else if (did_pager)
  	{
  		ClosePager(pset.queryFout);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 165,177 **** slashUsage(unsigned short int pager)
  
  	currdb = PQdb(pset.db);
  
! 	output = PageOutput(94, pager);
  
  	/* if you add/remove a line here, change the row count above */
  
  	fprintf(output, _("General\n"));
  	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
  	fprintf(output, _("  \\g [FILE] or ;         execute query (and send results to file or |pipe)\n"));
  	fprintf(output, _("  \\h [NAME]              help on syntax of SQL commands, * for all commands\n"));
  	fprintf(output, _("  \\q                     quit psql\n"));
  	fprintf(output, "\n");
--- 165,178 ----
  
  	currdb = PQdb(pset.db);
  
! 	output = PageOutput(95, pager);
  
  	/* if you add/remove a line here, change the row count above */
  
  	fprintf(output, _("General\n"));
  	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
  	fprintf(output, _("  \\g [FILE] or ;         execute query (and send results to file or |pipe)\n"));
+ 	fprintf(output, _("  \\gset prefix           execute query and store result in internal variables\n"));
  	fprintf(output, _("  \\h [NAME]              help on syntax of SQL commands, * for all commands\n"));
  	fprintf(output, _("  \\q                     quit psql\n"));
  	fprintf(output, "\n");
*** a/src/bin/psql/settings.h
--- b/src/bin/psql/settings.h
***************
*** 73,78 **** typedef struct _psqlSettings
--- 73,79 ----
  	printQueryOpt popt;
  
  	char	   *gfname;			/* one-shot file output argument for \g */
+ 	char	   *gvprefix;			/* one-shot prefix argument for \gset */
  
  	bool		notty;			/* stdin or stdout is not a tty (as determined
  								 * on startup) */
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
***************
*** 856,862 **** psql_completion(char *text, int start, int end)
  		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
  		"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
  		"\\e", "\\echo", "\\ef", "\\encoding",
! 		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
  		"\\set", "\\sf", "\\t", "\\T",
--- 856,862 ----
  		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
  		"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
  		"\\e", "\\echo", "\\ef", "\\encoding",
! 		"\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
  		"\\set", "\\sf", "\\t", "\\T",
*** /dev/null
--- b/src/test/regress/expected/psql_cmd.out
***************
*** 0 ****
--- 1,62 ----
+ -- \gset
+ select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
+ \echo :pref01_test01 :pref01_test02 :pref01_test03
+ 10 20 Hello
+ -- should fail bad name
+ select 10 as "bad name" \gset
+ invalid variable name: "bad name"
+ -- more in one line
+ select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
+ 1
+ select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y
+ 3
+ 4
+ select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y
+  x | y 
+ ---+---
+  5 | 6
+ (1 row)
+ 
+ 5 6
+ select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y
+  x | y 
+ ---+---
+  7 | 8
+ (1 row)
+ 
+ 7 8
+ \pset null '(null)'
+ select NULL as var1 \gset pref01_
+ \echo :pref01_var1
+ (null)
+ --should file - no returned one tuple
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) \gset pref01_
+ too many rows
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) where false \gset pref01_
+ no data found
+ -- should to work with cursors
+ \set FETCH_COUNT 1
+ select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
+ 1
+ select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y
+ 3
+ 4
+ select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y
+  x | y 
+ ---+---
+  5 | 6
+ (1 row)
+ 
+ 5 6
+ select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y
+  x | y 
+ ---+---
+  7 | 8
+ (1 row)
+ 
+ 7 8
+ --should file - no returned one tuple
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) \gset pref01_
+ too many rows
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) where false \gset pref01_
+ no data found
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 21,26 **** test: strings
--- 21,31 ----
  test: numerology
  
  # ----------
+ # verify console features
+ # ----------
+ test: psql_cmd
+ 
+ # ----------
  # The second group of parallel tests
  # ----------
  test: point lseg box path polygon circle date time timetz timestamp timestamptz interval abstime reltime tinterval inet macaddr tstypes comments
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 135,137 **** test: largeobject
--- 135,138 ----
  test: with
  test: xml
  test: stats
+ test: psql_cmd
*** /dev/null
--- b/src/test/regress/sql/psql_cmd.sql
***************
*** 0 ****
--- 1,33 ----
+ -- \gset
+ 
+ select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
+ 
+ \echo :pref01_test01 :pref01_test02 :pref01_test03
+ 
+ -- should fail bad name
+ select 10 as "bad name" \gset
+ 
+ -- more in one line
+ select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
+ select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y
+ select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y
+ select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y
+ 
+ \pset null '(null)'
+ select NULL as var1 \gset pref01_
+ \echo :pref01_var1
+ 
+ --should file - no returned one tuple
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) \gset pref01_
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) where false \gset pref01_
+ 
+ -- should to work with cursors
+ \set FETCH_COUNT 1
+ select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x
+ select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y
+ select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y
+ select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y
+ 
+ --should file - no returned one tuple
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) \gset pref01_
+ select 10 as test01, 20 as test02, 'Hello' as test03 from generate_series(1,3) where false \gset pref01_
