diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 1055af5..933e181 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -86,6 +86,10 @@ static backslashResult exec_command_errverbose(PsqlScanState scan_state, bool ac
 static backslashResult exec_command_f(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_g(PsqlScanState scan_state, bool active_branch,
 									  const char *cmd);
+static backslashResult process_command_g_options(char *first_option,
+												 PsqlScanState scan_state,
+												 bool active_branch,
+												 const char *cmd);
 static backslashResult exec_command_gdesc(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_gexec(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_gset(PsqlScanState scan_state, bool active_branch);
@@ -1280,19 +1284,40 @@ exec_command_f(PsqlScanState scan_state, bool active_branch)
 }
 
 /*
- * \g [filename] -- send query, optionally with output to file/pipe
- * \gx [filename] -- same as \g, with expanded mode forced
+ * \g  [(pset-option[=pset-value] ...)] [filename/shell-command]
+ * \gx [(pset-option[=pset-value] ...)] [filename/shell-command]
+ *
+ * Send the current query.  If pset options are specified, they are made
+ * active just for this query.  If a filename or pipe command is given,
+ * the query output goes there.  \gx implicitly forces expanded = 1 along
+ * with any other pset options that are specified.
  */
 static backslashResult
 exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd)
 {
 	backslashResult status = PSQL_CMD_SKIP_LINE;
+	char	   *fname;
 
-	if (active_branch)
+	/*
+	 * Because the option processing for this is fairly complicated, we do it
+	 * and then decide whether the branch is active.
+	 */
+	fname = psql_scan_slash_option(scan_state,
+								   OT_FILEPIPE, NULL, false);
+
+	if (fname && fname[0] == '(')
 	{
-		char	   *fname = psql_scan_slash_option(scan_state,
-												   OT_FILEPIPE, NULL, false);
+		/* Consume pset options through trailing ')' ... */
+		status = process_command_g_options(fname + 1, scan_state,
+										   active_branch, cmd);
+		free(fname);
+		/* ... and again attempt to scan the filename. */
+		fname = psql_scan_slash_option(scan_state,
+									   OT_FILEPIPE, NULL, false);
+	}
 
+	if (status == PSQL_CMD_SKIP_LINE && active_branch)
+	{
 		if (!fname)
 			pset.gfname = NULL;
 		else
@@ -1300,22 +1325,89 @@ exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			expand_tilde(&fname);
 			pset.gfname = pg_strdup(fname);
 		}
-		free(fname);
 		if (strcmp(cmd, "gx") == 0)
 		{
-			/* save settings, then force expanded = 1 */
-			pset.gsavepopt = savePsetInfo(&pset.popt);
+			/* save settings if not done already, then force expanded = 1 */
+			if (pset.gsavepopt == NULL)
+				pset.gsavepopt = savePsetInfo(&pset.popt);
 			pset.popt.topt.expanded = 1;
 		}
 		status = PSQL_CMD_SEND;
 	}
-	else
-		ignore_slash_filepipe(scan_state);
+
+	free(fname);
 
 	return status;
 }
 
 /*
+ * Process parenthesized pset options for \g
+ */
+static backslashResult
+process_command_g_options(char *first_option, PsqlScanState scan_state,
+						  bool active_branch, const char *cmd)
+{
+	bool		success = true;
+	bool		found_r_paren = false;
+
+	do
+	{
+		char	   *option;
+		size_t		optlen;
+
+		/* If not first time through, collect a new option */
+		if (first_option)
+			option = first_option;
+		else
+		{
+			option = psql_scan_slash_option(scan_state,
+											OT_NORMAL, NULL, false);
+			if (!option)
+			{
+				if (active_branch)
+				{
+					pg_log_error("\\%s: missing right parenthesis", cmd);
+					success = false;
+				}
+				break;
+			}
+		}
+
+		/* Check for terminating right paren, and remove it from string */
+		optlen = strlen(option);
+		if (optlen > 0 && option[optlen - 1] == ')')
+		{
+			option[--optlen] = '\0';
+			found_r_paren = true;
+		}
+
+		/* If there was anything besides right paren, parse/execute it */
+		if (optlen > 0)
+		{
+			char	   *valptr = strchr(option, '=');
+
+			if (valptr)
+				*valptr++ = '\0';
+			if (active_branch)
+			{
+				/* save settings if not done already, then apply option */
+				if (pset.gsavepopt == NULL)
+					pset.gsavepopt = savePsetInfo(&pset.popt);
+				success &= do_pset(option, valptr, &pset.popt, true);
+			}
+		}
+
+		/* Clean up after this option.  We should not free first_option. */
+		if (first_option)
+			first_option = NULL;
+		else
+			free(option);
+	} while (!found_r_paren);
+
+	return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
+}
+
+/*
  * \gdesc -- describe query result
  */
 static backslashResult
