diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index d5e47b56e7..491246fea6 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2656,6 +2656,22 @@ bar
+
+ COMP_KEYWORD_CASE
+
+
+ Determines which letter case to use when completing an SQL key word.
+ If set to lower or upper, the
+ completed word will be in lower or upper case, respectively. If set
+ to preserve-lower
+ or preserve-upper (the default), the completed word
+ will be in the case of the word already entered, but words being
+ completed without anything entered will be in lower or upper case,
+ respectively.
+
+
+
+
DBNAME
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 23b7a907f7..a50e7356f1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -697,7 +697,7 @@ static char **complete_from_variables(char *text,
const char *prefix, const char *suffix);
static char *complete_from_files(const char *text, int state);
-static char *pg_strdup_same_case(const char *s, const char *ref);
+static char *pg_strdup_keyword_case(const char *s, const char *ref);
static PGresult *exec_query(const char *query);
static void get_previous_words(int point, char **previous_words, int nwords);
@@ -3125,7 +3125,7 @@ create_or_drop_command_generator(const char *text, int state, bits32 excluded)
{
if ((pg_strncasecmp(name, text, string_length) == 0) &&
!(words_after_create[list_index - 1].flags & excluded))
- return pg_strdup_same_case(name, text);
+ return pg_strdup_keyword_case(name, text);
}
/* if nothing matches, return NULL */
return NULL;
@@ -3412,9 +3412,9 @@ complete_from_list(const char *text, int state)
if (completion_case_sensitive)
return pg_strdup(item);
else
- /* If case insensitive matching was requested initially, return
- * it in the case of what was already entered. */
- return pg_strdup_same_case(item, text);
+ /* If case insensitive matching was requested initially, adjust
+ * the case according to setting. */
+ return pg_strdup_keyword_case(item, text);
}
}
@@ -3451,9 +3451,9 @@ complete_from_const(const char *text, int state)
if (completion_case_sensitive)
return pg_strdup(completion_charp);
else
- /* If case insensitive matching was requested initially, return it
- * in the case of what was already entered. */
- return pg_strdup_same_case(completion_charp, text);
+ /* If case insensitive matching was requested initially, adjust the
+ * case according to setting. */
+ return pg_strdup_keyword_case(completion_charp, text);
}
else
return NULL;
@@ -3561,27 +3561,48 @@ complete_from_files(const char *text, int state)
/*
- * Make a pg_strdup copy of s and convert it to the same case as ref.
+ * 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.
*/
static char *
-pg_strdup_same_case(const char *s, const char *ref)
+pg_strdup_keyword_case(const char *s, const char *ref)
{
char *ret, *p;
unsigned char first = ref[0];
+ int tocase;
+ const char *varval;
- if (isalpha(first))
- {
- ret = pg_strdup(s);
- if (islower(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;
- }
+ 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
- return pg_strdup(s);
+ 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))
+ )
+ 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;
}