diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 85fe0ecb6abe8d87ebaec5a824fd50308f33b15c..4d107f6225f5bb8ab67b4e5c86590edf5131d75e 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -160,8 +160,7 @@ EOF
       Echo the actual queries generated by <command>\d</command> and other backslash
       commands. You can use this to study <application>psql</application>'s
       internal operations. This is equivalent to
-      setting the variable <varname>ECHO_HIDDEN</varname> from within
-      <application>psql</application>.
+      setting the variable <varname>ECHO_HIDDEN</varname> to <literal>on</>.
       </para>
       </listitem>
     </varlistentry>
@@ -320,8 +319,8 @@ EOF
       quietly. By default, it prints welcome messages and various
       informational output. If this option is used, none of this
       happens. This is useful with the <option>-c</option> option.
-      Within <application>psql</application> you can also set the
-      <varname>QUIET</varname> variable to achieve the same effect.
+      This is equivalent to setting the variable <varname>QUIET</varname>
+      to <literal>on</>.
       </para>
       </listitem>
     </varlistentry>
@@ -2720,8 +2719,9 @@ bar
         <term><varname>ECHO_HIDDEN</varname></term>
         <listitem>
         <para>
-        When this variable is set and a backslash command queries the
-        database, the query is first shown. This way you can study the
+        When this variable is set to <literal>on</> and a backslash command
+        queries the database, the query is first shown.
+        This feature helps you to study
         <productname>PostgreSQL</productname> internals and provide
         similar functionality in your own programs. (To select this behavior
         on program start-up, use the switch <option>-E</option>.)  If you set
@@ -2879,16 +2879,16 @@ bar
         <term><varname>ON_ERROR_ROLLBACK</varname></term>
         <listitem>
         <para>
-        When <literal>on</>, if a statement in a transaction block
+        When set to <literal>on</>, if a statement in a transaction block
         generates an error, the error is ignored and the transaction
-        continues. When <literal>interactive</>, such errors are only
+        continues. When set to <literal>interactive</>, such errors are only
         ignored in interactive sessions, and not when reading script
-        files. When <literal>off</> (the default), a statement in a
+        files. When unset or set to <literal>off</>, a statement in a
         transaction block that generates an error aborts the entire
-        transaction. The on_error_rollback-on mode works by issuing an
+        transaction. The error rollback mode works by issuing an
         implicit <command>SAVEPOINT</> for you, just before each command
-        that is in a transaction block, and rolls back to the savepoint
-        on error.
+        that is in a transaction block, and then rolling back to the
+        savepoint if the command fails.
         </para>
         </listitem>
       </varlistentry>
@@ -2898,7 +2898,8 @@ bar
         <listitem>
         <para>
         By default, command processing continues after an error.  When this
-        variable is set, it will instead stop immediately.  In interactive mode,
+        variable is set to <literal>on</>, processing will instead stop
+        immediately.  In interactive mode,
         <application>psql</application> will return to the command prompt;
         otherwise, <application>psql</application> will exit, returning
         error code 3 to distinguish this case from fatal error
@@ -2940,8 +2941,8 @@ bar
         <term><varname>QUIET</varname></term>
         <listitem>
         <para>
-        This variable is equivalent to the command line option
-        <option>-q</option>. It is probably not too useful in
+        Setting this variable to <literal>on</> is equivalent to the command
+        line option <option>-q</option>. It is probably not too useful in
         interactive mode.
         </para>
         </listitem>
@@ -2951,8 +2952,8 @@ bar
         <term><varname>SINGLELINE</varname></term>
         <listitem>
         <para>
-        This variable is equivalent to the command line option
-        <option>-S</option>.
+        Setting this variable to <literal>on</> is equivalent to the command
+        line option <option>-S</option>.
         </para>
         </listitem>
       </varlistentry>
@@ -2961,8 +2962,8 @@ bar
         <term><varname>SINGLESTEP</varname></term>
         <listitem>
         <para>
-        This variable is equivalent to the command line option
-        <option>-s</option>.
+        Setting this variable to <literal>on</> is equivalent to the command
+        line option <option>-s</option>.
         </para>
         </listitem>
       </varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 290598581ebdd8f427d08556d1040e9e068566ab..b74db204ea27aee587c9bbc6978c1b9dc245b092 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1291,7 +1291,7 @@ exec_command(const char *cmd,
 												 OT_NORMAL, NULL, false);
 
 		if (opt)
-			pset.timing = ParseVariableBool(opt);
+			pset.timing = ParseVariableBool(opt, "\\timing");
 		else
 			pset.timing = !pset.timing;
 		if (!pset.quiet)
@@ -2223,12 +2223,14 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	}
 
 	/* set expanded/vertical mode */
-	else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0)
+	else if (strcmp(param, "x") == 0 ||
+			 strcmp(param, "expanded") == 0 ||
+			 strcmp(param, "vertical") == 0)
 	{
 		if (value && pg_strcasecmp(value, "auto") == 0)
 			popt->topt.expanded = 2;
 		else if (value)
-			popt->topt.expanded = ParseVariableBool(value);
+			popt->topt.expanded = ParseVariableBool(value, param);
 		else
 			popt->topt.expanded = !popt->topt.expanded;
 		if (!quiet)
@@ -2246,7 +2248,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "numericlocale") == 0)
 	{
 		if (value)
-			popt->topt.numericLocale = ParseVariableBool(value);
+			popt->topt.numericLocale = ParseVariableBool(value, param);
 		else
 			popt->topt.numericLocale = !popt->topt.numericLocale;
 		if (!quiet)
@@ -2330,7 +2332,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
 	{
 		if (value)
-			popt->topt.tuples_only = ParseVariableBool(value);
+			popt->topt.tuples_only = ParseVariableBool(value, param);
 		else
 			popt->topt.tuples_only = !popt->topt.tuples_only;
 		if (!quiet)
@@ -2384,10 +2386,12 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 		if (value && pg_strcasecmp(value, "always") == 0)
 			popt->topt.pager = 2;
 		else if (value)
-			if (ParseVariableBool(value))
+		{
+			if (ParseVariableBool(value, param))
 				popt->topt.pager = 1;
 			else
 				popt->topt.pager = 0;
+		}
 		else if (popt->topt.pager == 1)
 			popt->topt.pager = 0;
 		else
@@ -2407,7 +2411,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "footer") == 0)
 	{
 		if (value)
-			popt->topt.default_footer = ParseVariableBool(value);
+			popt->topt.default_footer = ParseVariableBool(value, param);
 		else
 			popt->topt.default_footer = !popt->topt.default_footer;
 		if (!quiet)
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index e27dd5b1666d322f147180537fa8d0d3614dae89..c902cd2c2bf9835fa0d18a29b98f1566be6828d4 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -27,6 +27,11 @@
 #define DEFAULT_PROMPT2 "%/%R%# "
 #define DEFAULT_PROMPT3 ">> "
 
+/*
+ * Note: these enums should generally be chosen so that zero corresponds
+ * to the default behavior.
+ */
+
 typedef enum
 {
 	PSQL_ECHO_NONE,
@@ -48,6 +53,14 @@ typedef enum
 	PSQL_ERROR_ROLLBACK_ON
 } PSQL_ERROR_ROLLBACK;
 
+typedef enum
+{
+	PSQL_COMP_CASE_PRESERVE_UPPER,
+	PSQL_COMP_CASE_PRESERVE_LOWER,
+	PSQL_COMP_CASE_UPPER,
+	PSQL_COMP_CASE_LOWER
+} PSQL_COMP_CASE;
+
 typedef enum
 {
 	hctl_none = 0,
@@ -109,6 +122,7 @@ typedef struct _psqlSettings
 	PSQL_ECHO	echo;
 	PSQL_ECHO_HIDDEN echo_hidden;
 	PSQL_ERROR_ROLLBACK on_error_rollback;
+	PSQL_COMP_CASE comp_case;
 	HistControl histcontrol;
 	const char *prompt1;
 	const char *prompt2;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 94e9ab17ebfaae7e72307c1659044ee2a262c5a0..dc5bd3a92c2d8ef9508bc11ebd171119df07ae2b 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -667,31 +667,31 @@ showVersion(void)
 static void
 autocommit_hook(const char *newval)
 {
-	pset.autocommit = ParseVariableBool(newval);
+	pset.autocommit = ParseVariableBool(newval, "AUTOCOMMIT");
 }
 
 static void
 on_error_stop_hook(const char *newval)
 {
-	pset.on_error_stop = ParseVariableBool(newval);
+	pset.on_error_stop = ParseVariableBool(newval, "ON_ERROR_STOP");
 }
 
 static void
 quiet_hook(const char *newval)
 {
-	pset.quiet = ParseVariableBool(newval);
+	pset.quiet = ParseVariableBool(newval, "QUIET");
 }
 
 static void
 singleline_hook(const char *newval)
 {
-	pset.singleline = ParseVariableBool(newval);
+	pset.singleline = ParseVariableBool(newval, "SINGLELINE");
 }
 
 static void
 singlestep_hook(const char *newval)
 {
-	pset.singlestep = ParseVariableBool(newval);
+	pset.singlestep = ParseVariableBool(newval, "SINGLESTEP");
 }
 
 static void
@@ -705,12 +705,18 @@ echo_hook(const char *newval)
 {
 	if (newval == NULL)
 		pset.echo = PSQL_ECHO_NONE;
-	else if (strcmp(newval, "queries") == 0)
+	else if (pg_strcasecmp(newval, "queries") == 0)
 		pset.echo = PSQL_ECHO_QUERIES;
-	else if (strcmp(newval, "all") == 0)
+	else if (pg_strcasecmp(newval, "all") == 0)
 		pset.echo = PSQL_ECHO_ALL;
+	else if (pg_strcasecmp(newval, "none") == 0)
+		pset.echo = PSQL_ECHO_NONE;
 	else
+	{
+		psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
+				   newval, "ECHO", "none");
 		pset.echo = PSQL_ECHO_NONE;
+	}
 }
 
 static void
@@ -718,12 +724,12 @@ echo_hidden_hook(const char *newval)
 {
 	if (newval == NULL)
 		pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
-	else if (strcmp(newval, "noexec") == 0)
+	else if (pg_strcasecmp(newval, "noexec") == 0)
 		pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
-	else if (pg_strcasecmp(newval, "off") == 0)
-		pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
-	else
+	else if (ParseVariableBool(newval, "ECHO_HIDDEN"))
 		pset.echo_hidden = PSQL_ECHO_HIDDEN_ON;
+	else	/* ParseVariableBool printed msg if needed */
+		pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
 }
 
 static void
@@ -733,10 +739,31 @@ on_error_rollback_hook(const char *newval)
 		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
 	else if (pg_strcasecmp(newval, "interactive") == 0)
 		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
-	else if (pg_strcasecmp(newval, "off") == 0)
+	else if (ParseVariableBool(newval, "ON_ERROR_ROLLBACK"))
+		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON;
+	else	/* ParseVariableBool printed msg if needed */
 		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
+}
+
+static void
+comp_keyword_case_hook(const char *newval)
+{
+	if (newval == NULL)
+		pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
+	else if (pg_strcasecmp(newval, "preserve-upper") == 0)
+		pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
+	else if (pg_strcasecmp(newval, "preserve-lower") == 0)
+		pset.comp_case = PSQL_COMP_CASE_PRESERVE_LOWER;
+	else if (pg_strcasecmp(newval, "upper") == 0)
+		pset.comp_case = PSQL_COMP_CASE_UPPER;
+	else if (pg_strcasecmp(newval, "lower") == 0)
+		pset.comp_case = PSQL_COMP_CASE_LOWER;
 	else
-		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON;
+	{
+		psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
+				   newval, "COMP_KEYWORD_CASE", "preserve-upper");
+		pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
+	}
 }
 
 static void
@@ -744,14 +771,20 @@ histcontrol_hook(const char *newval)
 {
 	if (newval == NULL)
 		pset.histcontrol = hctl_none;
-	else if (strcmp(newval, "ignorespace") == 0)
+	else if (pg_strcasecmp(newval, "ignorespace") == 0)
 		pset.histcontrol = hctl_ignorespace;
-	else if (strcmp(newval, "ignoredups") == 0)
+	else if (pg_strcasecmp(newval, "ignoredups") == 0)
 		pset.histcontrol = hctl_ignoredups;
-	else if (strcmp(newval, "ignoreboth") == 0)
+	else if (pg_strcasecmp(newval, "ignoreboth") == 0)
 		pset.histcontrol = hctl_ignoreboth;
+	else if (pg_strcasecmp(newval, "none") == 0)
+		pset.histcontrol = hctl_none;
 	else
+	{
+		psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
+				   newval, "HISTCONTROL", "none");
 		pset.histcontrol = hctl_none;
+	}
 }
 
 static void
@@ -777,14 +810,18 @@ verbosity_hook(const char *newval)
 {
 	if (newval == NULL)
 		pset.verbosity = PQERRORS_DEFAULT;
-	else if (strcmp(newval, "default") == 0)
+	else if (pg_strcasecmp(newval, "default") == 0)
 		pset.verbosity = PQERRORS_DEFAULT;
-	else if (strcmp(newval, "terse") == 0)
+	else if (pg_strcasecmp(newval, "terse") == 0)
 		pset.verbosity = PQERRORS_TERSE;
-	else if (strcmp(newval, "verbose") == 0)
+	else if (pg_strcasecmp(newval, "verbose") == 0)
 		pset.verbosity = PQERRORS_VERBOSE;
 	else
+	{
+		psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
+				   newval, "VERBOSITY", "default");
 		pset.verbosity = PQERRORS_DEFAULT;
+	}
 
 	if (pset.db)
 		PQsetErrorVerbosity(pset.db, pset.verbosity);
@@ -805,6 +842,7 @@ EstablishVariableSpace(void)
 	SetVariableAssignHook(pset.vars, "ECHO", echo_hook);
 	SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook);
 	SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook);
+	SetVariableAssignHook(pset.vars, "COMP_KEYWORD_CASE", comp_keyword_case_hook);
 	SetVariableAssignHook(pset.vars, "HISTCONTROL", histcontrol_hook);
 	SetVariableAssignHook(pset.vars, "PROMPT1", prompt1_hook);
 	SetVariableAssignHook(pset.vars, "PROMPT2", prompt2_hook);
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index d839aeba267fcf3d3fc18445b5f247a86c0c48c9..870dd6f64bafb02d3a1fc92f3586d90bc73e756b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3575,7 +3575,7 @@ complete_from_files(const char *text, int state)
 
 /*
  * Make a pg_strdup copy of s and convert the case according to
- * COMP_KEYWORD_CASE variable, using ref as the text that was already entered.
+ * COMP_KEYWORD_CASE setting, using ref as the text that was already entered.
  */
 static char *
 pg_strdup_keyword_case(const char *s, const char *ref)
@@ -3583,38 +3583,22 @@ pg_strdup_keyword_case(const char *s, const char *ref)
 	char	   *ret,
 			   *p;
 	unsigned char first = ref[0];
-	int			tocase;
-	const char *varval;
-
-	varval = GetVariable(pset.vars, "COMP_KEYWORD_CASE");
-	if (!varval)
-		tocase = 0;
-	else if (strcmp(varval, "lower") == 0)
-		tocase = -2;
-	else if (strcmp(varval, "preserve-lower") == 0)
-		tocase = -1;
-	else if (strcmp(varval, "preserve-upper") == 0)
-		tocase = +1;
-	else if (strcmp(varval, "upper") == 0)
-		tocase = +2;
-	else
-		tocase = 0;
-
-	/* default */
-	if (tocase == 0)
-		tocase = +1;
 
 	ret = pg_strdup(s);
 
-	if (tocase == -2
-		|| ((tocase == -1 || tocase == +1) && islower(first))
-		|| (tocase == -1 && !isalpha(first))
-		)
+	if (pset.comp_case == PSQL_COMP_CASE_LOWER ||
+		((pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER ||
+		  pset.comp_case == PSQL_COMP_CASE_PRESERVE_UPPER) && islower(first)) ||
+		(pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER && !isalpha(first)))
+	{
 		for (p = ret; *p; p++)
 			*p = pg_tolower((unsigned char) *p);
+	}
 	else
+	{
 		for (p = ret; *p; p++)
 			*p = pg_toupper((unsigned char) *p);
+	}
 
 	return ret;
 }
diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c
index a713f806d31291c65cc3e3911a3f3b35b12ba2c5..7ad69ee59a8aa7596c5ccff7651992f1b5ac03d4 100644
--- a/src/bin/psql/variables.c
+++ b/src/bin/psql/variables.c
@@ -79,11 +79,16 @@ GetVariable(VariableSpace space, const char *name)
 }
 
 /*
- * Try to interpret value as boolean value.  Valid values are: true,
- * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
+ * Try to interpret "value" as boolean value.
+ *
+ * Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
+ * prefixes thereof.
+ *
+ * "name" is the name of the variable we're assigning to, to use in error
+ * report if any.  Pass name == NULL to suppress the error report.
  */
 bool
-ParseVariableBool(const char *value)
+ParseVariableBool(const char *value, const char *name)
 {
 	size_t		len;
 
@@ -112,7 +117,9 @@ ParseVariableBool(const char *value)
 	else
 	{
 		/* NULL is treated as false, so a non-matching value is 'true' */
-		psql_error("unrecognized Boolean value; assuming \"on\"\n");
+		if (name)
+			psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
+					   value, name, "on");
 		return true;
 	}
 	/* suppress compiler warning */
diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h
index fa253d24a2c9977a7b7aa8d93ccf5f05493fbcae..bd9cff030ea36f0b51ab14e514e1c94dc465b238 100644
--- a/src/bin/psql/variables.h
+++ b/src/bin/psql/variables.h
@@ -35,7 +35,7 @@ typedef struct _variable *VariableSpace;
 VariableSpace CreateVariableSpace(void);
 const char *GetVariable(VariableSpace space, const char *name);
 
-bool		ParseVariableBool(const char *val);
+bool		ParseVariableBool(const char *value, const char *name);
 int ParseVariableNum(const char *val,
 				 int defaultval,
 				 int faultval,